Entity Framework Object Context in ASP.NET Session object?
Asked Answered
L

2

5

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.

Leggy answered 5/3, 2010 at 1:28 Comment(0)
F
19

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.


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.

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.

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.

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 own ObjectContext, the owner of the first ObjectContext 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 the ObjectContext 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-lived ObjectContext instances; even when you think you've coded it to grab the most recent data from the database, the ObjectContext 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 an ObjectContext 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 an ObjectContext 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.

Froude answered 5/3, 2010 at 5:37 Comment(7)
Your statement about thread-safety is incorrect. The ObjectContext is indeed not thread-safe, and while session access is serialized, it is well possible that two different requests coming from the same user use the same instance at the same time. You can run into a situation where the first request is calling SaveChanges just after you validated the state of your entities, while a second is mutating some entity. This could lead to all sorts of terrible things, such as invalid state in the database and an permanently corrupted ObjectContext instance.Feathers
@Steven: Please read the MSDN documentation on session state here: msdn.microsoft.com/en-us/library/ms178581.aspx It says, and I quote, "...if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished." Please remove the downvote, there is no risk here unless programmer optimizations (which I clearly outlined) break this exclusivity.Froude
To clarify: It's not just access to session state that's serialized, it's the entire request that is serialized. That is a crucial difference. A second request cannot gain access to the session state until the first request has completely finished executing. HTTP requests on the same session are serialized (effectively single-threaded) if they use the session at all.Froude
Aaronaught, I have to apologize. Your long answer is actually very accurate and you very clearly and correctly state all the cons of having a session ObjectContext and I totally agree with you on this. In your answer you already state the problems of thread-safety when the default settings are changed, so my first comment should be ignored...Feathers
...However, I think it is unfortunate that you choose to start with saying "it's OK to keep an ObjectContext in the session state". I rather liked to see you write "No, it's dangerous to keep the ObjectContext in session state, unless you understand the following tradeoffs". This better summarizes your answer. The danger here is that developers could just read the first paragraph were you state "it's OK". To be honest that's why I down voted you. I initially only read the first paragraph. So again my humble apologies. Your up voted again.Feathers
That's actually a very good point; I don't want people to just read the first two lines and take this as an endorsement of the practice. I only wanted to clear up the common misconception that it will completely blow up the app; it's definitely a gray area as far as good practices go and I'm going to make that clearer.Froude
Just revisited this after all this time, thanks again for a brilliant answer!Leggy
M
3

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:

  1. 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.

  2. 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:)

  3. 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.

Moorwort answered 5/3, 2010 at 4:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.