Monday, July 2, 2012

WCF Versioning

Part 1: WCF Versioning
Part 2: WCF Versioning-Understanding the versioning and common issues
In Part 1 of WCF versioning, we have seen the common scenarios in handling the versioning of WCF services. Let us make the property Description from the Customer class at the service side to “isRequired = false” thus telling the clients that the service is not expecting the value to be set for this property and run it. It should be good now.
[DataMember(IsRequired = false, Order = 3)]
        public string Description
        {
            get { return m_Description; }
            set { m_Description = value; }
        }


To demonstrate the IExtensibleDataObject usage, let us add configuration for the service in the host project created as below:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      <system.serviceModel>
            <services>
                  <service name="HelloWorld.HelloWorld" behaviorConfiguration="HelloWorldBehavior">
                        <endpoint address="HelloWorld" binding="basicHttpBinding" contract="HelloWorld.IHelloWorld">
                        </endpoint>
                        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
                        <host>
                              <baseAddresses>
                                    <add baseAddress="http://localhost:8000/HelloWorld"/>
                              </baseAddresses>
                        </host>
                  </service>
            </services>
            <behaviors>
                  <serviceBehaviors>
                        <behavior name="HelloWorldBehavior">
                              <serviceMetadata httpGetEnabled="true"/>
                        </behavior>
                  </serviceBehaviors>
            </behaviors>
      </system.serviceModel>
</configuration>
Now let us add the service reference to the client project by running an instance of the host project to make the service available through basicHttpBinding.
If you go the Reference.cs file that is added to the client project after referencing the service through service reference, you will find that the class is already implementing the IExtensibleDataObject.
public partial class Customer : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged
Let us now discuss on what is this interface about. IExtensibleDataObject preserves unknown elements during serialization of data contracts. During de-serialization superfluous data is placed in a dictionary object so that the same data is provided to the client as provided in the xml during serialization.
Let us see that in practice in our example:
Go to Customer datacontract class in the service project and make the Customer class to implement the IExtensibleDataObject. Now your data contract class should look like as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace HelloWorld
{
    [DataContract]
    public class Customer:IExtensibleDataObject
    {
        private int m_Id;
        private string m_Name;
        private string m_Address;
        private string m_Description;

        [DataMember(IsRequired=true, Order=0)]
        public int Id
        {
            get { return m_Id; }
            set { m_Id = value; }
        }

        [DataMember(IsRequired = true, Order = 1)]
        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        [DataMember(IsRequired = false, Order = 2)]
        public string Address
        {
            get { return m_Address; }
            set { m_Address = value; }
        }

        [DataMember(IsRequired = false, Order = 3)]
        public string Description
        {
            get { return m_Description; }
            set { m_Description = value; }
        }


        #region IExtensibleDataObject Members
        private ExtensionDataObject m_extensionData;
        public ExtensionDataObject ExtensionData
        {
            get
            {
                return m_extensionData;
            }
            set
            {
                m_extensionData = value;
            }
        }

        #endregion
    }
}

If you observe the class code highlighted in bold, what this does is in case if a client has old proxy for the service and he did not upgraded with the latest changes that we made on the service side like removing some of the required properties and clients are not aware of. This helps in preserving unexpected data from the clients and sent back the same to the clients.
Now let us comment out one of the property in the datacontract which is decorated with “isRequired=true” and test what happens.
Now we are going to comment the property “Name” in the data contract.
  //[DataMember(IsRequired = true, Order = 1)]
        //public string Name
        //{
        //    get { return m_Name; }
        //    set { m_Name = value; }
        //}
Client is not aware of this change now.
Also notice that since now we have added service reference, we are going to make the following changes to the client project.
1.       Remove the Customer.cs file from the client project.
2.       Remove the interface copied in the program.cs and make the appropriate changes as below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;


namespace Client
{
    class Program
    {

