【问题标题】:Entity Framework Object Context in ASP.NET Session object?ASP.NET 会话对象中的实体框架对象上下文?
【发布时间】:2010-03-05 01:28:48
【问题描述】:

我们有一个多层的 Asp.NET Web 窗体应用程序。数据层有一个名为DataAccess 的类,它实现IDisposable,并有一个我们的实体框架对象上下文的实例作为私有字段。该类有许多返回各种实体集合的公共方法,并在释放时释放其对象上下文。

由于我们一直面临的许多问题,我们决定将对象上下文(或DataAccess 的实例)在服务器上的范围内保持更长时间是一个很大的优势。有人建议在 this post 的 HttpContext.Current.Items 集合中保留一个实例,以便每个 Http 请求都有一个实例。

我想知道的是:将我们的对象上下文的实例存储在HttpContext.Current.Session 对象中会产生什么问题/关注/问题????

  • 我假设 Session 对象已完成并设置为在用户会话到期时进行垃圾回收,因此该实例将被正确处理。
  • 我假设大多数默认浏览器设置将允许我们的应用毫无疑虑地放置其 SessionId cookie。
  • 对象上下文将处理的数据量并不巨大,不会对我们体面的服务器硬件造成问题,因为随着时间的推移缓存和相对较少的并发用户。

这将相对较快地实施,并且不会影响我们许多现有的单元测试。

我们将使用 AutoFac 和 ServiceProvider 类来提供实例。当需要 ObjectContext 的实例时,它将由类似于以下的代码返回:

private static Entities GetEntities(IContext context)
{
    if (HttpContext.Current == null)
    {
        return new Entities();
    }

    if (HttpContext.Current.Session[entitiesKeyString] == null)
    {
        HttpContext.Current.Session[entitiesKeyString] = new Entities();
    }

    return (Entities)HttpContext.Current.Session[entitiesKeyString];
}

干杯。

