Singleton ObjectContext: The remix

I realized that when I posted this, I didn’t actually post my way of solving the singleton context idea. Well it’s not a real singleguy anymore in the strictest sense. It is still used by pretty much all querying BUT it is used every postback. How did I do this? With a little enginuity, brilliance, and time. (Read luck and constant pounding on the keyboard)

The old idea was to have a single context that was held in a static (Therefore the context was static). Couple problems:

  • All queries and updates will be sharing this context… for every user. Hrm. I can’t see anything wrong with Sally Starshine hitting the old update button and saving Dick McGurk’s changes. Nothing wrong with that all.
  • Any changes to an object in the context will stay in the context until the various ways of “resetting” a static object are used, regardless of being persisted to the database. Basically, if you changed the user.UserName = “YouIdiot”, “YouIdiot” will now show up anywhere you use that context… which is everywhere.

So as you can see, disaster. Now, where could I have gone from here? Well insane first, then I found a solution… Page Requests. You see, when a request is made there are two events fired of importance:

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
    }

    protected void Application_EndRequest(object sender, EventArgs e)
    {
    }

Pretty snazy, right? Well these are fired with every request, so basically these are fired every postback… and then some. The next step I took was to make context creation and tear down methods. Something like on BeginRequest: If the context doesn’t exist, create and on EndRequest: If the context does exist, DESTROY. Well this worked at first… until I realized something. That cute little method BeginRequest fires on every request (Duh) meaning anytime the page requests an image. Hrm. That’s a lot of useless building right? Well it occurred to me the tear down on EndRequest was a good idea since it won’t do anything if the context doesn’t exist. No harm no foul. The problem was the BeginRequest. Fact is, the answer was stairing at me… I already had the build up method, I just had to call it when it mattered: First use. SO can you figure out what I did? I bet you can!

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Web;

    public class EntityContext
    {
        private TISQLEntities _context;
        private static String MAIN_CONTEXT_KEY = "MainContext";

        public static TISQLEntities Context
        {
            get
            {
               if (HttpContext.Current.Items[MAIN_CONTEXT_KEY] == null)
                {
                    CreateContext();
                }

              return (TISQLEntities)HttpContext.Current.Items[MAIN_CONTEXT_KEY];
            }
        }

        private static void CreateContext()
        {
           HttpContext.Current.Items[MAIN_CONTEXT_KEY] = new TISQLEntities();
        }

        public static void RemoveContext()
       {
           if (HttpContext.Current.Items[MAIN_CONTEXT_KEY] != null)
           {
             ((TISQLEntities)HttpContext.Current.Items[MAIN_CONTEXT_KEY]).Dispose();
             HttpContext.Current.Items[MAIN_CONTEXT_KEY] = null;
            }
         }
    }

Idiot simple, as it would have to be coming from me. You might have seen that I’m holding it in the current HTTPContext. This is to hold the needed context for the current request. Yippee.

Now for the use (In the Global.asax.cs file)

    protected void Application_EndRequest(object sender, EventArgs e)
    {
        EntityContext.RemoveContext();
    }

So what happens? First time I use the context:

    EntityContext.context.SiteUser.Load();

The EntityContext class creates the context and goes to town. When the request is done, the EntityContext class removes said context.

Now on a side not, you might notice that when doing unit tests, there is no HTTPContext. This is a problem and involves one more step (That I’m not completely happy with right now) that actually takes the old Static idea and applies it. Now you might thing that I’m undoing everything I just posted, but in reality when you unit test, most likely you’ll be doing it on your computer and therefore you won’t be running into anyone else’s context. (Unlike a web site)

    private static String MAIN_CONTEXT_KEY = "MainContext";
    private static TISQLEntities _context;

    public static void RemoveContext()
    {
        if (HttpContext.Current != null && HttpContext.Current.Items[MAIN_CONTEXT_KEY] != null)
        {
            ((TISQLEntities)HttpContext.Current.Items[MAIN_CONTEXT_KEY]).Dispose();
            HttpContext.Current.Items[MAIN_CONTEXT_KEY] = null;
        }

        if(_context != null)
        {
          _context = null;
        }
    }

    public static TISQLEntities Context
    {
        get
        {
            if (HttpContext.Current == null)
            {
                if(_context == null)
                {
                    _context = new TISQLEntities();
                }
                return _context;
            }

            if(HttpContext.Current.Items[MAIN_CONTEXT_KEY] == null)
            {
                HttpContext.Current.Items[MAIN_CONTEXT_KEY] = new TISQLEntities();
            }

            return (TISQLEntities)HttpContext.Current.Items[MAIN_CONTEXT_KEY];
        }
    }

I don’t like this one fully, but for now it’s good. There has to be a better way though.