StructureMap returns the located nHibenrate session object from the local area of โ€‹โ€‹the stream

[OR] How to define the StructureMap life cycle for UoW โ€‹โ€‹that will be used by HTTP requests and quartz jobs.

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

But I also have quartz.net that is planning a couple of tasks. The job uses the same unit of work to get the ISession object. In this case, when the scheduler starts the task, at first everything works fine, and the task runs normally a couple of times until the workflow identifier is repeated.

Imagine that when a task is scheduled, it starts working in flows with identifiers 11, 12, 13, and then with flow identifier 11 again. At this point, the structmap returns a session object that is already located, and I get a "System.ObjectDisposedException: session closed!" mistake.

So, from what I see, the session is stored in the local thread store, and after I delete the session at the end of my unit of work, the session object is still stored in the local thread store. It seems that after the thread completes, its local storage is not cleared, and somehow, when a new thread with the same identifier is created, the structmap looks at the session in the local storage of the old thread (which should be cleared for the new thread, which I I think) and returns a session object that is already located.

Questions:

  • Is there a way to clear the local thread store (when done)?
  • Is there an equivalent "ReleaseAndDisposeAllHttpScopedObjects" for stream scope objects?
  • Is there a way to nullify (or throw away) the located object in such a way that even if SM is looking for it, it will not find anyone and must create a new instance?

I hope I put my question clear. It took a couple of hours of my time, and yet I could not find a way around it. I appreciate any hint:>

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

+6
multithreading nhibernate structuremap
source share
2 answers

I was reviewing what I did to get StructureMap to work with UoW on Http and UoW on quartz, and I decided to share my solution here.

So the idea was that I wanted to use the StructureMap Hybrid scope to get an UoW instance when there is an http context, and also get a different UoW instance for each stream when there is no http context (for example, when a quartz job is triggered). Like this:

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

UoW for http worked fine. The problem was in UoW for each thread.

This is what happens. When quratz fires, it pulls the thread from the thread pool and starts the job using that thread. When the task begins, I request UoW. StructureMap looks under local storage for this stream to return UoW, but due to the fact that it cannot find any instance of it, and stores it in the streaming local storage. I get UoW and then does Perom Begin, Commit, Dispose and everything is fine.

The problem occurs when a thread is pulled from a thread pool that was previously used to run a task (and used UoW). Here, when you request UoW, StructureMap looks into the cache (local thread storage) and finds UoW and returns it to you. But the problem is that UoW is configured!

Thus, we cannot use UoW for each thread for quartz jobs, because the threads themselves are not located, and they store the old cached UoW. Basically, the life cycle of a thread does not correspond to the life cycle of quartz. . Therefore, I created my own life cycle for quartz work.

First I created my own http-quartz hybrid lifecycle hybrid:

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

Then I created the 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, such as the HttpContext for Quartz, to store the information related to the context. So I created the QuartzContext class. When a quartz job is running, the JobExecutionContext for this job must be registered in the QuartzContext. Then the actual cache (MainObjectCache) for the StructureMap instances will be created in this particular JobExecutionContext. Thus, after completing the task, the cache will also leave, and we will not have a problem with the UoW utility in the cache.

Also, since _jobExecutionContext is a ThreadStatic, when we request a cache from QuartzContext, it returns a cache from JobExecutionContext, which is stored for the same thread. Therefore, when several tasks are simultaneously running, their JobExecutionContexts are stored separately, and we will have separate caches for each job that is running.

 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 from which other jobs come from. This class extends the QuartzContext class. You can see that I register JobExecutionContext when the job starts.

 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 lifecycle and context classes, I looked at the source code of StructureMap to get this idea.)

Share your ideas, comments and suggestions:>

+2
source share

Why not create a new session for quartz tasks? The unit of work is usually a transaction operation on db. I can't imagine quartz jobs being transaction related with web request / responses. Creating new sessions is not expensive. Is it possible?

+1
source share

All Articles