ASP.NET会话对象中的实体框架对象上下文?

asp.net entity-framework httpcontext objectcontext session

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

由于我们一直面临的许多问题,我们认为在服务器上保持对象上下文(或DataAccess实例)的范围更长是一个很大的优势。建议在此帖子中保留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];
}

干杯。

一般承认的答案

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

也就是说,它可以在不引起任何重大灾难的情况下完成,你只需要确保你了解幕后发生的事情。如果您计划这样做, 请继续阅读,以便您知道自己正在做什么,并了解权衡取舍。


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

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

对象上下文将要处理的数据量不是很大,并且不会对我们体面的服务器硬件造成问题,关于随时间缓存和相对较少的并发用户。

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


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

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


如果您意识到所有这些问题并已采取措施来缓解这些问题,那很好。但我的问题是,为什么你认为会话级ObjectContext是如此的好主意呢?创建ObjectContext实际上是一个非常便宜的操作,因为元数据是为整个AppDomain缓存的。我猜赌你要么错误地认为它很贵,或者你试图在几个不同的网页上实施复杂的有状态进程,而后者的长期后果远比任何具体的都要糟糕。只需将ObjectContext放入会话即可对您造成伤害。

无论如何你要继续这样做,只要确保你出于正确的理由这样做,因为没有很多理由这样做。但是,正如我所说,这绝对是可能的,你的应用程序不会因此而爆炸。


更新 - 对于其他任何考虑使用downvoting的人,因为“同一会话上的多个请求可能导致线程安全问题”,请阅读ASP.NET会话状态概述文档的底部。不仅是序列化的会话状态的单独访问 ;任何获取会话的请求都会保留对整个请求完成之前未释放的会话的独占锁定。除了上面列出的一些优化之外,在默认配置中不可能有两个同时发出的请求,这些请求持有对ObjectContext的同一会话本地实例的引用。

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


热门答案

你应该为每个请求使用一个ObjectContext,你不应该存储它是Session。很容易破坏存储很长时间的ObjectContext中的数据:

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

  2. 如果有人从数据库中删除数据,而它仍处于上下文中会怎么样? ObjectContext缓存数据,并不会不时查看它是否仍然存在或者是否已更改:)

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

ObjectContext被设计为短暂的,最好在需要时在请求中创建它并在其末尾处置。

如果每个请求的上下文对您不起作用,那么您可能会做错事,但不要使用Session更糟糕。




许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因
许可下: CC-BY-SA with attribution
不隶属于 Stack Overflow
这个KB合法吗? 是的,了解原因