WCF services and shared assemblies

I often see situations on projects where they have design challenges with multiple webservices who share the same datacontracts (DTO). I already wrote a post about the separation of the service contract and the DTO’s, but i would like to extend that post with my vision on how we should work with mutiple services build on top of the same DTO’s.

To illustrate this I have build two WCF services which both have a reference to the DTO project called MyDataContracts. The solution for my services looks like this:

ServiceImplementation

Project 1 & 3 are service contracts and service implementations and project 2 contains the shared DTO’s.

The service contracts are very simple

[ServiceContract]
public interface ICustomerService
{
    [OperationContract]
    Customer GetCustomer(int value);
}
[ServiceContract]
public interface IInvoiceService
{
    [OperationContract]
    Invoice GetInvoice(int invoiceId);
}

The invoice DTO is implemented below

[DataContract]
public class Invoice
{
    int invoiceId = 0;
    Customer client;

    [DataMember]
    public int InvoiceId
    {
        get { return invoiceId; }
        set { invoiceId = value; }
    }

    [DataMember]
    public Customer Customer
    {
        get { return client; }
        set { client = value; }
    }
}

Because the invoice DTO has a datamember of the type customer the clients who want to interact with the invoice service have to know how a customer DTO will look like.

So that’s it for the service side. Now we take a look at the client side. When I consume services I usually create a service agent project which will be responsible for handling the calls to the services. This way I only have to implement exception handling once for the communication with my services. So my service agent project has two service references (two proxies). These two proxies will receive there own version of the customer DTO when I generate the proxy in the standard way. As you can see in the object browser below, both proxies have a Customer DTO generated. This can give you some annoying issues especially when you have to map values to your customer DTO and you have to pick a different one depending on the service you are communicating with.

ObjectBrowser

My client solution looks like this

ClientApplication

The first project is just a console application which interacts with my second project the service agent. I have created a service agent class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MyServiceAgents.CustomerProxy;
using MyServiceAgents.InvoiceProxy;
using System.ServiceModel;

namespace MyServiceAgents
{
    public class ServiceAgent
    {
        public Customer GetCustomer()
        {
            CustomerServiceClient proxy = new CustomerServiceClient();

            try
            {
                return proxy.GetCustomer(1);
            }
            catch (FaultException ex)
            {
                // catch any faultexception from the service
            }
            catch (CommunicationException cex)
            {
                // ...
            }
        }

        public Invoice GetInvoice()
        {
            InvoiceServiceClient proxy = new InvoiceServiceClient();

            try
            {
                return proxy.GetInvoice(1);
            }
            catch (FaultException ex)
            {
                // catch any faultexception from the service
                throw new Exception();
            }
            catch (CommunicationException cex)
            {
                // ...
            }
        }
    }
}

The service agent has two methods. Each method is responsible to communicate with one of the two services. To make sure my proxy classes are available i have to add the following using statements

  • using MyServiceAgent.CustomerProxy
  • using MyServiceAgent.InvoiceProxy
    Adding these using statements will give us the problem of ambiguous classes.

Ambiguous

Well take a look at what happened right now. On the right side we see the customer & invoice service. The service project references the contracts project which contain two DTO’s. On the left side I have the service agent project which contains two service references. Adding these service references added a proxy for both services and DTO’s which are needed for the communication. The customer DTO is created twice.

Overview1

To resolve this issue you could use a shared assembly for the DTO’s. You have to make sure that when the service reference is added on the client project it doesn’t generate any DTO’s anymore. To make this possible you have to share your DTO assembly with your consumers, which looks fine to me. This will give you the following situation.

Overview2

I hope that sharing an assembly doesn’t need any explanation. To make sure that adding a service reference doesn’t generate any DTO’s you only have to add a reference from your service agent project to the DTO assembly. This way Visual Studio knows how to handle the generation of your service reference. To be sure click on the advanced button when you add a service reference.

VisualStudioSetting

Make sure the checkbox ‘Reuse types in referenced assemblies’ is enabled and you are good.

This solution can be related the the following SOA pattern: Canonical schema The DTO’s are set as standard across service contracts within an inventory boundary.

Regards,

Dennis

Advertisements

