【问题标题】:Manage the lifetime of dbContext管理 dbContext 的生命周期
【发布时间】:2012-10-25 11:41:29
【问题描述】:

我想将 dbContext 的生命周期与会话的生命周期联系起来,例如,能够通过多个请求提交或放弃对 dbcontext 上的一组突变的更改。

还有其他(更好的?)方法可以做到这一点吗?如果不是,那么创建和处理上下文的合适机制是什么?我正在考虑在会话结束时进行清理的静态哈希表,但也许我做错了。我也在考虑只保留那些必须处理多个请求的上下文,并保留每个操作的其余部分的想法。有什么建议吗?

【问题讨论】:

  • 有几件事,首先MVC没有会话的概念,它是MVC的很大一部分,它是无状态的,即请求之间没有状态。其次,我个人认为将 DBcontext 保持打开这么长时间是一个非常糟糕的主意,因为跟踪的对象越多,它越慢。这意味着您的用户在网站上停留的时间越长,您的用户体验就会越差。
  • 您好,感谢您的反馈。你能建议另一种方法来解决这个问题吗?此外,我确实相信会议仍然可以发挥相当重要的作用。购物车、保持登录等等等等都是基于会话的——或者至少,我认为是。
  • 如果您使用的是 IOC 容器,您可以将上下文的生命周期设置为每个 Web 请求。这样,如果您在同一请求中的多个位置使用上下文,您将使用相同的上下文实例。
  • @Martijn 通常使用 MVC 我们处理的事情有点不同,例如使用 Auth 我们使用 cookie 来维护状态,这给人一种持久登录的错觉。对于您的 DBcontext,我建议将部分模型传递给视图直到其完成,或者将该部分模型保存到数据库中。如果你对为什么 EF 会因为大量跟踪项目而变慢感兴趣,请在此处查看我的帖子:blog.staticvoid.co.nz/2012/05/…

标签: entity-framework asp.net-mvc-4 dbcontext


【解决方案1】:

您可以使用 IoC(Inversion of Control) 容器来管理 DBContext 的生命周期,例如 StructureMap,步骤如下:

  1. 为 MVC 4 安装 nuget 包: http://nuget.org/packages/StructureMap.MVC4

  2. 阅读快速入门: http://docs.structuremap.net/QuickStart.htm

  3. 设置 DBContext 的范围: http://docs.structuremap.net/Scoping.htm

您还可以结合使用存储库和工作单元模式,通过多个请求提交或放弃对 dbcontext 上的一组突变的更改。

【讨论】:

  • 我可以在 Winform 项目中使用 IoC 来管理 DbContexts 的生命周期吗?如果有,有什么好的样品吗?
【解决方案2】:

这个问题在下面的 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 对象通常被设计为非常轻量级和一次性的。完全可以让它们退出范围并在您完成它们后立即死亡。

【讨论】:

  • 使用 IoC 的原因之一是减少耦合和增加内聚,我认为把“var db = ObjectFactory.GetInstance();”在控制器中是导致违反这种情况的原因。
  • 是的——可能是这样。您还可以通过注入所有控制器(首选)然后使用控制器的构造函数来使用StructureMap,例如:public MyController(MyContext context) { } - 这也将导致本地副本与相同的设置一起使用。然而,为了帮助发帖者实现这一点,我需要写一整套博客文章和代码示例,这似乎不适合这个论坛。因此,简单的答案可能不完美 - 但可行。
【解决方案3】:

让 DbContext 与会话一起运行并不是一个好的 idia。

  1. 当您有更多请求时,它会将部分数据从数据库加载到上下文中,这会使上下文变得更大,这意味着内存问题,

  2. 与上下文中每个请求的 DbContet 相比,您不会更新数据,因为另一个用户(另一个会话)可能已经更新了您已经加载到上下文中的数据。

Caching Entity Framework DbContexts per request

并阅读此内容以供您选择,

Asp.Net MVC and Session

【讨论】:

  • 我知道这些缺点。但是,我正在寻找适当的解决方案。既不会将数据表复制到暂存表,也不会使数据库模型变得异常复杂 - 更不用说该解决方案中的冲突解决问题(来自链接问题的解决方案 2),也不会在会话中而不是 dbcontext 中存储可能的复杂对象。对我来说是个好主意。
【解决方案4】:

通常在这种情况下,您需要将更改保存在临时存储中(如会话或 cookie 或数据库)。当您需要保存结果或查看新数据时,您会获取旧数据和更改并构建新对象。更改可以存储为新对象或动作序列。应用更改时要小心,可能会发生数据冲突。当然使用 Context\Request

【讨论】:

    【解决方案5】:

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

    【讨论】:

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