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

asp.net-mvc-3 entity-framework ioc-container unity-container

Question

Entity Framework is being used by me in an MVC 3 web application for data access. Additionally, I have used the repository pattern in a straightforward manner, handling all matters pertaining to products in the "ProductRepository" and all matters pertaining to users in the "UserRepository."

As a result, I create a singleton instance of the DataContext using the UNITY container, which I then inject into each of the repositories. A simple Google search reveals that everyone advises against using a singleton instance of the DataContext since it might result in memory leaks in the future.

The conclusion drawn from this article is that the solution is to create a singleton instance of the DataContext for each HTTP request (tell me if I'm wrong!).

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

UNITY, however, does not support the lifetime manager for "Per-web-request" requests. However, you may create your own unique lifetime manager that takes care of this for you. In actuality, this is covered in this article:

Web Request Singleton Per Call Context in Unity

The issue is that, while I have already installed the custom lifespan manager as stated in the article above, I'm not sure whether this is the best course of action. The location of the datacontext instance in the suggested approach is another thing I'm curious about. Am I overlooking something?

Is there really a better way to handle my "problem"?

Thanks!

Information on my implementation has been added.

Snippets from my Global.asax, Controller, and Repository are shown below. This paints a precise picture of how I implemented it.

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 Archive

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();
}

Input Device 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;
    }

}

Additional details Hello, I'll add any new links I find to the relevant problem and recommendations for solutions below:

  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. business layer linking linq to sql datacontext to httpcontext
  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
1
50
5/23/2017 12:34:37 PM

Accepted Answer

Yes, please use one context per request, and avoid mentioning context. To view all the issues that a common context generated, you can also go through the connected questions in that thread.

About Unity now. notion ofPerCallContextLifetimeManager works, however I believe the given implementation will not be compatible with multiple objects. You must usePerHttpRequestLifetimeManager 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);
    }
}

Know that Unity won't dispose of the context for you. Be note as well that defaultUnityContainer Execution won't ever make a callRemoveValue method.

if all repositories in your implementation are resolved in a singleResolve You don't need this lifetime manager if you call (for instance, if your controllers get instances of repositories in the constructor and you are resolving controllers). In this situation, utilize built-in (Unity 2.0)PerResolveLifetimeManager .

Edit:

I see a huge issue with your setting ofUnityContainer . Both repositories are being registered withContainerControllerLifetimeManager . Singleton instance per container lifetime is what this lifetime manager refers to. In other words, just one instance of each repository will be created, and that instance will be saved and utilized for all future calls. Due to this, it is irrelevant whatever lifespan you assigned.MyEntities . It is injected into the constructors of repositories, which will only be used once. The same instance of will continue to be used by both repositories.MyEntities they will employ a single instance produced during their development for the duration of yourAppDomain The worst-case situation is what you can accomplish.

Change your setup as follows:

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

Why is this sufficient? You are resolving a controller that depends on repositories, but only one instance of a repository is required, thus you may use the defaultTransientLifetimeManager for each call of which a new instance will be created. Repository constructor is invoked as a result, andMyEntities situation has to be fixed. However, you are aware that several repositories could want this instance, therefore you will set it up usingPerResolveLifetimeManager => Each controller resolution will only result in one occurrence ofMyEntities .

38
5/23/2017 12:10:40 PM

Popular Answer

As of Unity 3, a lifetime manager for each http request is already present.

PerRequestLifetimeManager

A LifetimeManager that holds onto the instance given to it during the lifetime of a single HTTP request. This lifetime manager enables you to create instances of registered types that behave like singletons within the scope of an HTTP request. See remarks for important usage information.

Comments from MSDN

Although the PerRequestLifetimeManager lifetime manager works correctly and can help in working with stateful or thread-unsafe dependencies within the scope of an HTTP request, it is generally not a good idea to use it when it can be avoided, as it can often lead to bad practices or hard to find bugs in the end-user's application code when used incorrectly.

It is recommended that the dependencies you register are stateless and if there is a need to share common state between several objects during the lifetime of an HTTP request, then you can have a stateless service that explicitly stores and retrieves this state using the Items collection of the Current object.

The recommendations advise keeping your service calls stateless even if you are need to utilize a single context per service (facade service).

By the way, Unity 3 is for.NET 4.5.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow