UnitOfWork in WCF and multiple threads

Feb 19, 2014 at 10:39 AM
Edited Feb 19, 2014 at 10:42 AM
Hi

First of all, thanks for a great IoC framework - keep up the good work! I'm already using it on a couple of projects, and it's working great :-).

Though, i have a question regarding UoW pattern with SimpleInjector. It should be noted i have already read various threads about it on stackoverflow, more specifically these two threads: http://stackoverflow.com/questions/13147509/mixed-lifestyle-for-per-thread-and-per-web-request-with-simple-injector and http://stackoverflow.com/questions/11041601/how-to-configure-simple-injector-to-run-background-threads-in-asp-net-mvc

I'm developing a WCF service. The Service has a dependency on a IUnitOfWork. I could just register the IUnitOfWork with RegsiterPerWcfOperation extension. However my WCF service also spawns some new jobs (threads) from the Quartz scheduler framework. One of these jobs also contains a dependency on my IUnitOfWork, therefor I am not able to use the RegsiterPerWcfOperation. I could do like one of the stackoverflow threads says, and register my IUnitOfWork like this:
ScopedLifestyle hybridLifestyle = Lifestyle.CreateHybrid(
                    () => OperationContext.Current != null || HttpContext.Current != null,
                    new WcfOperationLifestyle(),
                    new LifetimeScopeLifestyle());

container.Register<IUnitOfWork, UnitOfWork<TEntities>>(hybridLifestyle);
First of all I'm not sure if the null-check on OperationContext.Current is correct, as my WCF service does not run in AspNetCompatibilityMode, because of NET.TCP bindings - so the null-check on HttpContext.Current would not help me, unless i ran my WCF service with AspNetCompatibilityMode = true?

Okay, so my Quartz dependencies are registered in the container, and i've create a factory, so the framework can create instances of my IJob instances like this:
public class SimpleInjectorJobFactory : IJobFactory
    {
        #region Private Fields

        private static readonly ILog _log = LogManager.GetLog(typeof(SimpleInjectorJobFactory));
        private readonly Container _container;

        #endregion

        public SimpleInjectorJobFactory(Container container)
        {
            _container = container;
        }

        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            IJobDetail jobDetail = bundle.JobDetail;
            Type jobType = jobDetail.JobType;

            try
            {
                if (_log.IsDebugEnabled)
                {
                    _log.Debug(string.Format("Producing Instance of job: {0}, class: {1}", jobDetail.Key, jobType.FullName));
                }

                // Return job registrated in container
                return (IJob)_container.GetInstance(jobType);
            }
            catch (Exception ex)
            {
                _log.Error("Problem instantiating class", ex);
                throw new SchedulerException("Problem instantiating class", ex);
            }
        }

        public void ReturnJob(IJob job)
        {
        }
    }
The implementation of an IJob looks like this:
public class MyJob : IJob
    {
        #region Private Fields

        private static readonly ILog _log = LogManager.GetLog(typeof(MyJob));
        private readonly IUnitOfWork _unitOfWork;

        #endregion

        #region Constructor

        public MyJob(IUnitOfWork unitOfWork)
        {
            _unitOfWork = unitOfWork;
        }

        #endregion

        public void Execute(IJobExecutionContext context)
        {
            try
            {
                _unitOfWork.GetSomeDate().... //PSEUDO
            }
            catch (Exception ex)
            {
                _log.Error("Could not execute " + context.JobDetail.JobType, ex);
            }
        }
    }
If i should follow the advices from the threads specified above i would need to create a new lifetimescope like :
            try
            {
                using (container.BeginLifetimeScope())
                {
                 var uow = container.GetInstance<IUnitOfWork>();
                uow.GetSomeDate().... //PSEUDO
                 }
            }
However would'nt i get a tight coupling to the IoC framework with this soloution? I can't seem to find an appropriate way to solve this problem. I've read a little about the decorators, however i cannot seem to figure out how i could use them. I hope i can get a good example of, how i could solve my problem.

Thank you.
Coordinator
Feb 19, 2014 at 12:14 PM
Edited Apr 1, 2014 at 11:03 AM
You're almost there. You need to create the following hybrid lifestyle:
ScopedLifestyle hybridLifestyle = Lifestyle.CreateHybrid(
    container.GetCurrentWcfOperationScope() != null,
    new WcfOperationLifestyle(),
    new LifetimeScopeLifestyle());
You need to call either container.GetCurrentWcfOperationScope() or container.GetCurrentLifetimeScope() to determine which lifestyle to select.

Furthermore, you should absolutely prevent from needing to reference the Container in your MyJob (or any job implementation). Instead you should create a IJob decorator or proxy that adds lifetime scoping behavior to a job:
public class LifetimeScopeJobProxy : IJob {
    private readonly Type _jobType;
    private readonly Container _container;

    public LifetimeScopeJobProxy(Type jobType, Container container) {
        _jobType = jobType;
        _container = container;
    }

    public void Execute(IJobExecutionContext context) {
        using (_container.BeginLifetimeScope()) {
            var job = (IJob)_container.GetInstance(_jobType);
            job.Execute(context);
        }
    }
}
Now there are two ways to apply a proxy or decorator to the jobs that the SimpleInjectorJobFactory returns. The easiest way is to add the proxy manually inside the SimpleInjectorJobFactory.NewJob method:
return new LifetimeScopeJobDecorator(jobType, _container);
Much prettier would it be to let Simple Injector do the decoration for you, but that would be a lot more code in your case. Here's the way to do it:
public class LifetimeScopeDecorator : IJob {
    private readonly Func<IJob> _decorateeFactory;
    private readonly Container _container;

    public LifetimeScopeJobProxy(Func<IJob> decorateeFactory, Container container) {
        _decorateeFactory = decorateeFactory;
        _container = container;
    }

    public void Execute(IJobExecutionContext context) {
        using (_container.BeginLifetimeScope()) {
            var job = _decorateeFactory.Invoke();
            job.Execute(context);
        }
    }
}
Type[] jobTypes = /* fetch IJob implementations here */;

container.RegisterAll<IJob>(types);

container.RegisterDecorator(typeof(IJob), typeof(LifetimeScopeJobDecorator));

var jobs = new Lazy<IEnumerable<IJob>>(() => container.GetAllInstance<IJob>());
var jobIndexes = types.ToDictionary(t => t, t => types.IndexOf(t));
Func<Type, IJob> jobFactory = jobType =>jobs.Value.ElementAt(jobIndexes[jobType]);

var factory = new SimpleInjectorJobFactory(jobFactory);

// Inside the SimpleInjectorJobFactory:
    public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler) {
        // ...
        // replace "return (IJob)_container.GetInstance(jobType);" with:        
        return _jobFactory(jobType);
        // ...
    }
This last solution is a more complex, but this method gets really interesting when you have multiple decorators, especially if they need to be applied conditionally. This way the factory doesn't need to know anything about the applied decorators.
Marked as answer by thomas_t on 2/19/2014 at 10:47 AM