Click here to Skip to main content
Click here to Skip to main content
Go to top

Pattern for Creating Generic WCF Services

, 28 Nov 2011
Rate this:
Please Sign up or sign in to vote.
A pattern for creating generic WCF services.

Introduction

I work on a solution that makes a lot of use of Generics. We have just started to add in a Silverlight control to the solution and we are using WCF to communicate with the existing code base.

I have looked around to find a solution where by we could have generic services that make use of the existing generic model. I didn't really find what I was looking for so I have come up with a test project that presents a pattern for generic services and I would like to put it out there to see what others think of it.

Using the Code

This project contains a base service class with a corresponding contract class that are both generic.

[ServiceContract]
interface IBaseService<T> where T : class, INumber
{ 
    [OperationContract]
    string GetData(int value); 
}

public class BaseService<T> : IBaseService<T>
    where T : class, INumber
{
    private T _num;

    public BaseService(INumberFactory<t> numberFactory)
    {
         _num = numberFactory.Create();
    }

    public string GetData(int value)
    {
        var ret = value * _num.Get();

        return ret.ToString();
    }
}

This base service class also has a dependency that will be resolved based on the type of T. This is just a basic factory class that will new up instances of our different type classes.

Here is the interface INumber with a couple of basic implementations:

public class NumberOne : INumber
{
    public int Get()
    {
        return 1;
    }
}

public class NumberTwo : INumber
{
    public int Get()
    {
        return 2;
    }
}

public interface INumber
{
    int Get();
}

The next stage is to declare some .svc files, one for each type we want the service to be used with; in this instance, one for NumberOne and one for NumberTwo. Here we need to specify the full assembly details of our type class.

ServiceOne.svc
<%@ ServiceHost Language="C#" Debug="true" 
     Service="Service.BaseService`1[[Service.NumberOne, Service, 
              Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]"
     Factory="Service.Host.Factory" %>
ServiceTwo.svc
<%@ ServiceHost Language="C#" Debug="true" 
    Service="Service.BaseService`1[[Service.NumberTwo, Service, 
             Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]" 
    Factory="Service.Host.Factory" %>

As you can see, here we are using a Factory class to create instances of our service host. This allows us to use a custom ServiceHost class.

public class Factory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        var host = new UnityServiceHost(serviceType, baseAddresses);

        return host;
    }
}

Our custom service host class will add a new behaviour that will allow us to manipulate the endpoints of our services.

public class UnityServiceHost : ServiceHost
{
    private  IUnityContainer _unityContainer;

    public UnityServiceHost(Type serviceType, Uri[] baseAddresses)
        : base(serviceType, baseAddresses)
    {
    }

    protected override void OnOpening()
    {
        base.OnOpening();

        if (this.Description.Behaviors.Find<unityservicebehavior>() == null)
            this.Description.Behaviors.Add(new UnityServiceBehavior());
    }
}

For each endpoint on our service, we now apply a custom instance provider.

public class UnityServiceBehavior : IServiceBehavior
{
    private readonly IUnityContainer container;

    public UnityServiceBehavior()
    {
    }

    public void Validate(ServiceDescription serviceDescription, 
                         ServiceHostBase serviceHostBase)
    {
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, 
           ServiceHostBase serviceHostBase, Collection<serviceendpoint> endpoints, 
           BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, 
                ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
        {
            foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints)
            {
                string contractName = endpointDispatcher.ContractName;
                if (contractName != "IMetadataExchange" && 
                    contractName != "IHttpGetHelpPageAndMetadataContract")
                {
                    ServiceEndpoint serviceEndpoint = serviceDescription.Endpoints.FirstOrDefault(
                              e => e.Contract.Name == contractName);
                    endpointDispatcher.DispatchRuntime.InstanceProvider = 
                        new UnityInstanceProvider(serviceEndpoint.Contract.ContractType);
                }
            }
        }
    }
}

This instance provider will now use Unity to create the service and all its dependencies.

public class UnityInstanceProvider : IInstanceProvider
{
    private readonly Type contractType;
    public UnityInstanceProvider(Type contractType)
    {
        this.contractType = contractType;
    }
    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }
    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return IocManager.Container.Resolve(contractType);
    }
    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        IocManager.Container.Teardown(instance);
    }
}

Finally, we have the IocManager that contains the configuration for our dependencies.

public class IocManager
{
    private static IUnityContainer _container;
    private static object _syncRoot = new object();

    public static IUnityContainer Container
    {
        get
        {
            if (_container == null)
                lock (_syncRoot)
                {
                    _container = GetIocContainer();
                }
            return _container;
        }
    }

    private static IUnityContainer GetIocContainer()
    {
        IUnityContainer container = new UnityContainer();

        ConfigureUnityContainer(container);

        return container;
    }

    private static void ConfigureUnityContainer(IUnityContainer container)
    {
        container.RegisterType<ibaseservice<numberone>, 
                  BaseService<numberone>>();

        container.RegisterType<ibaseservice<numbertwo>, 
                  BaseService<numbertwo>>();

        container.RegisterType<inumberfactory<numberone>, NumberFactoryOne>();

        container.RegisterType<inumberfactory<numbertwo>, NumberFactoryTwo>();
    }
}

Points of Interest

I think this is a nice enough way of creating generic services. The only thing I don't really like is the fact that I need to include all the assembly information for the type declarations in the .svc classes as this could turn into a bit of a maintenance nightmare.

But I would be very interested in any other thoughts others might have on this design.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

alexis.coles

United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
QuestionI don't Understand what makes the code generic. PinmemberEndalew19-Oct-13 10:32 
Questionhow to use unity config file? Pinmemberchicky_china19-Nov-12 21:54 
GeneralMy vote of 4 PinmemberKanasz Robert29-Nov-11 22:40 
GeneralRe: My vote of 4 Pinmemberalexis.coles30-Nov-11 0:25 
GeneralRe: My vote of 4 Pinmemberevgeniy.gryaznov7-Oct-13 18:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140916.1 | Last Updated 28 Nov 2011
Article Copyright 2011 by alexis.coles
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid