c # – Registering functions in .Net Core Dependecy Injection automatically

Use of dependency injection in .Net Core. Currently, creating Func <> is not supported for injecting into the constructor, as is the case with other DI containers, and it is not intended to add it soon, if at all.

I wanted to stay away from the Service Locator pattern and continue inserting Funcs where needed. So I wrote an extension method to check the register type constructors and create and register all the required funs.

Public static class ServiceCollectionExtensions
{
/// 
    /// Registers all funcs in constructors at the ServiceCollection - important after all registrations
/// 
    /// 
    
    
    
    /// 
    
    
    
    public static IServiceCollection AddFactories (this IServiceCollection collection)
{
var factories = new hash set();
// Get a list of all registrations that are not factories
var registrations = collection.Where (s => s.ImplementationType! = null);
foreach (var registration in registrations)
{
// Get a list of functions in the constructors
var constructors = registration.ImplementationType.GetConstructors (BindingFlags.Public | BindingFlags.Instance);
var funcs = constructors.SelectMany (c => c.GetParameters ())
.Select (p => p.ParameterType)
.Where (t => t.IsGenericType && t.GetGenericTypeDefinition () == typeof (Func <>));
foreach (var func in funcs)
{
factories.Add (func);
}
}

// Every function builds the factory for it
foreach (var factory in factories)
{
var type = factory.GetGenericArguments (). First ();
collection.AddTransient (factory, BuildExpression (type));
}

Return collection;
}

/// 
    /// This build expression tree for a function that is equivalent
/// Func <IServiceProvider, Func> factory = serviceProvider => new function(serviceProvider.GetService);
/// 
    /// 
    
    
    
    /// 
    
    
    
    private static function BuildExpression (type)
{
var serviceProvider = Expression.Parameter (typeof (IServiceProvider), "serviceProvider");
var method = typeof (ServiceProviderServiceExtensions)
.GetMethod (nameof (ServiceProviderServiceExtensions.GetService), BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod (type);
var call = Expression.Call (method, serviceProvider);
var returnType = typeof (Func <>). MakeGenericType (type);
var returnFunc = Expression.Lambda (returnType, call);
var func = Expression.Lambda (typeof (Func<,>) .MakeGenericType (typeof (IServiceProvider), returnType), returnFunc, serviceProvider);
var factory = func.Compile () as a function;
Return factory;
}
}

Look for improvements. It is not certain that it is worthwhile to move the reflection call to find the extension method so that it is not in the loop. Apparently, it would not flow as well if I found it in the calling method and passed it on, or I could create a lazy field for it, but it could also be a micro-optimization, since this code is executed only once during the setup.

A simple test shows it works

public class TestFunc
{

public TestFunc ()
{
Date = DateTime.Now;
}
public DateTime Date {get; }
}


public class HostingFunc
{
private read-only func _Factory;
public HostingFunc (Func Factory)
{
_fabrik = factory;
}

public void TestMethod ()
{
Console.WriteLine (_factory (). Date);
}
}


static void Main (String[] args)
{
var serviceCollection = new ServiceCollection ();
serviceCollection.AddSingleton();
serviceCollection.AddTransient();
serviceCollection.AddFactories ();
var container = serviceCollection.BuildServiceProvider ();

var hostingFunc = container.GetService();
hostingFunc.TestMethod ();
Thread.Sleep (TimeSpan.FromSeconds (2));
hostingFunc.TestMethod (); // Should be different than above
Console.ReadLine ();
}