我们有一个多层的 Asp.NET Web 窗体应用程序。数据层有一个名为DataAccess
的类,它实现IDisposable
并具有我们的实体框架对象上下文的实例作为私有字段。该类具有许多返回各种实体集合的公共方法,并且在释放其对象上下文时将释放它。
由于我们遇到了许多问题,我们认为将对象上下文(或 DataAccess
实例)在服务器上的作用域中保留更长时间将是一个很大的优点。建议在 这篇文章 以便每个 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];
}
干杯。
We have a multi-layered Asp.NET Web Forms application. The data layer has a class called DataAccess
which impements IDisposable
and has an instance of our Entity Framework Object Context as a private field. The class has a number of public methods returning various collections of Entities and will dispose its Object Context when it is disposed.
Due to a number of problems we've been facing, we decided it would be a big plus to keep the Object Context (or an instance of DataAccess
) in scope for longer on the server. A suggestion was made to keep an instance in the HttpContext.Current.Items
collection from this post in order to have one instance per Http request.
What I'm wondering is: What issues / concerns / problems would arise from storing an instance of our Object Context in the HttpContext.Current.Session
object????
- I'm assuming that the Session object is finalised and set for garbage collection when a user's session expires, so the instance will be disposed properly.
- I'm assuming most default browser settings will let our app place its SessionId cookie without qualms.
- The amount of data the Object Context will be dealing with is not enormous and will not pose a problem for our decent server hardware, with regards to caching over time and relatively few concurrent users.
This will be relatively quick to implement and will not affect our many existing unit tests.
We'll be using AutoFac and a ServiceProvider class to supply instances. When an instance of the ObjectContext is required it will be returned by code similar to this:
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];
}
Cheers.
发布评论
评论(2)
我认为在会话状态中存储
ObjectContext
不是一个好的做法,因为该类旨在封装工作单元模式 - 您加载一些数据(实体),修改他们,提交您的更改(由 UOW 跟踪),然后您就完成了。 UOW 对象并非旨在或设计为长期存在的。也就是说,它可以在不造成任何重大灾难的情况下完成,您只需确保了解幕后发生的事情即可。 如果您打算这样做,请继续阅读,以便您知道自己将面临什么并了解权衡。
这实际上是不准确的,或者至少看起来是基于其措辞方式。会话到期/注销不会立即导致任何项目被处置。它们最终将被最终确定/处置,但这取决于垃圾收集器,您无法控制它何时发生。这里最大的潜在问题是,如果您碰巧在
ObjectContext
上手动打开一个连接,该连接不会自动关闭 - 如果您不小心,您最终可能会泄漏数据库连接,这会导致常规单元测试/集成测试/实时测试不会发现这些问题。请记住,增长是无限的。如果某个特定用户决定连续 12 个小时使用您的网站并整天运行不同的查询,那么上下文将会变得越来越大。
ObjectContext
没有自己的内部“垃圾收集”,它不会清除长时间未使用的缓存/跟踪实体。如果根据您的用例,您确定这不会成为问题,那么很好,但应该困扰您的主要问题是您对情况缺乏控制。另一个问题是线程安全。
ObjectContext
不是线程安全的。会话访问通常是序列化的,因此一个请求将阻塞等待其会话状态,直到同一会话的另一请求完成。但是,如果有人决定稍后进行优化,特别是页面级只读会话的优化,请求将不再持有独占锁,并且最终可能会遇到各种竞争条件或重入问题。最后但并非最不重要的当然是多用户并发问题。
ObjectContext
永远缓存它的实体,直到它被释放为止。如果另一个用户在自己的ObjectContext
上更改相同的实体,则第一个ObjectContext
的所有者将永远发现该更改。这些过时的数据问题可能非常难以调试,因为您实际上可以看到查询进入数据库并返回新数据,但ObjectContext
会用已经存在的旧的、过时的数据覆盖它。在缓存中。在我看来,这可能是避免长期存在的ObjectContext
实例的最重要原因;即使您认为您已对其进行编码以从数据库中获取最新数据,ObjectContext
也会认为它比您更聪明,并将旧实体返还给您。如果您了解所有这些问题并已采取措施缓解这些问题,那就好了。但我的问题是,为什么您认为会话级 ObjectContext 是一个好主意?创建
ObjectContext
实际上是一个非常便宜的操作,因为元数据是为整个 AppDomain 缓存的。我敢打赌,要么你错误地认为它很昂贵,要么你试图在几个不同的网页上实现复杂的有状态进程,而后者的长期后果比任何特定的后果都要糟糕得多只需将ObjectContext
放入会话中可能会造成损害。如果你无论如何都要继续做这件事,只要确保你这样做是出于正确的理由,因为没有太多充分的理由这样做。但是,正如我所说,这绝对是可能的,并且您的应用程序不会因此而崩溃。
更新 - 对于其他考虑否决此问题的人,因为“同一会话上的多个请求可能会导致线程安全问题”,请阅读 ASP.NET 会话状态概述文档。序列化的不仅仅是会话状态的单独访问;任何获取会话的请求都会对该会话保留独占锁,直到整个请求完成后才会释放该锁。除了我上面列出的一些优化之外,在默认配置中不可能有两个同时请求持有对
ObjectContext
的同一会话本地实例的引用。由于上面列出的几个原因,我仍然不会在会话状态中存储
ObjectContext
,但这不是线程安全问题,除非您将其设为线程安全问题。Storing an
ObjectContext
in the session state is not something I would consider to be a good practice since the class is intended to encapsulate a unit-of-work pattern - you load up some data (entities), modify them, commit your changes (which are tracked by the UOW), and then you're done with it. UOW objects are not intended or designed to be long-lived.That said, it can be done without causing any major catastrophes, you just have to make sure you understand what's going on behind the scenes. Please read on if you plan on doing this so that you know what you're getting yourself into and are aware of the trade-offs.
This is actually inaccurate, or at least seems to be based on the way it's worded. Session expiry/logout will not immediately cause any of the items to be disposed. They will eventually be finalized/disposed but that is up to the garbage collector and you have no control over when it happens. The biggest potential problem here is if you happen to manually open a connection on the
ObjectContext
, which won't get closed automatically - if you're not careful, you could end up leaking database connections, something that wouldn't be uncovered with regular unit tests/integration tests/live tests.Just keep in mind that the growth is unbounded. If a particular user decides to use your site for 12 straight hours running different queries all day then the context will just keep getting bigger and bigger. An
ObjectContext
doesn't have its own internal "garbage collection", it doesn't scavenge cached/tracked entities that haven't been used for a long time. If you're sure that this isn't going to be a problem based on your use cases then fine, but the main thing that should be bothering you is the fact that you lack control over the situation.Another issue is thread-safety.
ObjectContext
is not thread-safe. Session access is normally serialized, so that one request will block waiting for its session state until another request for the same session is complete. However, if somebody decides to make optimizations later on, specifically the optimization of page-level read-only sessions, requests will no longer hold an exclusive lock and it would be possible for you to end up with various race conditions or re-entrancy problems.Last but not least is of course the issue of multi-user concurrency. An
ObjectContext
caches its entities forever and ever until it is disposed. If another user changes the same entities on his ownObjectContext
, the owner of the firstObjectContext
will never find out about that change. These stale data problems can be infuriatingly difficult to debug, because you can actually watch the query go to the database and come back with fresh data, but theObjectContext
will overwrite it with the old, stale data that's already in the cache. This, in my opinion, is probably the most significant reason to avoid long-livedObjectContext
instances; even when you think you've coded it to grab the most recent data from the database, theObjectContext
will decide that it's smarter than you and hand you back the old entities instead.If you're aware of all of these issues and have taken steps to mitigate them, fine. But my question would be, why exactly do you think that a session-level
ObjectContext
is such a great idea? Creating anObjectContext
is really a very cheap operation because the metadata is cached for the entire AppDomain. I'd wager a guess that either you're under the mistaken impression that it's expensive, or you're trying to implementing complicated stateful processes over several different web pages, and the long-term consequences of the latter are far worse than any specific harm you may do by simply putting anObjectContext
into the session.If you're going to go ahead and do it anyway, just make sure you're doing it for the right reasons, because there aren't a whole lot of good reasons to do this. But, as I said, it's definitely possible to do, and your app is not going to blow up as a result.
Update - for anyone else considering downvoting this because "multiple requests on the same session could cause thread-safety issues", please read the bottom of the ASP.NET Session State Overview documentation. It is not just individual accesses of the session state that are serialized; any request that acquires a session keeps an exclusive lock on the session that is not released until the entire request is complete. Excepting some of the optimizations I listed above, it is impossible in the default configuration for there to ever be two simultaneous requests holding references to the same session-local instance of an
ObjectContext
.I still wouldn't store an
ObjectContext
in the session state for several of the reasons listed above, but it is not a thread-safety issue unless you make it one.每个请求应该使用一个 ObjectContext,不应该将其存储在 Session 中。长期保存在ObjectContext中的数据很容易被破坏:
如果插入的数据不违反ObjectContext中的规则,但违反了数据库中的规则怎么办?如果插入违反规则的行,是否会将其从上下文中删除?图像情况:您使用一个上下文,突然您请求更改一个表中的数据,将行添加到另一个表中,然后调用 SaveChanges()。其中一项更改会引发约束违规错误。你如何清理它?清理上下文并不容易,在下一个请求中获取新的上下文会更容易。
如果有人从数据库中删除数据,而数据仍在上下文中怎么办? ObjectContext 会缓存数据,并且不会时不时地检查数据是否仍然存在或是否已更改:)
如果有人更改了 web.config 并且会话丢失了怎么办?似乎您想依靠 Session 来存储有关登录用户的信息。表单身份验证 cookie 是存储此信息的更可靠的地方。会话在很多情况下都可能丢失。
ObjectContext 被设计为短暂的,最好在需要时在请求中创建它并在其结束时进行处置。
如果每个请求的上下文不适合您,那么您可能做错了什么,但不要通过使用 Session 使情况变得更糟。
You should use one ObjectContext per request, you shouldn't store it is Session. It is easy to ruin data in ObjectContext stored for a long time:
What if you insert data that don't violate rules in ObjectContext, but violate rules in database? If you insert a row that violates the rules, will you be deleting it from context? Image situation: You use one context and suddenly you have request that changes data in one table, adds row to another table, then you call SaveChanges(). One of changes throws constraint violation error. How do you clean it up? Cleaning context is not easy, it is easier just to get new one in next request.
What if someone deletes data from database, while it is still in context? ObjectContext caches data and doesn't look from time to time to check if it is still there or if they changed:)
What if someone changes web.config and Session is lost? It seems as if you want to rely on Session to store information about logged in user. Forms authentication cookie is more reliable place to store this information. Session can be lost in many situations.
ObjectContext was designed to be short lived, it is best to create it in request when needed and dispose at the end of it.
If context per request doesn't work for you, you are propably doing something wrong, but don't make it worse by using Session.