Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WCF Architecture, and Evolution, Version

Tags:

c#

wcf

This question is around how to architect WCF services to make it easy to evolve over time. Its difficult to get the depth of response to this without describing the problem.

Background

I am developing a large system of WCF services and clients. The server side is "easy" to update as there are only 10 servers in question running this code.

The clients are very difficult to update, despite the high degree of automation, at 300,000+ WCF clients, updates are something that will always take time, and only achieves a high update success rate over a period of two to three weeks.

Data Contracts

[DataContract]
public class MyContract
{
    [DataMember]
    public int Identity {get; set;}

    [DataMember]
    public string Name {get; set;}

    // More members
}

The DataContract is difficult to initialise and has a standard MyContractFactory class to initialise obtain the appropriate instance for your machine.

public class static MyContractFactory
{
    public static MyContract GetMyContract()
    {
        // Complex implementation
    }
}

ServiceContracts

The DataContract is very common across a range of web services.

namespace MyPrefix.WebServicve1
{
    [ServiceContract]
    public class IMyInterface1
    {
        [OperationContract]
        public void DoSomethingWithMyContract(MyContract data);
    }

    [ServiceContract]
    public class IMyInterface2
    {
        [OperationContract]
        public void DoSomethingDifferentWithMyContract(MyContract data);
    }
}

Client

My client is plugin based with plugins running in either separate processes or app domains depending on the level of trust we have in that plugin.

Implementation 1

My initial implementation of this (default WCF) ended up with the DataContract in one assembly, ServiceContract, and implementation in its own assembly.

The clients ended up with a very ugly,

MyWebService1.MyContract
MyWebService2.MyContract

With a copy and paste of the MyContractFactory in nearly every plugin. Whilst the DataContract was the same, the fact that the clients did not include the DataContract assembly meant that it appeared under different namespaces as different objects.

Implementation 2

The clients now include the DataContract assembly, ServiceContracts are in a separate assembly to the service implementation, clients may include some of the ServiceContract assemblies if it will aid with code reuse (no more copy and paste).

Question

With the second implementation I am now facing the difficulty of, how do I update my DataContract and ServiceContracts?

  1. Do I update the same assembly and increment the version number? How do I preserve backwards compatibility whilst all the clients upgrade? Breaking the clients until they update is not acceptable.

  2. Do I create a new assembly with a class that extends MyDataContract, new methods that accept the new type under a new ServiceContract? Does that mean for every minor change to my contracts I need a new assembly? How would I stop it from getting to literally hundreds in a couple of years time?

  3. Some other solution?

Regardless of the solutions I think through, they all seem to have a major downside.

There doesn't seem to be (at least to me) of,

  • Preserving backwards compatibility until clients update
  • Keeping the clients trim with no bloat as the software evolves over time
  • Not significantly polluting my ServiceContract (overloads of the OperationContract need a new "name"). I already have things like the below, and it strikes me a nightmare to maintain over time.

Operation Contract complexity

[OperationContract]
public void DoSomethingWithMyContract(MyContract data);

[OperationContract(Name = "DoSomethingWithMyDataByAdditionalData"]
public void DoSomethingWithMyContract(MyContract data, MyContract2 additionalData);

I am looking for a solution that has worked over a period of time in a large scale environment. Blog entries and the like are very welcome.

Update 1

Looking through the limitations of using "schemaless" changes, different namespaces seems like the only sure method. However, its not quite working as expected, e.g. below

[ServiceContract(
    Name = "IServiceContract",
    Namespace = "http://myurl/2012/05")]
public interface IServiceContract1
{
    // Some operations
}

[ServiceContract(
    Name = "IServiceContract",
    Namespace = "http://myurl/2012/06")]
public interface IServiceContract2
{
    // Some different operations using new DataContracts
}

With the following service

public class MyService : IServiceContract1, IServiceContract2
{
    // Implement both operations
}

and the following config

  <service behaviorConfiguration="WcfServiceTests.ServiceBehavior"
    name="Test.MyService">
    <endpoint
      address="2012/05"
      binding="wsHttpBinding"
      contract="Test.IServiceContract1">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
    <endpoint
      address="2012/06"
      binding="wsHttpBinding"
      contract="Test.IServiceContract2">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>

Results in two contracts with two different names, I expected that I can point my clients to,

http://myurl.com/MyService.svc/2012/05 for the old version, and http://myurl.com/MyService.svc/2012/06

, but it seems like if I want to preserve the ServiceContract name they have to be two separate services rather than separate endpoint addresses for the same service?

Update 2

I ended up using the method I have described under update 1. Whilst the WSDL looks wrong, the service is indeed backwards compatible under older clients when I've tested this.

like image 806
M Afifi Avatar asked May 29 '12 10:05

M Afifi


1 Answers

Both Microsoft and most of the respected WCF gurus out there will probably say the same thing: versioning should be handled using the contract namespaces.

I don't mean the .NET namespaces of your assemblies - I mean the actual WCF service (and data contract) namespaces - so you should have:

[ServiceContract(Namespace="http://services.yourcompany.com/Service1/V01"]

or something the like - some folks like to version by year/month:

[ServiceContract(Namespace="http://services.yourcompany.com/Service1/2012/05"]

This allows you to have multiple versions of the same service, and as long as clients come calling with an older version (indicated by the service namespace), they'll get the old version (as long as you still expose that).

See:

  • Best Practices: Data Contract Versioning
  • Service Versioning
like image 151
marc_s Avatar answered Oct 11 '22 11:10

marc_s