Sharing DataContracts between WCF Services

In a previous article, Multiple web service references sharing types, I wrote about sharing types between referenced web services. That article used the old ASP.NET Web Service technique, but nowadays WCF is the technology to use. In this article I will use much the same scenario as in the previous article, Visual Studio 2010 and of course WCF (.NET 4).

I will describe the scenario below, describe what may be an upcoming problem and what can be done to avoid it. Please read the previous article as well, it is much the same solution that is to be presented here.

If you are not familiar with WCF and DataContracts you can read David Chappell’s article here: Introducing Windows Communication Foundation in .NET Framework 4. Aaron Skonnard has written a great article about WCF 4, read it here: A Developer’s Introduction to Windows Communication Foundation 4.

The scenario

Let’s consider the following scenario:

One WCF service exposes a number of methods, in this example there will be only two:

  • GetCustomer: takes as parameter an integer, an ID of a customer, and returns the customer with that ID from the repository.
  • GetCustomerStatus: takes as parameter a customer and returns a string based on the age of the customer that tells the client whether the customer is allowed to place orders or not.

The interface exposed by the service is shown below. Please note the attribute [ServiceContract] on the interface and [OperationContract] on the methods.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using ServicesDataContracts;

namespace CustomerService
{
    [ServiceContract]
    public interface IHandler
    {
        [OperationContract]
        Customer GetCustomer(int id);

        [OperationContract]
        string GetCustomerStatus(Customer c);
    }
}

These two methods uses the same DataContract. A DataContract in the WCF world is a class defining a type exposed by the service. This class may contain a lot of stuff, methods and functions. The properties to be exposed to the consumers are marked with an attribute, [DataMember]. These properties may include logic in their get and set methods. The Customer DataContract is shown here, see the comments below the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;

namespace ServicesDataContracts
{
    [DataContract]
    public class Customer
    {
        [DataMember]
        public int ID { get; set; }

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

        [DataMember]
        public DateTime BirthDate { get; set; }

        ///
        /// True if the customer is 18+ years old, false otherwise.
        ///
        public bool IsAllowedToShop
        {
            get
            {
                if (DateTime.Now.Date.AddYears(-18) >= BirthDate.Date)
                    return true;

                return false;
            }
        }

        ///
        /// Returns a text based on the age of the customer.
        ///
        public string GetStatusMessage()
        {
            if (IsAllowedToShop)
                return string.Format(
                    "[{0}] {1} is allowed to place orders",
                    this.ID, this.Name);

            return string.Format(
                "[{0}] {1} is NOT allowed to place orders. Reason: too young, born {2}.",
                this.ID, this.Name, this.BirthDate.ToShortDateString());
        }
    }
}

Some notes on the code above:

  • I have decided to place the Customer class in a separate class library, the namespace is therefor ServicesDataContracts.
  • On line 5: the System.Runtime.Serialization namespace contains the DataContract attribute. Please note that it is also required to add a reference to System.Runtime.Serialization to use the DataContract attribute.
  • The DataMember attribute is added to the properties to be exposed by the service DataContract. In this example these properties are ID, Name and BirthDate.
  • The property IsAllowedToShop is not exposed.
  • The function GetStatusMessage is not exposed.
  • The properties exposed by the DataContract may contain logic in the get and set methods, but this is not transferred to the client when adding a service reference to client project.

A consumer of the service and the DataContract above may look like this:

CustomerService.HandlerClient customerClient = new CustomerService.HandlerClient();
CustomerService.Customer customer1 = customerClient.GetCustomer(1);
string statusText = customerClient.GetCustomerStatus(customer1);

This consumer holds a reference to the service through the CustomerService namespace. The namespace is defined when adding the service reference.

So far the service interface is small and tiny, but it will for sure grow over time. It comes to a point when it is hard to maintain the service and therefor it may be necessary to split it into a number of smaller services. Take a look at the picture below, it may make things clearer what I want to do with the WCF Service.

The service consumer shall be able to pass objects from one service to another, without any casting.

The split and types sharing

The scenario stated above is trivial and the service interface is small, but still let’s have a look at how to split it. The same procedure when splitting is probably applicable on larger services with a lot of DataContracts. The following will be done:

  • Create a service exposing the GetCustomer method. This service holds the customer repository, in this case it will be a fake repository.
  • Create a service exposing the GetCustomerStatus method.
  • The two services will use the same DataContract, the Customer class shown above.
  • Create a DataContract to be distributed to the client(s) (I will describe why below).
  • Create a client application, referencing the two services.

