Blog from Saravanan Arumugam

Let us talk about Technologies

Monthly Archives: January 2011

TCP implementation in IIS 7


This is my first time implementation of TCP with WCF. For completing this I had to undergo multiple steps. I faced multiple issues in different stages.

I want to present the steps to implement TCP in WCF with the Windows Activation Service (WAS) of IIS 7. Making the service consumable through TCP protocol involves the following steps.

Note: I use Windows 7 and the paper talks about steps in Windows 7. However it may be the same in Windows Vista as well.

 

1. Configure WAS

By default IIS is not ready for offering us the TCP support. We need to configure the Windows Activation Services to enable the support.

To do this, click Start –> Control Panel –> Programs and Features

Here we can find “Turn windows features on or off”. On clicking this, we’ll get the following pop up.

image

 

Expand the Microsoft.NET Framework 3.5.1 (or 3.0), check the “Windows Communication Foundation Non-HTTP Activation” and press OK. This will configure the IIS for supporting Non-HTTP protocols (net.tcp, net.pipe, net.msmq)

 

2. Turn on Windows NT services

To support Non-Http protocols, IIS 7.0 and WAS use a set of Windows NT services.

Net.Tcp Listener Adapter

Net.Tcp Port sharing service

To enable these services, click –> Start –> Administrative Tools –> Services (Or simply, Start –> All Programs –> Accessories –> Run and type Services.msc)

In the resulting Services windows, we can find these services.

By default these services are Disabled. To enable them, double click on each service and set the start up type as Automatic. (Select Automatic in the Startup type drop down and select Apply)

image

Once the service is enabled, you can right click on the service and select Start to start the service.

Note: For the other Non-HTTP protocols, net.pipe, net.msmq, we have the following services.

Net.Pipe Listener Adapter

Net.Msmq Listener Adapter

However Net.Msmq can’t be started directly. It requires extra configuration through “Programs and Features –> Turn windows features on or off” before starting up the service.

 

3. Enable TPC for the web site

Having enabled the support for TCP protocol, the next step is to enable the protocol in the web site.

I wanted to host my application in the Default Web Site in IIS 7.

To do this, click Start –> type IIS or inetmgr in the Start Menu’s search text box.

This will show up the “Internet Information Manager” application. Open the application.

Expand root node on the left pane, Expand the Sites, locate the Default Web Site (or any other site of your preference) in the list.

Right click on the Site –> Edit Bindings

image

By default http is enabled in all the Sites.

To enable TCP protocol, select the Add button. In the popup window, select the Type as net.tcp, and fill the binding information with as follows.

<prefered Tcp Port number>:*

image

On selecting Ok. You are enabling the tcp on the site.

 

4. Enable TCP for the Web Application

If the site is representing the web application, we should be good with the previous step. But in my case, I have an application (VersionTolerance) inside the Default Web Site.

So it is necessary to do an additional step of enabling the TCP protocol at the application level as well.

To do this, click and select the Web application inside the web site. (VersionTolerance inside the Default Web Site).

image

In the far right of the window, we can find “Manage Application”. Under this select Advanced Settings.

In the Advanced Settings window, Enabled Protocols option would have the value “http” by default. Now edit that value to show “http,net.tcp” (Careful: there is no space in the entire text)

image

With this step we are done with the configuration part of tcp protocol.

However I have faced some more obstacles in achieving the tcp communication.

 

5. Set Framework Version

When I tried to browse to WSDL of my service, the browser threw the following error message.

Parser Error Message: Unrecognized attribute ‘targetFramework’. Note that attribute names are case-sensitive.

Source Error:

Line 36: <connectionStrings />
Line 37: <system.web>
Line 38: <compilation debug="true" targetFramework="4.0">
Line 39: </compilation>
Line 40: <!--

Though this issue is not related to the set up of TCP protocol, it is inevitable.

When a web site is created, it would be associated with an application pool. An application pool can be understood as group of Web sites. Grouping web sites in the form of App pools give a convenient way for Administering the sites, underlying applications and their worker processes.

By default the “Default Web Site” site is created to support .Net framework version 2.0.

The root cause of the error message I faced was exactly this. I have to move the supporting frame work as 4.0 since a WCF application can be created with a framework 3.0 or above.

To change the framework version in the app pool, select the Application Pools under the root node of IIS 7. Double click the app pool that your site is associated with. In my case I have chosen DefaultAppPool.

