views:

595

answers:

3

I have a one way WCF service using the MSMQ Binding which is activated using Windows Activation Service in IIS 7.0.

I'm a big fan on NInject so I've been using the NInject extension for WCF, which for a typical HTTP WCF service would work great.

However, in WAS activate services there is no HTTP pipeline, so I can't use InRequestScope when binding my types because System.Web.HttpContext.Current is null. I'm struggling to find an alternative when using WAS that will give me what I want. AspCompatibility mode attribute doesn't work in this mode either.

I thought InThreadScope might work, but the service is created in a separate thread than what it is executed in.

So basically I need the equivalent of the the HttpContext for WCF+WAS to scope my objects at the request level. Is there some static object in this world that would work the same way or does anyone else have any ideas on something I can hack together?

A: 

i'm sure OperationContext is what you're looking for

Neil
+1  A: 

I implemented my own WCF extensions for Ninject 2.0 before I knew there was an this up on github. My implementation differs slightly, but I did come up with a solution to scoping objects:

using System;
using Ninject.Activation;

namespace Ninject.Contrib.Wcf {
  /// <summary>
  /// Defines Scope Callbacks for WCF Context.
  /// </summary>
  public class NinjectWcfScopeCallbacks {
    /// <summary>
    /// Defines WCF Context scope.
    /// </summary>
    public static readonly Func<IContext, object> WcfContext =
      ctx => (System.ServiceModel.OperationContext.Current != null
                ? System.ServiceModel.OperationContext.Current.
                    InstanceContext.
                    Extensions.Find<NinjectInstanceContext>()
                : null);

    /// <summary>
    /// Defines WCF Web Context scope.
    /// </summary>
    public static readonly Func<IContext, object> WcfWebContext = 
               ctx => System.ServiceModel.Web.WebOperationContext.Current;
  }
}

For completeness, this is how I use the callback defined above:

Bind<IHelloWorldService>()
        .To<HelloWorldService>()
        .InScope(NinjectWcfScopeCallbacks.WcfWebContext);

The haven't hosted WCF services in WAS, so not sure if you'd use the WcfWebContext or WcfContext defined above, but you can try 'em out and see. If WebOperationContext works, then you're all set. Otherwise, I found things are a bit more complicated. You'll note the code snippet above uses a NinjectInstanceContext class that is attached to the OperationContext. This is a class I wrote that uses Ninject 2.0's "cache and collect" mechanism that allows objects to be deterministically disposed. Basically, the class is implements IExtension<InstanceContext> which is a WCF construct for attaching almost anything to the OperationContext. This class also implement Ninject's INotifyWhenDisposed interface which is what provides support for deterministic disposal. Here's what the class definition looks like:

  /// <summary>
  /// Defines a custom WCF InstanceContext extension that resolves service instances
  /// using Ninject.  
  /// <remarks>
  /// The custom InstanceContext extension provides support for deterministic disposal
  /// of injected dependencies and service instances themselves by being hook into 
  /// Ninject's "cache and collect" mechanism (new in Ninject 2.0) for object life cycle 
  /// management.  This allows binding object instances to the lifetime of a WCF context
  /// and having them deterministically deactivated and disposed.
  /// </remarks>
  /// </summary>
  public class NinjectInstanceContext : 
                IExtension<InstanceContext>, INotifyWhenDisposed {
  }

The rest of my WCF extension for Ninject is the same as the one on github. What basically happens is that an instance provider is created which is plugged into the WCF "activation" chain -- I'm not using their specific terminology, just how I understand things. So, the idea is that your instance provider is supposed to supply instances of the WCF service class being requested. So, here's where we use Ninject to produce the service instance. By doing so, we can also activate and inject any dependencies. What the instance provider does in my implementation is wrap up the Ninject kernel in an instance if NinjectInstanceContext and attach it to the OperationContext. The creation of the service is then delegated to this WCF extension. When the instance provider is told to release a service, the NinjectInstanceContext that was attached to the OperationContext is disposed which by way of implementing INotifyWhenDisposed causes deterministic disposal of the service (and potentially its dependencies).

Hope this discussion helps. I'll see if I can get some more concrete code posted here if you're interested.

Peter Meyer
A: 

Thanks Paul, this is very helpful. I stumbled upon OperationContext after I asked this question and then separately found out about IExtension but wasn't sure exactly how to put them together. Your solution makes great use of cache and collect but I wasn't sure where to find NinjectInstanceContext until you edited your first post. I wouldn't mind comparing your class to the other posts I found on IExtension to see the differences when you have some time.

Ray Wits