I am using Simple Injector 5.3.3 and I would like to access at run time to a list of the types inheriting from a generic interface (say IHandleMessage<T>) that I registered in the container. How can I achieve this?
I read the documentation here: https://docs.simpleinjector.org/en/latest/advanced.html And therefore I tried something like this:
container.Collection.Register(typeof(IHandleMessages<>), new[]
{
typeof(Handler1),
typeof(Handler2),
typeof(Handler3)
});
But then, when using the method container.GetAllInstances<IHandleMessages<>>, I do need to specify something in the generic. Furthermore, I would like to get only the list of types, and not the instances (I will instantiate them at a later time).
I hope my question is clear, and I did not really find my answer beforehand.
Edit : I will elaborate on my problem.
We have a listener endpoint that we are building when starting the program. In this endpoint, we need to register all the command handlers that we already added in the SI container. The code looks roughly like this now :
public virtual IEndpoint GetReceiverEndpoint()
{
return GiveMe.AnEndpointBuilder("myEndPoint")
.UseUddiFromFramework()
.UseFactory(GetHandler<Handler1>)
.UseFactory(GetHandler<Handler2>)
.UseFactory(GetHandler<Handler3>)
.Start();
}
public T GetHandler<T>() where T : class
{
// Container is where we already registered our handlers
var scope = AsyncScopedLifestyle.BeginScope(Container);
return scope.GetInstance<T>();
}
So we have to manually register the Handler in the container, and then at the endpoint. Regularly, when it comes to adding an handler, a developer forgets to add it to the Endpoint, and this is something that we can see only at runtime (usually on the test environment). This is a mistake that I would like to avoid by doing something like
public virtual IEndpoint GetReceiverEndpoint()
{
var handlersTypes = // get all the implementation of IHandlerMessage<T>
var endpoint = GiveMe.AnEndpointBuilder("myEndPoint")
.UseUddiFromFramework();
foreach (var type in handlersTypes)
{
endpoint = endpoint.UseFactory(GetHandler<type>());
}
return endpoint;
}
public T GetHandler<T>() where T : class
{
var scope = AsyncScopedLifestyle.BeginScope(Container);
return scope.GetInstance<T>();
}
So that we do not need to register the type twice. Now I see that I could store all the types in a static list and use that list both in the DI container (to register the classes in the container) and then in the endpointBuilder (to ass those handlers to the endpoint). Is there something which is more elegant?
Simple Injector contains several methods methods that allow getting a list of registered types back. In your case I'd suggest using
Container.GetCurrentRegistrations(). For instance:This query goes through the registrations gets all registrations for a closed version of
IHandleMessages<T>(IsClosedTypeOfis an extension method provided by Simple Injector). The distinct is used to prevent duplicate implementation types to be returned in case a single class implementsIHandleMessages<T>multiple times.From this point on it gets ugly, but that's because -as you explained in the comments- the framework you are using doesn't support a non-generic overload of
UseFactory.I would, however, like to warn you about a few things:
GetHandler<T>method is resolving a concrete handler type (e.g.Handler1), while you are registering those handlers by their interface (i.e.IHandleMessages<T>). Simple Injector will not allow resolving those concrete types unless a. you registered them explicitly or b. configured Simple Injector to resolve unregistered types. Unregistered type resolution, however, has been turned off by default in recent versions because of the problems it caused with keeping your configuration verifiable. Your messaging framework might require you to supply a concrete type, but if it doesn't, I'd suggest changing the code that you do the following:.UseFactory<IHandleMessages<MyFirstMessage>>(this.GetHandlerForMessage<MyFirstMessage>).ScopedCommandHandlerProxy<T>). This method, however, requires resolving the handler by its interface and requires support from your messaging framework for handling with the interface rather than the implementation (as stated in the previous point). If this is not possible, you need to look for an integration point in the messaging framework that allows you to wrap the resolve and execution of a handler with a Simple Injector scope. If you already have a place where you dispose off the scope, you can ignore this warning.