        static void Main(string[] args)
        {

            MyServiceReference.HelloWorldClient proxy = new Client.MyServiceReference.HelloWorldClient("NetTcpBinding_IHelloWorld");
            MyServiceReference.Customer customer = new Client.MyServiceReference.Customer();
            customer.Id = 1;
            customer.Name = "John";
            customer.Address = "NYC";
            proxy.SetCustomer(customer, "IamnotRequired");

            MyServiceReference.Customer localCustomer = proxy.GetCustomer();

            Console.WriteLine("Customer ID is {0}", localCustomer.Id);
            Console.WriteLine("Customer Name is {0}", localCustomer.Name);
            Console.WriteLine("Customer Address is {0}", localCustomer.Address);
            Console.ReadLine();
        }
    }
}

Now put break points at the code in the data contract and check what is happening at the extensible object:

We have commented out the Name property in the DataContract and so our service does not support the property anymore. Client is not aware of this and it sends the Name property. Since, it is not expected by the service, it will be stored in a Dictionary object while setting the property and retrieved from the same while displaying the data at the client side. You can now store N number of unexpected properties in the dictionary object provided by the ExtensionDataObject type and return the same to the client. This makes the client not to lose the data what he has sent to the service.
Note that though the client sends the data it is useless at the service side. Nothing is getting processed at the service side based on the data sent by the client. Do you think this is somewhat desirable? If it is one property, then it is fine. Let us assume the service has 100 properties where only 50 are expected, this will result in unnecessary use of server resources which is unwanted.
Now the output has all the values returned though service does not support the Name property. Now remove the implementation part and run the host and the client, you will observe that the Name is not getting displayed. Test this by yourself.
Note: At the client side we are still using the NetTcpBinding, not the basicHttpBinding. Do you know why?
MyServiceReference.HelloWorldClient proxy = new Client.MyServiceReference.HelloWorldClient("NetTcpBinding_IHelloWorld");
Let us find out why? I am now making it to use the Http.
MyServiceReference.HelloWorldClient proxy = new Client.MyServiceReference.HelloWorldClient("BasicHttpBinding_IHelloWorld");
Now run the host and client projects and see what happens.

You will find that the customer object is null while we are consuming the method of the same service. This is because Http is stateless and hence it will not maintain state between two requests. This is a test for the stateless nature of Http J
The following is from stackoverflow.com site:
For example, the BasicHttpBinding can never have a transport-level session due to the connectionless nature of the HTTP protocol. The WSHttpBinding without security and without reliable messaging will also not maintain a transport-level session. In both of these cases, even though the service is configured with InstanceContextMode.PerSession and the contract with SessionMode.Allowed, the service will behave as a per-call service, and the calls to Dispose() are asynchronous; that is, the client is not blocked after the call while WCF disposes of the instance.
However, if you use the WSHttpBinding with security (its default configuration) or with reliable messaging, or the NetTcpBinding, or the NetNamedPipeBinding, then the service will behave as a per-session service.
A point to note is that even if our service does not support IExtensibleDataObject, still the client proxy generated will have it supported. There is a disadvantage of implementing IExtensibleDataObject. It carries risks of denial of service (DoS) and unnecessary use of server resources.

We can turn on and off, the support for IExtensibleDataObject either in code declaratively using attributes or in the configuration file as shown below.
Declaratively:
 <behaviors>
                  <serviceBehaviors>
                        <behavior name="HelloWorldBehavior">
                              <serviceMetadata httpGetEnabled="true"/>
                              <dataContractSerializer ignoreExtensionDataObject="true"/>
                        </behavior>
                  </serviceBehaviors>
            </behaviors>

Programmatically:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,IgnoreExtensionDataObject=true)]
    public class HelloWorld : IHelloWorld
