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:
- 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) - You ditch the WCF lifestyle and explicitly start a new
LifetimeScope
in each WCF operation before resolving the service that executes the operation. - 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.