ninject / Ninject.Web.Common

It provides the base infrastructure for all web type extension
Other
56 stars 40 forks source link

OWIN + Ninject w/ UseNinjectMiddleware doesn't dispose #41

Closed Michael-073 closed 2 years ago

Michael-073 commented 5 years ago

When registering a Ninject kernel as a middleware component in OWIN using UseNinjectMiddleware any injected object that implements IDisposable does not get disposed of at the end of a request.

I've put in console prints to watch when an instance was created and disposed of. I've noticed that instances are being created with each request but not being disposed of until some random time later.

I created an empty project to find the root cause. I used a simple model that tracks its instance number so I can tell what is being created and disposed of.

Model:

public class Demo : IDemo, IDisposable {
    public static int InstanceCount = 1; //Tracks the current number of 
                                         //instances that have been created.
    public Demo() {
        Id = InstanceCount++; 
        Debug.WriteLine("Initialized Instance " + Id);
    }
    public int Id { get; set; }
    public void Dispose() {
        Debug.WriteLine("Disposed instance" + Id);
        Id = -1;
    }
}

public interface IDemo {
    int Id { get; set; }
}

Controller

public class HomeController : Controller {
    private readonly IDemo demo;
    public HomeController(IDemo demo) { this.demo = demo; }

    public ActionResult Index() {
        ViewBag.Id = demo.Id;
        return View();
    }
}

Startup file:

public class Startup {
    public void Configuration(IAppBuilder app) {
        var kernel = CreateKernel();
        app.UseNinjectMiddleware(() => kernel);
    }

    public IKernel CreateKernel() {
        var kernel = new StandardKernel();
        kernel.Bind<IDemo>().To<Demo>().InRequestScope();
        return kernel;
    }
}

I've also made sure all of the packages are up to date since this was a known issue in a previous version of Ninject.

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.AspNet.Mvc" version="5.2.7" targetFramework="net462" />
  <package id="Microsoft.AspNet.Razor" version="3.2.7" targetFramework="net462" />
  <package id="Microsoft.AspNet.WebPages" version="3.2.7" targetFramework="net462" />
  <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="2.0.1" targetFramework="net462" />
  <package id="Microsoft.Owin" version="4.0.1" targetFramework="net462" />
  <package id="Microsoft.Owin.Host.SystemWeb" version="4.0.1" targetFramework="net462" />
  <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net462" />
  <package id="Ninject" version="3.3.4" targetFramework="net462" />
  <package id="Ninject.MVC5" version="3.3.0" targetFramework="net462" />
  <package id="Ninject.Web.Common" version="3.3.1" targetFramework="net462" />
  <package id="Ninject.Web.Common.OwinHost" version="3.3.1" targetFramework="net462" />
  <package id="Ninject.Web.Common.WebHost" version="3.3.1" targetFramework="net462" />
  <package id="Owin" version="1.0" targetFramework="net462" />
  <package id="WebActivatorEx" version="2.2.0" targetFramework="net462" />
</packages>

Expected

When viewing the home page for the application each request should print out:

Initialized Instance #

and then at the end of the request

Disposed instance #

where # is the current instance number

Actual

Each time the page is viewed only the initialization is printed.

Initialized Instance #

Question

Am I doing something wrong or is this a bug with Ninject and Owin? Is there a way to fix this issue?

Michael-073 commented 5 years ago

Let me add that I did test it without using OWIN middleware and it worked fine. I looked into the OnePerRequestHttpModule and found how the objects were being disposed up through that class. When I compared it with the OwinBootstrapper class I noticed that in the Execute method it awaits the end of the request, but then does nothing.

I copied the method from the OnePerRequest class for disposing of objects in the current request and tried to add that after the await statement in the OwinBootstrapper. I added these two lines:

var c = HttpContext.Current;
bootstrapper.Kernel?.Components.Get<ICache>().Clear(c);

This seems to have resolved my issue, but I wanted to post this on the repository to see if this would be the correct way to handle this issue or if my fix isn't the proper way to handle the disposal of the current request objects.

Michael-073 commented 2 years ago

So 3 years later and I had to revisit this issue.

Turns out I overcomplicated the problem. All I needed to do was to add the OnePerRequestModule to the system.webServer's modules node in the web.config file.

<system.webServer>
    <modules>
        <add name="OnePerRequestModule" type="Ninject.Web.Common.WebHost.OnePerRequestHttpModule"/>
    </modules>        
</system.webServer>

I'm closing this issue since I figured out what I did wrong.