Enabling InstanceProvider for Singleton Services
Many people like to use Dependency Injection containers (Unity, Windsor etc) to create WCF service objects and the common approach is to use WCF’s InstanceProvider extensibility hook to delegate instance creation to a DI container. By default this approach however doesn’t work for singleton services as InstanceProvider is never called. There is very simple fix to this problem which I’m going to explain in this post but first little background information.
WCF instancing revolve around following 3 objects:
InstanceContextProvider is very early in dispatch pipeline and has access to channel and message. This provider has the flexibility to reuse instance contexts based on either sessions or any other unique id in messages. When InstanceContextProvider returns an existing InstanceContext, dispatcher sees whether the returned InstanceContext has an active instance, if yes then that active instance gets used and InstanceProvider is never invoked. If there is no active instance then InstanceProvider is invoked to get one.
So when you configure your service’s InstanceContextMode=Single, there will always be cached instance and your provided will never be called. A simple fix is to overwrite the InstanceContext with a brand new one thus destroying the cached object.
dispatchRuntime.SingletonInstanceContext = new InstanceContext(serviceHostBase);
This above change will result in your InstanceProvider to be called (only first time) to get the required service instance. Here is complete code.
[ServiceContract]
public interface ICalc
{
[OperationContract]
int Add(int a, int b);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
public class Calc : ICalc
{
public int Add(int a, int b)
{
Console.WriteLine(this.GetHashCode());
return a + b;
}
}
class Program
{
static void Main(string[] args)
{
ServiceHost h = new ServiceHost(typeof(Calc));
h.Description.Behaviors.Add(new MyBv());
h.Open();
var cf = new ChannelFactory<ICalc>("*");
cf.Open();
var proxy = cf.CreateChannel();
for (int i = 0; i < 10; i++)
{
proxy.Add(11, 2);
}
Console.ReadLine();
}
}
public class MyBv : IServiceBehavior
{
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
var cd = serviceHostBase.ChannelDispatchers[0] as ChannelDispatcher;
var dispatchRuntime = cd.Endpoints[0].DispatchRuntime;
dispatchRuntime.SingletonInstanceContext = new InstanceContext(serviceHostBase);
dispatchRuntime.InstanceProvider = new MyInstanceProvider();
}
}
public class MyInstanceProvider : IInstanceProvider
{
public object GetInstance(InstanceContext instanceContext, System.ServiceModel.Channels.Message message)
{
var c = new Calc();
return c;
}
}