管理dbContext的生命周期

12

我希望把dbContext的生命周期与会话的生命周期绑定在一起,例如能够在多个请求中提交或放弃dbcontext上的一组更改。

是否有其他更好的方法来实现这一点?如果没有,什么机制适合创建和处理这些上下文?我正在考虑具有会话结束清理的静态哈希表,但也许我做得不对。我还考虑过只保留那些需要在多个请求中工作的上下文,并将其余的保留在每个操作中。有什么建议吗?


3
首先,MVC并没有会话的概念,这是MVC无状态的重要组成部分,即在请求之间没有状态。其次,我个人认为长时间保持DBcontext是一个非常糟糕的想法,因为它会随着被跟踪对象的增多而逐渐变慢。这意味着您的用户体验将随着用户在站点上停留的时间越久而变差。 - undefined
嗨,感谢您的反馈。您能提出另一种解决方法吗?此外,我确实认为会话仍然扮演着相当重要的角色。购物车、保持登录等等都是基于会话的 - 或者至少我认为是这样的。 - Martijn
5
如果您正在使用 IOC 容器,您可以将上下文的生命周期设置为每个 Web 请求。这样,如果您在同一请求中多次使用上下文,则将使用同一个上下文实例。 - Maess
1
@Martijn 通常我们在MVC中处理事情有些不同,例如使用Auth时我们使用cookie来维护状态,这给人以持久登录的错觉。对于您的DBcontext,我建议将部分模型传递到视图,直到完成,或将该部分保存到数据库中。如果您想知道为什么EF会在跟踪大量项目时变慢,请查看我的帖子:http://blog.staticvoid.co.nz/2012/05/entityframework-performance-and.html - undefined
5个回答

6

我可以在Winform项目中使用IoC来管理DbContext的生命周期吗?如果可以,有没有好的示例? - Masoud

4

以下SO帖子非常优雅地回答了这个问题:

StructureMap CacheBy InstanceScope.HttpSession not working

基本上,魔法来自以下代码(适应您的问题和较新的StructureMap语法):

ObjectFactory.Initialize(factory => {
    factory.For<MyContext>()
           .CacheBy(InstanceScope.HttpSession)
           .Use(new MyContext(_myConnectionString));
});

然后,在您的控制器中,只需使用以下代码创建该对象的实例:

var db = ObjectFactory.GetInstance<MyContext>();

由于您通过StructureMap设置了IoC(控制反转)实例的范围为HttpSession,只要会话保持不变,每次检索到的上下文都应该是相同的。

然而,请注意,特别是对于DbContext对象来说,这通常是一个非常糟糕的想法 - 因为您正在将一个状态跟踪对象与无状态环境混合在一起,很容易陷入一个错误事务或者对象状态异常的状态,导致你需要刷新你的会话才能继续进行数据库调用。

DbContext对象通常被设计为非常轻量级和可丢弃的。让它们在作用域之外死亡基本上是可以接受的。


6
使用IoC的原因之一是减少耦合,增加内聚性,我认为将“var db = ObjectFactory.GetInstance<MyContext>();”放在控制器中会导致这种情况被违反。 - Mohsen Alikhani
是的 - 可能是这样。您还可以通过注入所有控制器(首选)并为控制器设置构造函数,例如:public MyController(MyContext context) { },从而使用相同设置生成本地副本。但是,为了帮助发布者实施该方案,我需要编写一整个系列的博客文章和代码示例,这似乎不适合在此论坛上讨论。因此,提供简单的答案可能存在一些不完美之处,但仍然可行。 - Troy Alford

2
“将DbContext与会话一起使用不是一个好主意。”
  1. 当您有更多的请求时,它会从数据库中加载一些数据到上下文中,这使得上下文更大,这意味着内存问题,
  2. 您将无法与上下文中的DbContet请求相比较更新的数据,因为另一个用户(另一个会话)可能已经更新了您已经加载到上下文中的数据。
请参阅以下内容以获取您的选项: Caching Entity Framework DbContexts per request 阅读此内容: Asp.Net MVC and Session

我知道这种方法的缺点。但我正在寻找正确的解决方案。在数据库模型变得非常复杂,而且解决冲突问题也很麻烦的情况下(链接问题中的解决方案2),将数据表复制到暂存表中,或者将可能复杂的对象存储在会话中而不是dbcontext中,似乎都不是一个好主意。 - Martijn

1
通常在这种情况下,您需要将更改保存在临时存储中(例如会话、cookie或数据库)。当您需要保存结果或查看新数据时,您获取旧数据和更改并构建新对象。更改可以存储为新对象或一系列操作。应用更改时要小心,可能会发生数据冲突。当然,使用上下文/请求。

0

理论上,您可以将上下文存储到会话字典中,并在每个控制器中访问它。但是,您可能会遇到一些线程问题,因为在存储时所处的线程与检索时所处的线程不同。如果上下文不使用线程静态变量,则可能会起作用(但我不确定),否则不行。无论如何,这是糟糕的设计...在Web中没有人这样做...为什么要存储上下文?您可以以较低的价格在随后的HTTP请求中重新创建它。如果您需要跟踪属性更改,还有其他更适合Web的方法。


谢谢!你能给我一些其他更合适的方法的提示吗? - Martijn
  1. 最简单的方法是将ViewModel序列化为json并放入隐藏字段中。当页面提交时,您可以从json反序列化旧的ViewModel,并将其与用户修改的ViewModel进行比较。您执行比较的方式取决于您的需求。
  2. 从数据库检索数据,创建一个新的上下文,然后使用UpdateModel或TryUpdateModel MVC方法,使用用户提交的数据更新此模型的属性。
  3. 第一种和第二种方法的混合,使用TryUpdateModel在序列化为json的旧ViewModel上进行操作。
- Francesco Abbruzzese
  1. 如果您正在使用knockout.js,那么有一些库可以进行更改跟踪和其他高级操作,例如Mvc Controls Toolkit updatesManager js类或Breeze.js。但是这条路需要您学习很多,以了解所有这些框架的工作原理。
  2. 有一些Mvc控件,如Mvc Controls Toolkit和Telerik Datagrids,它们仅返回对服务器的更改,使用不同的格式。也就是说,它们告诉您在网格中表示的列表对象中哪些被修改、插入或删除。
- Francesco Abbruzzese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接