MVC,EF - Unity中的DataContext单例实例Per-Web-Request

Nim*_*ima 49 entity-framework ioc-container unity-container asp.net-mvc-3

我有一个MVC 3 Web应用程序,我使用Entity Framework进行数据访问.此外,我简单地使用了存储库模式,例如,所有与产品相关的东西都在"ProductRepository"中处理,所有与User相关的东西都在"UserRepository"中处理.

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

所以,受这篇文章的启发,为每个Web请求创建一个DataContext的单例实例就是答案(如果我错了,请纠正我!)

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"终身经理.但是,可以实现自己的自定义生命周期管理器,它可以为您处理此问题.实际上,这篇文章对此进行了讨论:

Unity中的Singleton Per Call上下文(Web请求)

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

实际上有更好的方法来解决我的"问题"吗?

谢谢!

**添加了有关我的实施的信息**

以下是我的Global.asax,Controller和Repository的片段.这清楚地说明了我的实施情况.

Global.asax中

  var container = new UnityContainer();
            container
                .RegisterType<ProductsRepository>(new ContainerControlledLifetimeManager())
                .RegisterType<CategoryRepository>(new ContainerControlledLifetimeManager())
                .RegisterType<MyEntities>(new PerResolveLifetimeManager(), dbConnectionString)
Run Code Online (Sandbox Code Playgroud)

调节器

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();
}
Run Code Online (Sandbox Code Playgroud)

产品库

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();
}
Run Code Online (Sandbox Code Playgroud)

控制器工厂

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

}
Run Code Online (Sandbox Code Playgroud)

添加信息 您好,我将发布我遇到的其他链接,涉及相关问题和解决方案建议:

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

Lad*_*nka 38

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

编辑:

我在你提供的配置中看到了相当大的问题UnityContainer.您正在注册两个存储库ContainerControllerLifetimeManager.此生命周期管理器表示每个容器生存期的Singleton实例 这意味着两个存储库只会被实例化一次,实例将被存储并重新用于后续调用.因此,你指定的生命周期并不重要MyEntities.它被注入到存储库的构造函数中,这些构造函数只会被调用一次.两个存储库都将使用MyEntities在构造期间创建的单个实例=它们将在您的整个生命周期中使用单个实例AppDomain.这是您可以实现的最糟糕的情况.

以这种方式重写您的配置:

var container = new UnityContainer();
container
  .RegisterType<ProductsRepository>()
  .RegisterType<CategoryRepository>()
  .RegisterType<MyEntities>(new PerResolveLifetimeManager(), dbConnectionString);
Run Code Online (Sandbox Code Playgroud)

为什么这个就足够了?您正在解析依赖于repsitories的控制器,但是不需要一次存储库实例,因此您可以使用default TransientLifetimeManager,这将为每个调用创建新实例.由于该存储库构造函数被调用,因此MyEntities必须解析实例.但是你知道多个存储库可能需要这个实例,所以你将设置它PerResolveLifetimeManager=>每个控制器的解析只会生成一个实例MyEntities.


Yor*_*rro 8

从Unity 3开始,每个http请求都有一个内置的生命周期管理器.

PerRequestLifetimeManager

LifetimeManager,用于保存在单个HTTP请求的生命周期内为其提供的实例.通过此生命周期管理器,您可以创建在HTTP请求范围内行为类似于单例的已注册类型的实例.有关重要的使用信息,请参阅备注.

MSDN的评论

虽然PerRequestLifetimeManager生命周期管理器可以正常工作并且可以帮助处理HTTP请求范围内的有状态或线程不安全的依赖关系,但是在可以避免使用它时通常不是一个好主意,因为它通常会导致错误在使用不当时练习或难以发现最终用户的应用程序代码中的错误.

建议您注册的依赖项是无状态的,如果需要在HTTP请求的生命周期内共享多个对象之间的公共状态,那么您可以使用无状态服务使用Items集合显式存储和检索此状态.当前对象.

这些评论说即使你被迫使用每个服务(外观服务)的单个上下文,你应该让你的服务调用无状态.

Unity 3适用于.NET 4.5.


neo*_*pir 5

我相信上显示的示例代码使用Unity DI在MVC:的NerdDinnerHttpContextLifetimeManager应满足您的需求.