MVC,EF - Unity 中的 DataContext 单例实例 Per-Web-Request [英] MVC, EF - DataContext singleton instance Per-Web-Request in Unity

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

问题描述

我有一个 MVC 3 Web 应用程序,我在其中使用实体框架进行数据访问.此外,我简单地使用了存储库模式,例如所有与产品相关的东西都在ProductRepository"中处理.并且所有与用户相关的内容都在UserRepository"中处理.

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".

因此,我使用 UNITY 容器来创建 DataContext 的单例实例,并将其注入到每个存储库中.在 Google 上快速搜索,每个人都建议您不要使用 DataContext 的单例实例,因为它将来可能会给您带来一些内存泄漏.

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.

因此,受这篇文章的启发,为每个 Web 请求制作 DataContext 的单例实例就是答案(如果我错了,请纠正我!)

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

但是,UNITY 不支持Per-web-request"终身经理.但是,可以实现您自己的自定义生命周期管理器,它会为您处理此问题.实际上,这是在这篇文章中讨论的:

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 :

Unity 中的单个调用上下文(Web 请求)

问题是,我现在已经实现了上面帖子中描述的自定义生命周期管理器,但我不确定这是否是实现它的方法.我还想知道 datacontext 实例在提供的解决方案中的位置?我错过了什么吗?

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"?

谢谢!

以下是我的 Global.asax、控制器和存储库的片段.这清楚地说明了我的实施情况.

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)

控制器

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

产品存储库

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

控制器工厂

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. https://github.com/geersch/EntityFrameworkObjectContext
  2. http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in​​-n-layered-ASP-NET-applications.aspx莉>
  3. 在业务中将 linq 附加到 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

推荐答案

不要共享上下文,每个请求使用一个上下文.您还可以查看该帖子中的链接问题,以查看共享上下文导致的所有问题.

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.

现在关于 Unity.PerCallContextLifetimeManager 的想法有效,但我认为提供的实现不适用于多个对象.您应该直接使用 PerHttpRequestLifetimeManager:

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

请注意,Unity 不会为您处理上下文.还要注意默认的 UnityContainer 实现永远不会调用 RemoveValue 方法.

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

如果您的实现在单个 Resolve 调用中解析所有存储库(例如,如果您的控制器在构造函数中接收存储库实例并且您正在解析控制器),则您不需要这个生命周期管理器.在这种情况下,请使用内置 (Unity 2.0) PerResolveLifetimeManager.

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.

我发现您提供的 UnityContainer 配置存在很大问题.您正在使用 ContainerControllerLifetimeManager 注册这两个存储库.这个生命周期管理器意味着每个容器生命周期的单例实例.这意味着两个存储库只会被实例化一次,并且实例将被存储并重用于后续调用.因此,您为 MyEntities 分配的生命周期并不重要.它被注入到存储库的构造函数中,这些构造函数只会被调用一次.两个存储库仍将使用在其构建期间创建的 MyEntities 的单个实例 = 它们将在您的 AppDomain 的整个生命周期中使用单个实例.这是你所能达到的最糟糕的情况.

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

为什么这就够了?您正在解析依赖于存储库的控制器,但不再需要存储库实例,因此您可以使用默认的 TransientLifetimeManager 为每次调用创建新实例.因为调用了存储库构造函数并且必须解析 MyEntities 实例.但是您知道多个存储库可能需要此实例,因此您将使用 PerResolveLifetimeManager => 进行设置 => 控制器的每次解析将仅生成 MyEntities 的一个实例.

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 - Unity 中的 DataContext 单例实例 Per-Web-Request的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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