image

In the Edit Application Pool dialog, select the .Net Framework Version to .Net Framework v4.x.

 

With all these step done, we are good to deploy the tcp service in IIS. Though I was talking about the tcp protocol throughout the paper, the same set of steps are applicable for net.pipe protocol as well.

Advertisements

What validates elements and attributes in config files?


While editing a config file, we can noticed error messages if we misspell or place wrong elements/attributes. We can also notice intellisense popping out to help us.

While the config file is an xml what can possibly validate the nodes and what can provide intellisense help?

Visual studio usually comes with an XSD file named DotNetConfig.xsd.

This schema file is used by the Visual studio to validate the config file entry. Providing help (through intellisense) while editing an XML is a regular feature provided by Visual Studio, if it finds a valid XSD file.

The DotNetConfig.xsd can be found in “C:\Program Files (x86)\Microsoft Visual Studio 10.0\Xml\Schemas” usually. (Replace the Visual Studio folder name based on the version of Visual studio being used)

Visual Studio 2010, provides support for working on all (almost) the previous versions of .net. i.e Framework 2.0, 3.0, 3.5 and 4.0.

So for validating configs when we work in different versions of framework, different versions of xsd files are provided.

DotNetConfig20.xsd

DotNetConfig30.xsd

DotNetConfig35.xsd

DotNetConfig.xsd (This is for the default framework i.e. Framework 4.0)

Versioning of Data Contracts in WCF


The main advantage of using Service Oriented architecture is that the server and the client can be completely decoupled. This is achieved by making the client know only the XML format of the message being sent to the server; they do not have a binary level code dependency.

To achieve the complete decoupling, both the server and client need to be version tolerant of its count part.

There are 3 scenarios from which the version conflict can occur between a service and a client. Let us see the scenarios one by one and understand the offer from WCF to over come them.

For our discussion, let us consider the following service.

