SimpleInjector
SimpleInjector copied to clipboard
using DI with a group of classes that do not implement a common interface
SimpleInjector registration binds a particular service type to a concrete class. If I have a whole group of classes that are in the "leaves" of the dependency tree, ie. they depend on other services, but they themselves do not provide any service, then I'm forced to resolve dependencies manually by using the container as a service locator - not the best option!
In my particular problem, I can create a group of classes using reflection. These classes share some common properties, but they don't implement some common interface. So, it would be great if:
- I could pass a collection of these class types to a SimpleInjector container, to create an instance of each and inject whatever dependencies they have.
- Later on, retrieve these created instances to perform some shared operation over them. Even if I could only get a collection of all instances SimpleInjector container created, I could test them to find the ones I'm interested in.
Is this possible in the current SimpleInjector implementation? Does this use case somehow violate principles that SimpleInjector is based on?
I know I could define an empty interface and have all my classes "implement" it, but it looks like more of a hack to compensate for the lack of before mentioned features.
Can you show me a concrete example of your code and what you're trying to achieve?
My C# application hosts an embedded Lua interpreter. Some of the C# classes in this application implement methods that are mapped into Lua interpreter's namespace so that they can be called from Lua code as plain Lua functions.
These C# classes that implement Lua callbacks have nothing much in common except that majority of them use some shared application services, like logger, messaging, etc. So, these are the classes I'd like to instantiate as singletons via SimpleInjector, to take advantage of Auto-wiring and get shared application services (logger, messaging) injected into them.
Later on, at a particular point, I need bind the Lua function callbacks with the Lua interpreter, and for this, I need access to the collection of singleton objects created earlier, by the SimpleInjector. So, that's briefly my problem statement.
I have sort of solved this problem by defining an empty interface (no properties, no methods), that all of these classes implement and then I can use the existing SimpleInjector APIs to achieve the goal. The benefit of using DI is much bigger than the downside of introducing a fake interface in order to work with the existing DI mechanism.
I think if SimpleInjector introduced an API like:
var instances = container.Resolve(collection-of-types);
that creates singleton instances for each given type and does auto-wiring for each one of them, that would be great. Adding another container API for retrieving a collection of all objects created by the SimpleInjector container that are not providing any service, which can then be filtered to get the original set, that would be very usefull, too.
I think I start to understand what you want to achieve and also which part of the Simple Injector API would benefit you.
If I have a whole group of classes that are in the "leaves" of the dependency tree
In Simple Injector terminology, those object are called "root components". They consume dependencies, but are not depended upon themselves.
Does this use case somehow violate principles that SimpleInjector is based on?
Nope. I'd say you're good.
These C# classes that implement Lua callbacks
Sounds like Humble Objects to me.
What you need is to get hold of the root components InstanceProducer instances. I couldn't find a useful example for you in the Simple Injector documentation about this, but it basically means that instead of calling container.Register(...) you call Lifestyle.Singleton.CreateProducer(...) and cache the returned InstanceProducer for later use. That later use means that you call InstanceProducer.GetInstance() to retrieve the instance identical to calling Container.Instance<T>() but now specific to that single registration.
Here's a simple example:
// Register
var dictionary = new Dictionary<string, InstanceProducer>
{
{ "A", Lifestyle.Singleton.CreateProducer(typeof(ServiceA), typeof(ServiceA), container) },
{ "B", Lifestyle.Singleton.CreateProducer(typeof(ServiceB), typeof(ServiceB), container) },
{ "C", Lifestyle.Singleton.CreateProducer(typeof(ServiceC), typeof(ServiceC), container) },
};
// Resolve:
InstanceProducer producerA = dictionary["A"];
object a = producerA.GetInstance();
This example assumes a dictionary-like approach where you need to resolve a root by a key. This is a scenario that is for instance useful for web scenario's where a incoming request (say /Home) maps to a specific component (say HomeController).
But another scenario would be to have a simple list or stream of objects:
// Register
InstanceProducer[] producers = new[]
{
Lifestyle.Transient.CreateProducer(typeof(ServiceA), typeof(ServiceA), container),
Lifestyle.Transient.CreateProducer(typeof(ServiceB), typeof(ServiceB), container),
Lifestyle.Transient.CreateProducer(typeof(ServiceC), typeof(ServiceC), container),
};
IEnumerable<object> stream =
from producer in producers
select producer.GetInstance();
// Resolve:
foreach (var service in stream)
{
// use service
}
In this example I changed the lifestyle from Singleton to Transient, to illustrate that every time you call InstanceProducer.GetInstance() the instance will be resolved according to its lifestyle. This means that with the example above, every time you iterate the stream, you are served with a fresh set of instances.
From this list you can of course filter depending on your needs.
Do note the following about InstanceProducer instances:
- Services that are solely created as
InstanceProducercan't be resolved by callingContainer.GetInstance<Service>()and, therefore, can't be injected into other components by Simple Injector (through auto wiring). CallingCreateProducer, therfore, works best on "root" components. - Even though you don't call
Container.Register, theInstanceProduceris still a 'registration' to the Simple in the sense that it will get checked by Simple Injector's diagnostic services. In other words:Verify()will also verify any service registered viaCreateProducer. - Such
InstanceProducerwill go through the exact same pipeline as anything that is registered viaContainer.Register, meaning that any interception and decoration will take place if applicable. As your components don't share a common interface, though, there will be little to decorate.
I hope this solves your issue. If not, please let me know. And if you have any further questions about this, please let me know.
Thank you Steven, worked like charm! HOWEVER, I think there's one more thing that may improve things a bit. Your solution requires that I keep a collection of InstanceProducer objects, so that, when the time is right, I can instantiate all instances.
Is there a way to obtain a producer handle, given the type, from either the container or Lifecycle? So that I can do something like:
IEnumerable<object> singletons= from type in some-expresion-that-yields-types select Lifestyle.Singleton.GetProducer(type, container).GetInstance();
So, something like GetProducer(Type t) API to be added, or maybe on the container?
PS. I've tried calling container.GetInstance(type), hoping that it would call the relevant producer, but it didn't happen and I got SimpleInjector.ActivationException claiming that type wasn't registered, but I did register it via CreateProducer.
When I take your approach and keep a collection of producers, which I use later on to trigger instance creation, all works... but it would be nicer if I didn't have to keep that collection.
Ooops! I managed to solve this last one using container.GetRootRegistrations(), then applied some filtering to get just those that I needed. So, all good, perfect, thanks heaps!
I've tried calling container.GetInstance(type), hoping that it would call the relevant producer, but it didn't happen and I got SimpleInjector.ActivationException claiming that type wasn't registered, but I did register it via CreateProducer.
That's correct. This is what I tried to explain when I said "Services that are solely created as InstanceProducer can't be resolved by calling Container.GetInstance<Service>()".
Is there a way to obtain a producer handle, given the type, from either the container or Lifecycle?
You already figured out the answer; the only way to do this is via the GetRootRegistration() call, but keep in mind that you might get other types that Simple Injector considers root types (e.g. types that you lazy-load via a factory), which would get up in the list. This is why my examples use lists and dictionaries to store the InstanceProducer instances in, because this is a reliable way to get them, while typically the list is only accessed from within the Composition Root, so having access to it shouldn't be a problem.