How to use Func<T> in built-in dependency injection

Func<T> does not get registered or resolved by default but there is nothing stopping you from registering it yourself.

e.g.

services.AddSingleton(provider => 
  new Func<IUnitOfWork>(() => provider.GetService<IUnitOfWork>()));

Note that you will also need to register IUnitOfWork itself in the usual way.


As far as I'm aware deferring dependencies like this isn't possible using the current default IoC container within ASP.NET Core. I've not been able to get it working anyway!

To defer the initialisation of dependencies like this you'll need to implement an existing, more feature rich IoC container.


You can register a Func<T> or a delegate with a ServiceCollection. I recommend a delegate because it allows you to distinguish between different methods with identical signatures.

Here's an example.

public interface IThingINeed {}
public class ThingINeed : IThingINeed { }

public delegate IThingINeed ThingINeedFactory();

public class DelegateRegistrationTests
{
    [Test]
    public void RegisterDelegateFromDependency()
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddTransient<IThingINeed, ThingINeed>();
        serviceCollection.AddTransient<ThingINeedFactory>(
            provider => provider.GetService<IThingINeed>);
        var serviceProvider = serviceCollection.BuildServiceProvider();
        var factoryMethod = serviceProvider.GetService<ThingINeedFactory>();
        var output = factoryMethod();
        Assert.IsInstanceOf<ThingINeed>(output);
    }
}

This almost looks like a service locator because the function we're resolving is actually IServiceCollection.GetService<ThingINeedFactory>(). But that's hidden in the composition root. A class that injects this delegate depends on the delegate, not on the implementation.

You can use the same approach if the method you want to return belongs to a class that the container must resolve.

public interface IThingINeed
{
    string SayHello();
}

public class ThingINeed : IThingINeed
{
    private readonly string _greeting;

    public ThingINeed(string greeting)
    {
        _greeting = greeting;
    }

    public string SayHello() => _greeting;
}

public class ThingINeedFactory
{
    public IThingINeed Create(string input) => new ThingINeed(input);
}

public delegate IThingINeed ThingINeedFactoryMethod(string input);

public class DelegateRegistrationTests
{
    [Test]
    public void RegisterDelegateFromDependency()
    {
        var serviceCollection = new ServiceCollection();
        serviceCollection.AddSingleton<IThingINeed, ThingINeed>();
        serviceCollection.AddSingleton<ThingINeedFactory>();
        serviceCollection.AddSingleton<ThingINeedFactoryMethod>(provider => 
            provider.GetService<ThingINeedFactory>().Create);
        var serviceProvider = serviceCollection.BuildServiceProvider();
        var factoryMethod = serviceProvider.GetService<ThingINeedFactoryMethod>();
        var created = factoryMethod("abc");
        var greeting = created.SayHello();
        Assert.AreEqual("abc", greeting);
    }
}

Here's an extension method to (maybe) make it a little bit easier:

public static class ServiceCollectionExtensions
{
    public static IServiceCollection RegisterDelegate<TSource, TDelegate>(
        this IServiceCollection serviceCollection,
        Func<TSource, TDelegate> getDelegateFromSource) 
            where TDelegate : class 
    {
        return serviceCollection.AddSingleton(provider =>
            getDelegateFromSource(provider.GetService<TSource>()));
    }
}

serviceCollection
    .RegisterDelegate<ThingINeedFactory, ThingINeedFactoryMethod>(
        factory => factory.Create);