Versioning strategies:
There are two types of versioning as dictated by Microsoft. One is the strict versioning policy and other one Practical Versioning.
Strict Versioning:
Any changes to the service or data contract require formal versioning. That is for every small change we need to version our service and the old clients should still be continuing with the old service version without any affect. New clients will be consuming the newly versioned service.
Practical Versioning:
If the changes are made to the service or data contract which will not require new properties or methods that are expected by the service, we do not need to create a version for the service. If the service can forgive the missing elements or can preserve unknown elements, we do not need to create a version for the service. Only when the client is affected with the change, a version is required.
How can we version the contracts?
One way is to create a copy of the same class and name it with a different one and inherit the version1.0 class and expose the second one through the same end point or another end point based on the changes.
Microsoft suggests following the below conventions for creating a WCF service for the first time:
è Define DataContracts by providing explicit Name and NameSpace. Also set the data members with IsRequired and Order attribute values.
è Define ServiceContracts by providing explicit Name and NameSpace. Use the data contracts for parameters and return types.
è Produce contracts and policy for the service. May include one or more endpoints and define the behaviors for each endpoint if needed.

This is copied from the Microsoft WCF Webcasts. This flow chart gives clear picture on Non-strict service contract versioning.


In semi-strict versioning, even if you have added an operation or parameter changed, we need to create another version but in once case we can still expose the service through the same endpoint.

In the strict service contract versioning, we need to create another version of the service contract without inheriting the same class and should expose it through another endpoint.
Now let us see in practice what the above flow charts speak about the versioning from Microsoft web casts.
In our example, we have two methods in the interface IHelloWorld:
[ServiceContract(SessionMode=SessionMode.Allowed)]
    public interface IHelloWorld
    {
        [OperationContract]
        void SetCustomer(Customer customer, string IamNotRequired);

        [OperationContract]
        Customer GetCustomer();

      
     
    }
Let us assume we have only one endpoint using netTcpBinding. So client has a proxy which supports two methods from the contract IHelloWorld.
IHelloWorld is the initial version of the service contract.
It is exposed through one endpoint.
host.AddServiceEndpoint(typeof(HelloWorld.IHelloWorld), new NetTcpBinding(), "net.tcp://localhost:9000/HelloWorld");
Client has the version 1.0 proxy.
Now, we have added a new method in the service contract. (New version 2.0???)
[ServiceContract(SessionMode=SessionMode.Allowed, Namespace="http://www.ourexamples.com/2012/06/01")]  
 public interface IHelloWorld
    {
        [OperationContract]
        void SetCustomer(Customer customer, string IamNotRequired);

        [OperationContract]
        Customer GetCustomer();

        [OperationContract]
        void WasteMethod();
     
    }
Non-strict approach:
Now the old clients still assume that the contract has two methods supported and consume the two. But the new clients will now get the 3 methods. Observe that still the endpoint through which the new version is exposed is still the same. Notice that we did not change the namespace.
This one is already tested in our previous examples and it worked without any issues.
Semi-strict approach:
To add a new method, first create an interface with different namespace and inherit the IHelloWorld interface and add a new method to it.
We have played with lot of changes in the previous examples and so I am pasting my code for testing this once again.
Here goes my DataContract class code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace HelloWorld
{
    [DataContract(Namespace="http://www.microsoft.com/HelloWorld/2012/06")]
    public class Customer:IExtensibleDataObject
    {
        private int m_Id;
        private string m_Name;
        private string m_Address;
        private string m_Description;

        [DataMember(IsRequired=true, Order=0)]
        public int Id
        {
            get { return m_Id; }
            set { m_Id = value; }
        }

        //[DataMember(IsRequired = true, Order = 1)]
        //public string Name
        //{
        //    get { return m_Name; }
        //    set { m_Name = value; }
        //}

        [DataMember(IsRequired = false, Order = 2)]
        public string Address
        {
            get { return m_Address; }
            set { m_Address = value; }
        }

        [DataMember(IsRequired = false, Order = 3)]
        public string Description
        {
            get { return m_Description; }
            set { m_Description = value; }
        }


        #region IExtensibleDataObject Members
        private ExtensionDataObject m_extensionData;
        public ExtensionDataObject ExtensionData
        {
            get
            {
                return m_extensionData;
            }
            set
            {
                m_extensionData = value;
            }
        }

        #endregion
    }
}

