Pages

Tuesday, May 31, 2005

Implementing Service Interface in .NET

Context

Your application is deployed on the Microsoft Windows operating system. You have decided to expose a piece of your application's functionality as an ASP.NET Web Service. Interoperability is a key issue so you cannot use complex data types that are present only in the Microsoft .NET Framework.

Background

When you insert an audio compact disc (CD) into your computer often the program that you use to play the CD informs you of various pieces of information regarding the recording. This information might include track information, cover art, reviews, and so on. To demonstrate an implementation of the Service Interface pattern, this is implemented as an ASP.NET Web service.

Implementation Strategy

Service Interface describes a separation of interface mechanics and application logic. The interface is responsible for implementing and enforcing the contract for a service that is being exposed and the application logic is responsible for the business functionality that the interface uses in a particular way. This example uses an ASP.NET Web service to implement the service interface.

Note: The application logic that is shown here is an example of the Table Data Gateway pattern. In a typical application, there would be some additional business functionality that the implementation would provide. To focus on Service Interface, such additional business functionality is omitted from this example.





Service Interface Implementation






An ASP.NET Web Service is used to implement Service Interface. Implementing this as a Web Service makes this piece of functionality accessible to any number of disparate systems using Internet standards, such as XML, SOAP, and HTTP. Web services depend heavily upon the acceptance of XML and other Internet standards to create an infrastructure that supports application interoperability.




Because the focus is on interoperability between the consumer and the provider you cannot rely on complex types that may or may not be present on different platforms. This leads you to define a contract that provides interoperability. The approach described below involves defining a data transfer object using an XML schema, generating the data transfer object using platform specific tools and then relying on the platform to implement the service interface code that uses the data transfer object. This is not the only approach that will work. The .NET Framework generates all the pieces of functionality for you. However, there are cases in which it generates service interfaces that are not easily interoperable. On the other hand, you could specify the interface using Web Services Description Language (WSDL) and XML schema and then use the wsdl.exe utility to generate service interfaces for your application..




Contract






