I've created a custom host factory to support container based serviced instantiation and imperfect async cancellation. The service is instantiated on a per-call basis:
public class DIServiceHostFactory : ServiceHostFactory
{
protected IContainer Container { get; private set; }
public DIServiceHostFactory()
: this(ContainerFactory.GetContainerForMap<DIServiceHostFactory>())
{
}
public DIServiceHostFactory(IContainer container)
{
this.Container = container;
}
protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new DIServiceHost(this.Container, serviceType, baseAddresses);
}
}
public class DIServiceHost : ServiceHost
{
protected IContainer Container { get; private set; }
public DIServiceHost() : this(ContainerFactory.GetContainerForMap<DIServiceHost>()) { }
public DIServiceHost(IContainer container)
{
this.Container = container;
}
public DIServiceHost(IContainer container, Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)
{
this.Container = container;
}
protected override void OnOpening()
{
Description.Behaviors.Add(CreateBehavior());
base.OnOpening();
}
protected virtual DIServiceBehavior CreateBehavior()
{
return new DIServiceBehavior(this.Container);
}
}
public class DIServiceBehavior : IServiceBehavior
{
protected IContainer Container { get; private set; }
public DIServiceBehavior(IContainer container)
{
this.Container = container;
}
public virtual void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
protected virtual DIInstanceProvider ConstructProvider(ServiceDescription description)
{
return new DIInstanceProvider(description.ServiceType, Container);
}
public virtual void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers.OfType<ChannelDispatcher>())
{
foreach (EndpointDispatcher ed in cd.Endpoints)
{
ed.DispatchRuntime.InstanceProvider = ConstructProvider(serviceDescription);
}
}
}
public virtual void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
public class DIInstanceProvider : IInstanceProvider
{
protected Type ServiceType { get; private set; }
protected IContainer Container { get; private set; }
protected ILogger Logger { get; private set; }
public DIInstanceProvider(Type serviceType, IContainer container)
{
this.ServiceType = serviceType;
this.Container = container;
ILogger logger;
container.TryResolve<ILogger>(out logger);
this.Logger = logger;
}
public virtual object GetInstance(InstanceContext instanceContext, Message message)
{
if (this.Logger != null)
this.Logger.Info(message.ToString());
CancellationTokenSource cancellationToken = new CancellationTokenSource();
var s = this.Container.Resolve(this.ServiceType);
var service = s as ICancellableService;
if (service != null)
{
service.CancellationToken = cancellationToken.Token;
instanceContext.Faulted += (sender, e) => cancellationToken.Cancel();
instanceContext.Closing += (sender, e) => cancellationToken.Cancel();
}
return s;
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public virtual void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}
The primary usage is through the netmsmq binding, I notice when I first start the application the client receives no errors and the MSMQ just holds the data until I hit the service over HTTP through a web browser, I'm not sure if this error is generated at this time. After that it will process through the queue until the app pool suspends due to inactivity (currently set at 20min). Then I receive the follow error frequently (but not with every client request, I had ~250 errors in my event log this morning and over 1500 messages waiting in my queue):
WebHost failed to process a request.
Sender Information: System.ServiceModel.ServiceHostingEnvironment+HostingManager/17653682
Exception: System.ServiceModel.ServiceActivationException: The service '/MerchReport/reportconsumer.svc' cannot be activated due to an exception during compilation. The exception message is: Object reference not set to an instance of an object.. ---> System.NullReferenceException: Object reference not set to an instance of an object.
at United.UEL.WCF.DIInstanceProvider..ctor(Type serviceType, IContainer container)
at United.UEL.WCF.DIServiceBehavior.ConstructProvider(ServiceDescription description)
at United.UEL.WCF.DIServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
at System.ServiceModel.Description.DispatcherBuilder.InitializeServiceHost(ServiceDescription description, ServiceHostBase serviceHost)
at System.ServiceModel.ServiceHostBase.InitializeRuntime()
at System.ServiceModel.ServiceHostBase.OnBeginOpen()
at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Open()
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.ActivateService(ServiceActivationInfo serviceActivationInfo, EventTraceActivity eventTraceActivity)
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
--- End of inner exception stack trace ---
at System.ServiceModel.ServiceHostingEnvironment.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath, EventTraceActivity eventTraceActivity)
at System.ServiceModel.ServiceHostingEnvironment.EnsureServiceAvailableFast(String relativeVirtualPath, EventTraceActivity eventTraceActivity)
Process Name: w3wp
Process ID: 13740
It appears that my container is not configured when MSMQ hits it. Currently I call my bootstrapper in the Global.asax.Application_Start event, is there possibly a better place to call it?