namespace BookShop
{
    [ServiceContract(Name = "BookShop", Namespace = "http://www.8fingergenie.com/&quot;)]
    public interface IBookShopService
    {
        [OperationContract]
        Book GetBookByTitle(string title);
        [OperationContract]
        Book GetBook(long id);
        [OperationContract]
        long CheckAvailability(Book book);
        [OperationContract]
        long AddBook(string title, string author);
        [FaultContract(typeof(Fault))]
        [OperationContract]
        void BuyBook(Book book);
    }
}

 

The definition of the service contract is as follows.

namespace BookShop
{
     public class BookShopService : IBookShopService
    {
        #region Private fields
        private static List<BookStock> bookShelf = new List<BookStock>();
        private static long nextBookId = 1;
        #endregion
        #region IBookShop Members
        public long AddBook(string title, string author)
        {
            BookStock bookStock = bookShelf.Find(delegate(BookStock b)
            {
                return (b.Book.Title == title);
            });
            if (bookStock == null)
            {
                bookStock = new BookStock();
                bookStock.Book = new Book();
                bookStock.Book.Author = author;
                bookStock.Book.Title = title;
                bookStock.Book.BookID = nextBookId++;
                bookStock.Quantitiy = 1;
            }
            else
            {
                bookStock.Quantitiy++;
            }
            bookShelf.Add(bookStock);
            return bookStock.Book.BookID;
        }
        public void BuyBook(Book book)
        {
            BookStock bookStock = bookShelf.Find(delegate(BookStock b)
            {
                return b.Book == book;
            });
            if (bookStock == null)
            {
                Fault f = new Fault();
                f.FaultReason = "Requested Book is not available";
                throw new FaultException<Fault>(f);
            }
            bookStock.Quantitiy–;
        }
        public Book GetBookByTitle(string title)
        {
            BookStock bookStock = bookShelf.Find(delegate(BookStock b)
            {
                return b.Book.Title == title;
            });
            return bookStock.Book;
        }
        public Book GetBook(long id)
        {
            BookStock bookStock = bookShelf.Find(delegate(BookStock b)
            {
                return b.Book.BookID == id;
            });
            return bookStock.Book;
        }
        public long CheckAvailability(Book book)
        {
            BookStock bookStock = bookShelf.Find(delegate(BookStock b)
            {
                return b.Book.Title == book.Title;
            });
            return bookStock.Quantitiy;
        }
        #endregion
    }
}

 

Main attraction is on the Data Contract here.

namespace BookShop
{
    [DataContract(Namespace = "http://8fingergenie.com&quot;)]
    public class Book
    {
        [DataMember(IsRequired = false)]
        public long BookID;
        [DataMember(IsRequired = true)]
        public string Title;
        [DataMember(IsRequired = false)]
        public string Author;
    }
    [DataContract(Namespace = "http://8fingergenie.com&quot;)]
    public class BookStock
    {
        [DataMember(IsRequired = false)]
        public Book Book;
        [DataMember(IsRequired = true, EmitDefaultValue = true)]
        public long Quantitiy;
    }
}

 

Adding New Member to a Data Contract

Adding a new Member in the Data contract is the most common version change that occurs.

But the DataContractSerializer will simply ignore the new members while deserializing the type. So both the client and server can operate with the data having the new members that were not present in the original contract.

Here in the Book type, let us add a new member (Edition) and analyze the impact of this.

So the result becomes,

[DataContract(Namespace = "http://8fingergenie.com")]
   public class Book
   {
       [DataMember(IsRequired = false)]
       public long BookID;

       [DataMember(IsRequired = true)]
       public string Title;

       [DataMember(IsRequired = false)]
       public string Author;

       [DataMember(IsRequired = true)]
       public string Edition;
   }

Note that the Edition is mentioned as a Required field.

But the client has the original version of the contract i.e, without the new member.

<xs:schema xmlns:tns="http://8fingergenie.com&quot; elementFormDefault="qualified" targetNamespace="http://8fingergenie.com&quot; xmlns:xs="http://www.w3.org/2001/XMLSchema"&gt;
   <xs:complexType name="Book">
     <xs:sequence>
       <xs:element minOccurs="0" name="Author" nillable="true" type="xs:string" />
       <xs:element minOccurs="0" name="BookID" type="xs:long" />
       <xs:element name="Title" nillable="true" type="xs:string" />
     </xs:sequence>
   </xs:complexType>
   <xs:element name="Book" nillable="true" type="tns:Book" />

</xs:schema>

However with the older version of the data contract, the communication still works well because of the inbuilt version tolerance capacity of WCF. As described previously the new member would be ignored while deserialization process.

The resulting on wire message is shown below.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<ActivityId CorrelationId="ecc9eedb-c493-49f8-a508-e935099321ac" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">8ad0e625-587e-4484-b92c-e4ef4bd9fba7</ActivityId>
</s:Header>
<s:Body>
<GetBookResponse xmlns="http://www.8fingergenie.com/">
<GetBookResult xmlns:a="http://8fingergenie.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Author>Spencer Johnson</a:Author>
<a:BookID>1</a:BookID>
<a:Edition>1.0</a:Edition>
<a:Title>Who Moved My Cheese</a:Title>
</GetBookResult>
</GetBookResponse>
</s:Body>
</s:Envelope>

The Edition is present in the message. But while deserializing it to an old version, the new member is ignored.

 

Members Missing from a Data Contract

In situations when a sender (service or client) sends a data to a receiver with missing member/element, the default behavior of WCF is to accept the data with Missing member.

How does it do it?

When a member is missing, if that is an optional member in the receiving end, DataContractSerializer fills the missing member with default value. That is null for reference types and 0 for value types.

Note: In the data contract by default every member is an optional member, except if explicitly specified as IsRequired = true.

When the missing member is a required field, WCF would throw an exception. This is possibly the best thing to do at this situation.

From my example, if we’ll come across this situation if the client passes the Book object to Server.

Here the client has the Author, BookID and Title as members, whereas the service has an additional member to it “Edition”.

    [DataContract(Namespace = "http://8fingergenie.com&quot;)]
    public class Book
    {
        [DataMember(IsRequired = false)]
        public long BookID;

        [DataMember(IsRequired = true)]
        public string Title;

        [DataMember(IsRequired = false)]
        public string Author;

        [DataMember(IsRequired = false)]
        public string Edition;
    }

When the client makes a call to the server without Edition in the member filled as null.

If the Edition is marked with IsRequired = true, then any such communication would result in exception.

 

Round tripping

The scenarios discussed so far are adequate for most scenarios. However there is an interesting scenario called roundtripping.

Let us say v1 is the original contract. v2 is a new contract version with newer members in it.

Suppose there is a communication happens from an entity (client or server), let us call it Point-A, with v2 version of contract to an entity, Point-B, with v1 version of the contract. And then after some manipulation,  the receiver Point-B has to send back the data to the original sender Point-A.

Here there are two communications happening. When Point-A sends the data contract, Point-B would not have any idea of the additional members.

Then how would Point-A hold the additional member values and send them back to Point-A?

This can be done by implementing IExtensibleDataObject in the Point-B. This interface implements only one Property called ExtensionData.

When the DataContractSerializer deserializes data at Point-B, the unknown members are accumulated into the System.Runtime.Serialization.ExtensionDataObject.

During the second communication when the data is transmitted from Point-B to Point-A, the ExtensionDataObject is serialized into the v2 members.

In my example, in the client side I have a search button with which I can either search by Title of the book or id of the book.

        protected void SearchButton_Click(object sender, EventArgs e)
        {
            using (BookShopClient client = new BookShopClient(preferedBinding))
            {
                Book bookSearched = null;
                if (!string.IsNullOrEmpty(SearchByTitleText.Text))
                {
                    GetBookByTitleRequest request = new GetBookByTitleRequest(SearchByTitleText.Text);
                    GetBookByTitleResponse response = client.GetBookByTitle(request);
                    bookSearched = response.GetBookByTitleResult;
                }
                else if (!string.IsNullOrEmpty(SearchByIdText.Text))
                {
                    long bookId;
                    long.TryParse(SearchByIdText.Text, out bookId);
                    GetBookRequest request = new GetBookRequest(bookId);
                    GetBookResponse response = client.GetBook(request);
                    bookSearched = response.GetBookResult;
                }
                if (bookSearched != null)
                {
                    CheckAvailabilityRequest request = new CheckAvailabilityRequest(bookSearched);
                    CheckAvailabilityResponse response = client.CheckAvailability(request);
                    ResultLabel.Text = String.Format("{0} books are available!", response.CheckAvailabilityResult);
                }
            }
        }

When I search using the id or title, GetBook() operation is called which in turn returns a Book object.

Here in the client side, Book doesn’t have the member “Edition”. But when the server returns the response, the Book object returned would contain Edition.

During deserialization, BookID, Title, Author are mapped to the respective members of the Book object in client. Edition value received from server is stored in ExtentionData property of the Book object.

When the CheckAvailabilty() operation is called, the same Book object is sent as parameter. Here during the serialization, “Edition” is retrieved from ExtensionData and put as a Book’s member, so that the service would get back the original data without any loss.

Here is the on wire data.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<ActivityId CorrelationId="b6955eb2-06ba-4f98-a1c0-fa128ab9fe8a" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">db08b8e9-94d3-4849-9fb6-3703a2572d7e</ActivityId>
</s:Header>
<s:Body>
<GetBookResponse xmlns="http://www.8fingergenie.com/">
<GetBookResult xmlns:a="http://8fingergenie.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Author>Spencer Johnson</a:Author>
<a:BookID>1</a:BookID>
<a:Edition>1.0</a:Edition>
<a:Title>Who Moved My Cheese</a:Title>
</GetBookResult>
</GetBookResponse>
</s:Body>
</s:Envelope>

 

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://www.8fingergenie.com/BookShop/CheckAvailability</Action>
<ActivityId CorrelationId="4c4d8eda-a7ed-4b2b-97e2-4bb875d39292" xmlns="http://schemas.microsoft.com/2004/09/ServiceModel/Diagnostics">5a4db2e8-5dc6-4dec-89ac-308f46c1538c</ActivityId>
</s:Header>
<s:Body>
<CheckAvailability xmlns="http://www.8fingergenie.com/">
<book xmlns:d4p1="http://8fingergenie.com" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<d4p1:Author>Spencer Johnson</d4p1:Author>
<d4p1:BookID>1</d4p1:BookID>
<d4p1:Edition>1.0</d4p1:Edition>
<d4p1:Title>Who Moved My Cheese</d4p1:Title>
</book>
</CheckAvailability>
</s:Body>
</s:Envelope>

You can notice that Edition is available in GetBook() operation’s response and the same value can be seen in CheckAvailability() operation’s request. Between these the value is actually preserved in ExtentionData Property.

 

Summary

WCF has made all the necessary moves for decoupling the service provider and consumer by providing various Version Tolerance mechanisms.

We have seen in this paper about three scenarios where version tolerance mechanism of WCF ensures that the service is intact regardless of data contracts’ version changes.