April 2005 - Posts

Using Type Definition Assemblies to ensure efficient SOAs

A common argument against SOAs is that using web services has an unacceptable performance overhead. Even though studies show that ASP.NET web services have decent performance compared to remoting, performance worries are often justifiable when the architecture dictates that everything should be a service. Performance wise, web services don’t stand a chance compared to tightly coupled, direct method invocations.
Many developers chose performance over flexibility when they’re facing performance trade offs.
To ensure efficient service oriented architectures, common functionality must be accessible through multiple interfaces. The first step towards achieving this is to delegate the actual implementation of a service to a business component instead of having the business logic in the web service itself. This enables you to make tight calls to your business components within the service boundary while still thinking in terms of services. However, in doing this you introduce a new aspect into your architecture. When service invocations don’t necessarily come through a web service endpoint, how does one keep a mutual understanding of type definitions between the different businesses objects?
For example; how can you ensure that all business objects have a common understanding of what an order is? Web services and their clients use the XSD schema information embedded within the WSDL contract to establish this understanding, but the WSDL doesn’t apply to direct method calls nor would it be efficient to use XSD schemas to share type information between middle tier business objects. To enforce type fidelity across multiple business objects in multiple application domains you can create a type definition assembly with classes defining the various types used by business objects and web services alike. This assembly can be referenced in all business objects which use the custom types. This type definition assembly can even be used by clients to provide a richer programming experience than then shallow types WSDL.exe generates. Attentive readers might have concerns about the web services being too tightly coupled when the same assembly is referenced both in the service and in the client. If you share this concern, you still have the option to use WSDL.exe to generate client types representing the various types defined in the XSD schema.

Separate type definitions, used in conjunction with service agents and façades, make it easy to make it transparent whether you’re using a web service or a business object directly. For more information on type definition assemblies and SOAs read Jeffrey Hasan’s excellent book Expert Service-Oriented Architectures.

Edit and Continue in ASP.NET 1.x

Steve Cellius, CTO at BV Networks, showed off his developer's toolbox on the April Norwegian .NET User Group (NNUG) meeting. The slide deck is available for download here (registration required).
Steve's session wasn't all about tools, he also did gave some tips on how to use Visual Studio .NET more efficiently. One of the things he demoed was how to attach to the ASP.NET worker process to debug web applications instead of starting the application in debug mode.
This tip reminded me of a "secret feature" in Visual Studio .NET which most developers aren't aware of: edit and continue for ASP.NET applications.
ASP.NET monitors the application for changes in files and does necessary updates if a change occurs. You can exploit this feature to make ASP.NET compile your code files whenever a change is detected in them. If you add an ASPX page the virtual folder and make its src attribute point to a source file, this file will be compiled when ASP.NET detects that it has changed. Only one src attribute is allowed per page, so you might have to add some dummy pages to your project if you have business entities or other classes in your web project.
If you use src attributes instead of precompiled code behind classes, you can edit and continue when you stumble across an error while debugging your application. To do this, just choose "Detach All" from the "Debug" menu. Then edit your code and save the file. ASP.NET will detect that the file changed and recompile it. To continue debugging select "Processes" from the "Debug" menu and check the "Show system processes" and select the ASP.NET worker process from the list and attach to the process. On Windows XP this process is named ASPNET_WP.exe, on Windows Server 2003 it is named W3WP.exe. When you refresh the browser, you'll hit the breakpoint with you new bug free code running.
If using src attributes to trick ASP.NET into compiling your code isn't really your cup of tea, you can still enjoy the benefits of edit and continue, but you'll have to build the solution yourself before you continue.

Everyone knows that Visual Studio .NET 2005 has proper edit and continue support. Still it many developers will be stuck working on ASP.NET 1.x projects for some time. If you are one of those developers, the poor mans edit and continue feature will probably make waiting for ASP.NET 2.0 projects easier. At least you'll save some time not having to start and stop the debugger all the time.

Solving web service interop problems

This week I solved a problem concerning web service interoperability posted on the Norwegian .NET User Group (NNUG) forum. The solution shows how to tackle incompatible SOAP envelopes, so I reckoned I post the problem and solution to my blog as well, since it is of general interest.

The Problem
A developer had been assigned a task which seemed trivial. He was to retrieve business information thru a web service provided by a thrid party. The service had a simple contract with a single operation accepting a business information message as a parameter. When the operation was invoked, the proxy always returned empty value objects. The developer had used Fiddler to assert that an actual SOAP message was returned from the service. The response message looked like this:

    1
 <?xml version="1.0" encoding="utf-8"?>
    2 <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    3     <SOAP-ENV:Header />
    4     <SOAP-ENV:Body>
    5         <ForetakSokResponse xmlns="http://dbonline.no/test-webservices/xsd/ForetakInfo">
    6             <ForetakData>
    7                 <Dunsnr>820584895</Dunsnr>
    8                 <Orgnr>974217368</Orgnr>
    9                 <KodeType></KodeType>
   10                 <KodeTekst>Aktivt</KodeTekst>
   11                 <Navn>MARKED BEDRIFT</Navn>
   12                 <Adresse>RING 43</Adresse>
   13                 <Postnr>0245</Postnr>
   14                 <Poststed>OSLO</Poststed>
   15             </ForetakData>
   16             <Meldinger>
   17                 <MeldingsKode>999</MeldingsKode>
   18                 <MeldingsTekst>Kommando ferdig behandlet.</MeldingsTekst>
   19             </Meldinger>
   20         </ForetakSokResponse>
   21     </SOAP-ENV:Body>
   22 </SOAP-ENV:Envelope>

