There are three different ASP.NET server-side containers which can be used to store the data at the server side:
- Application – it’s a shared container for the whole application (a collection of static data)
- Session – it’s a user-session based container, identified by a session identifier (appended to HTTP responses in a cookie) so that it stores user-specific data
- Items – it’s “even more fine grained session”, it’s not only specific to the user but also to the single request so the container is emptied just after the processing ends on the server and before the response is sent to the client
There’s an interesting pattern behind server-side containers I call Pseudosingleton. The pattern makes is easier to access the container data by wrapping it in a static property with a sole “get” accessor. But instead of using a static backend field (like Singleton does), Pseudosingleton stores the data in one of the three server-side containers.
Let’s just see an example of storing the ORM session context in the Items container.
What would be the benefit of such approach? Well, the client code can access the object just like there’s only one, single instance and the actual reference is initialized once per items/session/application (depending of the container used):
This really makes the client code clean, understandable and maintanable. Also, the implementation of the static field can be changed anywhere (for example to use a different server-side container) but the client code does not change.
The pseudosingleton can also easily be created for the Session container:
and for the Application container. This time, however, you have to take care of the possible concurrent execution of two (or more) threads accessing the data. Suppose the application pseudosingleton is written exactly like the two above and two concurrent threads execute the “if (HttpContext.Current.Application[DATAKEY] == null )” one after the other.
Of course, the container will be empty for both threads and both of them will happily continue the execution of the “if” body, thus initializing the data object twice!
To prevent such unintended behavior, an additional trick is required, known as “a lock with two lookups”:
That’s it. Now, when we are able to create pseudosingletons in an uniform way for all server-side containers, two interesting questions arise.
First question : which containers can be used to store the ORM sessions in real-world applications? The answer is – the Items container is the best choice. ORM sessions are created once per requests and there’s no memory footprint on the server as more and more client requests are served. The session container is a bad choice just because of the memory consumption. You probably could have your ORM sessions stored in the session container for every single user but only assuming that you have few users. The application container, on the other hand, could be an interesting choice and I saw at least one application which does so. There are few pros, like the ORM session is created only once and it’s shared among all requests. The cons are obvious – as more and more clients access the data, the object grows in memory and ultimately the whole database is retrieved and stored in memory. This could crash the server because of the lack of resources. Concluding – the Items container is your best choice.
Second question would be – is there a chance to dispose the data stored in a pseudosingleton? Many objects which could be stored there implement IDisposable which should be called somewhere to prevent nasty leaks. And the answer is: for the two containers, Items and Session, there are event handlers in the Application class which can be used to dispose the data.
For example, for the Items container, the corresponding event would be the EndRequest event: