我已经阅读了很多文章,解释如何设置实体框架的DbContext,以便使用各种DI框架只创建和使用每个HTTP web请求。

为什么一开始这是个好主意?使用这种方法有什么好处?在某些情况下,这是个好主意吗?在每个存储库方法调用实例化dbcontext时,是否有一些事情是使用这种技术可以做而不能做的?


当前回答

我喜欢它的地方在于,它将工作单元(正如用户看到的那样——即页面提交)与ORM意义上的工作单元对齐。

因此,您可以使整个页面提交都是事务性的,如果您公开CRUD方法,并且每个方法都创建一个新的上下文,则无法做到这一点。

其他回答

实体框架需要特别注意的另一个问题是,当组合使用创建新实体、延迟加载,然后使用这些新实体时(从相同的上下文中)。如果你不使用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.

我很确定这是因为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

在这个问题或讨论中没有真正解决的一件事是DbContext不能取消更改的事实。您可以提交更改,但不能清除更改树,因此如果您使用了每个请求上下文,那么无论出于何种原因需要丢弃更改,都是不走运的。

Personally I create instances of DbContext when needed - usually attached to business components that have the ability to recreate the context if required. That way I have control over the process, rather than having a single instance forced onto me. I also don't have to create the DbContext at each controller startup regardless of whether it actually gets used. Then if I still want to have per request instances I can create them in the CTOR (via DI or manually) or create them as needed in each controller method. Personally I usually take the latter approach as to avoid creating DbContext instances when they are not actually needed.

这也取决于你从哪个角度看问题。对我来说,每个请求实例从来没有意义。DbContext真的属于Http Request吗?就行为而言,这是错误的地方。您的业务组件应该创建上下文,而不是Http请求。然后,您可以根据需要创建或丢弃业务组件,而不必担心上下文的生命周期。

我同意之前的观点。可以说,如果你打算在单线程应用中共享DbContext,你将需要更多的内存。例如,我在Azure上的web应用程序(一个额外的小实例)需要另外150mb的内存,而我每小时大约有30个用户。

这是一个真实的例子:应用程序已经在中午12点部署