And here goes my interface code:
[ServiceContract(SessionMode=SessionMode.Allowed, Namespace="http://www.ourexamples.com/2012/06/01")]
    public interface IHelloWorld
    {
        [OperationContract]
        void SetCustomer(Customer customer, string IamNotRequired);

        [OperationContract]
        Customer GetCustomer();

     
    }
And here goes my service class code:
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,IgnoreExtensionDataObject=true)]
    public class HelloWorld : IHelloWorld
    {
        private Customer customer;
        #region IHelloWorld Members

        public void SetCustomer(Customer customer, string IamNotRequired)
        {
            this.customer = customer;
            Console.WriteLine("Setting Customer");
        }

        public Customer GetCustomer()
        {
            return this.customer;
        }

        #endregion


    }
Here goes my Host code:
namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(HelloWorld.HelloWorld)))
            {
                host.AddServiceEndpoint(typeof(HelloWorld.IHelloWorld), new NetTcpBinding(), "net.tcp://localhost:9000/HelloWorld");
                host.Open();
                Console.ReadLine();
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
      <system.serviceModel>
            <services>
                  <service name="HelloWorld.HelloWorld" behaviorConfiguration="HelloWorldBehavior">
                        <endpoint address="HelloWorld" binding="basicHttpBinding" contract="HelloWorld.IHelloWorld ">
                        </endpoint>
                        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
                        <host>
                              <baseAddresses>
                                    <add baseAddress="http://localhost:8000/HelloWorld"/>
                              </baseAddresses>
                        </host>
                  </service>
            </services>
            <behaviors>
                  <serviceBehaviors>
                        <behavior name="HelloWorldBehavior">
                              <serviceMetadata httpGetEnabled="true"/>
                              <dataContractSerializer ignoreExtensionDataObject="true"/>
                        </behavior>
                  </serviceBehaviors>
            </behaviors>
      </system.serviceModel>
</configuration>
Here goes my client project program.cs code:
namespace Client
{
    class Program
    {

        static void Main(string[] args)
        {
            Console.WriteLine("I am a old client");

            MyServiceReference.HelloWorldClient proxy = new Client.MyServiceReference.HelloWorldClient("NetTcpBinding_IHelloWorld");
            MyServiceReference.Customer customer = new Client.MyServiceReference.Customer();
            customer.Id = 1;
            //customer.Name = "John";
            customer.Address = "NYC";
            proxy.SetCustomer(customer, "IamnotRequired");

            MyServiceReference.Customer localCustomer = proxy.GetCustomer();

            Console.WriteLine("Customer ID is {0}", localCustomer.Id);
            //Console.WriteLine("Customer Name is {0}", localCustomer.Name);
            Console.WriteLine("Customer Address is {0}", localCustomer.Address);
            Console.ReadLine();
        }
    }
}
Now let us test the versioning of the service.
We want to add two new methods to the service. So, we first need to inherit the old interface with the new one and add the two methods to the new interface. Also note that we need to change the namespace.
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
    [ServiceContract(SessionMode=SessionMode.Allowed, Namespace="http://www.ourexamples.com/2012/06/01")]
    public interface IHelloWorld
    {
        [OperationContract]
        void SetCustomer(Customer customer, string IamNotRequired);

        [OperationContract]
        Customer GetCustomer();

     
    }

    [ServiceContract(SessionMode = SessionMode.Allowed, Namespace = "http://www.ourexamples.com/2012/06/02")]
    public interface IHelloWorld_V2 : IHelloWorld
    {

        [OperationContract]
        void WasteMethod();


        [OperationContract]
        void ShowMe();

    }
Now our service class should implement the new interface.
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession,IgnoreExtensionDataObject=true)]
    public class HelloWorld : IHelloWorld_V2
    {
        private Customer customer;
        #region IHelloWorld Members

        public void SetCustomer(Customer customer, string IamNotRequired)
        {
            this.customer = customer;
            Console.WriteLine("Setting Customer");
        }

        public Customer GetCustomer()
        {
            return this.customer;
        }

        #endregion


        #region IHelloWorld_V2 Members

        public void WasteMethod()
        {
            Console.WriteLine("Waste Method");
        }

        public void ShowMe()
        {
            Console.WriteLine("I am from new interface version 2");
        }

        #endregion
    }