13 Responses to WCF services and shared assemblies

  1. Pingback: WCF Services And Shared Assemblies? Dennis Van De Laar | Microsoft WCF Knowledge Sharing

  2. Geoff says:

    So in this example where would your domain Invoice class be located? Would it exist as two separate classes in both the client and server assemblies? Not in the shared assembly where the service contract resides? If so, it seems there may be some similar logic on both the client and server versions that might get duplicated. If not you may be exposing service specific details to the client(s).

    Likewise how might the mapping be different / similar / shared on both sides? Would you use something like AutoMapper to convert between DTO and DO within/after each service method?

    I’m liking what you are saying but a sample solution and/or some more specifics might help with the full picture.

    • dennisv says:

      Hi Geoff,
      Thanks for your feedback. I will provide a working solution as soon as possible.The domain invoice will only be on the serverside. You should map your domain invoice to you DTO invoice. The DTO invoice will be in the shared assembly. This assembly will be referenced at client and serverside. You can map your DTO’s on the client side to UI controls or other UI classes.

      So the step taking from the service to the client will be:
      Domain invoice –> Automapper –> DTO invoice –> Send over the wire –> DTO incoive –> UI control (Invoice control).

      The only assembly shared will be the DTO assembly. The domain object will stay on the serverside so there will be no details shared with the clients. This way you are also able to modify your service implementation without updating your client’s proxy. I hope i was able to answer your questions. I will update my post to clarify certain parts.

      • Geoff says:

        Thanks you for the post and the clarification. That makes sense. In my case on the client side the immediate consumer of the class isn’t a UI control. I can see some need for some common functionality useful on both the client and the server, while some data and functionality is only applicable at one or the other.

        For example the raw data in the DTO’s may have all the data that is needed but there might be calculated properties, readonly convenience properties, helper methods, calculations, and maybe even light validation that might be useful on both the client and the server. If that were put into the shared classes then they are no longer just DTO’s and it is the uglier shortcut you were referring to.

        If not there I was just thinking through how to best structure things if there is a need for both shared and different functionality on the client and server…

  3. Lula says:

    Thanks 🙂
    This works like a charm.

  4. Tee says:

    Hi Dennis, thanks for your nice post. But i’m now facing an issue. When my other assembly reference to service agent to invoke methods, I cannot send wcf message due to error “there was no endpoint listening at that could accept the message” although I can send wcf message and get return value in service agent itself (just test). What’s the source of this problem? Can you help me, thanks. Sorry for my bad English

    • dennisv says:

      Hi Tee,

      I am not sure if I understand your issue correctly, but i’ll try to answer your question. So you have a problem with invoking the service from your service agent project, but you are able to invoke the service from another (test) client. The first thing that comes to my mind is that the configuration file which contains the WCF configuration for your service agent isn’t correct. Maybe you are using a different binding in your client configuration then in you service configuration. Also take a look at the contract which your have defined on your endpoint. These contracts should match. The contract is used to resolve a endpoint at service side, so when you have a contract mismatch then you can get the error “there was no endpoint listening at that could accept the message”

      • Tee says:

        Thanks for your quick reply Dennis. My problem is: I have WCF service agent that reference to WCF services, my Project A reference to WCF Service agent to use it as a bridge to WCF services. On my WCF service Agent, I tried to invoke and get result as expected, but when I try to do that on my Project A, I got this error even I paste the app.config from WCF service agent to Project A. I solved this problem by using default Web.config WCF service application without manually config service. But I don’t understand why I manually config my service and ending point I can’t use my service agent from Project A. My service on Web.config is set like this:

  5. Tee says:

    Oops. I forgot my sourcecode tag

    <service name="VLUSM_Service.SystemService"
                   behaviorConfiguration="defaultBehavior">
            <endpoint address="/SystemService"
                      binding="wsHttpBinding"
                      contract="VLUSM_Service.IPortfolioService" />
     </service>
     
    • dennisv says:

      Hi Tee,

      Why do you want to invoke your service from Project A? The reason I use service agents (in one project) is because I have a central place to implement service communication. This is the place where I instantiated my proxies and write my try catch blocks to make sure communication (good or bad) with the service is handled correctly.

      You are using the contract ‘IPortfolioService’ in the web.config. Did you also configured the configuration file of your client to use the same contract?

      • Tee says:

        Hi Dennis, I’m using Prism to develop my application. Thus, I have to separate multiple modules, and these modules use Service Agent as a central place to manage all my service communication.

  6. I relish, cause I discovered just what I used to be taking a look for.

    You have ended my four day long hunt! God Bless you man.
    Have a great day. Bye

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: