Using Ninject with Owin and InRequestScope
Asked Answered
I

2

9

We are trying to use Ninject within an Owin with WebAPI pipeline. We have everything setup according to this documentation, but we cannot get InRequestScope() to work.

Here's the significant part of the startup.cs

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

        // Web API routes
        config.MapHttpAttributeRoutes();

        // Ninject Setup
        app.UseNinjectMiddleware(NinjectConfig.CreateKernel);
        app.UseNinjectWebApi(config);
    }

}

NinjectConfig looks something like this:

public sealed class NinjectConfig
{
    public static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();

        INinjectModule[] modules =
        {
            new ApplicationModule()
        };       

        instance.Load(modules);

        // Do we still need to do this wtih Owin?
        instance.Bind<IHttpModule>().To<OnePerRequestHttpModule>();
    }
}

Our ApplicationModule lives in a separate infrastructure project with access to all of our different layers, for handling DI & Mapping:

public class ApplicationModule: NinjectModule
{

    public override void Load()
    {
        // IUnitOfWork / EF Setups
        Bind<ApplicationContext>().ToSelf().InRequestScope();

        Bind<IUnitOfWork>().ToMethod(ctx => ctx.Kernel.Get<ApplicationContext>()});

        Bind<ApplicationContext>().ToMethod(ctx => ctx.Kernel.Get<ChromLimsContext>()}).WhenInjectedInto<IDal>();

        // other bindings for dals and business objects, etc.
    }
}

Then we have a couple interfaces:

public interface IUnitOfWork()
{
    void SaveChanges();

    Task SaveChangesAsync();
}

and

public interface IDal()
{
    // Crud operations, Sync and Async
}

then our actual classes using these:

public class SomeBusinessObject
{
    private IUnitOfWork _uow;
    private IDal _someDal;

    public SomeBusinessObject(IUnitOfWork uow, IDal someDal)
    {
        _uow = uow;
        _someDal = someDal;
    }

    public Task<SomeResult> SaveSomething(Something something)
    {
        _someDal.Save(something);
        _uow.SaveChanges();
    }
}

Some Dal

public class SomeDal : IDal {

    private ApplicationContext _applicationContext;

    public SomeDal(ApplicationContext applicationContext)
    {
        _applicationContext = applicationContext;
    }

    public void Save(Something something)
    {
        _applicationContext.Somethings.Add(something);
    }
}

Our EF DbContext

public class ApplicationContext : DbContext, IUnitOfWork
{
    // EF DBSet Definitions

    public void SaveChanges()
    {
        base.SaveChanges();
    }

}

The expectation is that for every request, a single instance of ApplicationContext is created and injected into the business objects as an IUnitOfWork implementation and into the IDals as an ApplicationContext.

Instead what is happening is that a new instance of ApplicationContext is being created for every single class that uses it. If I switch the scope from InRequestScope to InSingletonScope, then (as expected) exactly 1 instance is created for the entire application, and injected properly into the specified classes. Since that works, I'm assuming this isn't a binding issue, but instead an issue with the InRequestScope extension.

The only issue I could find similar to what I'm experiencing is this one, but unfortunately the solution did not work. I'm already referencing all of the packages he specified in both the WebApi and Infrastructure projects, and I double checked to make sure they are being copied to the build directory.

What am I doing wrong?

Edit: Some additional information. Looking at the Ninject source code in both Ninject.Web.WebApi.OwinHost and Ninject.Web.Common.OwinHost, it appears that the Owin Middleware adds the OwinWebApiRequestScopeProvider as the IWebApiRequestScopeProvider. This provider is then used in the InRequestScope() extension method to return a named scope called "Ninject_WebApiScope". This will be present until the target class that being injected into switches. The named scope then disappears, and a new scope is created. I think this may be what @BatteryBackupUnit was referring to in their comment, but I don't know how to correct it.

Ivey answered 4/12, 2014 at 20:57 Comment(7)
My guess would be that your second Bind<ApplicationContext>() is causing this. You're not setting it to be InRequestScope()Epoxy
Unfortunately that didn't work. I've also tried only binding the ApplicationContext to itself InRequestScope, and then only binding IUnitOfWork ToMethod(ctx => ctx.Kernel.Get<ApplicationContext>()).InRequestScope()Ivey
Does .InRequestScope() work for any binding? There's a "known" issue related to ninject nuget package installation / upgrade where it isn't setup correctly and the Request-Scope specific ninject stuff isn't registered correctly. .InRequestScope() has no effect - sadly not even throwing an exception.Tardif
@Tardif That's a good question. Unfortunately everything we're doing is coming through the Owin pipeline, so all of the behaviors with InRequestScope seem to be consistent, which is that they don't work as expected.Ivey
I have seen this same behaviour. It seems like InRequestScope uses some NamedScope stuff underneath. What seems to be happening is it is using a scope called something like "Ninject_WebApiScope", but when it tries to get that scope it throws an UnknownScopeException, meaning it returns a null scope object which has the effect of a transient scope. That named scope should be created when WebApi calls BeginScope, but it doesn't seem to work for some reason.Tensimeter
I have the same problem.Downtime
I converted to AutofacDowntime
D
5

This thread is related to this issue...

https://github.com/ninject/Ninject.Web.WebApi/issues/17

I've found that behavior of InRequestScope seems to change depending on how you inject them. For example...

public ValuesController(IValuesProvider valuesProvider1, IValuesProvider valuesProvider2)
{
    this.valuesProvider1 = valuesProvider1;
    this.valuesProvider2 = valuesProvider2;
}

Ninject will create and inject the same instance of IValuesProvider. However if the method were written as...

/// <summary>
/// Initializes a new instance of the <see cref="ValuesController"/> class.
/// </summary>
/// <param name="valuesProvider">The values provider.</param>
public Values2Controller(IKernel kernel)
{
    this.valuesProvider1 = kernel.Get<IValuesProvider>();
    this.valuesProvider2 = kernel.Get<IValuesProvider>();
}

...this will create two new instances.

Downtime answered 21/1, 2016 at 8:52 Comment(1)
We ended up switching to SimpleInjector, but this answer was the best, pointing out that this is a known bug in NinjectIvey
V
3

Based on the information in the link in @Mick's post I ended up adding my own extension method like this. I am not sure about the downsides.

public static class CustomRequestScope
{
    public static Ninject.Syntax.IBindingNamedWithOrOnSyntax<T> InCustomRequestScope<T>(this Ninject.Syntax.IBindingInSyntax<T> syntax)
    {
        return syntax.InScope(ctx => HttpContext.Current.Handler == null ? null : HttpContext.Current.Request);
    }
}

I do consider switching to another container because of this issue.

Varrian answered 2/3, 2016 at 15:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.