MVC、EF - Unity 中的 DataContext 单例实例 Per-Web-Request
我有一个 MVC 3 Web 应用程序,我在其中使用实体框架进行数据访问。此外,我简单地使用了存储库模式,其中例如所有与产品相关的内容都在“ProductRepository”中处理,所有与用户相关的内容都在“UserRepository”中处理。
因此,我使用 UNITY 容器来创建 DataContext 的单例实例,并将其注入到每个存储库中。在 Google 上快速搜索一下,每个人都建议您不要使用 DataContext 的单例实例,因为它可能会在将来给您带来一些内存泄漏。
因此,受这篇文章的启发,为每个 Web 请求创建一个 DataContext 的单例实例就是答案(如果我错了,请纠正我!)
但是,UNITY 不支持“Per-web-request”生命周期管理器。但是,可以实现您自己的自定义生命周期管理器,它会为您处理这个问题。实际上,这篇文章对此进行了讨论:
问题是,我现在已经实现了上面帖子中描述的自定义生命周期管理器,但我不确定这是否是实现它的方法。我还想知道 datacontext 实例在提供的解决方案中的位置?我错过了什么吗?
实际上有更好的方法来解决我的“问题”吗?
谢谢!
** 添加了有关我的实现的信息 **
以下是来自我的 Global.asax、控制器和存储库的片段。这清楚地展示了我的实施情况。
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;
}
}
添加信息 您好,我将发布我遇到的有关相关问题和解决方案建议的其他链接:
- https://github。 com/geersch/EntityFrameworkObjectContext
- http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in-n-layered-ASP-NET-applications.aspx
- 将 linq to sql datacontext 附加到 httpcontext业务层
- http://weblogs.asp.net/shijuvarghese/archive/2008/10/24/asp-net-mvc-tip-dependency-injection-with-unity-application-block。 aspx
- http://msdn.microsoft.com/en-us /library/bb738470.aspx
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!)
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:
- https://github.com/geersch/EntityFrameworkObjectContext
- http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in-n-layered-ASP-NET-applications.aspx
- attaching linq to sql datacontext to httpcontext in business layer
- http://weblogs.asp.net/shijuvarghese/archive/2008/10/24/asp-net-mvc-tip-dependency-injection-with-unity-application-block.aspx
- http://msdn.microsoft.com/en-us/library/bb738470.aspx
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
从 Unity 3 开始,每个 http 请求已经有一个内置的生命周期管理器。
PerRequestLifetimeManager< /a>
MSDN评论
评论说,即使您被迫为每个服务(外观服务)使用单个上下文,您也应该保持服务调用无状态。
顺便说一下,Unity 3 适用于 .NET 4.5。
As of Unity 3, there is already a built-in lifetime manager per http request.
PerRequestLifetimeManager
Remarks by MSDN
The remarks say that even you are forced to use a single context per service (facade service), you should keep your service calls stateless.
Unity 3 is for .NET 4.5 by the way.
我相信 NerdDinner:MVC 中的 DI 使用 Unity 来实现其
HttpContextLifetimeManager
应该可以满足您的需求。I believe the sample code shown on NerdDinner: DI in MVC using Unity for its
HttpContextLifetimeManager
should meet your needs.我不想不必要地阻止您,并且一定要进行实验,但如果您继续使用 DataContext 的单例实例,请确保您能成功。
它看起来在您的开发环境中工作正常,但可能无法正确关闭连接。如果没有生产环境的负载,这一点将很难看到。在高负载的生产环境中,未处理的连接将导致巨大的内存泄漏,然后高 CPU 尝试分配新内存。
您是否考虑过从每个请求连接模式中获得什么?在请求中打开/关闭一次连接(例如 3-4 次)可以获得多少性能?值得这么麻烦吗?此外,这使得延迟加载失败(在您的视图中读取数据库查询)更容易犯错。
如果这令人沮丧,我很抱歉。如果您确实看到了好处,就去吧。我只是警告你,如果你弄错了,可能会产生严重的适得其反的后果,所以要小心。像 entity profiler 这样的东西对于正确操作来说是非常宝贵的 - 它会告诉你打开和关闭的连接数 - 除其他外非常有用事物。
I don't want to unnecessarily discourage you and by all means experiment but if you go ahead and use singleton instances of DataContext make sure you nail it.
It can appear to work fine on your dev environment but it could be failing to close connections properly. This will be hard to see without the load of a production environment. On a production environment with high load, undisposed connections will cause huge memory leaks and then high CPU trying to allocate new memory.
Have you considered what you are gaining from a connection per request pattern? How much performance there is to gain from opening/closing a connection once over say 3-4 times in a request? Worth the hassle? Also this makes lazy loading fails (read database queries in your view) a lot easier offences to make.
Sorry if this came across discouraging. Go for it if you really see the benefit. I'm just warning you that it could backfire quite seriously if you get it wrong so be warned. Something like entity profiler will be invaluable to getting it right - it tells you number of connections opened and closed - amongst other very useful things.
我前几次看到问题和答案。它是过时的。 Unity.MVC3 的生命周期管理器为 HierarchicalLifetimeManager。
而且效果很好。
I saw question and answer few times ago. It is dated. Unity.MVC3 has life time manager as HierarchicalLifetimeManager.
and it works nice.
我建议这样解决:
http://forums.asp.net/t/1644386.aspx/1
此致
I would propose to solve it like this:
http://forums.asp.net/t/1644386.aspx/1
Best regards
我通过使用 Castle.DynamicProxy 解决了这个问题。我需要“按需”注入某些依赖项,这意味着它们需要在使用时解决,而不是在“依赖项”构建时解决。
为此,我像这样配置我的容器:
我的想法是提供一种“按需”检索实例的方法。每当使用实例的任何方法时都会调用 lambda。依赖对象实际上持有对代理对象的引用,而不是对象本身。
OnDemandInjectionFactory:
OnDemandInterceptor:
I solved this by using Castle.DynamicProxy. I needed to have certain dependencies be injected "On Demand" meaning they needed to be resolved at time of use, not at time of "Depender" build up.
To do this I configure my container like so:
The idea being that I provide a method to retrieve the instance "on demand." The lambda gets invoked whenever any of the methods of the instance are used. The Dependent object is actually holding a reference to a proxied object, no the object itself.
OnDemandInjectionFactory:
OnDemandInterceptor:
在Unity3中,如果要使用
需要注册
UnityPerRequestHttpModule
我是使用WebActivatorEx来实现的,代码如下:
In Unity3, if you want to use
You need to register
UnityPerRequestHttpModule
I do this by using WebActivatorEx, the code is as below:
PerRequestLifetimeManager 和 UnityPerRequestHttpModule 类位于 Unity.Mvc 包,它依赖于 ASP.NET MVC。如果您不想拥有这种依赖关系(例如您正在使用 Web API),则必须将它们复制粘贴到您的应用程序中。
如果您这样做,请不要忘记注册 HttpModule。
编辑:
在 CodePlex 关闭之前,我将在此处包含这些类:
PerRequestLifetimeManager and UnityPerRequestHttpModule classes are in Unity.Mvc package which has a dependency on ASP.NET MVC. If you don't want to have that dependency (e.g. you are using Web API) you will have to copy-paste them in to your app.
If you do that, don't forget the register the HttpModule.
Edit:
I'll include the classes here before CodePlex shuts down:
是的不共享上下文并为每个请求使用一个上下文。您还可以检查该帖子中的链接问题,以查看共享上下文引起的所有问题。
现在谈谈Unity。 PerCallContextLifetimeManager 的想法是可行的,但我认为提供的实现不适用于多个对象。您应该直接使用
PerHttpRequestLifetimeManager
:请注意,Unity 不会为您处理上下文。另请注意,默认的
UnityContainer
实现永远不会调用RemoveValue
方法。如果您的实现在单个
Resolve
调用中解析所有存储库(例如,如果您的控制器在构造函数中接收存储库的实例并且您正在解析控制器),则不需要此生命周期管理器。在这种情况下,请使用内置 (Unity 2.0)PerResolveLifetimeManager
。编辑:
我在您提供的
UnityContainer
配置中发现了相当大的问题。您正在使用 ContainerControllerLifetimeManager 注册这两个存储库。这个生命周期管理器意味着每个容器生命周期的单例实例。这意味着两个存储库只会实例化一次,并且实例将被存储并重用于后续调用。因此,您为MyEntities
分配的生命周期并不重要。它被注入到存储库的构造函数中,该构造函数仅被调用一次。两个存储库仍将使用在构建过程中创建的MyEntities
的单个实例 = 它们将在您的AppDomain
的整个生命周期中使用单个实例。这是你能实现的最糟糕的情况。以这种方式重写您的配置:
为什么这就足够了?您正在解析依赖于存储库的控制器,但不需要多次存储库实例,因此您可以使用默认的 TransientLifetimeManager ,它将为每个调用创建新实例。由于调用了存储库构造函数,并且必须解析 MyEntities 实例。但您知道多个存储库可能需要此实例,因此您将使用
PerResolveLifetimeManager
=> 设置它控制器的每次解析只会生成一个MyEntities
实例。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 usePerHttpRequestLifetimeManager
directly:Be aware that Unity will not dispose context for you. Also be aware that default
UnityContainer
implementation will never callRemoveValue
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 withContainerControllerLifetimeManager
. 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 toMyEntities
. It is injected to repositories' constructors which will be called only once. Both repositories will use still that single instance ofMyEntities
created during their construction = they will use single instance for whole lifetime of yourAppDomain
. That is the worst scenario you can achieve.Rewrite your configuration this way:
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 andMyEntities
instance must be resolved. But you know that multiple repositories can need this instance so you will set it withPerResolveLifetimeManager
=> each resolving of controller will produce only one instance ofMyEntities
.