So why should the DataContract be distributed separately to the service consumers? When adding service references to the two services created, the Customer class will be of different types depending on what service the client will use. This makes it harder to get a customer from the service exposing the GetCustomer method and pass this customer on to the service exposing the GetCustomerStatus. The customer returned by the first service must be casted to a customer object type handled by the second service. In this example the customer class is small, but it would still be nice to just be able to pass the object on to the next service.

The first two things in the list above is easy, add two WCF Service Applications to the Visual Studio solution. Add the methods from the interface above to the new services, the GetCustomer method goes into the first interface and the GetCustomerStatus method goes into the second one. Both services need to reference the DataContract class library, the same library used above. After that, compile the solution.

The trick to make the services share the same types on the client side is to have the DataContract in a separate class library. This makes it possible to use the svcutil tool that comes with Visual Studio. With that tool you can extract the DataContracts defined in a class library, so let’s do it.

DataContract extraction

Open a Visual Studio command prompt and execute the following command, the output is shown below:

svcutil /dconly <path to the output DataContract class library dll>


This command will extract the DataContracts from the .dll file and create a number of .xsd files. The .xsd files can be distributed to the service consumer teams around, that will consume and use theses DataContracts. In this example I will use the .xsd file with the same name as the DataContract class library .dll file and generate a class file to be included in the client project. So let’s do this. Note that the .xsd file in my case is called ServicesDataContracts.xsd since the class library holding the contract is called ServicesDataContracts. The out parameter lets us decide the name of the class file to be generated. The output is shown below.

svcutil /dconly /language:CS ServicesDataContracts.xsd /out:CustomerDataContracts.cs


The class file generated by the above command line is added to a client side class library project. In my case I decided to call this project ConsumerDataContracts.

Adding service references

Now it’s time to add service references to the two services created above. But before that, let the client application reference the class library where the ConsumerDataContracts class was added. The solution will look like this:

There you can see the DataContracts class library and the reference to that from the ServiceConsumer project. In my case I used a Windows Forms application to consume the services.

Now it’s time to add the service references. Right-click and select “Add Service Reference…”. Enter the address to the first service, give it a namespace of your choice (I entered “CreatorService”), click the Advanced button and make sure to select “Reuse types in specified referenced assemblies” and after that select the ConsumerDataContracts reference. This looks like this:

Click OK. The service reference will now be added to the project. Repeat these things adding a reference to the second WCF service. I called the second service reference ControllerService. The solution will now look like this:

Since we added the services with the reuse types settings like this, the two service references will use the Customer DataContract in the ConsumerDataContracts.

Now we’re done, it is now possible for the consumer to make calls to the services like this:

CreatorService.CreatorClient creatorServiceClient =
   new CreatorService.CreatorClient();
Customer customer1 = creatorServiceClient.GetCustomer(1);

ControllerService.ControllerClient controllerServiceClient =
   new ControllerService.ControllerClient();
string statusText = controllerServiceClient.GetCustomerStatus(customer1);

Some notes on the code above:

  • On line 1 and 2: The CreatorService client is created
  • On line 3: A customer object is returned from the CreatorService
  • On line 5 and 6: The ControllerService client is created
  • On line 7: The customer object is passed to the ControllerService

The service method invocation on line 7 wouldn’t be possible, it would generate a compilation error, if we hadn’t reused the types when we added the service references earlier.

What just happened?

I will try to summarize what we have done:

  • We wanted to split an existing WCF service into smaller parts.
  • We wanted the smaller services to share types on the client side
  • We created two smaller services using the same datacontract class library as the larger one
  • We extracted the datacontracts and added them to a client class library project
  • We added service references to the client project and let them reuse types from the client class library project where we added the extracted datacontracts
  • We tried the services out by getting a customer object from one service and pass this on as a parameter to another service

No casting is needed and the client code will be very clean and nice. The code on the service side is exactly the same as before the split, the only difference is that half the code goes into one service and the other half goes into the other service.

As soon as I have solved the problems to attach files to a post, I will publish the solution I used to try this out. This solution contains both the service before and after the split. The service consumer consists of two forms, one that uses the “large” service and one that uses the two smaller services.

