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
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