Reputation: 1815
Having the following interface and their implementations...
public interface IEmpty<T> { }
public class Empty1 : IEmpty<Empty1>{ }
public class Empty2 : IEmpty<Empty2>{ }
public class EmptyN : IEmpty<EmptyN>{ }
allows me to register them and inject them explicitly into constructors
public class NewClass1 {
private IEmpty<Empty1> Empty;
public NewClass1(IEmpty<Empty1> empty)
{
Empty = empty;
}
public string EmptyType => $"{Empty.GetType()}";
}
but when I tried to resolve all implementations of 'IEmpty<>' at once...
var allIEmpties = host.Services.GetServices(typeof(IEmpty<>));
allIEmpties.ToList().ForEach(empty => Console.WriteLine(empty.GetType()));
... execution of the code threw a 'NotSupportedException' (Cannot create arrays of open type), which I kind of understand, but leaves me wondering if it can be done and how it would have to be done to get a handle on all Services implementing IEmpty.
Would anyone have an idea of how to achieve this?
My motivation to get this working is to
Using these additional nuget packages:
I have created this proof of concept snippet in LinqPad, in case you'd want to have a go:
void Main()
{
var args = new List<string>();
var host = CreateHostBuilder(args.ToArray()).Build();
var newClass = host.Services.GetService<NewClass1>();
Console.WriteLine(newClass.EmptyType);
var oneEmpty = host.Services.GetService(typeof(IEmpty<Empty2>));
Console.WriteLine(oneEmpty.GetType());
var allIEmpties = host.Services.GetServices(typeof(IEmpty<>));
allIEmpties.ToList().ForEach(empty => Console.WriteLine(empty.GetType()));
}
IHostBuilder CreateHostBuilder(string[] args)
{
var hostBuilder =
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, builder) => {
builder.SetBasePath(Directory.GetCurrentDirectory());
})
.ConfigureServices((context, services) => {
services.AddTransient<IEmpty<Empty1>, Empty1>();
services.AddTransient<IEmpty<Empty2>, Empty2>();
services.AddTransient<IEmpty<EmptyN>, EmptyN>();
services.AddTransient<NewClass1>();
});
return hostBuilder;
}
public class NewClass1 {
private IEmpty<Empty1> Empty;
public NewClass1(IEmpty<Empty1> empty)
{
Empty = empty;
}
public string EmptyType => $"{Empty.GetType()}";
}
public interface IEmpty<T> {}
public class Empty1 : IEmpty<Empty1>{ }
public class Empty2 : IEmpty<Empty2>{ }
public class EmptyN : IEmpty<EmptyN>{ }
Upvotes: 2
Views: 1066
Reputation: 172835
Resolving a list of generic types based on its open-generic definition by calling GetServices(typeof(IEmpty<>))
is not supported by MS.DI. Although technically possible, there is no DI Container that I'm familiar with that actually supports this.
There are many possible ways to solve your issue. You could, for instance, introduce a non-generic IEmpty
(marker) interface that IEmpty<T>
inherits from.
You can also go through the code base using Reflection, as you already mentioned, or you can go through the registrations in the ServiceCollection
to get all registered IEmpty<T>
registrations. This list can than be used to get the list. For instance:
var emptyTypes =
from s in services
where s.ServiceType.IsGenericType
where s.ServiceType.GetGenericTypeDefinition() == typeof(IEmpty<>)
select s.ServiceType;
foreach (Type emptyType in emptyTypes)
{
var empty = host.Services.GetRequiredService(emptyType);
Console.WriteLine(empty.GetType());
}
Upvotes: 3