Question

I'm developing a Windows Service which will host two things:

  • WCF services
  • a "regular" windows service for periodic job execution (using Quartz.net)

So basically, one application (executable) hosting these two service types.

Both these service types need to use Repositories and other injected dependencies. I'm using Simple Injector.

I already got the WCF part up and running, using Simple Injectors WCF extentions:

public class DependencyResolver : IPackage
{
    public void RegisterServices(Container container)
    {
        container.EnablePerWcfOperationLifestyle();
        var wcfLifestyle = new WcfOperationLifestyle();

        container.Register<IUnitOfWork, UnitOfWork>(wcfLifestyle);
        container.Register<ICustomerRepository, CustomerRepository>(
            wcfLifestyle);
        //etc...
        
        container.RegisterWcfServices(Assembly.GetExecutingAssembly());
    }
}

My question: can I reuse these repository and other registrations inside the "regular" windows service part, i.e. the one NOT using WCF? Because I'm not sure how the WcfOperationLifestyle will affect them if I do.

I'd like to use transient lifestyle each time a quartz job is fired that needs one or more repositories. And while that job is running, a WCF call can come in obviously, which should then use a new repository instance independently from the repository currently being used in the scheduled job.

Should I create new registrations? If so, how should I then call them from my executing code?

Was it helpful?

Solution

WARNING: This answer is severely outdated. See the integration guide for the latest information.


It's very unlikely for any component to have a scoped lifestyle in one part of the application (WCF) and act as Transient in one other part. In your case it is far more likely that your unit of work must be scoped in Quartz as well, but since there is no implicit scope (such as an HTTP request, WCF session, etc) you will have to explicitly start/end some scope where the operations run in and register them as such.

In Simple Injector there are several implicit technology-specific scoped lifestyles such:

  • WebApiRequestLifestyle
  • WebRequestLifestyle
  • WcfOperationLifestyle

Explicit scped lifestyles are:

  • LifetimeScopeLifestyle (which defines a thread-specific scope)
  • ExecutionContextScopingLifestyle (which defines a async-context-specific scope).

So when running inside Quartz your unit of work should have either the LifetimeScopeLifestyle or ExecutionContextScopeLifestyle. The choice between the two is easy: if the operation is asynchronous and your methods return Task<T>, you need ExecutionContextScopeLifestyle, otherwise you need LifetimeScopeLifestyle. This Q/A gives more information about how to start and lifetimescope with Quartz.

So with that in mind, I think you basically have three options:

  1. See both services (WCF and Quartz) as two independent applications that happen to live in the same AppDomain and create two Composition Roots with each their own SimpleInjector.Container instance. (you might also want to consider really splitting the service up two seperate service applications)
  2. You ditch the WCF lifestyle and explicitly start a new LifetimeScope in each WCF operation before resolving the service that executes the operation.
  3. See both services as one intrinsic whole and have one Composition Root with one container, but stay using dependency injection in your WCF service classes. This means you will have to use hybrid lifestyles to get the proper lifestyle.

Which option is best for you depends on lots of factors. Creating two Composition Roots for instance, might be really easy to do and by extracting common registrations to a shared method, you can make the Composition Roots very maintainable. All scoped lifestyles inherit from ScopedLifestyle and this allows you to let this shared method accept a ScopedLifestyle, which allows the shared method to stay oblivious from for which service it is called. When it becomes hard is when you have components where really just one single instance must be alive in the complete AppDomain (since Singleton means 'single per container').

Instead of letting your WCF services have any logic, you can make them really thin layers that are just the glue between WCF, your IOC container, and the application. So instead of injecting dependencies into your WCF services, you can simply resolve the actual service when the WCF operation is called:

[OperationContract]
public SomeData DoSomething() {
    using (Bootstrapper.Container.BeginLifetimeScope()) {
        return Bootstrapper.Container.GetInstance<IDoSomethingService>()
            .DoSomething();
    }
}

This allows you to register all scoped components with the same LifetimeScopeLifestyle. This model works really well when you have a message driven architecture, since this means your WCF layer will consist of at most two methods as can be read here. This approach can make things easier, since the WcfOperationLifestyle has a bit unexpected behavior, because it doesn't cache services 'per WCF operation', but for the duration of the WCF service class (the class that contains those operation methods), and in the end it is WCF and your configuration that determines how long such service should live. This can be PerCall, PerSession or even Single.

Downside of this approach however is that if you have a very wide WCF service (with many methods), you probably need to refactor a lot to get to this and have a lot of ceremony in your WCF service classes.

The third option is to use an Hybrid lifestyle as follows:

ScopedLifestyle hybridLifestyle = Lifestyle.CreateHybrid(
    container.GetCurrentWcfOperationScope() != null,
    new WcfOperationLifestyle(),
    new LifetimeScopeLifestyle());

container.Register<IUnitOfWork, UnitOfWork>(hybridLifestyle);

You can use this hybridLifestyle as the lifestyle to register your instances. This allows your configuration to work with both WCF as outside WCF. This of course means you will have to start a lifetime scope explicitly as discussed here.

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