Unity.MVC3管理对象生命周期

Most IDependencyResolver implementations using Unity do not work with IDisposable and using them can lead to memory leaks and connection issues. Unity.Mvc3 is an assembly that includes a fuller implementation of IDependencyResolver that uses a child container per web request to ensure that IDisposable instances are disposed of at the end of each request. All of these complexities are taken care of by the assembly and integration into a project is as simple as adding a single line of code.

Background

ASP.NET MVC 3 has a new mechanism for integrating an IoC container into the MVC pipeline. It involves writing an implementation of IDependencyResolver and registering it using the staticDependencyResolver.SetResolver method. It is a trivial process to write a simple DependencyResolver for Unity, but the vast majority of versions that you will see do not handle IDisposable, leading to memory leaks in your applications when dealing with disposable components. Here is a typically naive implementation:

public class OverlySimpleUnityDependencyResolver : IDependencyResolver
{
    private readonly IUnityContainer _container;
 
    public OverlySimpleUnityDependencyResolver(IUnityContainer container)
    {
        _container = container;
    }
 
    public object GetService(Type serviceType)
    {
        return _container.IsRegistered(serviceType)
            ? _container.Resolve(serviceType)
            : null;
    }
 
    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _container.ResolveAll(serviceType);
    }
}

Whilst this works perfectly well for most types, if you were to register a type that implements IDisposable, you will find that the Dispose method never gets called. If you use an object relational mapper (ORM) such as NHibernate or Entity Framework, failing to dispose of your ISession / ObjectContext / DBContext will leave connections open, resulting in database timeouts and other serious problems. It is therefore imperative to handle IDisposable correctly within your IoC container.

In the vast majority of IoC containers, you can solve this problem relatively easily by registering your IDisposable objects with a lifestyle of per web request. That way, at the end of the request, the container will automatically call dispose for you. It is a shame that Unity does not come with such a lifestyle and whilst you can find many implementations on the Net, most are far too simplistic and do not deal with the IDisposable issue at all.

public class HttpContextLifetimeManager<T> : LifetimeManager, IDisposable {
public override object GetValue() {
    return HttpContext.Current.Items[typeof(T).AssemblyQualifiedName];
}
public override void RemoveValue() {
    HttpContext.Current.Items.Remove(typeof(T).AssemblyQualifiedName);
}
public override void SetValue(object newValue) {
    HttpContext.Current.Items[typeof(T).AssemblyQualifiedName]
        = newValue;
}
public void Dispose() {
    RemoveValue();
}

This example does ensure that only a single instance of a type is instantiated per web request, but has no code to deal with IDisposable. It is possible to use the Application_EndRequest method in the Global.asax and loop through all objects in the HttpContext.Current.Items collection, check if they implement IDisposable and if so, call Dispose, but this is rather long-winded and it is far from ideal.

Fortunately, there is another technique that is simple and effective and is how Unity.Mvc3 addresses the problem - the child container. All the leading IoC containers (including Unity) allow the creation of a child container from the main, application level container. The key point that makes a child container useful is that whilst the main container is around for the lifetime of the application, a child container can have a much shorter lifetime. Once the child container is disposed, all the objects that have been instantiated by the child container (and registered with an appropriate lifetime) will also be disposed. Using this feature, we can write a dependency resolver to ensure that a child container is created for every web request and at the end of the request, the child container is disposed and thus all IDisposable objects resolved by the container are also disposed.

This is exactly what the Unity.Mvc3 project does which is now available as a NuGet package Unity.Mvc3and also via CodePlex. NOTE that I have not put a dependency on the Unity NuGet package as I am assuming that many people will have the Unity DLL's from a source other than NuGet. Therefore, please ensure that you also download Unity either as a NuGet package or as part of Enterprise Library.

Using Unity.Mvc3

The code needed to integrate Unity.Mvc3 is very straightforward. As you would normally do when using an IoC container, we create the container and configure it using the fluent interface (obviously you can use the XML configuration if you are feeling sadistic). Note that we are using the RegisterControllers extension method which is included with the Unity.Mvc3 assembly. This method register all non-abstract controllers in the calling assembly, alleviating the need to explicitly register each one. Then we call the static SetResolver method to use the Unity.Mvc3 resolver. And that is all there is to it.

protected void Application_Start()
{
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);
 
    var container = new UnityContainer();
    container.RegisterType<IService1, Service1>();
    container.RegisterType<IService2, Service2>();
    container.RegisterType<IExampleContext, ExampleContext>(
        new HierarchicalLifetimeManager());
 
    container.RegisterControllers();
     
    DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}

The only thing that is unusual in this code is the lifetime of the DbContext - HierarchicalLifetimeManager. This lifetime is very important as without it, DbContext will not be disposed. Referring to the MSDN documentation, HierarchicalLifetimeManager is "a special lifetime manager which works like ContainerControlledLifetimeManager, except that in the presence of child containers, each child gets it's own instance of the object, instead of sharing one in the common parent". This is exactly what we want. By using this lifetime, DbContext is a singleton within the child container and when the child container gets disposed, so does the DbContext.

To put it simply, in order for Unity to dispose of any object resolved using the child container, you must register that object type using the HierarchicalLifetimeManager.

Conclusion

It is extremely important to dispose of IDisposable objects and failing to do so can lead to memory issues and problems with other finite resources. In an ASP.NET MVC3 website scenario, many types will need to have a lifetime of per web request. Bu using the Unity.Mvc3 NuGet package or downloading the assembly or source from CodePlex, you can address these requirements and deal with IDisposable safely and efficiently. Integration is very straightforward and the source code is available under the MIT licence, so there is very little reason not to use the Unity.Mvc package in your future projects.

posted on 2011-06-21 19:21  MoonWalker  阅读(745)  评论(0编辑  收藏  举报

导航