1

In our MVC application we predominantly use Ninject to inject dependencies into controllers. As such, our default lifetime scope is InRequestScope(). We have now added an IHttpModule that uses common dependencies as the controllers (i.e., UserService). The problem is that HttpModules can be pooled by ASP.NET and IIS and reused over multiple requests. Because the injected dependencies are set to InRequestScope, subsequent requests will often get an ObjectDisposedException when referencing the injected dependencies inside the HttpModule. How can I specify InRequestScope() for the UserService when injecting into a controller and InScope() when injecting into an HttpModule.

Here is a simplified version of our registration:

public static class NinjectWebCommon
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    public static void Start(){
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
        DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
        bootstrapper.Initialize(CreateKernel);
    }

    public static void Stop(){
        bootstrapper.ShutDown();
    }

    private static IKernel CreateKernel(){
        var kernel = new StandardKernel();
        kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
        kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
        kernel.Bind<IHttpModule>().To<CustomModule>(); // needs a UserService
        kernel.Bind<IUserService>().To<UserService>().InRequestScope(); // injected into controllers and the CustomModule
        DependencyResolver.SetResolver(new Services.NinjectDependencyResolver(kernel));
        return kernel;
    }
}
ekad
  • 14,436
  • 26
  • 44
  • 46
DeveloperRob
  • 155
  • 1
  • 9
  • this may be partially interesting to you: http://stackoverflow.com/questions/25167439/ninject-conditional-self-bind-to-change-scope-for-task-scheduler-not-working-p/25174916#25174916 – BatteryBackupUnit Aug 07 '15 at 04:59

1 Answers1

2

Check out .When() on the binding syntax. You can use it to specify a specific scope for a service under specific circumstances. here's an example:

class Program
{
    static void Main(string[] args)
    {
        var kernel = new StandardKernel();

        var scope1 = new object();
        var scope2 = new object();

        kernel.Bind<IWidget>().To<WidgetA>().InScope(c => scope1);
        kernel.Bind<IWidget>().To<WidgetA>().WhenInjectedInto<WidgetController>().InScope(c => scope2);

        var service = kernel.Get<GeneralWidgetService>();
        service.Print();

        service = kernel.Get<GeneralWidgetService>();
        service.Print();

        var controller = kernel.Get<WidgetController>();
        controller.Print();

        controller = kernel.Get<WidgetController>();
        controller.Print();

        // when scope2 changes, WidgetController gets a new widget, while WidgetService continues getting the existing widget
        scope2 = new object();

        service = kernel.Get<GeneralWidgetService>();
        service.Print();

        controller = kernel.Get<WidgetController>();
        controller.Print();
    }
}

public class WidgetController
{
    private readonly IWidget _widget;

    public WidgetController(IWidget widget)
    {
        _widget = widget;
    }

    public void Print()
    {
        Console.WriteLine("WidgetController ID: " + _widget.ID);
    }

}

public class GeneralWidgetService
{
    private readonly IWidget _widget;

    public GeneralWidgetService(IWidget widget)
    {
        _widget = widget;
    }

    public void Print()
    {
        Console.WriteLine("GeneralWidgetService ID: " + _widget.ID);
    }

}


public interface IWidget
{
    string ID { get; }
    string Name { get; }
}

public class WidgetA : IWidget
{
    private readonly string _id = Guid.NewGuid().ToString();

    public string ID { get { return _id; } }
    public string Name { get { return "AAAAAAAAAAAAAAAAA"; } }
}

Output:

GeneralWidgetService ID: 09f61af7-c70d-45fe-834a-6cc94e1e3c40
GeneralWidgetService ID: 09f61af7-c70d-45fe-834a-6cc94e1e3c40
WidgetController ID: 2c2bf05f-d251-41be-b9e0-224f02839ead
WidgetController ID: 2c2bf05f-d251-41be-b9e0-224f02839ead
GeneralWidgetService ID: 09f61af7-c70d-45fe-834a-6cc94e1e3c40
WidgetController ID: 519a2930-5b71-4cbb-b84e-a1d712ec5398

Dave Thieben
  • 5,388
  • 2
  • 28
  • 38
  • I think my confusion stemmed from how IHttpModules are registered. I have used .When() binding syntax with decorators but blanked in this case. Here is the relevant code that has passed my initial tests. I'm calling dispose on the TestService inside the dispose of TestModule. `kernel.Bind().To(); kernel.Bind().To().InRequestScope(); kernel.Bind().To().WhenInjectedInto().InScope(c => null);` Thanks! – DeveloperRob Aug 11 '15 at 01:50
  • if you are using the Ninject.Web extensions, it should automatically register your HttpModules for you, and inject their dependencies. – Dave Thieben Aug 11 '15 at 13:17
  • I'm using Ninject.Web.Common and Ninject.Web.Mvc. When I remove `kernel.Bind().To();`, the module does not get wired up. Am I missing something? – DeveloperRob Aug 11 '15 at 14:52
  • ah, you are right. you need the binding but it will handle the rest. – Dave Thieben Aug 11 '15 at 15:38
  • I still need the .When() syntax to provide different scopes between controllers and modules. – DeveloperRob Aug 11 '15 at 19:28