November 2007 Archives

We are currently building a handful of entity services using WCF. Each is built around a collection of stored procedures that receive and return XML. The other day I got to thinking: Why go through the trouble of (de)serializing XML into objects? Why not read the SOAP body directly, use XSLT to convert it into the expected form, pass it to the stored procedures, and transform the results back into a SOAP message? This would surely increase performance by avoiding unnecessary (de)serialization. In our case, the performance hit was worth the cost because the services were doing more than simply transforming XML; however, I was still curious: How would one go about performing such a transformation at the SOAP-message-level in WCF?

There is a reference on MSDN that provides some good background. After reading it, I learned that any service wanting to work at this level had to specify that its operations receive and (if applicable) return Message objects. With one of these in hand, an XmlReader can be instantiated by calling its GetReaderAtBodyContents method. In turn, this can be passed to the XSLT processor. If a return value is required, XML can be written to the resulting SOAP message using the Message class's static CreateMessage method. Here is a short example:

In this sample, the service gets an XmlReader over the incoming SOAP message's body, instantiates an XmlWriter that will write to a given buffer, transforms the XML of the reader putting the results in the writer, and creates a SOAP response message with the contents of the buffer placed in its body.

The astute reader may have notices something odd about that snippet. It instantiates a BodyWriter object which is abstract. That true, but the one created in the code above isn't the one in System.ServiceModel.Channels; it's one of my own creation. To see all of the details, check out the whole sample.

After performing this little experiment, my curiosity was temporarily satified; however, I still had a couple of questions about all this:

The XSLT stylesheet can't be written a priori unless the shape of the input XML is well known. The XML returned by our stored procedures conforms to an XML Schema, so the shape of that input is consistent and predictable. What about the body of the SOAP message, however? To insure that it too conforms to a known shape, ideally, it would be contractually obligated to conform to a certain XML Schema. How though does one go about creating message contracts starting with an XML Schema? I know how to do this with data contracts but not message contracts.

What if the operation contracts of a service expect explicit arguments rather than Message objects and the interface is already in use? Is there any way to create a new service type that implements the contract and is able to work at the message-level to perform the types of transformations described above?

Can the simple example be implemented without the little nested BodyWriter class? It isn't providing much besides bloat.

How could the sample be written using a streaming model rather than a buffered approach?

Michele Leroux Bustamante suggested three approaches for defining faults in WCF at Dev Connections this week:

Expose the CLR exceptions directly by declaring them as fault contracts in the service’s interface definition.

Define a data contract for each type of fault that can occur and declare when each may be raised by associating them with the operations that may throw them.

Create a couple of data contracts, ReceiverFault and SenderFault, with members that indicate the actual error condition and associate them with every operation.

When we first started using WCF, we used approach one.This was simple and allowed us to cope with exceptions the same way that we always had in .NET; however, it wasn’t long before we ran into problems.Usually, authors, speakers, and bloggers discourage this approach because it can expose sensitive or technology-specific information beyond a service’s boundary, a practice that makes a system non-service-oriented.This is reason enough, but I’ll provide another in case you still aren’t persuaded.

Our Visual Studio solutions were all laid out the same way: we had a project for contracts, data access, service type (i.e., service implementation), a host, and our unit tests.The latter includes a reference to the data access project and the host project, so that we could write tests that exercised the data access library and the service.When adding the service reference in the test project, we didn’t want to reuse the types in referenced assemblies because this would cause our test harness to use different data types than those used by our eventual clients.For this reason, in the Service Reference Settings dialog box of Visual Studio 2008 beta 2, we unchecked “Reuse types in referenced assemblies” (as seen below).

After doing so, updating the service reference generated code that did not include the proxy class!After a lot of head scratching, we noticed that the output window had some warnings that looked rather alarming:

Custom tool warning: Cannot import wsdl:portTypeDetail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporterError: ISerializable type with data contract name 'MyException' in namespace 'http://schemas.datacontract.org/2004/07/MyCompany.MyProject.Common' cannot be imported. The data contract namespace cannot be customized for ISerializable types and the generated namespace 'MyCompany.MyProject.MyService' does not match the required CLR namespace 'MyCompany.MyProject.Common'. Check if the required namespace has been mapped to a different data contract namespace and consider mapping it explicitly using the namespaces collection.

What on Earth does that mean and why wasn’t it an error?After some digging, we found that we had to edit the SVCMAP file of the client (seen under the service reference when all files in the Solution Explorer are visible) to map the XML namespace to the CLR namespace as the error message describes.We defined this mapping like this:

After updating the SVCMAP file as above, the proxy class was always generated. What on Earth?Why?I’m afraid I don’t know.But, I do know that every client had to know to make this change and then had to do it before they could use our exceptions that were being defined directly as fault contracts.

Because of this, we had two other choices: Bustamante’s second or third fault handling approaches.The second one would inevitably be a lot of code.Each exceptional condition that could be raised by a downstream library or service would need a fault contract.Each would have a description, error code, etc.Considering that we already had a such a hierarchy deriving from System.Exception that was used to map error codes raised in our stored procedures (triggered by calls to RAISERROR), we didn’t want a second hierarchy for faults.Because of this, we adopted Bustamante’s third approach. Rather than explain the specifics of this approach, I suggest you go straight to the source. Watch Bustamante’s Web cast on fault handling.Specifically, check out the material around time codes 24:00 and 49:00.

When building services, it’s important to remember that these are tenets, not laws. This is important because service-oriented systems are built differently depending on their size. Michele Leroux Bustamante underscored this distinction earlier this week at Dev Connections by using the terminology “big SOA” and “little SOA”. When building little SOAs, it is difficult and some times impractical to adhere to the above guidelines. For example, in the figure below, service one and two are crossing the boundaries by accessing the same data store. While this may clash with the first SOA tenant, that’s OK.

In the case of little SOA, dogmatic conformance to the tenets could incur performance, maintenance, and other such cost that will inhibit its success. Big SOA is the orchestration and composition of multiple little SOAs. If unquestioned devotion to the tenets of SOA impede the construction of the little SOA building blocks, there can be no enterprise-level service-based architecture.

The construction of these different sized services vary in other ways as well. For example, a big SOA is often built from many smaller, entity services. An entity service is one that provides basic CRUD for database entities. These services often provide dozens of different operations to perform this type of basic functionality; however, they aren’t limit to simple CRUD functionality and commonly include other operations that are still relatively low-level. For example, one may create an asset service which exposes basic asset-related CRUD functionality in addition to other capabilities such as grouping and relating assets. Regardless, all of these operations end up altering database records.

When building this type of service compared to one that makes up part of a big SOA, it is impractical and unnecessary to adopt the recommended practice of explicitly defining the message contracts of the entity services. If this guideline is adapted, the entity service will require two message contracts for each operation, resulting in an overwhelming number. Furthermore, these messages of such services often contain nothing more than a data contract, resulting in a senseless wrapper. Though Juval Löwy advices in his book Programming WCF Services that service contracts should expose only a handful of operations (with a suggested upper limit of 20), little SOA building blocks like entity services usually can’t follow this recommendation.

On the other hand, when building the services of a big SOA that cross organizational boundaries, small, specialized, highly-interoperable services are paramount. To achieve this, the definition of message contracts is a must. Unlike little entity services, however, these wide-reaching services often expose very few operations and only a handful of message contracts. This is very manageable and merits the cost of defining, versioning, and maintaining the extra message-level contracts.

The point is this: architects can’t be unquestionably apply the tenets of SOA in a back-or-which fashion. We have to open-mindedly question and think critically about our problems and the established service-oriented guidelines for building loosely coupled architectures.