Landman Code Exploring my outer regions of coding.

A blog about C#, Delphi, assembler and general developer stuff.

Landman Code Exploring my outer regions of coding.

A blog about C#, Delphi, assembler and general developer stuff.

Binding the ObjectContext Life-Cycle to the current ASP.NET request

This spider thread picture from flickr is from Infinity Rain and has the nc-nd-2.0 license. My team has switched (with the coaching of class-a) to LINQ to Entities from the ADO.NET Entity Framework for our new applications. Because we came from ADO.NET Typed Datasets we hade to revise our architecture. Together with a team member we designed our new architecture (and asked Alex Thissen to review it). We used a new project to test our architecture and use pair programming to solve problems which arose during the development of the business layer.

We encapsulated the ObjectContext from the Entity Framework using the repository pattern. How we did this I might post about later, but now I’m going to discuss something else.

The problem was that each entity got it’s own repository, but you have to share the ObjectContext, or nothing works except the stuff that happens on that entity alone. Anyone who uses the LINQ to Entities (or LINQ to SQL) can guess what happened, a lot of Exceptions and non updating data. So there are two ways we could solve it, create an repository factory which would inject the correct ObjectContext or make some kind of singleton access to a ObjectContext. We chose for the second solution because we already had an repository factory which we’re planning to use as a unit of work pattern. So that repository factory couldn’t be a singleton and we still had to solve the same problem of the ObjectContext instances.

The singleton pattern I would have normally used was the fully lazy instantiation singleton by Jon Skeet which in my opinion should be mentioned in the MSDN P&P Article: Implementing Singleton in C#. But we wanted a ObjectContext per request, so it’s not really a singleton but more a more managed life-cycle. Although most of our applications are web based, I’d like it if it could survive outside of ASP.NET. Alex Thissen gave us a sample of how he did it, and we used it for a while. The code he provided is below.

public class DataContextFactory
{
    private const string DATACONTEXTKEY = "NorthwindataContext";
    private static object myLock = new object();

    internal static NorthwindEntities CurrentContext
    {
        get
        {
            NorthwindEntities context = DataContext;
            if (context == null)
            {
                lock (myLock)
                {
                    if (context == null)
                    {
                        context = new NorthwindEntities 
                        DataContext = context;
                    }
                }
            }
            return context;
        }
    }

    private static NorthwindEntities DataContext
    {
        get
        {
            if (IsInWebContext)
            {
                return (NorthwindEntities)HttpContext.Current.Items[DATACONTEXTKEY];
            }
            else
            {
                return (NorthwindEntities)CallContext.GetData(DATACONTEXTKEY);
            }
        }

        set
        {
            if (IsInWebContext)
            {
                HttpContext.Current.Items[DATACONTEXTKEY] = value;
            }
            else
            {
                CallContext.SetData(DATACONTEXTKEY, value);
            }
        }
    }

    private static bool IsInWebContext
    {
        get { return HttpContext.Current != null; }
    }
}

We used it in the beginning and it worked. But when I started developing a T4 template for our repositories I looked at the code again and it didn’t feel right, especially the locking. Because both the HttpContext.Current as the CallContext are unique for each thread, so why do we need to lock if only one thread access the same HttpContext.Current or CallContext (please correct me if I'm wrong). A little bit searching on the internet lead me to SimoneB's post about the ASP.NET Singleton-per-Page pattern, I combined this with the CallContext knowledge in my own DataContextFactory. The code for that factory is located below.

internal class DataContextFactory
{
    public static NorthwindEntities CurrentContext
    {
        get
        {
            if (IsInWebContext)
            {
                // support the difference between pages in Server.Transfer
                Page currentPage = HttpContext.Current.Handler as Page;
                if(currentPage == null)
                    throw new NullReferenceException("The page is null");
                return (currentPage.Items["NorthwindEntitiesSingleton"] ??
                       (currentPage.Items["NorthwindEntitiesSingleton"] =
                        new NorthwindEntities())) as NorthwindEntities;
            }
            else
            {
                NorthwindEntities result = CallContext.GetData("NorthwindEntitiesSingleton") as  NorthwindEntities;
                if (result == null)
                {
                    result = new NorthwindEntities();
                    CallContext.SetData("NorthwindEntitiesSingleton", result);
                }
                return result;
            }
        }
    }
    private static bool IsInWebContext
    {
        get { return HttpContext.Current != null; }
    }
}

Because our repositories are generated this DataContextFactory is also generated and therefore it has the string copied allover the place.

Our repositories and the repository factory both store the DataContext in a private field, so if the repository instance is stored in a session, the context will remain the same, but if you create a new repository you’ll have the current DataContext.  To make this more transparent we used the following rule: when we need to use the unit of work pattern, we store the repository factory in the session and only request repositories from that factory. If it’s just a simple page, request the desired repository directly. Below is an example of how a repository looks like using the ContextFactory.

public partial class CategoryRepository: ICategoryRepository
{
    private NorthwindEntities context;

    /// <summary>
    /// Implement this partial method to execute code after the constructor.
    /// </summary>
    partial void AfterConstructed();

    public CategoryRepository() :
        this (DataContextFactory.CurrentContext)
    {
    }
    internal CategoryRepository(NorthwindEntities context)
    {
        this.context = context;
        AfterConstructed();
    }
    // The rest
}

I just have one problem I haven’t figured out yet, the DataContext implements IDisposable, so when I’m done with it I should call Dispose to clean the resources. The problem is, I don’t know when to correctly call Dispose. So if anybody has a good idea how to solve this?

Tags: , , , , ,

1 comments:

  1. spiceboy said:

    Have you tried to open your Context within a Using Clause? Then within the using, you put all your code to work with the entities and the using clause will dispose the context for you.

    at 17 January, 2011 21:19  

Post a Comment