Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

StructureMap returns a disposed nHibenrate session object from thread local scope

[OR] How to define a StructureMap life cycle for UoW to be consumed by http requests and quartz jobs

I have this web application which uses SM for IoC. I am using HybridHttpOrThreadLocalScoped scope to store my nHibernate ISession objects. This works ok in a session per request fashion for my web requests.

But I also have quartz.net that schedules couple of jobs. The job uses the same unit of work to get the ISession object. In this scenario when the scheduler start the job, everything works fine at first and the job runs fine for couple of times UNTIL the job thread id gets repeated.

Imagine that when the job is scheduled it start to run in threads with ids 11, 12, 13, and then with thread id 11 again. At this point structuremap returns a session object which is already disposed and I get "System.ObjectDisposedException: Session is closed!" error.

So from what I can see, the session is kept in thread local storage and after I dispose the session at the end of my unit of work, the session object is still kept in the thread local storage. It seems that after the thread terminates its local storage is not cleared and somehow when a new thread with the same id is created, structuremap looks up the session in the old thread local storage (which is supposed to be cleared for the new thread I believe) and returns the session object which is already disposed.

Questions:

  1. Is there a way to clear the thread local storage (on termination)?
  2. Is there an equivalent of "ReleaseAndDisposeAllHttpScopedObjects" for thread-scoped objects?
  3. Is there a way to nullify (or eject) the disposed object so even if SM looks for it then it wouldn't find any and has to create a new instance?

I hope I made my question clear. This has taken couple of hours of my time and still I haven't found a way around it. I appreciate any hint :>

Update: I added my own solution to make a UoW served by StructureMap work with both http requests and quartz jobs. Let me know if you have a better/easier/simpler solution.

like image 696
kaptan Avatar asked Jul 03 '10 01:07

kaptan


2 Answers

I was revisiting what I did to make StructureMap work with UoW per Http and UoW per quartz job and I decided to share my solution here.

So the idea was that I wanted to use StructureMap Hybrid scope to get an instance of UoW when there is a http context and also get a different instance of UoW per thread when there is no http context (like when a quartz job fires). Like this:

For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<UnitOfWork>();

The UoW for http worked fine. The problem was UoW per thread.

Here is what happens. When a quratz job fires it pulls a thread from the thread pool and starts executing the job using that thread. When the job starts I request a UoW. StructureMap looks under the local storage for that thread to return the UoW, but because it can't find any it instantiates one and saves it under thread's local storage.I get the UoW, then perfom Begin, Commit, Dispose and everything is fine.

The problem happens when a thread is pulled from thread pool which was used before to fire a job (and used a UoW). Here when you request a UoW, StructureMap looks in the cache (thread local storage) and finds a UoW and returns it to you. But the problem is the UoW is disposed!

So we cannot really use UoW per thread for quartz jobs because the threads themselves are not disposed and they hold the old cached disposed UoWs. Basically life cycle of a thread does not match the life cycle of a quartz job. That's why I created my own life cycle for a quartz job.

First I created my own http-quartz hybrid life cycle class:

public class HybridHttpQuartzLifecycle : HttpLifecycleBase<HttpContextLifecycle, QuartzLifecycle>
{
    public override string Scope { get { return "HybridHttpQuartzLifecycle"; } }
}

Then I created my QuartzLifecyle class:

public class QuartzLifecycle : ILifecycle
{

    public void EjectAll()
    {
        FindCache().DisposeAndClear();
    }

    public IObjectCache FindCache()
    {
        return QuartzContext.Cache;
    }

    public string Scope { get { return "QuartzLifecycle"; } }
}

Then I need to create some context class like HttpContext for Quartz to hold the context related info. So I created QuartzContext class. When a quartz job is fired, the JobExecutionContext for that job should be registered in QuartzContext. Then the actual cache (MainObjectCache) for StructureMap instances will be created under that specific JobExecutionContext. So this way after the job execution finishes the cache will go away too and we won't have problem of disposed UoW in cache.

Also since _jobExecutionContext is ThreadStatic, when ever we request the cache from QuartzContext, it will return the cache from the JobExecutionContext that is saved for the same thread. So when multiple jobs are running at the same time, their JobExecutionContexts are saved separately and we will have separate caches for each running job.

public class QuartzContext
{

    private static readonly string _cacheKey = "STRUCTUREMAP-INSTANCES";

    [ThreadStatic]
    private static JobExecutionContext _jobExecutionContext;

    protected static void Register(JobExecutionContext jobExecutionContext)
    {
        _jobExecutionContext = jobExecutionContext;
        _jobExecutionContext.Put(_cacheKey, new MainObjectCache());
    }

    public static IObjectCache Cache 
    { 
        get 
        {
            return (IObjectCache)_jobExecutionContext.Get(_cacheKey);
        } 
    }
}  

I have an abstract class called BaseJobSingleSession that other jobs derive from. This class extends the QuartzContext class. You can see that I register the JobExecutionContext when the job is fired.

abstract class BaseJobSingleSession : QuartzContext, IStatefulJob
{
    public override void Execute(JobExecutionContext context)
    {
        Register(context);
        IUnitOfWork unitOfWork = ObjectFactory.GetInstance<IUnitOfWork>();

        try
        {
            unitOfWork.Begin();

            // do stuff ....

            unitOfWork.Commit();
        }
        catch (Exception exception)
        {
            unitOfWork.RollBack();

        }
        finally
        {
            unitOfWork.Dispose();
        }
    }
}

Finally I defined the life cycle for UoW:

For<IUnitOfWork>().LifecycleIs(new HybridHttpQuartzLifecycle()).Use<UnitOfWork>();

(For life cycle and context classes I looked into the StructureMap source code to get the idea.)

Please share your ideas, comments and suggestions : >

like image 57
kaptan Avatar answered Nov 04 '22 02:11

kaptan


Why not create a new session for the quartz jobs? A unit of work is typically a transactional operation on the db. I can't imagine the quartz jobs being transactionally related to the web request/responses. Creating new sessions is not expensive. Is this a possibility?

like image 1
rcravens Avatar answered Nov 04 '22 03:11

rcravens