public class MyController : Controller
{
public MyController(Func<MyItem> itemFactory)
{
Item = itemFactory.Invoke();
This should work out of box. However it only works if you manually register this dependecy ala
c#
services.AddSingleton<Func<MyItem>>(provider => provider.GetService<MyItem>);
To understand Func<> as a constructor argument means attempt to use the provider to create the dependency wrapped in a factory provider.
Manually creating the factories. It is not possible to create a generic factory due dotnet/extensions#478 not being implemented yet.
https://stackoverflow.com/questions/35736070/how-to-use-funct-in-built-in-dependency-injection
This feature has been wanted for over 2 years.
Per aspnet/DependencyInjection#474 and aspnet/AspNetCore#3036, it鈥檚 unlikely that M.E.DI will support open generic type factories any time soon, so you cannot implement this directly on top of the DI container. I would also say that adding support for Func<T> explicitly into the DI container would be a bit weird, and it would certainly conflict with explicit function dependency registrations.
Instead, I would suggest you to create your own service resolver abstraction on top. Something like this:
```c#
public interface IServiceResolver
{
T Get();
}
public class ServiceResolver
{
private readonly IServiceProvider _serviceProvider;
public ServiceResolver(IServiceProvider provider) => _serviceProvider = provider;
public T Get() => _serviceProvider.GetService
}
Example usage:
```c#
var services = new ServiceCollection();
// add generic service factory
services.AddTransient(typeof(IServiceResolver<>), typeof(ServiceResolver<>));
// add some dependency
services.AddTransient<FooService>();
var provider = services.BuildServiceProvider();
// get the resolver (of course also works via constructor injection)
var fooServiceFactory = provider.GetService<IServiceResolver<FooService>>();
// call `Get()` to actually resolve the service
var fooService = fooServiceFactory.Get();
@poke implicit lazy factory support has been an IOC paradigm _in c#_ for over half a decade.
It's absurd that it's not supported. Even if it's not implicitly enabled, it's borderline criminal the lack of proper support to implement it yourself due to the poorly designed service provider interface.
I don't see why this is a problem to implement for open generics. Surely it should be possible to register something like the following (which sort-of uses the solution from @poke, but requires an extra step if the T of the wrapped service is an open generic type):
public static ServiceDescriptor UnwrappingServiceDescriptor(ServiceDescriptor descriptor)
{
return ServiceDescriptor.Describe(
descriptor.ServiceType,
sp => sp.GetRequiredService(typeof(WrappedService<>).MakeGenericType(descriptor.ServiceType)).Service,
descriptor.Lifetime
);
}
var wrappedDummyService = ServiceDescriptor.Describe(
typeof(IMyOpenGenericService<>),
typeof(MyOpenGenericImplementation<>),
ServiceLifetime.Transient
);
var services = new ServiceCollection();
services.TryAdd(UnwrappingServiceDescriptor(wrappedDummyService));
// ... Other required registrations (that are registered using the WrappedService interface) ...
var provider = services.BuildServiceProvider();
var unwrappedService = provider.GetService<IMyOpenGenericService<int>>();
unwrappedService.Should().NotBeNull();
Something like this is a potential way to enable segregated service registrations (client1 services cannot depend on client2 registrations (unless explicitly using the WrappedService interface directly), but global services can depend on either depending on which client context they are using. Of course the actual implementation is a bit more complicated than this, but this is just a simplified example.
@davidfowl @pakrym Do we expect we'll do this in 3.0?
Not for 3.0. We need more design to allow new features that are not in container specs.
Yes, not in 3.0. I would move it to a future milestone instead of Discussions @muratg
If what you need is Lazy<T>, then that can easily be done by registering an open generic of Lazy<>, like this SO answer: https://stackoverflow.com/a/45775657/304986
services.AddTransient(typeof(Lazy<>), typeof(Lazier<>));
internal class Lazier<T> : Lazy<T> where T : class
{
public Lazier(IServiceProvider provider)
: base(() => provider.GetRequiredService<T>())
{
}
}
The lack of this is especially a problem with multiple service registrations. The alternative way at this moment to get hold of a factory func is really just Func<T> factory = () => serviceProvider.GetService<T>(). But this doesn't work at all when dealing with multiple registrations, and there's no way to get a service at a specific slot/index either.
If something in this spirit would be implemented I would ask not only Func<T> but also IEnumerable<Func<T>> be special cased, similarly to how IEnumerable<T> is special.