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.