MVC, EF - DataContext singleton instance Per-Web-Request in Unity

Nima picture Nima · Mar 3, 2011 · Viewed 33.7k times · Source

I have a MVC 3 web application, where I am using the Entity Framework for the data access. Furthermore, I have made a simple use of the repository pattern, where e.g. all Product related stuff is handled in the "ProductRepository" and all User related stuff is handled in the "UserRepository".

Thus, I am using the UNITY container, to make a singleton instance of the DataContext, which I inject into each of the repositories. A quick search on Google, and everyone recommends you to NOT use a singleton instance of the DataContext, as it might give you some memory leaks in the future.

So, inspired by this post, making a singleton instance of the DataContext for each web request is the answer (please correct me if I am wrong!)

http://blogs.microsoft.co.il/blogs/gilf/archive/2010/05/18/how-to-manage-objectcontext-per-request-in-asp-net.aspx

However, UNITY does not support the "Per-web-request" lifetime manager. But, it is possible to implement your own custom lifetime manager, which handles this for you. Actually, this is discussed in this post :

Singleton Per Call Context (Web Request) in Unity

The question is, I have now implemented the custom lifetime manager as described in the above post, but I am unsure if this is the way to do it. I am also wondering about where the datacontext instance is disposed in the provided solution? Am I missing out something?

Is there actually a better way of solving my "issue"?

Thanks!

** Added information about my implementation **

The following is snippets from my Global.asax, Controller and Repository. This gives a clear picture of my implementation.

Global.asax

  var container = new UnityContainer();
            container
                .RegisterType<ProductsRepository>(new ContainerControlledLifetimeManager())
                .RegisterType<CategoryRepository>(new ContainerControlledLifetimeManager())
                .RegisterType<MyEntities>(new PerResolveLifetimeManager(), dbConnectionString)

Controller

private ProductsRepository _productsRepository;
private CategoryRepository _categoryRepository;

public ProductsController(ProductsRepository productsRepository, CategoryRepository categoryRepository)
{
   _productsRepository = productsRepository;
   _categoryRepository = categoryRepository;
}

public ActionResult Index()
{
   ProductCategory category = _categoryRepository.GetProductCategory(categoryId);
   . 
   . 
   . 
}

protected override void Dispose(bool disposing)
{
    base.Dispose(disposing);
    _productsRepository.Dispose();
    _categoryRepository.Dispose();
}

Product Repository

public class ProductsRepository : IDisposable
{

private MyEntities _db;

public ProductsRepository(MyEntities db)
{
    _db = db;
}

public Product GetProduct(Guid productId)
{
    return _db.Product.Where(x => x.ID == productId).FirstOrDefault();
}

public void Dispose()
{
    this._db.Dispose();
}

Controller Factory

public class UnityControllerFactory : DefaultControllerFactory
{
    IUnityContainer _container;

    public UnityControllerFactory(IUnityContainer container)
    {
        _container = container;
    }

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            throw new HttpException(404, String.Format("The controller for path '{0}' could not be found" +
                "or it does not implement IController.",
                 requestContext.HttpContext.Request.Path));
        }

        return _container.Resolve(controllerType) as IController;
    }

}

Addition information Hi, I will post additional links that I come across, concerning the related issue and solution suggestions:

  1. http://cgeers.wordpress.com/2009/02/21/entity-framework-objectcontext/#objectcontext
  2. http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in-n-layered-ASP-NET-applications.aspx
  3. attaching linq to sql datacontext to httpcontext in business layer
  4. http://weblogs.asp.net/shijuvarghese/archive/2008/10/24/asp-net-mvc-tip-dependency-injection-with-unity-application-block.aspx
  5. http://msdn.microsoft.com/en-us/library/bb738470.aspx

Answer

Ladislav Mrnka picture Ladislav Mrnka · Mar 4, 2011

Yes do not share context and use one context per request. You can also check linked questions in that post to see all problems which a shared context caused.

Now about Unity. Idea of PerCallContextLifetimeManager works but I think provided implementation will not work for more than one object. You should use PerHttpRequestLifetimeManager directly:

public class PerHttpRequestLifetime : LifetimeManager
{
    // This is very important part and the reason why I believe mentioned
    // PerCallContext implementation is wrong.
    private readonly Guid _key = Guid.NewGuid();

    public override object GetValue()
    {
        return HttpContext.Current.Items[_key];
    }

    public override void SetValue(object newValue)
    {
        HttpContext.Current.Items[_key] = newValue;
    }

    public override void RemoveValue()
    {
        var obj = GetValue();
        HttpContext.Current.Items.Remove(obj);
    }
}

Be aware that Unity will not dispose context for you. Also be aware that default UnityContainer implementation will never call RemoveValue method.

If your implementation resolves all repositories in single Resolve call (for example if your controllers receives instances of repositories in constructor and you are resolving controllers) you don't need this lifetime manager. In such case use build-in (Unity 2.0) PerResolveLifetimeManager.

Edit:

I see pretty big problem in your provided configuration of UnityContainer. You are registering both repositories with ContainerControllerLifetimeManager. This lifetime manager means Singleton instance per container lifetime. It means that both repositories will be instantiated only once and instance will be stored and reused for subsequent calls. Because of that it doesn't matter what lifetime did you assign to MyEntities. It is injected to repositories' constructors which will be called only once. Both repositories will use still that single instance of MyEntities created during their construction = they will use single instance for whole lifetime of your AppDomain. That is the worst scenario you can achieve.

Rewrite your configuration this way:

var container = new UnityContainer();
container
  .RegisterType<ProductsRepository>()
  .RegisterType<CategoryRepository>()
  .RegisterType<MyEntities>(new PerResolveLifetimeManager(), dbConnectionString);

Why this is enough? You are resolving controller which is dependent on repsitories but no repository instance is needed more then once so you can use default TransientLifetimeManager which will create new instance for each call. Because of that repository constructor is called and MyEntities instance must be resolved. But you know that multiple repositories can need this instance so you will set it with PerResolveLifetimeManager => each resolving of controller will produce only one instance of MyEntities.