Now I am going to update the endpoints with the interface name for the contracts.
namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            using (ServiceHost host = new ServiceHost(typeof(HelloWorld.HelloWorld)))
            {
                host.AddServiceEndpoint(typeof(HelloWorld.IHelloWorld_V2), new NetTcpBinding(), "net.tcp://localhost:9000/HelloWorld");
                host.Open();
                Console.ReadLine();
            }
        }
    }
}
<endpoint address="HelloWorld" binding="basicHttpBinding" contract="HelloWorld.IHelloWorld_V2">
Now build the host and now create a new console application for working with a new client and add the service reference to the service. Now the new client should have access to the new methods added in the service.
This is how the code looks like for my new client:
Console.WriteLine("I am a new client");
            MyServiceReference.HelloWorld_V2Client proxy = new NewClient.MyServiceReference.HelloWorld_V2Client("NetTcpBinding_IHelloWorld_V2");
            MyServiceReference.Customer customer = new NewClient.MyServiceReference.Customer();
            customer.Id = 1;
            //customer.Name = "John";
            customer.Address = "NYC";
            proxy.SetCustomer(customer, "IamnotRequired");
           

            MyServiceReference.Customer localCustomer = proxy.GetCustomer();

            Console.WriteLine("Customer ID is {0}", localCustomer.Id);
            //Console.WriteLine("Customer Name is {0}", localCustomer.Name);
            Console.WriteLine("Customer Address is {0}", localCustomer.Address);

            proxy.WasteMethod();
            proxy.ShowMe();

            Console.ReadLine();
Now run the host and the old client and new client programs and see if they work.

Yes. It worked without any exceptions.
Formal Versioning:
Formal Versioning dictates that every change we do to the service requires creation of new service and the new service should be exposed through a new end point and new clients should consume the new service through the newly created endpoint.
So, if we are creating a new service, the methods in the old service should be copied to the new one. No inheritance should be followed. Create a new service class for each new service.
For this example, we will host our service in the IIS to practice the hosting of WCF in IIS. You can refer to my article on how to host in IIS http://ilovemicrosoft.blogspot.com/search/label/WCF%20hosting%20in%20IIS
Let us start creating a simple WCF library and modify the interface as below:
// NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config.
[ServiceContract(Name="HelloWorldService",Namespace="http://www.helloworld.com/2012/7/1")]
    public interface IHelloWorld
    {

        [OperationContract]
        string ShowMessage(string message);


    }
Now modify the service class as below:
[ServiceBehavior(Namespace="http://www.helloworld.com/2012/7/1")]
    public class HelloWorld : IHelloWorld
    {


        #region IHelloWorld Members

        public string ShowMessage(string message)
        {
            return string.Format("Message {0} received from HelloWorld", message);
        }

        #endregion
    }
Our web.config file should look as below:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
      <system.serviceModel>
            <services>
                  <service name="HelloWorld.HelloWorld" behaviorConfiguration="myServiceBehavior">
                        <endpoint address="HelloWorld" binding="basicHttpBinding" contract="HelloWorld.IHelloWorld">
                        </endpoint>
                        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
                  </service>
            </services>

            <behaviors>
                  <serviceBehaviors>
                        <behavior name="myServiceBehavior">
                              <serviceThrottling maxConcurrentCalls="1000" maxConcurrentSessions="1000" maxConcurrentInstances="1000" />
                              <serviceMetadata httpGetEnabled="true" />
                        </behavior>
                  </serviceBehaviors>
            </behaviors>
      </system.serviceModel>
      <system.webServer>
            <directoryBrowse enabled="true" />
      </system.webServer>
