Whenever you start working with a modern ORM tool, one of the biggest challenges is figuring out how to manage the "scope" (or sometimes called "context" or "data context"). It is this transactional space where the ORM tool keeps track of changes being made to "persistent objects" in your application so that they can be saved to your "persistence layer" (usually a database) when you're ready to commit them. But figuring out when to create a scope, when to close it, and all the annoying problems that result from closing a scope too early can be very frustrating, and they often are what drive people back to the "raw" ADO.NET they know.
So let's simplify that process for OpenAccess. I'll show you how you can use a HttpModule to automate the management of your OpenAccess scope in ASP.NET applications.
SCOPE <> DATABASE CONNECTION
If you remember one thing, remember this: an open scope does not equal an open connection to your database. It was this fact that really helped the proverbial light bulb go-off inside my head when it comes to understanding scope management. The scope is an in-memory object related to tracking changes you're making to your persistent objects. Leaving a scope "open" for the duration of a HTTP Request is not going to leave a connection to your database open for the same period of time. Rather, a scope will automatically open- and then quickly close- connections to your database when operations requiring interaction with the persistence layer are executed.
That means it is okay to create a scope at the beginning of a request, and then dispose of it at the end of a request. In fact, doing so will help us avoid a lot of errors that can creep in to a ASP.NET project when scope is closed early (like the dreaded "IObjectScope is already closed" errors).
HTTPMODULE FOR SCOPE
What programming mechanism does ASP.NET provide for interacting with every HTTP request? The HttpModule (HttpHandlers only respond to requests with specific extensions, making Modules the better choice here). The basic approach to our module is this:
That sounds easy enough. There is a catch, though. Session is not available in the HttpModule BeginRequest event (it's null). So how then will we store our new Scope in Session? We'll use the less known PreRequestHandlerExecute and PostRequestHandlerExecute events, which fire just before and just after the page event handlers are executed. To use the events, we'll wire them up in our Init method:
//Wire-up Pre and Post RequestHandlers
//Get the defined session "keys" from the Web.Config string key = ConfigurationManager.AppSettings["OpenAccessScopeKey"];
transKey = ConfigurationManager.AppSettings[
commKey = ConfigurationManager.AppSettings[
//Use the keys from Web.Config, or if null, use default values
SCOPE_KEY = key ??
TRANSACTION_KEY = transKey ??
COMMITTABLE_KEY = commKey ??
In addition to wiring-up our events, we also grab a few values from our web.config that will be used to store our scope in Session. This config-based keys approach makes it easy to use the same key in both our HttpModule and our application code and reduces the "brittleness" of the code that would be introduced by hard-coded values.
Next, we need to actually write code for the Pre and PostExecuteHandler events. In these events, we need to create and dispose our scope, respectively. The code we need is pretty simple:
sender, EventArgs e)
//Verify that the HttpContext Session is not null if (HttpContext.Current.Session != null)
//Create a new object scope and save it in Session
HttpContext.Current.Session[SCOPE_KEY] = ObjectScopeProvider1.GetNewObjectScope();
//Create new transaction for the scope
//Verify the HttpContext Session is not null if (HttpContext.Current.Session != null)
//TEST HELPER: Use this setting to make all transactions rollback //thus preventing any data from changing in the DB if (ConfigurationManager.AppSettings["OpenAccessRollbackAll"] != null &&
//Commit the open transaction
ObjectScopeProvider1.Database().Properties.TransactionProvider = TransactionProvider.OpenAccess;
//Dispose the open Transaction object
//Clear session variables (remove items from session)
As you can see, when the request starts, we're simply using the ObjectScopeProvider class (a class provided automatically by the OpenAccess wizards in Visual Studio) to create a new ObjectScope and then we save it in Session. At the end of the request, we commit any open transactions (unless we're in "test rollback" mode), dispose our scope and transaction objects, and then remove all values from Session. Now, when we want to write code in our application, we can simply use the Scope stored in session and not have to worry about opening or closing a scope manually in our application code. In fact, we can even improve the ObjectScopeProvider class in our web projects to look for an existing scope in Session automatically, like so:
//If Scope found in session, cast and return it if (HttpContext.Current.Session != null && HttpContext.Current.Session[ConfigurationManager.AppSettings["OpenAccessScopeKey"]] != null)
//If no scope found in session, return new Scope
( theObjectScopeProvider1.myScope ==
theObjectScopeProvider1.myScope = GetNewObjectScope();
ISN'T SESSION A BAD THING
Normally, and in many cases, yes, especially when you're faced with taking an application in to a multi-server environment. But in this case, since we're only using session for the duration of a single request, we're not faced with the same problems. We add our Scope to Session on the start of a request, use it for all OpenAccess processing in that request, and then close it and remove it from Session when the request is done. That means we don't care if the next request goes to a different server, because the process will start over and a new Scope will be created and destroyed.
Could you leave your Scope open longer than a single request? In theory, yes, but that does introduce some rather tricky scenarios that you'd have to handle in your application, such as how to handle "orphaned" Scopes that do not get closed by some application event. So for web applications, opening and closing a Scope once per request is usually the safest and easiest to manage approach.
And that's all there is to it. I'm attaching to this post a sample C# project that contains my implementation of the OpenAccess HttpModule for managing Scope. To use it, simply add the project to your solution, and then reference it in your OpenAccess-enabled web project. Don't forget to register the HttpModule in your web.config, too:
Hopefully this will help simplify your OpenAccess web projects and help make the process of managing Scope almost invisible.
Download OpenAccess Scope HttpModule (VS 2008 C#)
@toddanglin on Twitter