Suppose we have an interface:
public interface ICanFetch<TItem, TParams>
{
Task<List<TItem>> FetchItemsAsync(TParams @params);
}
I implement this interface in different DAOs. Now I have a use case where I need to look up all the implementations of this interface from the container (through DI or directly from the container), think implementations of ICanFetch<Item1, Params1>
, ICanFetch<Item2, Params2>
etc.
I tried to do something like this:
var items = container.ResolveMany(typeof(ICanFetch<,>));
But apparently the container doesn't work like this, because despite me registering everything using [RegisterMany]
(using MEF attribute model) which works like intended on other use cases, I can't resolve all the fetchers. Is there something I am doing wrong, or is it simply not implemented? If the latter - can I somehow implement that myself? Apart from introducing covariance to the interface because Tasks and covariance are not compatible.
The main issue is that ICanFetch<,>
is an open generic type and ICanFetch<Item1, Params1>
is a closed generic type (generic type with provided generic type parameters, Item1 and Params1).
There is no inheritance relationship between an open and a closed type, i.e. ICanFetch<Item1, Params1>
is not ICanFetch<,>
.
For example with familiar Dictionary:
(typeof(Dictionary<,>)).IsAssignableFrom(typeof(Dictionary<int, int>)); // False
(typeof(Dictionary<,>)).IsAssignableTo(typeof(Dictionary<int, int>)); // False
Fortunately, the built-in reflection API of GetGenericTypeDefinition
allows you to get the open generic type that a closed one is based of:
(typeof(Dictionary<int, int>).GetGenericTypeDefinition() == typeof(Dictionary<,>)); // True
This is exactly what we want in our case - to be able to match ICanFetch<Item1, Params1>
with ICanFetch<,>
.
However, this won't work with ResolveMany
since its API just allows for type comparisons.
But it will with GetServiceRegistrations
for us to get the service type that we can subsequently use in a SelectMany
with ResolveMany
to get our instances.
var items = container.GetServiceRegistrations()
.Where(x =>
x.ServiceType.IsGenericType &&
x.ServiceType.GetGenericTypeDefinition() == (typeof(ICanFetch<,>)))
.SelectMany(x => container.ResolveMany(x.ServiceType, serviceKey: x.OptionalServiceKey))
.ToList();
Caveat, is that I haven't used DryIoc. Please comment if something is to correct in the Linq to achieve the same functionality as originally intended.