</configuration>
Now host the service in the IIS. Create a console project for the client application.
Add reference of the service from the client application and test the service. Our client application should like below:
namespace Client
{
   
    class Program
    {
        static void Main(string[] args)
        {
            HelloWorldServiceClient proxy = new HelloWorldServiceClient();
            Console.WriteLine(proxy.ShowMessage("Client1 says Hello to you"));
            Console.ReadLine();
        }
    }
}
Now run the client application and test if it is able to communicate with the service.

Now we need to add another method to the service as a part of enhancements to the existing service. According to the Formal versioning, we now need to create another service which should be exposed through another end point.
There are 3 ways to implement the versioning of the services. One way is to create base interface which will have the common methods to be supported in all the services. All the new services created will inherit the common service type. Second way is to copy the existing interface methods into the new service contract and add the new methods in it. Third way is to let the new service class implement the old and new service contract.
Let us see one by one:
One way is to create base interface which will have the common methods to be supported in all the services. All the new services created will inherit the common service type. Note that we need to add the common methods to the base type.
[ServiceContract(Name = "HelloWorldService", Namespace = "http://www.helloworld.com/2012/7/1")]
    public interface IHelloWorldBase
    {
        [OperationContract]
        string BaseMessage(string message);

    }
[ServiceContract(Name = "HelloWorldService", Namespace = "http://www.helloworld.com/2012/7/2")]
    public interface IHelloWorldV1: IHelloWorldBase
    {
        [OperationContract]
        string ShowMessageV1(string message);
    }
[ServiceContract(Name = "HelloWorldService", Namespace = "http://www.helloworld.com/2012/7/3")]
    public interface IHelloWorldV2: IHelloWorldBase
    {
        [OperationContract]
        string ShowMessageV2(string message);
    }
Second way is to copy the existing interface methods into the new service contract and add the new methods in it. We are going to follow this method in our example.
[ServiceContract(Name = "HelloWorldService", Namespace = "http://www.helloworld.com/2012/7/2")]
    public interface IHelloWorldV1
    {
        [OperationContract]
        string ShowMessageV1(string message);
    }
[ServiceContract(Name = "HelloWorldService", Namespace = "http://www.helloworld.com/2012/7/3")]
    public interface IHelloWorldV2
    {
[OperationContract]
        string ShowMessageV1(string message);

        [OperationContract]
        string ShowMessageV2(string message);
    }
Third way is to let the new service class implement the old and new service contract.
Old service class:
[ServiceBehavior(Namespace="http://www.helloworld.com/2012/7/2")]
    public class HelloWorldV1 : IHelloWorldV1
    {


        #region IHelloWorld Members

        public string ShowMessageV1 (string message)
        {
            return string.Format("Message {0} received from HelloWorld", message);
        }

        #endregion
    }
New service class:
[ServiceBehavior(Namespace="http://www.helloworld.com/2012/7/2")]
    public class HelloWorldV2 : IHelloWorldV1, IHelloWorldV2
    {


      

        public string ShowMessageV1 (string message)
        {
            return string.Format("Message {0} received from HelloWorld", message);
        }
        public string ShowMessageV2 (string message)
        {
            return string.Format("Message {0} received from HelloWorld2", message);
        }


    }

Back to our example:
We create another interface and follow the second method described above. We also will create a new service class for the new version of the service.
[ServiceContract(Name = "HelloWorldService", Namespace = "http://www.helloworld.com/2012/7/2")]
    public interface IHelloWorldV2
    {
        [OperationContract]
        string ShowMessage(string message);

        [OperationContract]
        string ShowAnotherMessage(string message);
    }