【问题讨论】:

    标签: asp.net entity-framework session httpcontext objectcontext


    【解决方案1】:

    在会话状态中存储ObjectContext 不是我认为的好习惯,因为该类旨在封装工作单元模式 - 您加载一些数据(实体),修改它们,提交您的更改(由 UOW 跟踪),然后您就完成了。 UOW 对象并非旨在或设计为长期存在的。

    也就是说,它可以在不造成任何重大灾难的情况下完成,你只需要确保你了解幕后发生的事情。 如果您打算这样做,请继续阅读,以便您了解自己的目标并了解权衡取舍。


    我假设 Session 对象已完成并在用户会话到期时设置为垃圾回收,因此该实例将被正确处理。

    这实际上是不准确的,或者至少似乎是基于它的措辞方式。会话到期/注销不会立即导致任何项目被处置。它们将最终被最终确定/处置,但这取决于垃圾收集器,您无法控制它何时发生。这里最大的潜在问题是,如果您碰巧在ObjectContext 上手动打开了一个连接,该连接不会自动关闭 - 如果您不小心,您最终可能会泄漏数据库连接,而这不会被发现定期进行单元测试/集成测试/实时测试。

    Object Context 将处理的数据量并不巨大,不会对我们体面的服务器硬件造成问题,因为随着时间的推移缓存和相对较少的并发用户。

    请记住,增长是无限的。如果特定用户决定连续 12 小时使用您的网站,整天运行不同的查询,那么上下文将变得越来越大。 ObjectContext 没有自己的内部“垃圾收集”,它不会清除长时间未使用的缓存/跟踪实体。如果您确定根据您的用例这不会成为问题,那很好,但应该困扰您的主要事情是您缺乏对情况的控制。


    另一个问题是线程安全。 ObjectContext 不是线程安全的。会话访问通常是序列化的,因此一个请求将阻塞等待其会话状态,直到对同一会话的另一个请求完成。但是,如果有人决定稍后进行优化,特别是页面级只读会话的​​优化,请求将不再持有排他锁,您最终可能会遇到各种竞争条件或重入问题.

    最后但并非最不重要的当然是多用户并发问题。 ObjectContext 永远缓存它的实体,直到它被释放。如果另一个用户自己更改了相同的实体ObjectContext,则第一个ObjectContext 的所有者将永远了解该更改。这些陈旧数据问题可能非常难以调试,因为您实际上可以看到查询进入数据库并返回新数据,但ObjectContext 将用缓存中已经存在的旧数据覆盖它。在我看来,这可能是避免长期存在的ObjectContext 实例的最重要原因;即使您认为您已经对其进行了编码以从数据库中获取最新数据,ObjectContext 也会认为它比您更聪明,并将旧实体交还给您。


    如果您了解所有这些问题并已采取措施缓解这些问题,那很好。但我的问题是,您究竟为什么认为会话级别的ObjectContext 是个好主意?创建ObjectContext 确实是一个非常便宜的操作,因为元数据是为整个 AppDomain 缓存的。我敢打赌,要么你错误地认为它很昂贵,要么你试图在几个不同的网页上实现复杂的有状态过程,而后者的长期后果比任何特定的要糟糕得多只需将ObjectContext 放入会话中,可能会造成伤害。

    如果你还是要继续这样做,请确保你这样做是出于正确的理由,因为没有很多好的理由可以这样做。但是,正如我所说,这绝对是可能的,并且您的应用不会因此而崩溃。


    更新 - 因为“同一会话上的多个请求可能导致线程安全问题”而考虑对此进行否决的任何其他人,请阅读ASP.NET Session State Overview 文档的底部。被序列化的不仅仅是会话状态的单个访问;任何获取会话的请求都会对会话保持独占锁,直到整个请求完成才释放。除了我上面列出的一些优化之外,在默认配置中不可能有两个同时请求持有对 ObjectContext 的同一会话本地实例的引用。

    由于上面列出的几个原因,我仍然不会在会话状态中存储 ObjectContext,但这不是线程安全问题,除非您将其设为一个。

    【讨论】:

    • 您关于线程安全的说法不正确。 ObjectContext 确实不是线程安全的,虽然会话访问是序列化的,但来自同一个用户的两个不同请求很可能同时使用同一个实例。您可能会遇到这样一种情况,即在您验证实体的状态之后,第一个请求正在调用 SaveChanges,而第二个请求正在改变某个实体。这可能会导致各种可怕的事情,例如数据库中的无效状态和永久损坏的 ObjectContext 实例。
    • @Steven:请在此处阅读有关会话状态的 MSDN 文档:msdn.microsoft.com/en-us/library/ms178581.aspx 它说,我引用,“...如果对同一个会话发出两个并发请求(通过使用相同的SessionID 值),第一个请求获得对会话信息的独占访问权。第二个请求只有在第一个请求完成后才执行。”请删除反对票,除非程序员优化(我已明确概述)打破了这种排他性,否则这里没有风险。
    • 澄清一下:序列化的不仅仅是对会话状态的访问,而是整个请求。这是一个至关重要的区别。在第一个请求完全完成执行之前,第二个请求无法访问会话状态。如果同一会话上的 HTTP 请求完全使用该会话,则它们将被序列化(实际上是单线程)。
    • Aaronaught,我必须道歉。您的长答案实际上非常准确,并且您非常清楚和正确地说明了拥有会话 ObjectContext 的所有缺点,我完全同意您的看法。在您的回答中,您已经说明了更改默认设置时的线程安全问题,所以我的第一条评论应该被忽略......
    • ...但是,我认为不幸的是,您选择以“可以将 ObjectContext 保持在会话状态”开头。我更喜欢看到你写“不,将 ObjectContext 保持在会话状态是很危险的,除非你了解以下权衡”。这更好地总结了你的答案。这里的危险在于,如果您说“没关系”,开发人员可能只会阅读第一段。老实说,这就是为什么我对你投了反对票。我最初只阅读了第一段。所以我再次谦虚的道歉。你再次投了赞成票。
    【解决方案2】:

    每个请求你应该使用一个 ObjectContext,你不应该将它存储为 Session。 ObjectContext中长期存储的数据很容易毁掉:

    1. 如果插入的数据不违反 ObjectContext 中的规则,但违反了数据库中的规则,该怎么办?如果您插入违反规则的行,您会从上下文中删除它吗?图像情况:您使用一个上下文,突然您请求更改一个表中的数据,将行添加到另一个表,然后调用 SaveChanges()。其中一项更改引发约束冲突错误。你怎么清理它?清理上下文并不容易,在下一个请求中获取新上下文更容易。

    2. 如果有人从数据库中删除了仍在上下文中的数据怎么办? ObjectContext 会缓存数据,不会时不时查看它是否还在,或者它们是否发生了变化:)

    3. 如果有人更改 web.config 并且会话丢失怎么办?似乎您想依靠 Session 来存储有关登录用户的信息。表单身份验证 cookie 是存储此信息的更可靠的地方。在许多情况下,会话可能会丢失。

    ObjectContext 被设计为短暂的,最好在需要时在请求中创建它并在它结束时处理它。

    如果每个请求的上下文对您不起作用,则您可能做错了什么,但不要使用 Session 使情况变得更糟。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-11
      • 1970-01-01
      • 2013-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多