I'd like to know how to register the classes and setup a Simple Injector container to instantiate the classes in the following way. ie go from manual DI to having the below Consumer class have the CompositeService injected and the object graph and lifetimes setup as follows:
To bring some context (if it helps) the Consumer class might be a ViewModel from a WPF application which gets instantiated when the View is requested.
public class Consumer
{
public Consumer()
{
var sharedSvc = new SharedService();
var productSvc = new ProductService(sharedSvc, new MathHelper());
var compositeSvc = new CompositeService(sharedSvc, productSvc, new MathHelper());
compositeSvc.Process();
}
}
where: MyContext should be shared within the scope of the calls. ProductService and CompositeService can be transient or shared within the scope. MathHelper must be transient.
Q: How can the above be achieved with Simple Injector?
OR more specifically: How can I instantiate multiple MathHelpers within the context of the Simple Injector Scope?
I've read up on http://simpleinjector.readthedocs.org/en/latest/lifetimes.html and read and followed the SO answer https://stackoverflow.com/a/29808487/625113 however, it seems either everything can be transient or scoped but not certain specific objects scoped and the rest transient (which seems odd).
Update 1
The following with Simple Injector will achieve the SharedService result, but if I want ProductService and CompositeService to also have a scoped lifetime it wont work:
cont.RegisterLifetimeScope<SharedService>();
cont.Register<MathHelper>();
cont.Register<ProductService>();
cont.Register<CompositeService>();
using (cont.BeginLifetimeScope())
{
var compositeSvc = cont.GetInstance<CompositeService>();
compositeSvc.Process();
}
If I register either or both of the ProductService or CompositeService as RegisterLifetimeScope I get a Lifetime mismatch exception. ie
cont.RegisterLifetimeScope<SharedService>();
cont.Register<MathHelper>();
cont.RegisterLifetimeScope<ProductService>();
cont.Register<CompositeService>(); // or cont.RegisterLifetimeScope<CompositeService>();
using (cont.BeginLifetimeScope())
{
var compositeSvc = cont.GetInstance<CompositeService>(); // Exception thrown
compositeSvc.Process();
}
Throws an exception leading to this page: https://simpleinjector.readthedocs.org/en/latest/LifestyleMismatches.html
I can under that in relation to Singleton should be dependent on Transient and can infer a sort of understanding that the same could be said in this case that Simple Injector is warning that Scoped can't depend on Transient because Transient isn't managed within the scope.
So my question is more specifically how can I instantiate multiple MathHelpers within the context of the Simple Injector Scope?
Update 2 - Further background and example
Brief background - This situation arose as I have a 4 year old, 2-tier, WPF based application currently using Ninject which has the bloated mixed Service architecture that @Steven describes in his blog series (ie the Services have become a mash of mixed, semi-related, command and queries). Most of these services are a good candidate for separating out into ICommandHandler/IQueryHandler architecture...but you can't do things overnight, so first crack was to convert from Ninject to SimpleInjector (yes I know Ninject can do the same thing in regards to this architecture but there are other reasons for moving to SimpleInjector).
As far as "scoping" the dependency resolution, a "scope" (in this application) is considered to be for the life of a form so one DbContext (like the SharedService in the example above) is shared amoungst the services that the form/viewModel require and MOST of the services are per scope with some injected services or helper classes needing to be injected as Transient.
This (to me) is analogous to Mark Seemann's hand-coded example from http://blog.ploeh.dk/2014/06/03/compile-time-lifetime-matching/ where he has a Per Request (singleton-scoped) service which has
Transient objects injected into it.
Edit: I had misread Mark Seemann's example and was reading the code as if the BarController were a service. So whilst the BarController object composition is the same the lifetime is not. That said the SomeThreadUnsafeService could just as easily have a new SomeServiceThatMustBeTransient injected into it but, I stand corrected, his example doesn't do this.
Hence I was wanting to know how to do the object composition Mark Seemann does in Simple Injector but outside the context of web reqeusts (my assumption is that Simple Injector's Per web request scoping is in essence a specific type of Lifetime Scoping).
To address @Steve and @Ric .net's comment and answer, I can see that there is the potential to end up with the scenario where 2 different services use another, shared service that uses a transient object (storing state) and the supposedly transient object becomes a Singleton Scoped object in the context of "some" of those services. eg
public class SingletonScopedService1
{
private readonly TransientX _transientA;
public SingletonScopedService1(TransientX transientA)
{
_transientA = transientA;
}
public void PokeTransient()
{
_transientA.Poke();
}
}
public class SingletonScopedService2
{
private readonly SingletonScopedService1 _service1;
private readonly TransientX _transientB;
public SingletonScopedService2(SingletonScopedService1 service1, TransientX transientB)
{
_service1 = service1;
_transientB = transientB;
}
public void GoFishing()
{
_service1.PokeTransient();
// This TransientX instance isn't affected
_transientB.Poke();
}
}
public class SingletonService3
{
private readonly SingletonScopedService1 _service1;
public SingletonService3(SingletonScopedService1 service1)
{
_service1 = service1;
}
public void DoSomething()
{
_service1.PokeTransient();
}
}
If DoSomething() is called on SingletonScopedService3 and GoFishing() is called on SingletonScopedService2 (and assuming TransientX maintains state) then results "may" be unexpected depending on the purpose of TransientX.
So I'm happy to accept this since the application is operating as expected (but also accept that the current composition is fragile).
With that said, can my original composition or Mark Seemann's example be registered with Simple Injector with the required life-times or is it strictly not possible by design and better to manually compose the object graph (or inject a Func as @Ric .net suggests) for the instances where this is required until further refactoring/hardening can be done?
Update 3 - Conclusion
Whilst Ninject allows you to register my original composition like:
var kernel = new StandardKernel();
kernel.Bind<SharedService>().ToSelf().InCallScope();
kernel.Bind<MathHelper>().ToSelf();
kernel.Bind<ProductService>().ToSelf().InCallScope();
kernel.Bind<CompositeService>().ToSelf().InCallScope();
Simple Injector by design does not and I believe my second example is an example as to why.
FYI: In my real-world case, for the few instances that the object graph had this, I've manually constructed the graph (just to get switched to Simple Injector) with the intent on refactoring these potential issues out.