Login Skip Navigation LinksWilsonORMapper > Forums Search
Demo Version Demo Version
Download and try for yourself a fully working demo version, including sample apps and documentation.  The only limitation is that the demo version only works inside the debugger.

PayPal Subscribe
Get It All for $50 USD:
WebPortal, ORMapper,
Source Code, All Updates
PayPal

User Login User Login
Log In
 
 
Reset Password

Wilson ORMapper Forums Wilson ORMapper Forums : Advanced Topics : Where to keep thread-specific IsolatedContext for "global" access?

Date Post
6/5/2006 10:54:26 AM

I've been reading Fowler's Patterns of Enterprise Application Architecture and am quite satisfied that the Wilson ORMapper can fit in nicely with a couple of the patterns in the book.  Namely these are UnitOfWork and Repository.  Essentially what I want to do is have a UnitOfWork class which wraps the persiting and transactional calls, and several Repository classes which wrap the GetObjectSet and OPath building scenarios.  When using a single, application wide-instance of an ObjectSpace, the implementation is fairly trivial - just use a Singleton'd ObjectSpace (Static member).  But I am building a server app that basically needs to have one and only one instance of ObjectSpace per "request", or I guess per thread is the more accurate term.  Since it is essential that the UnitOfWork and Repositories need to be using the same ObjectSpace instance, which is conveniently provided via IsoloatedContext (I.C.), I need a sort of "global" point of access to get it.  I definately don't want to pass around the I.C. in method parameters.  So the class that creates the I.C. (the "manager" bascially) has a method returning an I.C. which has the logic of knowing if it needs to create a new I.C. or return an existing one that was created in a previous call to the method but on the same request thread.  I think this is the thread-specific Registry pattern in POEAA, which is demontrated using Java-specific code in the book.  From my research, it seems there are three or four options for places to stuff the I.C.:

  • a static member with the [ThreadStatic] attribute
  • the System.Runtime.Remoting.CallContext
  • a NamedDataSlot
  • the HttpContext.Curent.Items collection

The HttpContext is probably the best for ASP.NET scenarios, but I denfinately don't want to be referencing System.Web way down in the Domain or Data Access layer.  What is your recommended approach?

Thanks,

David Martines

 

6/5/2006 11:22:17 AM Hi David:

This is actually a hard question, but only because the facts are not at all obvious.  My thought if you had asked this back a year ago would be that you should use a NamedDataSlot, because I didn't want to assume the web HttpContext either, and because I didn't even know about the ThreadStatic attribute.  But alas, things are not so simple afterall in web-land, and so now I will tell you what I'm currently doing in my WebPortal code, and why.

The first problem is that there are some cases where ASP.NET might switch threads on a single request !  I believe its safe to say these cases are rather esoteric and not likely to happen, but its also probably not a good idea for you to assume that no one else will introduce one of those cases when you're designing the architecture for everyone else to use.  So like it or not, it turns out to be rather essential to use the web HttpContext, even though you may not feel right about doing that in the layer of your app that is working with ObjectSpaces.  (See http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html for more details on thread-switching.)

But maybe you still also have reason to believe there may be someone using your business objects outside of ASP.NET, or maybe your web app also works with non-context threads like mine does (timer-driven events).  In that case I think the best thing to do is to have your logic test for the existence of HttpContext.Current, and if it exists then you should use it for the reason stated above.  But if it does not exist then it turns out that you should use the ThreadStatic attribute and not a NamedDataSlot due to performance reasons (see http://blogs.msdn.com/junfeng/archive/2005/12/31/508423.aspx for the details).

Here's the relevant code from my WebPortal (I removed a few other pieces are aren't immediately relevant):

    static public class Manager
    {
        private static readonly string KEY_Connection = "Wilson.WebPortal.Connection";
        private static readonly string KEY_ObjectSpace = "Wilson.WebPortal.ObjectSpace";
        
        private static ObjectSpace db;

        // Used for non-web-context cases, like timer events or other threads
        // See http://blogs.msdn.com/junfeng/archive/2005/12/31/508423.aspx
        [ThreadStatic()]
        private static ObjectSpace dbContext;
        
        public static ObjectSpace DB {
            get {
                if (Manager.DbContext == null) {
                    Manager.DbContext = Manager.db.IsolatedContext;
                }
                return Manager.DbContext;
            }
        }
        
        // Use Context.Items when available, otherwise use thread static variable
        // See http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html
        private static ObjectSpace DbContext {
            get {
                if (HttpContext.Current != null) {
                    return HttpContext.Current.Items[KEY_ObjectSpace] as ObjectSpace;
                }
                else {
                    return Manager.dbContext;
                }
            }
            set {
                if (HttpContext.Current != null) {
                    HttpContext.Current.Items[KEY_ObjectSpace] = value;
                }
                else {
                    Manager.dbContext = value;
                }
            }
        }

        public static void ResetContext() {
            Manager.DbContext = null;
        }

        static Manager() {
            string mappingType = "Wilson.WebPortal.Core.ORMaps.config";
            ConnectionStringSettings connection = ConfigurationManager.ConnectionStrings[KEY_Connection];

            Provider provider = (Provider) Enum.Parse(typeof(Provider), connection.ProviderName, true);
            Manager.db = new ObjectSpace(mappingType, connection.ConnectionString, provider);
        }
    }

Thanks, Paul Wilson
6/5/2006 1:27:07 PM Hi Paul,
Thanks for those links.  Based on these facts, is it safe to assume that outside of ASP.NET the use of ThreadStatic members is the best way?   I am building a domain layer service that shoud be able to run in any of these configurations:
  1. as a remoted object hosted by a windows service
  2. as a remoted object hosted in IIS
  3. as a locally installed component in an ASP.NET site
  4. as a locally installed component in a Windows Forms app.

So it looks like having to test for HttpContext is absolutely necessary if I am in configurations 2 and 3, whereas configs 1 and 4 must use a ThreadStatic member.  I think I read somewhere that the System.Web assembly is not installed as part of the framework on XP Home or Windows 98.  Do you know if that's true?  If so, is there any way around that without having to provide a separate compilation for those OS's?



6/5/2006 4:06:19 PM All I know for sure, based on that one link, is that the ThreadStatic attribute is better than the NamedDataSlot -- which personally seems very wrong of the CLR team to me, but at least we know now.  I'm assuming that Remoting.CallContext would have a higher overhead, but I could be wrong -- I'm basing that on other Remoting things, like ContextBoundObject, definitely having more overhead.  Of course sometimes you may not have a choice if you're using remoting.  :)

As for XP Home and 98, I haven't had the "pleasure" of having to worry about them, but the chart at http://msdn.microsoft.com/library/en-us/dndotnet/html/framewkwinsupp.asp seems to suggest that none of System.Web is going to be supported.  I'm not surprised that HttpContext is not supported, but I was pretty sure System.Web.Caching was supported on all platforms -- but it seems even that isn't true afterall.  Of course there are still some ways you can first test for an HttpContext using reflection, but I have no clue if that would be a dog on performance for the systems that would support it, so you'll have to test and decide on your own.

Thanks, Paul Wilson