我已经阅读了很多文章,解释如何设置实体框架的DbContext,以便使用各种DI框架只创建和使用每个HTTP web请求。
为什么一开始这是个好主意?使用这种方法有什么好处?在某些情况下,这是个好主意吗?在每个存储库方法调用实例化dbcontext时,是否有一些事情是使用这种技术可以做而不能做的?
我已经阅读了很多文章,解释如何设置实体框架的DbContext,以便使用各种DI框架只创建和使用每个HTTP web请求。
为什么一开始这是个好主意?使用这种方法有什么好处?在某些情况下,这是个好主意吗?在每个存储库方法调用实例化dbcontext时,是否有一些事情是使用这种技术可以做而不能做的?
当前回答
Another understated reason for not using a singleton DbContext, even in a single threaded single user application, is because of the identity map pattern it uses. It means that every time you retrieve data using query or by id, it will keep the retrieved entity instances in cache. The next time you retrieve the same entity, it will give you the cached instance of the entity, if available, with any modifications you have done in the same session. This is necessary so the SaveChanges method does not end up with multiple different entity instances of the same database record(s); otherwise, the context would have to somehow merge the data from all those entity instances.
这是一个问题,因为单例DbContext会成为一个定时炸弹,最终会缓存整个数据库和。net对象在内存中的开销。
只有通过使用. notracking()扩展方法来使用Linq查询,才能避免这种行为。而且现在的电脑有很多内存。但这通常不是理想的行为。
其他回答
我同意之前的观点。可以说,如果你打算在单线程应用中共享DbContext,你将需要更多的内存。例如,我在Azure上的web应用程序(一个额外的小实例)需要另外150mb的内存,而我每小时大约有30个用户。
这是一个真实的例子:应用程序已经在中午12点部署
实体框架需要特别注意的另一个问题是,当组合使用创建新实体、延迟加载,然后使用这些新实体时(从相同的上下文中)。如果你不使用IDbSet。创建(vs只是新建),当它从创建它的上下文检索时,对该实体的惰性加载将不起作用。例子:
public class Foo {
public string Id {get; set; }
public string BarId {get; set; }
// lazy loaded relationship to bar
public virtual Bar Bar { get; set;}
}
var foo = new Foo {
Id = "foo id"
BarId = "some existing bar id"
};
dbContext.Set<Foo>().Add(foo);
dbContext.SaveChanges();
// some other code, using the same context
var foo = dbContext.Set<Foo>().Find("foo id");
var barProp = foo.Bar.SomeBarProp; // fails with null reference even though we have BarId set.
我喜欢它的地方在于,它将工作单元(正如用户看到的那样——即页面提交)与ORM意义上的工作单元对齐。
因此,您可以使整个页面提交都是事务性的,如果您公开CRUD方法,并且每个方法都创建一个新的上下文,则无法做到这一点。
我很确定这是因为DbContext根本不是线程安全的。所以分享从来不是个好主意。
微软有两个相互矛盾的建议,许多人以完全不同的方式使用dbcontext。
一个建议是“Dispose DbContexts as soon as possible” 因为DbContext激活会占用有价值的资源,比如db 连接等等…… 另一种是每个请求一个DbContext 推荐的
这些相互矛盾,因为如果你的Request做了很多与Db无关的事情,那么你的DbContext就会被毫无理由地保留。 因此,当你的请求只是等待随机的东西完成时,让你的DbContext保持活跃是浪费…
所以很多遵循规则1的人在他们的“存储库模式”中有他们的DbContext,并为每个数据库查询创建一个新的实例,即每个请求X*DbContext
它们只是尽快获取数据并处理上下文。 许多人认为这是一种可以接受的做法。 虽然这有占用你的db资源的最低时间的好处,但显然牺牲了所有的UnitOfWork和缓存糖果EF提供。
保持DbContext的一个多用途实例最大限度地提高了缓存的好处,但由于DbContext不是线程安全的,每个Web请求都运行在它自己的线程上,每个请求的DbContext是你能保持的最长时间。
所以EF的团队建议每个请求使用1 Db Context,这显然是基于这样一个事实:在一个Web应用程序中,一个UnitOfWork最有可能是在一个请求中,这个请求有一个线程。所以每个请求一个DbContext就像UnitOfWork和缓存的理想好处。
但在很多情况下,情况并非如此。 我认为记录一个单独的UnitOfWork,因此有一个新的DbContext后请求记录在异步线程是完全可以接受的
最后,DbContext的生命周期被限制在这两个参数上。UnitOfWork和Thread