Tagged with: , , , ,
Posted in .NET, C#, WCF
18 comments on “Sharing DataContracts between WCF Services
  1. mea johanson says:

    Hi, I was searching a page of best softwares on the internet and i found a google blog , so nature and usefull blog . Also , many software is free . I liked that page and i advice to all of you :http://oncowepa.blogspot.com/

  2. Asela says:

    This doesn’t work, simply the service reference regenerates the proxy classes in the client again ignoring the reuse types…

  3. Stefan says:

    I agree with Asela, this scenario doesn’t work …

  4. Matt says:

    It worked great for me! Thanks!

  5. Sid says:

    please upload sample files…

  6. Roland says:

    Doesn’t work for me either…very frustrating. I wonder if it is a Visual Studio SP1 issue

  7. Your scenario wouldn’t work, if consumer will not have a copy of ServicesDataContracts.DLL.

  8. orgone says:

    I’m extremely impressed along with your writing skills as neatly as with the structure on your weblog. Is that this a paid subject matter or did you customize it your self? Anyway stay up the excellent high quality writing, it is uncommon to look a nice weblog like this one today..

  9. Kumar K says:

    Really good article. but it is concidering CollectionDataContracts . i added service reference , it is referening the contractassembly for the DataContracts but it is creating classes for the CollectDataContract eventhough if it is available in my contract assemblt :( .

  10. Bruce Jia says:

    This is really a nice post. But a question – why do you need to extract DataContract from the DLL if you have distributed the DLL to consumer side? Looking forward to your reply. Thanks a lot for your excellent blog.

  11. I think we should user
    svcutil.exe /dCOnly /language:CS *.xsd
    instead of
    svcutil /dconly /language:CS ServicesDataContracts.xsd /out:CustomerDataContracts.cs

  12. Sam says:

    Indeed a very educative and nice article thanks. I am working on a scenario that is a mix of your both articles i.e.” Using interfaces when implementing web services” and “Sharing DataContracts between WCF Services” , somehow it is not working, the exact scenario is to coonsume a WCF service and ASP.net Webservice using a common proxy i.e.

    ASP.Net Webservice
    CommonConsumer (To Consume using sameproxy)
    WCFWebservice

    As an Example (referring to the same example in your aricle Using interfaces when implementing web services)

    The WeightCalculator should be able to consume
    i) TypeACalculator webservice that is a ASP .net webservice
    ii) TypeWCFCalculator WCF webservice
    using a single proxy.

    I have tried creating multiple bindings of different types in order to make WCF service same as ASP.net Webservice but no luck.

    Thanks in advance for your suggestions and inputs

  13. Sam says:

    Awaiting inputs and suggestions

  14. Eyad says:

    @Kumar K for using collection types ex:
    [CollectionDataContract(Name = "DataObject")]
    public class DataObject : Dictionary
    {

    }
    you need to add this to ServiceRefrence.svcmap file

    Hope it helps…

  15. Eyad says:

    The code didn’t show above, hope now I see it

  16. Nick says:

    Hi,

    This is a great article and works a treat. I actually got it working by referencing the DLL that contains the contracts directly, rather than having to export the xsd and then have the second library with the exported .cs file.

    My only concern is that because the client needs to have the DLL, how will this work for non-.Net clients needing to communicate with the WCF services?

  17. xr280xr says:

    Excellent walk-through, thank you. I agree with ibrahim cobani, *.xsd should be used. The other files are important (and sometimes required) for more complex DataContracts. I also needed to check mscorlib as an assembly to reuse types from, otherwise it generates type definitions for system types. Is there a reason not to use the “Reuse types in all referenced assemblies” option?

    Otherwise it worked for me with the exception of a few return types that use generics. I have a return type called ServiceResponse. VS generates types such as ServiceResponseOfboolean when T is a bool but these were not generated by svcutil. I think it would be helpful if you could explain how, specifically, checking Reuse Types maps the service definition to the types in the ConsumerDataContracts assembly. Thanks again for the article!

  18. xr280xr says:

    The type is ServiceResponse(T), a generic class. My syntax was stripped above.

1 Pings/Trackbacks for "Sharing DataContracts between WCF Services"
  1. [...] order to share exceptions with other platforms, there is a good blog post here, I summarised that here for our [...]

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>