[ServiceBehavior(Namespace = "http://www.helloworld.com/2012/7/2")]
    public class HelloWorldV2 : IHelloWorldV2
    {



        #region IHelloWorldV2 Members

        public string ShowMessage(string message)
        {
            return string.Format("Message {0} received from HelloWorld", message);
        }

        public string ShowAnotherMessage(string message)
        {
            return string.Format("Message {0} received from HelloWorldV2", message);
        }

        #endregion
    }
Now how to host two versions of the same service on multiple endpoints in IIS? You have to create a new endpoint in the web.config file as below:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
      <system.serviceModel>
            <services>
                  <service name="HelloWorld.HelloWorld" behaviorConfiguration="myServiceBehavior">
                        <endpoint address="HelloWorld" binding="basicHttpBinding" contract="HelloWorld.IHelloWorld">
                        </endpoint>
                        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
                  </service>
                  <service name="HelloWorld.HelloWorldV2" behaviorConfiguration="myServiceBehavior">
                        <endpoint address="HelloWorldV2" binding="basicHttpBinding" contract="HelloWorld.IHelloWorldV2">
                             
                        </endpoint>
                        <endpoint address="mexV2" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
                  </service>
            </services>

            <behaviors>
                  <serviceBehaviors>
                        <behavior name="myServiceBehavior">
                              <serviceThrottling maxConcurrentCalls="1000" maxConcurrentSessions="1000" maxConcurrentInstances="1000" />
                              <serviceMetadata httpGetEnabled="true" />
                        </behavior>
                  </serviceBehaviors>
            </behaviors>
      </system.serviceModel>
      <system.webServer>
            <directoryBrowse enabled="true" />
      </system.webServer>
</configuration>
Now to be able to host both of the services in the IIS in a same virtual directory, we need to create another .svc file for the second service and add the assembly name of the new service class.
So, our file structure in the virtual directory folder will be as below:

If you check HelloWorld.svc, the file will have following lines:
<% @ServiceHost Service="HelloWorld.HelloWorld" language="C#" debug="false" %>
<%@ Assembly Name="HelloWorld" %>
HelloWorldV2.svc file:
<% @ServiceHost Service="HelloWorld.HelloWorldV2" language="C#" debug="false" %>
<%@ Assembly Name="HelloWorld" %>
To get the first service reference, use the http://localhost/HelloWorld/HelloWorld.svc and for the new one http://localhost/HelloWorld/HelloWorldV2.svc to access the WSDL and for service reference of the project.
[To Parent Directory]

  7/2/2012  2:23 PM        <dir> bin
  7/2/2012  1:26 PM          116 HelloWorld.svc
  7/2/2012  2:21 PM          118 HelloWorldV2.svc
  7/2/2012  2:12 PM         1166 web.config
Now let us create another client project and refer to the new service.
So my new client code should look like below:

            MyServiceReference.HelloWorldServiceClient proxy = new NewClient.MyServiceReference.HelloWorldServiceClient();
           Console.WriteLine( proxy.ShowMessage("Client 2"));
            Console.WriteLine(proxy.ShowAnotherMessage("Client 2"));
            Console.ReadLine();
And if we run both the clients, we should see both running without any issues.

Similarly for the data contract versioning, provide explicit Name, Namespace, set data members name, isRequired and order.
Follow the below flow charts copied from Microsoft webcasts presentation for better understanding:

The above flow chart basically says that if changing the data contract does not affect the client and no operation need to be changed in the service, just make the change and do not worry about versioning the service contract. If the data contract has a significant change like you added a new property which is required or modified the property isRequired=false to true or changed the data type of a property which do not support the previous data type casting, then we need to create a new data contract which will have the old properties with the new changes and we also version the service contract and expose the new changes to the service through new endpoint. Refer the previous example where we have created entirely new service and exposed through a new end point.

Strict data contract versioning says that any changes to the data contract must be versioned and it should be incorporated in a new service and exposed thru a new end point. Old clients still good to work with the original version and the new clients will be consuming the new version.

No comments:

Post a Comment