At first glance, this is a perfectly valid SOAP envelope containing a response message consisting of two complex types; “ForetakData” and “Meldinger”. However whenever the service was invoked, the developer always ended up with an instance of the ForetakSokResponse class with empty arrays for ForetakData and Meldinger.

The Solution
The web service was developed using Apache Axis. This framework serializes complex types in a slightly different way than the .NET XmlSerializer. The message about must declare the XML schema instance namespace in order to be deserialiezable in .NET  framework. The message below shows how the message must be altered to be XmlSerializer compliant:

    1
 <?xml version="1.0" encoding="utf-8"?>
    2 <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    3     <SOAP-ENV:Header />
    4     <SOAP-ENV:Body>
    5         <ForetakSokResponse xmlns="http://dbonline.no/test-webservices/xsd/ForetakInfo">
    6             <ForetakData xmlns="">
    7                 <Dunsnr>820584895</Dunsnr>
    8                 <Orgnr>974217368</Orgnr>
    9                 <KodeType></KodeType>
   10                 <KodeTekst>Aktivt</KodeTekst>
   11                 <Navn>MARKED BEDRIFT</Navn>
   12                 <Adresse>RING 43</Adresse>
   13                 <Postnr>0245</Postnr>
   14                 <Poststed>OSLO</Poststed>
   15             </ForetakData>
   16             <Meldinger xmlns="">
   17                 <MeldingsKode>999</MeldingsKode>
   18                 <MeldingsTekst>Kommando ferdig behandlet.</MeldingsTekst>
   19             </Meldinger>
   20         </ForetakSokResponse>
   21     </SOAP-ENV:Body>
   22 </SOAP-ENV:Envelope>

Notice the xmns:xsi attribute on the SOAP-ENV:Envelope element and the xmlns attributes on the ForetakData and the Meldinger elements. These are the only differences from the original message. And this message deserializes without a problem.

Detecting differences in a seemingly correct SOAP envelope and the envelope the proxy expects requires profound knowledge of WSDL and SOAP implementations, and is generally difficult. I use Think Tectures excellent Web Service Contract First (WSCF) tool to create an ASP.NET mock implementation of the service and compare the response messages from this service to the response messages from the web service I’m having interop problems with. This turns the painstaking task of spotting the differences into a breeze.

Changing the original services was not an option, so this problem had to be solved in the client. Luckily; the .NET SOAP client implementation has been designed with extensibility in mind. You can extend the request and response processing pipes in any way you like through SoapExtensions. A SoapExtension enables you to do custom processing on the request or response at four different stages; before and after serialization and before and after deserialization.

With our challenge we need to alter the SOAP envelope before it is deserialized by the client. In other words we need to hook our SoapExtension in at the BeforeDeserialize stage.

Below is the complete C# source code for a SoapExtension that makes the SOAP envelope .NET compliant. The attributes needed by the XmlSerialier to deserialize the message are injected in the InjectNamespaceDeclarations method at the end of the message.

    1 using System;
    2 using System.IO;
    3 using System.Web.Services.Protocols;
    4 
    5 namespace DBClient
    6 {
    7     public class NamespaceInjectorExtension : SoapExtension
    8     {
    9         private Stream oldStream;
   10         private Stream newStream;
   11         public override Stream ChainStream(Stream stream)
   12         {
   13             oldStream = stream;
   14             newStream = new MemoryStream();
   15             return newStream;
   16         }
   17         public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
   18         {
   19             return attribute;
   20         }
   21         public override object GetInitializer(Type serviceType)
   22         {
   23             return typeof(NamespaceInjectorExtension);
   24         }
   25         public override void Initialize(object initializer)
   26         {
   27             return;
   28         }
   29         public override void ProcessMessage(SoapMessage message)
   30         {
   31             switch (message.Stage) 
   32             {
   33                 case SoapMessageStage.BeforeDeserialize:
   34                     InjectNamespaceDeclarations(message);
   35                     break;
   36                 case SoapMessageStage.BeforeSerialize:
   37                 case SoapMessageStage.AfterSerialize:
   38                 case SoapMessageStage.AfterDeserialize:
   39                     break;
   40                 default:
   41                     throw new ArgumentException(string.Format("Invalid Stage: {0}",message.Stage.ToString()));
   42             }
   43         }
   44         private void InjectNamespaceDeclarations(SoapMessage message)
   45         {
   46             TextReader reader = new StreamReader(oldStream);
   47             string xml=reader.ReadToEnd();
   48 
   49             int nsDeclPos=xml.IndexOf("xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"")+"xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\"".Length;
   50             xml=xml.Insert(nsDeclPos," xmlns:xsi=\http://www.w3.org/2001/XMLSchema-instance\");
   51             int foretakDataPos=xml.IndexOf("<ForetakData>")+"<ForetakData".Length;
   52             xml=xml.Insert(foretakDataPos," xmlns=\"\"");
   53             int meldingerPos=xml.IndexOf("<Meldinger>")+"<Meldinger".Length;
   54             xml=xml.Insert(meldingerPos," xmlns=\"\"");
   55 
   56             StreamWriter writer=new StreamWriter(newStream);
   57             writer.Write(xml);
   58             writer.Flush();
   59             newStream.Position=0;
   60         }
   61     }
   62 }

To attach the SoapExtension