Using Ninject To Manage Critical Resources
This post does not pertain to the most recent (shortly after the release of this post) versions of Ninject. Ninject now handles the deactivation of all objects bound ‘InRequestScope’.
Recently a discussion with a reader led to this Stack Overflow question. Here is a summary of the problem:
- He was using the Unit Of Work / Ninject code that I published here for Truck Tracker.
- The number of database connections / threads was spiraling upward even though the Unit Of Work (and thus the database connection) was bound in ‘InRequestScope’ within the Ninject module.
The result is that the application eventually cannot connect to the database and fails.
The ‘InRequestScope’ binding ties the lifetime of the object to the instance of ‘HttpContext.Current’. Ninject deactivates objects when the object they are tied to gets garbage collected. Ninject uses an instance of a ‘WeakReference’ object coupled to a timer to determine if the garbage collector has run. It then purges the cached objects. A thorough post about Ninject’s lifecycle management was written previously by Nate Kohari.
I set up a test project to investigate a bit. I put a break point on the ‘Dispose’ method for the UnitOfWork instance. I then banged on the site by continually clicking refresh. I found the number of open database connections did indeed increase linearly with every request. The ‘Dispose’ break point was never reached. I then modified my code to include the following in my web application’s ‘EndRequest’ event:
void NinjectWebsiteApplication_EndRequest(object sender, System.EventArgs e) { GC.Collect(); }
Now when I banged on the site, the ‘Dispose’ break point is reached and the connection thread count is under control.
Having all the evidence in front of me, it is now clear as to what is going on. Without the call to ‘GC.Collect’, garbage collection occurs too infrequently. Ninject’s implementation of life cycle management based upon garbage collection may not be able to manage the lifecycle of critical resources. Maybe I was naive to not take responsibility for such resources. For instance, I could easily control the ‘end of life’ for such critical resources by using the following code:
void NinjectWebsiteApplication_EndRequest(object sender, System.EventArgs e) { var uow = Kernel.Get<IUnitOfWork>(); uow.Dispose(); }
Here, I am fetching the UnitOfWork instance and manually calling dispose. This code does not seem ‘too bad’, but feels a bit like I am short-circuiting the purpose of using an IOC container. I want the container to be responsible for the lifetime of the objects. It seemed to me that the ‘InRequestScope’ binding could be handled better by Ninject. Why not register for the ‘EndRequest’ event and release all the ‘InRequestScope’ bindings?
I posted this to the Ninject group and as usual got a very prompt reply. The Ninject group has some amazing people who never sleep working on the project. Remo suggested adding the following to the ‘NinjectHttpApplication’
public abstract class NinjectHttpApplication : HttpApplication, IHaveKernel { private static IKernel _kernel; protected NinjectHttpApplication() { EndRequest += NinjectHttpApplication_EndRequest; } protected static void NinjectHttpApplication_EndRequest(object sender, System.EventArgs e) { _kernel.Components.Get<ICache>().Clear(HttpContext.Current); } /* Other code removed for brevity */ }
After adding this code, the ‘UnitOfWork’ instances are collected at the end of every request. Remo mentioned that this will be included in an upcoming release of the Ninject MVC extension.
In the meantime, be aware of how critical resources are being managed by your IOC container. I am not sure how other containers work. I am very happy with Ninject and have a number of projects using the library. As I mentioned the Ninject community is amazing.
If you are using an IOC container, how do you manage critical resources?
The information on this blog is great. Thanks!
I have been using your Truck Tracker example as a model for a learning project.
I am having a problem implementing logging on each page request if the user is authenticated. I want to update a LastActivityDate in the database.
Currently, I’m using the Application_AuthenticateRequest event to do this. This works well for pages that return a simple view but, for any controller that commits to the repository I receive this error “No active transaction” from the Commit method.
This makes sense as there is a Commit called in the Application_AuthenticateRequest event.
The problem is that I only have one Unit of Work per request. Should I use Ninject to create a new Unit of Work for the commit in Application_AuthenticateRequest? Or this there a better way to implement this? Can I use the same unit of work for both?
Thanks.
Hi Rob,
Thanks for reading and I am glad you are enjoying the blog.
I don’t believe there will be any issues if you use the same unit of work (a single transaction) for both the update to the user and the other update. As long as the updates you were doing are self-consistent. For instance you wouldn’t want to delete the user and then try to update the user’s fields. I have used the same unit of work to save whole object graphs (truck plus hundreds of locations) and this seems not much different. In fact the transactions are more performant when there is a bit of bundling.
I also found that in most controllers I needed to authenticate the user for a specific truck. I created an abstract base class controller that encapsulated this activity and provided a few properties / methods to any derived controller. This may be another option for you. Worth a look.
Bob