MVC,EF - DataContext的单个实例每个Web请求在Unity [英] MVC, EF - DataContext singleton instance Per-Web-Request in Unity

查看:491
本文介绍了MVC,EF - DataContext的单个实例每个Web请求在Unity的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个MVC 3 web应用程序,在这里我用的是实体框架数据访问。此外,我还做了一个简单的使用存储库模式,例如,其中的所有产品相关的东西在ProductRepository和所有用户相关的东西来处理,在UserRepository处理。

因此​​,我现在用的UNITY容器,使DataContext的,这是我注入到每个存储库的单一实例。在谷歌快速搜索,每个人都建议你不要使用的DataContext的单一实例,因为它可能给你在未来的一段内存泄漏。

因此​​,通过这个帖子的启发,使得在DataContext的单一实例每个Web请求是答案(请纠正我,如果我错了!)

<一个href=\"http://blogs.microsoft.co.il/blogs/gilf/archive/2010/05/18/how-to-manage-objectcontext-per-request-in-asp-net.aspx\">http://blogs.microsoft.co.il/blogs/gilf/archive/2010/05/18/how-to-manage-objectcontext-per-request-in-asp-net.aspx

但是,团结不支持每个Web请求终身经理。但是,它可以实现自己的自定义生命周期管理器,它为您处理此。其实,这是在这篇文章中讨论:

<一个href=\"http://stackoverflow.com/questions/1151201/singleton-per-call-context-web-request-in-unity\">Singleton每调用上下文(网络请求)在Unity

现在的问题是,如上面描述后我现在已经实现的定制生命周期管理器,但我不能确定这是否是做它的方式。我也想知道DataContext的实例配置在提供解决方案在哪里?我失去了一些东西?

有没有真正解决我的问题的一个更好的办法?

谢谢!

**添加了有关我的信息执行**

以下是从我的Global.asax,控制器和存储库片段。这给了我实现的清晰画面。

的Global.asax

  VAR集装箱=​​新UnityContainer();
            容器
                .RegisterType&所述; ProductsRepository&GT;(新ContainerControlledLifetimeManager())
                .RegisterType&所述; CategoryRepository&GT;(新ContainerControlledLifetimeManager())
                .RegisterType&所述; MyEntities&GT;(新PerResolveLifetimeManager(),dbConnectionString)

控制器

 私人ProductsRepository _productsRepository;
私人CategoryRepository _categoryRepository;公众的ProductsController(ProductsRepository productsRepository,CategoryRepository categoryRepository)
{
   _productsRepository = productsRepository;
   _categoryRepository = categoryRepository;
}公众的ActionResult指数()
{
   产品分类类别= _categoryRepository.GetProductCategory(的categoryId);
   。
   。
   。
}保护覆盖无效的Dispose(BOOL处置)
{
    base.Dispose(处置);
    _productsRepository.Dispose();
    _categoryRepository.Dispose();
}

产品信息库

 公共类ProductsRepository:IDisposable接口
{私人MyEntities _db;公共ProductsRepository(MyEntities DB)
{
    _db = DB;
}公共产品GetProduct(GUID的productId)
{
    返回_db.Product.Where(X =&GT; x.ID ==的productId).FirstOrDefault();
}公共无效的Dispose()
{
    this._db.Dispose();
}

控制器厂

 公共类UnityControllerFactory:DefaultControllerFactory
{
    IUnityContainer _container;    公共UnityControllerFactory(IUnityContainer容器)
    {
        _container =容器;
    }    保护覆盖一个IController GetControllerInstance(RequestContext的RequestContext的,类型controllerType)
    {
        如果(controllerType == NULL)
        {
            抛出新的HttpException(404的String.Format(以下简称控制器路径{0}找不到+
                还是没有实现一个IController。
                 requestContext.HttpContext.Request.Path));
        }        返回_container.Resolve(controllerType)作为一个IController;
    }}

附加信息
我会后,我遇到的其他环节,涉及相关的问题及对策建议:


  1. http://cgeers.word$p$pss.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在业务层的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


解决方案

是<一个href=\"http://stackoverflow.com/questions/3653009/entity-framework-and-connection-pooling/3653392#3653392\">do不能共享语境,并使用每个请求一个上下文。您还可以检查链接的问题,在该职位,看看哪一个共享环境造成的所有问题。

现在关于统一。的 PerCallContextLifetimeManager 作品的想法,但我认为提供的实现将不会为多个对象工作。您应该使用 PerHtt prequestLifetimeManager 直接

 公共类PerHtt prequestLifetime:LifetimeManager
{
    //这是非常重要的一部分,为什么我相信提到的原因
    // PerCallContext执行是错误的。
    私人只读的Guid = _key Guid.NewGuid();    公众覆盖对象的GetValue()
    {
        返回HttpContext.Current.Items [_key]
    }    公共覆盖无效的SetValue(对象为newValue)
    {
        HttpContext.Current.Items [_key] =为newValue;
    }    公共覆盖无效RemoveValue()
    {
        VAR OBJ =的GetValue();
        HttpContext.Current.Items.Remove(OBJ);
    }
}

请注意,团结不会为你处理上下文。另外要注意,默认 UnityContainer 实施将永远不会调用 RemoveValue 方法。

如果您的实施解决了单所有存储库解析调用(例如,如果你的控制器接收构造函数库的实例,你解决控制器),你不需要这个一生经理。在这种情况下,使用内置的(统一2.0) PerResolveLifetimeManager

编辑:

我看到你提供的 UnityContainer 配置pretty大问题。您正在其上注册 ContainerControllerLifetimeManager 这两个库。这一辈子经理意味着每个集装箱的寿命Singleton实例。这意味着这两个仓库将只有一次实例化和实例就会被储存和后续调用重用。因为,它无关紧要并赋给它一辈子 MyEntities 。它是注入资源库的构造,将只调用一次。这两个仓库将使用仍 MyEntities 其建设过程中产生=他们将使用单实例为你的的AppDomain的整个生命周期的单个实例。这是可以实现的最坏的情况。

重写你的配置是这样的:

  VAR集装箱=​​新UnityContainer();
容器
  .RegisterType&LT; ProductsRepository&GT;()
  .RegisterType&LT; CategoryRepository&GT;()
  .RegisterType&所述; MyEntities&GT;(新PerResolveLifetimeManager(),dbConnectionString);

这是为什么不够?您解决控制器,它是依赖于repsitories但没有库实例需要更多的那么一旦所以你可以使用默认的 TransientLifetimeManager 这将为每个调用创建新实例。由于该仓库构造函数和 MyEntities 实例必须得到解决。但是,你知道,多个存储库可以根据需要这种情况下,所以你将它设置 PerResolveLifetimeManager =>每个控制器解决将产生 MyEntities <只有一个实例/ code>。

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

解决方案

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.

这篇关于MVC,EF - DataContext的单个实例每个Web请求在Unity的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