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>
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
Each time the page is viewed only the initialization is printed.
Initialized Instance #
Am I doing something wrong or is this a bug with Ninject and Owin? Is there a way to fix this issue?
I pulled a copy of the Ninject.Web.Common repository on GitHub and within the OwinHost project I made some alterations to the OwinBootstrapper.cs file
I added two lines in the Execute method right after the await next(context)
line
var currentContext = HttpContext.Current;
this.bootstrapper.Kernel?.Components.Get<ICache>().Clear(currentContext);
public Func<IDictionary<string, object>, Task> Execute(Func<IDictionary<string, object>, Task> next)
{
this.bootstrapper.Initialize(() =>
{
var kernel = this.createKernelCallback();
lock (this.modules)
{
kernel.Load(this.modules);
}
return kernel;
});
return async context =>
{
using (var scope = new OwinRequestScope())
{
context[NinjectOwinRequestScope] = scope;
await next(context);
var currentContext = HttpContext.Current;
this.bootstrapper.Kernel?.Components.Get<ICache>().Clear(currentContext);
}
};
}
I then built the project as a local nuget package and replaced the referenced package on nuget with the one local one.