As described in Service Interface a contract exists which allows providers of a service and consumers to interoperate. There are three aspects to this contract when implementing it as an ASP.NET Web service:



  • Specify XML schema. The definition of the data that is transferred between the consumer and the provider is specified using an XML schema. The input to the service is a simple variable of the type long; therefore a schema is not needed for this scenario because simple types are built into the SOAP specification. However, the return type of the Web service is not a simple type, so the type must be specified using an XML schema. In this example, the schema is contained in the Recording.xsd file.


  • Data transfer object. The .NET framework has a tool called xsd.exe which, given an XML schema, can generate a data transfer object to be used by the code that implements the Web service. In this example, the name of the data transfer object is Recording and it is contained in the Recording.cs file.


  • Service Interface implementation. A class that inherits from System.Web.Services.WebService and specifies at least one method that is marked with the [WebMethod] attribute. In this example, the class is called RecordingCatalog and it is contained in the RecordingCatalog.asmx.cs file. This class is responsible for making the call to the service implementation and also for translating the output of the service implementation into the format that the Web service will use. The functionality to translate the data is encapsulated in a class called RecordingAssembler and contained in the RecordingAssembler.cs file. This class is an example of an assembler, which is a variant of the Mapper pattern. [Fowler03]



  • The following diagram depicts the relationship of the classes that implement the service interface.




    Figure 1: Service Interface class diagram



    Recording.xsd






    The definition of the information that will be transferred to the client is specified using an XML schema. The following schema defines two complex types; Recording and Track.





    <?xml version="1.0" encoding="utf-8" ?>
    <xs:schema xmlns:tns="http://msdn.microsoft.com/practices" elementFormDefault="qualified"
    targetNamespace="http://msdn.microsoft.com/patterns" xmlns:
    xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="Recording" type="tns:Recording" />
    <xs:complexType name="Recording">
    <xs:sequence>
    <xs:element minOccurs="1" maxOccurs="1" name="id" type="xs:long" />
    <xs:element minOccurs="1" maxOccurs="1" name="title" type="xs:string" />
    <xs:element minOccurs="1" maxOccurs="1" name="artist" type="xs:string" />
    <xs:element minOccurs="0" maxOccurs="unbounded" name="Track" type="tns:Track" />
    </xs:sequence>
    </xs:complexType>
    <xs:complexType name="Track">
    <xs:sequence>
    <xs:element minOccurs="1" maxOccurs="1" name="id" type="xs:long" />
    <xs:element minOccurs="1" maxOccurs="1" name="title" type="xs:string" />
    <xs:element minOccurs="1" maxOccurs="1" name="duration" type="xs:string" />
    </xs:sequence>
    </xs:complexType>
    </xs:schema>

    The Recording type has an ID, artist, title, and an unbounded number of Track types. A Track type also has ID, title, and duration elements.


    Recording.cs






    As mentioned earlier, the .NET Framework has a xsd.exe command-line tool, which takes as input an XML schema and outputs a class that can be used in your program. The generated class is used as the return value of the Web service. The command that was used to generate the Recording.cs class is as follows:





    xsd /classes Recording.xsd

    The output that was produced by running this command is shown below:
    //------------------------------------------------------------------------------
    // <autogenerated>
    // This code was generated by a tool.
    // Runtime Version: 1.0.3705.288
    //
    // Changes to this file may cause incorrect behavior and will be lost if
    // the code is regenerated.
    // </autogenerated>
    //------------------------------------------------------------------------------

    //
    // This source code was auto-generated by xsd, Version=1.0.3705.288.
    //
    using System.Xml.Serialization;


    /// <remarks/>
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://msdn.microsoft.com/practices")]
    [System.Xml.Serialization.XmlRootAttribute(Namespace=http://msdn.microsoft.com/practices,
    IsNullable=false)]
    public class Recording {

    /// <remarks/>
    public long id;

    /// <remarks/>
    public string title;

    /// <remarks/>
    public string artist;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Track")]
    public Track[] Track;
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlTypeAttribute(Namespace="http://msdn.microsoft.com/practices")]
    public class Track {

    /// <remarks/>
    public long id;

    /// <remarks/>
    public string title;

    /// <remarks/>
    public string duration;
    }



    RecordingCatalog.asmx.cs






    After the types are defined, you need to implement the actual Web service implementation. This class encapsulates all of the Service Interface behavior. The service that is being exposed is defined explicitly by using the [WebMethod] attribute.





    [WebMethod]
    public Recording Get(long id)
    { /* */ }

    The Get method takes as input an id and returns a Recording object. As described in the XML schema a Recording may also include a number of Track objects. The following is the implementation.



    using System.ComponentModel;
    using System.Data;
    using System.Web.Services;

    namespace ServiceInterface
    {
    [WebService(Namespace="http://msdn.microsoft.com/practices")]
    public class RecordingCatalog : System.Web.Services.WebService
    {
    private RecordingGateway gateway;

    public RecordingCatalog()
    {
    gateway = new RecordingGateway();
    InitializeComponent();
    }

    #region Component Designer generated code
    //
    #endregion

    [WebMethod]
    public Recording Get(long id)
    {
    DataSet ds = RecordingGateway.GetRecording(id);
    return RecordingAssembler.Assemble(ds);
    }
    }
    }

    The Get method makes a call to the RecordingGateway to retrieve a DataSet. It then makes a call to the RecordingAssembler.Assemble method to translate the DataSet into the generated Recording and Track objects.


    RecordingAssembler.cs






    The reason this class is part of the service interface is because of the need to translate the output of the application logic into the objects that are being sent out over the Web service. The RecordingAssembler class is responsible for translating the return type of the service implementation, in this case an ADO.NET DataSet, into the Recording and Track types that were generated in a previous step.





    using System;
    using System.Collections;
    using System.Data;

    public class RecordingAssembler
    {
    public static Recording Assemble(DataSet ds)
    {
    DataTable recordingTable = ds.Tables["recording"];
    if(recordingTable.Rows.Count == 0) return null;

    DataRow row = recordingTable.Rows[0];

    Recording recording = new Recording();
    recording.id = (long)row["id"];
    string artist = (string)row["artist"];
    recording.artist = artist.Trim();
    string title = (string)row["title"];
    recording.title = title.Trim();

    ArrayList tracks = new ArrayList();
    DataTable trackTable = ds.Tables["track"];
    foreach(DataRow trackRow in trackTable.Rows)
    {
    Track track = new Track();
    track.id = (long)trackRow["id"];
    string trackTitle = (string)trackRow["title"];
    track.title = trackTitle.Trim();
    string duration = (string)trackRow["duration"];
    track.duration = duration.Trim();
    tracks.Add(track);
    }
    recording.Track = (Track[])tracks.ToArray(typeof(Track));

    return recording;
    }
    }

    Assembler classes in general are somewhat ugly. Their job is to translate from one representation to another so they are usually straightforward but always depend on both representations. These dependencies make them susceptible to changes from both representations.


    Although assemblers are useful, you may not always want to create one yourself if there are readily available alternatives that meet your needs. As an alternative in this case, you could use XML serialization to create an instance of an XMLDataDocument, associate it with the DataSet and return the XML instead. For details on this approach, see the "DataSets, Web Services, DiffGrams, Arrays, and Interoperability" article on MSDN: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnservice/html/service02112003.asp?frame=true.


    Application Logic






    The application logic in this example is probably too simple for most enterprise applications. The reasoning for this that the pattern focuses on the Service Interface so the implementation portion is shown more for completeness instead of being a representative example. This implementation uses a Table Data Gateway to retrieve data from a database. The Table Data Gateway class, called RecordingGateway, retrieves the recording record and the track records associated with the recording. The result is returned in a single DataSet. For a detailed discussion of the database schema used and of DataSet, see Implementing Data Transfer Object in .NET with a DataSet.




    RecordingGateway.cs






    This class fills a DataSet with two results sets: recording and track. The client passes in the ID of the recording record that is desired. The class performs two queries against the database to fill the DataSet. The last thing it does is to define the relationship between the recording and its track records.





    using System;
    using System.Collections;
    using System.Data;
    using System.Data.SqlClient;

    public class RecordingGateway
    {
    public static DataSet GetRecording(long id)
    {
    String selectCmd =
    String.Format(
    "select * from recording where id = {0}",
    id);

    SqlConnection myConnection =
    new SqlConnection(
    "server=(local);database=recordings;Trusted_Connection=yes");
    SqlDataAdapter myCommand =
    new SqlDataAdapter(selectCmd, myConnection);

    DataSet ds = new DataSet();
    myCommand.Fill(ds, "recording");

    String trackSelect =
    String.Format(
    "select * from Track where recordingId = {0} order by Id",
    id);

    SqlDataAdapter trackCommand =
    new SqlDataAdapter(trackSelect, myConnection);
    trackCommand.Fill(ds, "track");

    ds.Relations.Add("RecordingTracks",
    ds.Tables["recording"].Columns["id"],
    ds.Tables["track"].Columns["recordingId"]);

    return ds;
    }
    }




    Note: The example shown here is not meant to describe the only way to fill a DataSet. There are many ways to retrieve this data from the database. For example, you could use a stored procedure.





    Tests



    The unit tests focus on testing the internal aspects of the implementation. One unit test tests the retrieval of information from the database (RecordingGatewayFixture) and the other tests the conversion of a DataSet into Recording and Track objects (RecordingAssemblerFixture).



    RecordingGatewayFixture






    The RecordingGatewayFixture class tests the output of the RecordingGateway, which is a DataSet. This verifies that, given an ID, a proper DataSet is retrieved from the database with both recording and track information.





    using NUnit.Framework;
    using System.Data;

    [TestFixture]
    public class RecordingGatewayFixture
    {
    private DataSet ds;
    private DataTable recordingTable;
    private DataRelation relationship;
    private DataRow[] trackRows;

    [SetUp]
    public void Init()
    {
    ds = RecordingGateway.GetRecording(1234);
    recordingTable = ds.Tables["recording"];
    relationship = recordingTable.ChildRelations[0];
    trackRows = recordingTable.Rows[0].GetChildRows(relationship);
    }

    [Test]
    public void RecordingCount()
    {
    Assertion.AssertEquals(1, recordingTable.Rows.Count);
    }

    [Test]
    public void RecordingTitle()
    {
    DataRow recording = recordingTable.Rows[0];
    string title = (string)recording["title"];
    Assertion.AssertEquals("Up", title.Trim());
    }


    [Test]
    public void RecordingTrackRelationship()
    {
    Assertion.AssertEquals(10, trackRows.Length);
    }

    [Test]
    public void TrackContent()
    {
    DataRow track = trackRows[0];

    string title = (string)track["title"];
    Assertion.AssertEquals("Darkness", title.Trim());
    }

    [Test]
    public void InvalidRecording()
    {
    DataSet ds = RecordingGateway.GetRecording(-1);
    Assertion.AssertEquals(0, ds.Tables["recording"].Rows.Count);
    Assertion.AssertEquals(0, ds.Tables["track"].Rows.Count);
    }
    }



    RecordingAssemblerFixture






    The second fixture tests the RecordingAssembler class by testing the conversion of a DataSet into Recording and Track objects:





    using NUnit.Framework;
    using System.Data;
    using System.IO;
    using System.Xml;

    [TestFixture]
    public class RecordingAssemblerFixture
    {
    private static readonly long testId = 1234;
    private Recording recording;

    [SetUp]
    public void Init()
    {
    DataSet ds = RecordingGateway.GetRecording(1234);
    recording = RecordingAssembler.Assemble(ds);
    }

    [Test]
    public void Id()
    {
    Assertion.AssertEquals(testId, recording.id);
    }

    [Test]
    public void Title()
    {
    Assertion.AssertEquals("Up", recording.title);
    }

    [Test]
    public void Artist()
    {
    Assertion.AssertEquals("Peter Gabriel", recording.artist);
    }


    [Test]
    public void TrackCount()
    {
    Assertion.AssertEquals(10, recording.Track.Length);
    }

    [Test]
    public void TrackTitle()
    {
    Track track = recording.Track[0];
    Assertion.AssertEquals("Darkness", track.title);
    }

    [Test]
    public void TrackDuration()
    {
    Track track = recording.Track[0];
    Assertion.AssertEquals("6:51", track.duration);
    }

    [Test]
    public void InvalidRecording()
    {
    DataSet ds = RecordingGateway.GetRecording(-1);
    Recording recording = RecordingAssembler.Assemble(ds);
    Assertion.AssertNull(recording);
    }
    }

    After running these tests you have confidence that the retrieval of information from the database works correctly and you can translate the database output into the data transfer objects. However, the tests do not address end-to-end functionality nor do they test all of the service interface code. The following example tests the full functionality. It is referred to as a functional or acceptance test since it verifies that the whole interface works as expected. The approach described below retrieves a DataSet from the RecordingGateway. It then makes a call using the web service to retrieve the exact same Recording. After it is received it simply compares the two results. If they are the equal then Service Interface works correctly.


    Note: Only a sample of possible acceptance tests are shown here. You should also note that there are also other ways to do this type of testing. This is just one way of performing the tests.


    AcceptanceTest.cs






    The following are some sample acceptance tests for the service interface:





    using System;
    using System.Data;
    using NUnit.Framework;
    using ServiceInterface.TestCatalog;

    [TestFixture]
    public class AcceptanceTest
    {
    private static readonly long id = 1234;
    private DataSet localData;
    private DataTable recordingTable;

    private RecordingCatalog catalog = new RecordingCatalog();
    private ServiceInterface.TestCatalog.Recording recording;

    [SetUp]
    public void Init()
    {
    // get the recording from the database
    localData = RecordingGateway.GetRecording(id);
    recordingTable = localData.Tables["recording"];

    // get the same recording from the web service
    recording = catalog.Get(id);
    }


    [Test]
    public void Title()
    {
    DataRow recordingRow = recordingTable.Rows[0];
    string title = (string)recordingRow["title"];
    Assertion.AssertEquals(title.Trim(), recording.title);
    }

    [Test]
    public void Artist()
    {
    DataRow recordingRow = recordingTable.Rows[0];
    string title = (string)recordingRow["artist"];
    Assertion.AssertEquals(title.Trim(), recording.artist);
    }

    // continued
    }



    Resulting Context



    The following are the benefits and liabilities related to using an ASP.NET Web service as an implementation of Service Interface:



    Benefits





  • Separation of concerns. The separation of the service interface and application logic is important because they are likely to vary independently. Implementing the interface portion as an ASP.NET Web service facilitates the separation.


  • Interoperability. Basing the interface on Internet standards, such as XML and SOAP, allow for different clients to access the Web service, no matter which operating system they are using.


  • ASP.NET Web services and Microsoft Visual Studio.NET. The environment makes working with Web services very straightforward. The xsd.exe tool demonstrated in this example provides a tool to translate an XML schema into a C# or Microsoft Visual Basic .NET class. To create the Web service, this example used a predefined template in the Microsoft Visual Studio .NET development system and generated the majority of the RecordingCatalog.asmx.cs file.



  • Liabilities





  • Data Transformation. In many cases, there must be a data transformation from the application logic representation to the representation that is being used by the service interface. This transformation is always problematic due to the dependencies introduced by having a class that depends on both representations. In this example, the RecordingAssembler class depends on the DataSet returned by the RecordingGateway as well as the generated Recording and Track classes.


  • Synchronization. Keeping the schema and the generated code both updated is not automatic. Therefore, any change to the schema requires that you rerun the xsd.exe tool to regenerate the Recording.cs class.



  • Related Patterns



  • Table Data Gateway [Fowler03]. The RecordingGateway shown here is an example of this pattern.


  • Mapper [Fowler03] The RecordingAssembler shown here is a variant of the Mapper pattern, which is often referred to as an assembler.


  • Implementing Data Transfer Object in .NET with a DataSet. This pattern describes the database schema that is used in this example.



  • Nine Solutions For The Problem Of .NET Serialization

    Introduction

    I recently ran into an article entitled Nine reasons not to use Serialization, and decided to check it out and see what this was all about.

    I found that the argument posted by the author were a little inaccurate. Not because there aren't any problems with .NET Serialization, but because the examples given seemed somewhat misguided. I recently received messages admonishing my initial attack on the author as rude and; being the good man that I am, decided to apologize and rewrite my article focusing entirely on the points. Before I begin though I would like to say something regarding serialization in general. I can only hope that the reader realizes that although the concept of serialization is addressed in .NET, it is not a unique facet of the technology. The pattern of persisting state is as ancient as boolean gate feedback, indeed it premised memory. As such, there are certain well known issues associated with serialization which do not refute the well known benefits identified by its pattern. So point for point here we go.


    Well, points 1, 2, 3 and 4 are essentially the same problem. Fortunately, it's one that's plaguing the entire software development and information technology community so there have been several steps taken towards addressing issues such as this. The chief solution is design. I'm not going to say that a day when software will provide all the answers for us will never come but I think we all know, author of the aforementioned article included, that that day is not today, nor is it tomorrow, nor ... well you get the idea. One simple solution to the problem is to use structural patterns, more specifically the proxy pattern. The indirection this affords you will more than likely compensate for the alleged lack of adaptability in .NET Serialization.


    Point 5 accuses XmlSerialization of not being secure. Well, on its face, I guess that's true. But if we passed user names and passwords un-encoded in GET requests between web pages, it wouldn't make ASP.NET insecure. Moreover, the public visibility problem the author highlighted in XML Serialization is by design. If you think about it, there really would not be much of a point to serializing to XML an entity that is only consumed by the system that creates it and then privatizing it. By default, it should be publicly visible or you have no business putting it in XML. That being said, there are countless imaginative solutions to the security issue addressed by the author. A simple approach would be to do the following:



    1. Don't write to a file, write to a StringWriter
    2. Convert the internal string to a byte array using the Encoding object
    3. New school way) Wrap the bytes array in a MemoryStream and encrypt the stream

      or


      Old school way) Loop through the byte array and XOR the values with something


    4. Send the encrypted stream to the WebService as a byte array. Obviously, if you go the old-school route, you'll just be sending the byte array.


    P.S. - Chances are the WebService recipient requires a token to validate the request. Use that to generate your keys.


    Point 6 is kinda not fair because it focuses on one form of serialization. I would simply say to this, write a more efficient serializer for your specific needs. SDK development is always geared towards common functionality across broad ranges. This usually hampers performance because of implementation logistics. What you're supposed to do when you run into situations that a framework cannot solve for you is, write an adapter. (See design patterns). You then judge the flexibility of a framework not on its shortcomings, but on the extent of plug-ability into the architecture you as an end-user of the SDK have. So if you're concerned with space so much so that neither XML nor Binary serialization work for you, write a HoffmanSerilzer that utilizes the compression algorithms of its namesake. Seriously, though another quick and easy solution to the problem would be using smart serialization. The concept is basic and has been applied numerous times in all forms of critical decision software (that macro pattern includes but is not limited to application servers, databases, web servers, etc). Here the premise, create a custom serialization standard (yikes! I said that but bear with me). Next create a toolkit that abstracts the native interaction mechanisms away from any application you might produce. As far as any application you might create is concerned, this toolkit is a black box. Next write your applications on top of this toolkit. Finally standardize, standardize, standardize.


    Points 7 & 8: If this sounds familiar, it's because it's standard industry practice. I could not have planned it better since this leads me right back to my response to point 6. You don't' really want to know what's under the hood of .NET, and in reality you shouldn't.


    As to points 8 & 9; the benchmark used by the esteemed author is really very not very meaningful. It does not in any way present a reasonable premise. We should always regard testing as a function of feasibility in a linear f(x) kind of way. The proposed solution is incorrect. Of course, the run-time overhead of deserializing 100,000 objects will be high, just as the network overhead of creating a 100,000 distinct connections to a database to query a 100,000 rows and return the results as a dataset will be. I doubt that there is any hardware out there that would not register a spike at that algorithm! It's kind of like a Big-0 issue. If you do not design a sort algorithm well, it won't work well, even if you don't notice it. Now I don't want to sound insulting. I am by no means Neo, but this is just the kind of thing that separates the CS majors from the 21 days majors. It's easy to see that both example patterns are absurdly inefficient. Even if you run it on a 64 bit processor with 100 gig of memory, the problem will still exist. I see this all the time in the industry. Computers and software are so powerful these days you can make obvious design and implementation mistakes and not even notice it. In the example for point 8, a flyweight pattern should be used. The inefficiency has little to do with .NET much less Serialization. Take a trip down memory lane with me to CS100 and you'll remember: "When inheritance breaks encapsulation, use delegation or composition". (And indeed, when does inheritance not break encapsulation?) The issues raised in point 9 are consistent with problems 'inherent' with inheritance. At least some part of the sub-class is defined in the parent class. You could re-employ delegation or designing the solution around composition. Restricting your object interactions to well designed interfaces will produce fewer implementation dependencies.

    Object-Oriented Tiered Application Design

    Introduction


    Most of the .NET code I see make heavy use of datasets, and a fairly simple client-server (2-tier) design. This is fine, because .NET has great support for using and displaying datasets on the screen. However, for more complex applications, the approach does not scale well in terms of maintainability. At some point, developers have to start coding business objects of some kind.


    The problem is that using datasets insulates the developer from the basic incompatibility (called the impedance mismatch) between objects and databases. When you start using business objects, then you are suddenly faced with a huge range of options and challenges that you never encountered when using datasets. The good news is that, once they start using objects, few developers go back. It is an exciting and productive environment, with great opportunities for code re-use.


    This article is an introduction to the subject, with a view to helping readers understand the ways in which you can design object-oriented applications, and some of the challenges that you may encounter. The focus is on small to medium-sized business applications that can benefit from an object-oriented design, but probably won't be needing Microsoft Enterprise Services any time soon.


    Apologies in Advance (to the Experts)


    There is very little standard terminology in this field. If I call a widget a wodget, and you are used to calling it a wadget, don't be upset with me! I try to define what I mean by every term I use, so there should be no confusion.


    A Multi-tiered Application


    You've probably (hopefully) heard of 3-tiered systems. The common tiers mentioned are presentation, business logic, and database. In practice, most object-oriented applications have more than 3 tiers -- they have a framework of interconnected components, typically found inside multiple DLLs, EXEs, and third party applications, generally categorized into layers/tiers. The components may all be located on a single computer, or they may be spread across multiple computers.


    From the developer's perspective, a primary idea behind a tiered system is to split logic into different pieces of code, so that one or the other can be changed, extended, or rewritten without affecting the rest. There are other advantages too, such as scalability. Performance is not an advantage of object-oriented applications, except where it is obtained through scalability.


    One of the driving forces used in the design and implementation of the tiers is that we should minimize duplication of logic. In a complex application, duplication of logic will lead to bugs. (This is true of a simpler system too, but in that case, it is easier to manage).


    Some possible tiers of a multi-tiered system are:



    • Database -- this consists of the DBMS (e.g. SQL Server), the table data and structure, and logic that is coded inside of the database. The most obvious example of logic that is coded in the database are stored procedures or triggers. However, it is worth noting that the structure of the database itself is logic, in the form of data type validation, relationship validation, etc.
    • Data Access Tier -- separate from the database, is the code that accesses the database. There are many reasons to keep this code in its own layer -- support for multiple DBMSs, automated data auditing, connection management, hiding of connection strings, retrieving of Identity values, etc.
    • Object/Relational mapping -- at some point in the application, we have to translate objects to SQL, and vice versa. An O/R Mapping component takes care of this requirement. We could just code the SQL inside of the higher level objects, but there are numerous benefits to encapsulating the logic at a single point in the system. Some of these benefits can include insulating developers from knowing the database structure, supporting powerful query interfaces for the users, and insulating other objects from database structure changes.
    • Business Domain objects -- (also called entity/business objects). These are objects that contain properties that reflect data. At this level, you will find objects named address, client, etc. Commonly, they also embed relationship logic -- for example, a client object may have a property or method that retrieves the address object for that client instance. In this case, the objects are known as Active Domain Objects, because they can actively access related data.
    • Service Layer -- Typically, service objects will not have names like client, or address. They are pieces of processing and workflow logic, that usually correspond closely to use case scenarios. For example, in a shopping cart application, the service layer could handle the processing of the order. This might include sending data to an external system, emailing the purchaser, and saving the order data in a database.
    • Controller Logic -- (also called UI Process logic, Presentation logic). When dealing with multiple User Interfaces, e.g., both WinForms and Web, it makes sense to try and split the non-UI logic into its own layer. This is very difficult to do, but there are pre-built frameworks that help support this type of logic. They are known collectively as MVC (Model-View-Controller). The Controller logic is the C in MVC. In a shopping cart application, the controller logic could handle the flow of the web pages as the user progressed through the order process.
    • UI/View -- This is the piece of the application that the user interacts with. When the controller logic is in its own layer, the UI typically only contains UI Mapping code, i.e., mapping object properties to and from screen fields. Of course, it also contains code to interact with the controller objects.

    Disclaimer -- Most applications do not have all of these tiers. It is perfectly fine to combine tiers, according to your own unique needs.


    Examples


    At this point, I think we need some overly-simplistic examples:










































    ExampleWhat is it?
    SQL Server, Oracle, MS AccessDatabase, DBMS
    stored procedureif it has significant logic, Business Domain Object, otherwise Database
    ASP pageUI and Controller
    ASPX pageUI
    ASPX page code-behindController
    Response.RedirectController
    Windows FormUI
    Windows Form code-behindController
    Address classBusiness domain object
    conn=new OleDBConnection;
    conn.Open;
    Data Access Tier
    myAddress.Street = Reader.GetString(3)

    or

    cmd.AddParameter(myAddress.Street)
    O/R Mapping
    using (DBConn conn = ConnFactory.Create())
    {
    conn.Save(address);
    client.AddressId = address.Id;
    conn.Save(client);
    }
    Business Domain Object or Service Layer

    If you look closely, you can see some interesting things above:



    • ASP pages encouraged the combination of UI and Controller logic. ASP.NET improves the situation through code-behind classes. The benefits are primarily that the code is easier to read and maintain, as is the HTML.
    • Stored procedures that contain business logic automatically break up your business logic into multiple places. Since stored procedures are not compatible with your other code, (i.e., you cannot make use of helper functions within a stored procedure), this can lead to duplicate code. It will be interesting to see how Yukon (support for C# on the database) opens up possibilities in this area.

    Challenge #1 - Validation


    The first challenge in a tiered system is validation. It is a challenge, because it is almost impossible to write validation without duplicating the logic. This is because multiple layers of the system need access to the same validation logic, and some layers intrinsically contain validation logic:



    • often, the user interface must be nice to the user, and show validation problems before the user presses the Save button.
    • the controller objects and service layer may need to enforce validation.
    • the business domain objects must enforce validation.
    • the data objects usually have validation logic already, in the form of data types on the properties.
    • the database has validation logic already, in the form of data types, foreign key relationships, and field length limits.

    Often, application designers will just ignore this challenge, and accept that validation will have to be duplicated across multiple pieces of code. However, there are some other approaches:



    • put the validation in a separate Rules component, that can be re-used by the UI and the other layers -- probably the best solution, but also the hardest to implement.
    • put the validation in the business domain objects, or the controller logic, and hope that programmers do not bypass the layer.
    • put validation in the UI, and in the database -- this is more common than you would think, because it is the simplest to implement.

    Whatever the choice, it is practically impossible to achieve zero-duplication of validation logic. Depending on your particular needs, you will need to choose the best approach.


    Challenge #2 - Security


    Like validation, security needs to be enforced at multiple levels.



    • At the UI, we may want to disable a Save button, or certain fields/menus/buttons.
    • in our business domain logic, we may want to prevent certain actions based on the context of the actions - for example, we may only allow the save of an address object if it is linked to the user's own user-id.
    • in our database, we may want to apply permissions to tables and/or stored procedures.

    Security is difficult when it is dynamic (specified in detail by an admin user), and context-sensitive, i.e., you cannot always tie security directly to a specific table or data object. If your security is simpler than that, then you may be able to make do it by implementing your security at the Controller, and/or at the database.


    For the more complex, dynamic cases, an approach that I have used in the past was to attach security information to the data objects. The business domain objects attach the security data, based on the current context. The security is then enforced by the code that persists the data objects, i.e., the O/R mapping layer. This worked well for me, but YMMV (Your Mileage May Vary).






    In addition to the problem of security enforcement, you have to deal with database connection strings -- if a user has access to the connection string, they can then establish a database connection directly (using SQL Query Analyzer), and are able to bypass object-based security. In that case, the only effective security is at the database level -- a very strong argument for using stored procedures, (because of the fine-grained control of security that they offer).


    Another approach to the connection string dilemma is to disallow direct connections to the database. This is done by placing the data access tier on a server machine, together with the connection string. Security is then enforced at the data access level, through the use of a separate security component, usually using security tokens/tickets to communicate the user's permissions.


    A final approach is to encrypt the connection string at the client. This is difficult to do well, because it requires that a private key be stored somewhere, and it is hard to find a place that is secure. It is possible to do, but in .NET 1.1, you have to use some unmanaged APIs.


    Challenge #3 - to MVC or not to MVC


    MVC (Model-View-Controller) is a powerful technique to separate controller logic from the UI. You'll find MVC used most often in complex, workflow oriented web applications. This is because web-based applications lend themselves well to MVC -- MVC operates in a state-driven manner, just like a web application. It is a much less intuitive pattern for a WinForms application.


    In the MVC acronym, M refers to the business data objects or service layer, V refers to the UI, and C to the controller logic. The intention of the C is to encapsulate input controller logic, i.e., the code-behind of the page or form that interacts with the model. Often though, the meaning is interpreted as application controller logic, i.e., controlling the flow of pages and forms.


    Many consider MVC to be the most poorly understood and implemented pattern, primarily because of the confusion over what the C means. The problem is that application controller code can be re-used across different UIs, where-as an input controller is specific to a particular UI. Many MVC implementations combine the two types of controllers, which is OK, but not re-usable across different UIs.


    For example, a web page has a completely different interface to a WinForms app, yet in MVC, we may code a single piece of controller logic to handle both. When we do this, the UI has a tendency to be created for the lowest common denominator, which leads to a very uninspiring application on the higher end interfaces, i.e., WinForms.


    Of course, this can be managed, primarily by creating input controllers for each platform, and generic, shared application controllers -- but it is still a challenge.


    My experience with MVC is that it is nice and convenient to have the controller logic separated, specially when dealing with a web application with processes, e.g., an order process. However, I was at the mercy of the MVC framework that I chose, and in the end, I prefer control over convenience. But that's just me -- a control freak.


    Challenge #4 - Transactions


    The functions that the user performs drive the need for transactions, and functions are defined at the level of the controller or service layer logic -- so it makes sense that transactions should be initiated and committed at that level. In most environments, this implies that database connections should be initiated and terminated at the same level, since few platforms support transactions that transcend database connections.


    Thus, the challenge is to provide database specific functionality (transactions), at a level of the application that may be substantially removed (in terms of intermediate layers) from the database. It may even be that the data access tier is on a completely separate machine.


    One solution is conceptually simple - create an abstraction that represents a database connection/transaction, and can be used inside of the controller logic to begin, commit, and rollback transactions. This has a profound effect on the design of the data access tier. It means that the data access tier cannot automatically open and close database connections -- it becomes a managed resource at the level of the controller logic. In .NET terms, the data access tier has to support the IDisposable interface.


    Alternatively, you can make use of automatic transactions, which are supported in .NET, COM+, and MTS. This has the additional benefit of supporting distributed transactions.


    Achieving Reuse


    Within your own organization, it can be extremely beneficial to establish some reusable components that fulfill the needs of particular components of the system. Some you can code yourselves, but others (e.g., Object/Relational mappers) are usually easier to purchase.


    For a single application, it may make sense to code everything from scratch. But after the first, it gets tired real fast, especially typing SQL strings, doing O/R mapping, and creating domain objects. I'd rather concentrate on the real application logic. As an example, in my own work environment, I work primarily with semi-complex web-based applications that have their own databases. Some of my reusable components are:



    • OID generator - this is an article by itself, but basically, I prefer to use GUID fields instead of INT fields as primary keys in my database. This component generates guaranteed-unique GUIDs, for assigning to a Primary Key before saving to the database.
    • Data Access DLL - a simple data access component that works with the OID generator, encourages parameterized queries, and allows management of transactions.
    • Simple Security component - supports adding, editing, and authenticating of users, with support for roles. Passwords are salted and hashed in the database.
    • Audited Security component - for those projects with more stringent security requirements, extends the simple security component by adding the ability to audit logins, and lock users out after x failed logins.
    • POP Server component - based on a 3rd party component, supports forwarding of email based on configurable rules.
    • Domain object templates - using a 3rd party tool, initial sets of domain objects and O/R mappings are generated based on the database structure.

    Microsoft promotes their own re-usable "Application Blocks". These are of varying quality, and I do not specifically recommend any of them. I do recommend against the Microsoft Data Application Block, because it is SQL Server specific and does not encourage good data access practices.


    References



    • Data Access Patterns: Database Interactions in Object-Oriented Applications - by Clifton Nock. This is an excellent reference that breaks down the different components of data access. It tackles everything from object relational mapping, down to data access tiers.
    • Patterns of Enterprise Application Architecture - By Martin Fowler et al. - This is the definitive book on designing object-oriented business systems. It tackles everything from the UI down to the data access, in a nicely clear and concise manner.
    • Application Architecture for .NET - Designing Applications and Services - By Microsoft Patterns and Practices division. It is a good, short book, focused on creating very complex business applications, making use of Microsoft tools and products.
    • Expert One-on-One Visual Basic .NET Business Objects - by Rockford Lhotka. A very practical approach to developing a complex tiered application. The author presents his own vision of an architecture that can be reused across multiple applications.

    Monday, May 30, 2005

    Web Service - Best Practices

    Summary


    Web Services technology is well established as a communication technology for the Internet offering greatest interoperability. Their standardization process is going on at great speed, which will lead to even broader acceptance. Nonetheless judging from mailing lists and user groups, discussions there is still quite some confusion about the different Web Services Design approaches. What does “Document/Literal” mean compared “RPC-style”, how does SOAP “message-style” fit into this?


    This article will clarify and explain in detail the different Web Service Design Methodologies as defined by the Web Services Standardization Groups, clarify the terms, highlight their differences. Here we will focus on the following Web Services Design Approaches, evaluate their strength and weaknesses and explore how far each style supports in designing an Interoperable Web Service.



    1. RPC/Encoded Style
    2. RPC/Literal Style
    3. Document/Literal Style
    4. Document /Literal Wrapped Style


    Introduction


    In their relatively short time of existence Web Services have gained an enormous acceptance and a broad usage on the market. One reason for this surely is, their very early open standardization which has been driven by all major players on the market. On the other side, all these players also have their preferences on how Web Services should look like and how they should communicate. This, and the fact, that different communication styles are required, has lead to standards that today support different ways of how web services messages can be formatted and how they can communicate.


    The relevant standards for describing and using Web Services are the Web Services Description Language (WSDL), a standardized XML Schema-like language used for specifying Web Service and the Simple Object Access Protocol (SOAP), the actual communication protocol for Web Services.. Before Moving to the Real Design Approaches let's clarify some of the terms that is frequently used in the world of web services.


    Communication Patterns


    Let’s start with the communication patterns. With Web Services we can essentially distinguish three different ways of communication:


    • Remote procedure call: Client sends a SOAP request to the service provider and waits for a SOAP response (synchronous communication).
    • Messaging: Client sends a SOAP request and expects no SOAP response back (one-way communication).
    • Asynchronous callback: A client calls service with one of the above methods. Later, the two parties switch roles for a callback call. This pattern can be build from either of the first two.


    SOAP Formatting Rules


    Now let’s turn to how the SOAP Message of a Web Services can be formatted (essentially the message’s <soap:body> Element) WSDL 1.1 distinguishes two different binding styles (referred to as soap:binding styles): RPC and Document.



    • RPC Style

      The RPC style specifies that the <soap:body> contains an element with the name of the web method being invoked (wrapper element). This element in turn contains an entry for each parameter and the return value of this method.


    • Document Style

      If the style is of type document, there is no wrapper element as with the RPC style. Instead, the message parts appear directly under the <soap:body> element. There are no SOAP formatting rules for what the <soap:body> contains; it contains what the sender and receiver agreed upon as an XML document.


    The second formatting rule is the ‘Use’ attribute. This concerns how types are represented in XML. It indicates whether the message parts are encoded using some encoding rules, or whether the parts define the concrete schema of the message. The two offered choices are:



    • Encoding

      If use is encoded, then each message part references an abstract type using the type attribute. The message is produced using an encoding specified by the encodingStyle attribute. The mostly used SOAP Encoding is a set of serialization rules defined in SOAP 1.1. The rules specify how objects, structures, arrays and object graphs should be serialized. In general, the applications using SOAP encoding is focused on remote procedure calls and will likely use RPC message style.


    • Literal

      If use is Literal, then each part references a concrete schema definition using either the element or type attribute, i.e., data is serialized according to a given schema. In practice, this schema is usually expressed using W3C XML Schema.


    Table 1 summarizes the options for different web services parameters. An important realization is that, three independent decisions are to be made by a web services developer. What is the “Communication Pattern” to be used? What is the SOAP formatting “Style” to be used ? and finally what is the SOAP message encoding Type to be used ?















    WSDL Parameters Available Options
    Communication PatternsRemote Procedure Call or One-way messaging
    StyleDocument or RPC
    UseEncoded or Literal


    Table 1. Web Services Parameters.


    Although in theory any combination of these options is possible, in practice there is a clear preference of one combination over the other, and also the standards and the Web Services Interoperability organization (WS-I) has a clear preference.


    Thus in practice, only Document/Literal and RPC/Encoded have gained wide spread usage and are directly supported by most platforms as indicated in Table 2. The table also shows the results of the WS-I conformance tests for the different style/use combinations.



















    Style/Use combinationSupported Soap tool kitWS-I Conformance
    RPC/EncodedMicrosoft, Axis 1.1Failed
    RPC/LiteralAxis 1.1Failed
    Document/LiteralMicrosoft , Axis1.1Passed


    Table 2. Web Services Format Support.


    The Document/Encoded has not been tested, since it is not supported by the used platforms. In fact there is no real world usage of Document/Encoded combination.


    A Simple Web Service Example


    Now let’s look in more detail into the mostly used and supported style/use formats RPC/Encoded and Document/Literal. We will illustrate each of this style use combinations by means of a web method called “SendTemperature” which takes a user defined complex object as its parameter and returns void type as described in listing 1.


    We choose an example with a complex data type to make the differences between the various styles more obvious.

    public void SendTemperature (Temperature[] TempCollection){}

    public class Temperature
    {
    /// <remarks/>
    public int Id;

    /// <remarks/>
    public string Name;

    /// <remarks/>
    public System.Double Temperature;
    }


    Listing 1. SendTemperature Web Method implemented in C#.


    We will show how the various Web Services formats are implemented in the WSDL file for this web method with their respective SOAP request formats and highlight their differences. The implementations were done using Microsoft VS.NET and the Axis SOAP toolkit.


    Note that the namespaces, prefixes and the service part of the WSDL files have been ignored in this article for simplicity. The Listing 2 shows the common namespaces and prefixes that were used.

    xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:s="http://www.w3.org/2001/XMLSchema"
    xmlns:s0="http://interop.webservices.fhso.ch/{service name}"
    xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
    xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    targetNamespace="http://interop.webservices.fhso.ch/{service name}/ ”


    Listing 2. Namespaces and used prefixes.




    1. RPC/Encoded Style


      RPC/Encoded is essentially a format following the classical “remote procedure call” style in which a client sends a synchronous request to a server to execute an operation. The SOAP request contains the name of method to be executed and the parameter it takes. The server running the Web Service converts this request to appropriate objects, executes the operation and sends the response back to the client as SOAP message. At the client side, this response is used to form appropriate objects and return the required information to the client. In RPC-style Web Services, the complete method is specified in the WSDL file and in the SOAP body, including the sent parameters and the return values. So we have a rather tight coupling with this style.


      Listing 3 shows the WSDL defined for the SendTemperature Method in the RPC/Encoded style.

      <types>
      <s:schema targetNamespace="http://interop.webservices.fhso.ch/rpcencoded">
      <s:complexType name="ArrayOfTemperature">
      <s:complexContent mixed="false">
      <s:restriction base="soapenc:Array">
      <s:attribute d7p1:arrayType="s0:Temperature[]" ref="soapenc:arrayType"
      xmlns:d7p1="http://schemas.xmlsoap.org/wsdl/"/>
      </s:restriction>
      </s:complexContent>
      </s:complexType>
      <s:complexType name="Temperature">
      <s:sequence>
      <s:element minOccurs="1" maxOccurs="1" name="Id" type="s:int"/>
      <s:element minOccurs="1" maxOccurs="1" name="Name" type="s:string"/>
      <s:element minOccurs="1" maxOccurs="1" name="value" type="s:double"/>
      </s:sequence>
      </s:complexType>
      </s:schema>
      </types>
      <message name="SendTemperatureSoapIn">
      <part name="Collection" type="s0:ArrayOfTemperature"/>
      </message>
      <message name="SendTemperatureSoapOut"/>
      <portType name="TemperatureRpcEncodedSoap">
      <operation name="SendTemperature">
      <input message="s0:SendTemperatureSoapIn"/>
      <output message="s0:SendTemperatureSoapOut"/>
      </operation>
      </portType>
      <binding name="TemperatureRpcEncodedSoap" type="s0:TemperatureRpcEncodedSoap">
      <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="SendTemperature">
      <soap:operation soapAction="http://interop.fhso.ch/soapformat/SendTemperature"/>
      <input>
      <soap:body use="encoded"
      encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </input>
      <output>
      <soap:body use="encoded"
      encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
      </output>
      </operation>
      </binding>

      Listing 3. RPC/Encoded WSDL for SendTemperature.


      Notice the binding style, which is set to ‘rpc’ and use, which is set to ‘encoded’. Under the <message> section there can be any number of <part> elements each containing a type attribute which is unique to ‘rpc’ style. Now let’s look what happens when we invoke the SendTemperature web method, sending an array with two entries. Listing 4 shows the resulting SOAP message.

      <soap:Envelopexmlns:
      soap="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
      xmlns:tns="http://interop.webservices.fhso.ch/rpcencoded"
      xmlns:types="http://interop.webservices.fhso.ch/rpcencoded/encodedTypes"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Body soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
      <SendTemperature>
      <Collection href="#id1"/>
      </SendTemperature>
      <soapenc:Array id="id1" soapenc:arrayType="tns:Temperature[2]">
      <Item href="#id2"/>
      <Item href="#id3"/>
      </soapenc:Array>
      <tns:Temperature id="id2" xsi:type="tns:Temperature">
      <Id xsi:type="xsd:int">3</Id>
      <Name xsi:type="xsd:string">Station1</Name>
      <value xsi:type="xsd:double">34.3</value>
      </tns:Temperature>
      <tns:Temperature id="id3" xsi:type="tns:Temperature">
      <Id xsi:type="xsd:int">56</Id>
      <Name xsi:type="xsd:string">Station3</Name>
      <value xsi:type="xsd:double">23.6</value>
      </tns:Temperature>
      </soap:Body>
      </soap:Envelope>

      Listing 4. RPC/Encoded SOAP Message for SendTemperature.


      Each parameter is type encoded in the SOAP message. Notice also the ‘href’ tags in the SOAP message, which is an essential part of the RPC/Encoded style and which is used to refer the items in the array. Under any literal style, the ‘href’ tag is not available. Let’s analyze the WSDL and its SOAP message format.


      Strengths:



      • The definition of the WSDL file follows the intuitive and well-known remote-procedure-call style.
      • The operation name appears in the message, so that the receiver has an easy time dispatching the message to its implementation.
      • If you are using data graphs or polymorphism in your service, this is the only possible style that can be used for the types described in this article.

      Weaknesses:



      • The SOAP message includes the type encoding information such as xsi:type="xsd:int", xsi:type="xsd:string", xsi:type="xsd:double" which is an overhead.
      • In general it is harder to validate the SOAP message.
      • RPC style causes a rather tight coupling between service provider and client, any changes to the interface would break the contract between the service and the client.
      • Depending on the information that may need to handle simultaneously, memory constraints may make RPC messaging unfeasible as marshalling takes place in-memory.
      • Is not supported by the WSI conformance standard.

      Now let’s look at the implementation of the same Web Method in RPC/Literal format and see if it can eliminate some of the drawbacks of RPC/Encoded style.



    2. RPC/Literal Style


      The WSDL looks almost similar to the previous one except the change in the ‘use’ setting from ‘encoded’ to ‘literal’ in the soap:body as shown in Listing 5. As described above, this means that the data type definition is not provided by the referenced XML Schema instead of RPC encoding.

      <types>
      <s:schema elementFormDefault="qualified"
      targetNamespace="http://interop.webservices.fhso.ch/rpcliteral">
      <!-- there are no global element declarations. There's nothing in the
      schema that completely describes the content of soap:Body -->
      <s:complexType name="ArrayOfTemperature">
      <s:sequence>
      <s:element minOccurs="0" maxOccurs="unbounded" name="Temperature"
      nillable="true"
      type="s0:Temperature"/>
      </s:sequence>
      </s:complexType>
      <s:complexType name="Temperature">
      <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="Id" type="s:int"/>
      <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string"/>
      <s:element minOccurs="0" maxOccurs="1" name="value" type="s:double"/>
      </s:sequence>
      </s:complexType>
      </s:schema>
      </types>
      <message name="SendTemperatureSoapIn">
      <part name="Collection" type="s0:ArrayOfTemperature"/>
      </message>
      <message name="SendTemperatureSoapOut"/>
      <portType name="TemperatureRpcLiteralSoap">
      <operation name="SendTemperature">
      <input message="s0:SendTemperatureSoapIn"/>
      <output message="s0:SendTemperatureSoapOut"/>
      </operation>
      </portType>
      <binding name="TemperatureRpcLiteralSoap"
      type="s0:TemperatureRpcLiteralSoap">
      <soap:binding style="rpc"
      transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="SendTemperature">
      <soap:operation
      soapAction="http://interop.fhso.ch/soapformat/SendTemperature"/>
      <input>
      <soap:body use="literal"
      namespace="http://interop.fhso.ch/soapformat/SendTemperature"/>
      </input>
      <output>
      <soap:body use="literal" />
      </output>
      </operation>
      </binding>

      Listing 5. RPC/Literal WSDL for SendTemperature.


      However, there is still a major drawback with this style. The XML Schema alone does not tell you what the message body Infoset contains, you must also know the RPC rules. Therefore, the schema describing an RPC/Literal message is not sufficient to validate that message.


      Now let’s look at the SOAP message format for RPC/Literal shown in Listing 6. Note that the type encoding is completely removed.

      <soapenv:Envelope
      xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <soapenv:Body>
      < ! – SendTemperature is the name of the procedure being invoked.
      Collection is a parameter of that procedure.
      Note that Collection is not namespace qualified. The two Temperature
      elements are contents of the Collection parameter. This Collection can
      be thought of as an array of Temperature items. Note that the Temperature
      is namespace qualified but is in a different namespace than SendTemperature.
      These namespace rules are unique to RPC-style messages -- >
      <SendTemperature xmlns="http://interop.fhso.ch/soapformat/SendTemperature">
      <Collection xmlns="">
      <ns1:Temperature xmlns:ns1="http://interop.webservices.fhso.ch/rpcliteral">
      <ns1:Id>2</ns1:Id>
      <ns1:Name> Station1</ns1:Name>
      <ns1:value>34.2</ns1:value>
      </ns1:Temperature>
      <ns2:Temperature xmlns:ns2="http://interop.webservices.fhso.ch/rpcliteral">
      <ns2:Id>56</ns2:Id>
      <ns2:Name> Station 3</ns2:Name>
      <ns2:value>23.6</ns2:value>
      </ns2:Temperature>
      </Collection>
      </SendTemperature>
      </soapenv:Body>
      </soapenv:Envelope>

      Listing 6. RPC/Literal SOAP message for SendTemperature.


      Strengths:



      • The WSDL is still about as straightforward as with the RPC/Encoded style.
      • The operation name still appears in the SOAP message.
      • The type encoding is eliminated from the message and hence increase the throughput performance

      Weaknesses:



      • Still the contract between the service and the clients are tightly coupled.
      • It is still hard to validate the transferred data by the SOAP message.
      • Is not supported by the WSI conformance standard either

      Now let’s move to the Document/Literal type and see if this style can reduce the existing drawbacks.



    3. Document/Literal


      The major difference of the Document style to the above RPC style is that, the client sends the service parameters in a normal XML document to the server instead of a discrete set of parameter values of methods. This makes the Document style to a more loosely coupled style of interaction than the RPC format.


      The Web Service provider processes the normal XML document, executes the operation and sends the response to the client again as a normal XML document. There is no direct mapping between the server objects (parameters, method calls etc) and the values in XML documents. The application is responsible for mapping the XML data values. The SOAP message of a Document style carries one or more XML documents within its SOAP body. The protocol places no constraint on how the document needs to be structured; this is completely handled at the application level. Additionally, Document style Web Services follows the asynchronous processing paradigm.


      The WSDL for this style has more changes compared to RPC style as shown in Listing 7.

      <types>
      <s:schema elementFormDefault="qualified"
      targetNamespace="http://interop.webservices.fhso.ch/docLit">
      < ! - - this element declaration describes the entire contents
      of the soap:Body in the request message.
      This is a feature of document/Literal that RPC/Literal lacks - ->
      <s:element name="Collection">
      <s:complexType>
      <s:sequence>
      <s:element minOccurs="0" maxOccurs="unbounded" name="Temperature"
      nillable="true"
      type="s0:Temperature"/>
      </s:sequence>
      </s:complexType>
      </s:element>
      <s:complexType name="Temperature">
      <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="Id" type="s:int"/>
      <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string"/>
      <s:element minOccurs="0" maxOccurs="1" name="value" type="s:double"/>
      </s:sequence>
      </s:complexType>
      < ! – Similarly this element declaration describes
      the contents of the soap body in the response
      message. In this case the response is empty -- >
      <s:element name="SendTemperatureResponse">
      <s:complexType/>
      </s:element>
      </s:schema>
      </types>
      <message name="SendTemperatureSoapIn">
      <part name="parameters" element="s0:Collection"/>
      </message>
      <message name="SendTemperatureSoapOut">
      <part name="parameters" element="s0:SendTemperatureResponse"/>
      </message>
      <portType name="TemperatureDocLitSoap">
      <operation name="SendTemperature">
      <input message="s0:SendTemperatureSoapIn"/>
      <output message="s0:SendTemperatureSoapOut"/>
      </operation>
      </portType>
      <binding name="TemperatureDocLitSoap" type="s0:TemperatureDocLitSoap">
      <soap:binding style="document"
      transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="SendTemperature">
      <soap:operation soapAction=
      "http://interop.webservices.fhso.ch/documentliteral/SendTemperature"
      style="document"/>
      <input>
      <soap:body use="literal" />
      </input>
      <output>
      <soap:body use="literal" />
      </output>
      </operation>
      </binding>

      Listing 7. Document/Literal WSDL for SendTemperature.


      Note that, the binding style is set to ‘document’ and use to ‘literal’. Under the ‘message’ section only a single <part> element is possible, which contains an element attribute.


      This part points to a schema element declaration that describes the entire contents of the soap body. Note that, the collection is now defined as an element rather that a type. The primary feature of Document/Literal, and its key benefit compared to RPC/Literal is the use of schema element declaration to completely describe the contents of the <soap:body>. This means that you can tell what the message body Infoset contains just by looking at the schema with no need for additional rules. Consequently you could take the schema describing a Document/Literal message and use it to validate the message - you can’t do this with RPC/Literal.


      Now let’s have a look at the corresponding SOAP message format shown in Listing 8. Note that there is no type encoding data specified and that the operation name is missing.

      <soap:Envelope
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Body>
      < -- note that the Operation name is missing in the message -- >
      <Collection xmlns="http://interop.webservices.fhso.ch/docLit">
      <Temperature>
      <Id>2</Id>
      <Name>Station 1</Name>
      <value>34.2</value>
      </Temperature>
      <Temperature>
      <Id>56</Id>
      <Name>Station 3</Name>
      <value>23.5</value>
      </Temperature>
      </Collection>
      </soap:Body>
      </soap:Envelope>

      Listing 8. Document/Literal SOAP message for SendTemperature.


      The summarized strengths and weaknesses of this approach are as follows.


      Strengths:



      • No type encoding information in the SOAP Message.
      • You can always validate the message with any XML validator. Everything within the soap body is defined in the schema.
      • With document style the rules are less rigid and many enhancements and changes can be made to the XML schema without breaking the interface.
      • Document style services can maintain application state if multiple procedures are called in a particular sequence.
      • Document style is better suited for asynchronous processing.
      • Many document-messaging services are able to choose between DOM and SAX handlings of the document and as a result are able to minimize in-memory processing

      Weaknesses:



      • The WSDL is getting bit more complicated.
      • The operation name in the in the SOAP message is lost. Without the name, the dispatching can be difficult or impossible.

      We have seen that the Document/Literal style eliminates most of the drawbacks of the RPC/Literal style – on the other side it introduces a new one: it looses the operation name in the SOAP message.


      Since the fourth format option, Document/Encoding, has no practical usage, it will not be described. Instead we will look at an extension to the Document/Literal style, the Document/Literal wrapped format.



    4. Document/Literal wrapped


      This style has been suggested by Microsoft to fix the drawback from the standard Document/Literal style. Although there is no specification in WSDL 1.1 standard for this style, many current SOAP toolkits support it.


      Let’s first look at the WSDL in Listing 9 and its corresponding SOAP message in Listing 10.

      <types>
      <s:schema elementFormDefault="qualified"
      targetNamespace="http://interop.webservices.fhso.ch/docLitWrapped">
      <s:element name="SendTemperature">
      <s:complexType>
      <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="Collection"
      type="s0:ArrayOfTemperature"/>
      </s:sequence>
      </s:complexType>
      </s:element>
      <s:complexType name="ArrayOfTemperature">
      <s:sequence>
      <s:element minOccurs="0" maxOccurs="unbounded" name="Temperature"
      nillable="true"
      type="s0:Temperature"/>
      </s:sequence>
      </s:complexType>
      <s:complexType name="Temperature">
      <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="Id" type="s:int"/>
      <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string"/>
      <s:element minOccurs="0" maxOccurs="1" name="value" type="s:double"/>
      </s:sequence>
      </s:complexType>
      <s:element name="SendTemperatureResponse">
      <s:complexType/>
      </s:element>
      </s:schema>
      </types>
      <message name="SendTemperatureSoapIn">
      <part name="parameters" element="s0:SendTemperature"/>
      </message>
      <message name="SendTemperatureSoapOut">
      <part name="parameters" element="s0:SendTemperatureResponse"/>
      </message>
      <portType name="TemperatureDocLitWrappedSoap">
      <operation name="SendTemperature">
      <input message="s0:SendTemperatureSoapIn"/>
      <output message="s0:SendTemperatureSoapOut"/>
      </operation>
      </portType>
      <binding name="TemperatureDocLitWrappedSoap"
      type="s0:TemperatureDocLitWrappedSoap">
      <soap:binding style="document"
      transport="http://schemas.xmlsoap.org/soap/http"/>
      <operation name="SendTemperature">
      <soap:operation soapAction=
      "http://interop.webservices.fhso.ch/docLitWrapped/SendTemperature"
      style="document"/>
      <input>
      <soap:body use="literal"/>
      </input>
      <output>
      <soap:body use="literal"/>
      </output>
      </operation>
      </binding>

      Listing 9. Document/Literal wrapped WSDL for SendTemperature.


      First, notice in Listing 9 that the operation name is now reintroduced into the WSDL file in the first ‘element’ tag. Also notice that this SOAP message in Listing 10 looks similar to the RPC/Literal SOAP Message. But there is a subtle difference. In the RPC Literal SOAP message the <SendTemperature> child of <soap:body> was the name of the operation. In the Document/literal wrapped soap message, the <SendTemperature> is the name of the element to which the single input message’s part refers. This pattern is a sly way of putting the operation name back into the SOAP message.


      These are the characteristics of the document/Literal wrapped pattern:



      • The input message has a single part
      • The part is an element
      • The element has the same name as the operation.
      • The element’s complex type has no attributes.
      <soap:Envelope
      xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Body>
      <! – the following is an xml document described in the service’s
      contract using XML schema. In this case SendTemperature may or may not
      be the name of the remote procedure being invoked by this message.
      Also, Collection may or may not be the name of the parameter. We know the
      structure of the xml document but We don’t know how the service
      is going to process it -- >
      <SendTemperature xmlns="http://interop.webservices.fhso.ch/docLitWrapped">
      <Collection>
      <Temperature>
      <Id>2</Id>
      <Name>Station 1</Name>
      <value>34.2</value>
      </Temperature>
      <Temperature>
      <Id>56</Id>
      <Name>Station 3</Name>
      <value>23.6</value>
      </Temperature>
      </Collection>
      </SendTemperature>
      </soap:Body>
      </soap:Envelope>

      Listing 10. Document/Literal wrapped message for SendTemperature.


      The summarized strengths and weaknesses of this approach are:


      Strengths:



      • Contains all the strengths of Document/Literal style.
      • The method name appears in the SOAP message.

      Weaknesses:



      • The WSDL is even more complicated, but this is still a very minor weakness.
      • If you have overloaded operations in your web service, you cannot use this style.


    So far, we saw that Document/Literal and Document/Literal wrapped style brought us a lot of flexibility and strengths compared to any other design style. Yet there are some issues remaining to be addressed.


    Limitations of Document/Literal and Document/Literal wrapped styles


    Suppose, we have overloaded operations as shown in the following Listing 11.

    public void SendTemperature (Temperature[] TempCollection){}
    public void SendTemperature (Temperature[] TempCollection, int week){}


    Listing 11. Overloaded SendTemperature Method.


    In this situation, it is not possible to use Document/Literal wrapped style even though the WSDL specification allows to have overloaded operations. The reason for this is that, when you add a wrapped pattern to a WSDL document you require an element to have the same name as the operation. (see Listing 9). The issue comes when you want to have two elements with the same name in the XML Schema. So the alternative we have for overloaded operations is to go for Document/Literal non-wrapped or one of the RPC styles.


    Let us see how we can realize this in Document/Literal. Here is the modification of the schema section of the WSDL for the above two web methods.

    <types>
    <s:schema elementFormDefault="qualified"
    targetNamespace="http://interop.webservices.fhso.ch/docLit">
    <s:element name="Collection">
    <s:complexType>
    <s:sequence>
    <s:element minOccurs="0" maxOccurs="unbounded" name="Temperature"
    nillable="true" type="s0:Temperature"/>
    <s:element minOccurs="0" maxOccurs="1" name="week" type="s:int"/>
    </s:sequence>
    </s:complexType>
    </s:element>
    <s:complexType name="Temperature">
    <s:sequence>
    <s:element minOccurs="0" maxOccurs="1" name="Id" type="s:int"/>
    <s:element minOccurs="0" maxOccurs="1" name="Name" type="s:string"/>
    <s:element minOccurs="0" maxOccurs="1" name="value" type="s:double"/>
    </s:sequence>
    </s:complexType>
    <s:element name="SendTemperatureResponse">
    <s:complexType/>
    </s:element>
    </s:schema>
    </types>


    Listing 12. Changed Schema Section of the Document/Literal WSDL for SendTemperature.


    We just added another element to the collection. And everything else remains similar as in the Listing 7. It is interesting to look at the client proxy generated by the VS.NET wsdl.exe utility.

    Public void SendTemperature([System.Xml.Serialization.XmlElementAttribute("Protocol",
    IsNullable=true)] Temperature[] Temperature, int week,
    [System.Xml.Serialization.XmlIgnoreAttribute()] bool weekSpecified) {
    this.Invoke("SendTemperature", new object[] {
    Temperature,
    week,
    weekSpecified});
    }


    Listing 13. C# proxy code generated for the overloaded SendTemperature.


    Notice that an additional Boolean variable called “weekSpecified” is automatically created and now the client can invoke this operator overloaded web method in two ways. If a client invokes this method with the week specified set to “false” we call the first overloaded method of Listing 11 - and the SOAP request is still identical to the one of Listing 10.

    SendTemperature(Temperatue_Object,7,false);


    On the other hand if a client call is made with the weekSpecified set to “true”, then it refers to the overloaded method of Listing 11 - and now the SOAP request looks like the following with the “week” parameter being passed as an XML tag.

    SendTemperature(Temperature_Object,7,true);
    <soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
    <Collection xmlns="http://interop.webservices.fhso.ch/docLit">
    <Temperatue>
    <Id>2</Id>
    <Name>Station 1</Name>
    <value>34.2</value>
    </Temperature>
    <Temperature>
    <Id>56</Id>
    <Name>Station 3</Name>
    <value>23.5</value>
    </Temperature>
    <week>7</week>
    </Collection>
    </soap:Body>
    </soap:Envelope>


    Listing 14. SOAP Message for the second overload format.


    Now we have seen how the problem with Document/Literal wrapped is nicely solved by the standard Document/Literal.


    Conclusion


    Document-centric and RPC Web Services fill quite different niches. Document-style web services are more suitable for most enterprise-to-enterprise interaction over the Internet. Developing a Document-style web service may require a little more effort than RPC-style. We saw that Document/Literal Style and Document/Literal wrapped style brought us a lot of flexibility and strengths in Interoperability compared to any other design style. Anytime you are not interfacing to a preexisting RPC, the benefits of the document-style usually outweigh the fact to interface to the service. RPC/Encoded Approach is highly discouraged in Designing a web service if your main requirement is interoperability.


    The WS-I Basic profile 1.0 discourages the use of RPC/Encoded approach and motivates Document/Literal and RPC/Literal as the only allowed style/use combinations. Many believe that RPC/Literal would go away with the future versions of WSI- profile.


    References



    About the Authors


    Nalaka Withanage


    Nalaka Withanage is an Electronics & Telecommunications Engineer and a Microsoft Certified Solutions Developer for .NET. He has being involved in Designing and Implementing Web Services based Distributed Systems as business solutions over the past 3+ years with J2EE, XML, ASP.NET, C#, VB.NET and .NET Compact Framework technologies.


    Martin Kropp


    Martin Kropp is a professor for Computer Science at the University of Applied Sciences Solothurn / Northwest Switzerland in Olten. He teaches software engineering with emphasis on object-oriented technology. He has defined and conducted several research projects for solving real world problems through Distributed systems technology including .NET and web services.