Question

i have a question regarding UoW pattern with SimpleInjector or any other IoC framework. It should be noted i have already read various threads about it on stackoverflow, more specifically these two threads: Mixed lifestyle for Per Thread and Per Web Request with Simple Injector and 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 in SimpleInjector. 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
{
    private static readonly ILog _log = 
        LogManager.GetLog(typeof(SimpleInjectorJobFactory));
    private readonly Container _container;

    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
{
    private static readonly ILog _log = LogManager.GetLog(typeof(MyJob));
    private readonly IUnitOfWork _unitOfWork;

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

    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 lifetime scope like:

try
{
    using (container.BeginLifetimeScope())
    {
        var uow = container.GetInstance<IUnitOfWork>();
        uow.GetSomeDate().... //PSEUDO
    }
}

However wouldn't I get a tight coupling to the IoC framework with this solution? 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.

Was it helpful?

Solution

You're almost there. You need to create the following hybrid lifestyle:

var 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). That's an anti-pattern. Instead you should create a IJob decorator that adds lifetime scoping behavior to a job:

// This class is part of your Composition Root
public class LifetimeScopeJobDecorator : IJob {
    private readonly IJob _decoratee;
    private readonly Container _container;

    public LifetimeScopeJobDecorator(IJob decoratee, Container container) {
        _decoratee = decoratee;
        _container = container;
    }

    public void Execute(IJobExecutionContext context) {
        using (_container.BeginLifetimeScope()) {
            _decoratee.Execute(context);
        }
    }
}

Now there are two ways to apply decorator to the jobs that the SimpleInjectorJobFactory returns. The easiest way is to add the decorator manually inside the SimpleInjectorJobFactory.NewJob method:

var job = (IJob)_container.GetInstance(jobType);
return new LifetimeScopeJobDecorator(job, _container);

Much prettier would it be to let Simple Injector do the decoration for you, but that would be a more code in your case. Here's the way to do it:

Type[] jobTypes = /* fetch IJob implementations here */;

// Instead of calling jobTypes.ToList().ForEach(container.Register), we register the
// types as a collection of IJob types.
container.RegisterAll<IJob>(types);

// Here we register the decorator
container.RegisterDecorator(typeof(IJob), typeof(LifetimeScopeJobDecorator));

// We create a jobFactory delegate.
// we use Lazy<T> because at this stage in the application the container might not
// be fully initialized yet.
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]);

// And pass that job factory on to the SimpleInjectorJobFactory.
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 more complex, but this method gets really interesting when you have multiple decorators, especially if they need to be applied conditionally based on the implementation type. This way the factory doesn't need to know anything about the applied decorators.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top