Dependency injection 如何使用Func<;T>;内置依赖注入

Dependency injection 如何使用Func<;T>;内置依赖注入,dependency-injection,asp.net-core,Dependency Injection,Asp.net Core,使用asp.net 5我希望我的控制器被注入Func而不是T 例如: 公共家庭控制器(Func uow) 而不是 公共家庭控制器(Interfaces.IUnitOfWork uow) 内置DI是否有可能,或者我是否被迫移动到外部DI?据我所知,使用ASP.NET Core中的当前默认IoC容器不可能延迟此类依赖项。反正我也没能让它工作 要延迟此类依赖项的初始化,您需要实现一个功能更丰富的现有IoC容器。Func默认情况下不会注册或解析,但没有任何东西阻止您自己注册它 e、 g servic

使用
asp.net 5
我希望我的控制器被注入
Func
而不是
T

例如:

公共家庭控制器(Func uow)
而不是

公共家庭控制器(Interfaces.IUnitOfWork uow)

内置DI是否有可能,或者我是否被迫移动到外部DI?

据我所知,使用ASP.NET Core中的当前默认IoC容器不可能延迟此类依赖项。反正我也没能让它工作

要延迟此类依赖项的初始化,您需要实现一个功能更丰富的现有IoC容器。

Func
默认情况下不会注册或解析,但没有任何东西阻止您自己注册它

e、 g

services.AddSingleton(提供者=>
新建Func(()=>provider.GetService());

请注意,您还需要以通常的方式注册IUnitOfWork本身。

您可以向
服务集合注册
Func
或委托。我建议使用委托,因为它允许您区分具有相同签名的不同方法

这里有一个例子

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);
    }
}
公共接口需要{}
公共类ThingIdeed:ItingIdeed{}
公共委托i需要ThingINeedFactory();
公共类委派注册测试
{
[测试]
公共无效RegisterDelegateFromDependency()
{
var servicecolection=新servicecolection();
serviceCollection.AddTransient();
serviceCollection.AddTransient(
provider=>provider.GetService);
var serviceProvider=servicecolection.BuildServiceProvider();
var factoryMethod=serviceProvider.GetService();
var输出=factoryMethod();
Assert.IsInstanceOf(输出);
}
}
这看起来几乎像一个服务定位器,因为我们正在解析的函数实际上是
IServiceCollection.GetService()
。但这隐藏在构图的根中。注入此委托的类取决于委托,而不是实现

如果要返回的方法属于容器必须解析的类,则可以使用相同的方法

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);
    }
}
需要的公共接口
{
字符串SayHello();
}
公共类ThingIdeed:我需要
{
私有只读字符串\u;
公共物品需求(字符串问候语)
{
_问候=问候;
}
公共字符串SayHello()=>\u问候语;
}
公营物流工厂
{
公共IThingIdemand创建(字符串输入)=>新的ThingIdemand(输入);
}
公共委托i需要ThingINeedFactoryMethod(字符串输入);
公共类委派注册测试
{
[测试]
公共无效RegisterDelegateFromDependency()
{
var servicecolection=新servicecolection();
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
serviceCollection.AddSingleton(提供程序=>
provider.GetService().Create);
var serviceProvider=servicecolection.BuildServiceProvider();
var factoryMethod=serviceProvider.GetService();
创建的风险值=工厂法(“abc”);
var greeting=created.SayHello();
主张平等(“abc”,问候语);
}
}
这里有一个扩展方法(可能)让它变得更简单:

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);
公共静态类ServiceCollectionExtensions
{
公共静态IServiceCollection RegisterDelegate(
此IServiceCollection服务集合,
Func getDelegateFromSource)
TDelegate:类在哪里
{
返回serviceCollection.AddSingleton(提供程序=>
getDelegateFromSource(provider.GetService());
}
}
服务集合
.RegisterDelegate(
factory=>factory.Create);

虽然.net core的默认依赖项注入中没有内置的Func构建支持,但我们可以构建一个扩展方法来添加所有缺少的Func。我们只需要确保在注册结束时打电话

public static class ServiceCollectionExtensions
{
    private static MethodInfo GetServiceMethod;

    static ServiceCollectionExtensions()
    {
        Func<IServiceProvider, object> getServiceMethod = ServiceProviderServiceExtensions.GetService<object>;
        GetServiceMethod = getServiceMethod.Method.GetGenericMethodDefinition();
    }

    /// <summary>
    /// Registers all Funcs in constructors to the ServiceCollection - important to call after all registrations
    /// </summary>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static IServiceCollection AddFactories(this IServiceCollection collection)
    {

        // Get a list of all Funcs used in constructors of regigstered types
        var funcTypes = new HashSet<Type>(collection.Where(x => x.ImplementationType != null)
            .Select(x => x.ImplementationType)
            .SelectMany(x => x.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
            .SelectMany(x => x.GetParameters())
            .Select(x => x.ParameterType)
            .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>)));

        // Get a list of already registered Func<> and remove them from the hashset
        var registeredFuncs = collection.Select(x => x.ServiceType)
            .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>));
        funcTypes.ExceptWith(registeredFuncs);

        // Each func build the factory for it
        foreach (var funcType in funcTypes)
        {
            var type = funcType.GetGenericArguments().First();
            collection.AddTransient(funcType, FuncBuilder(type));
        }

        return collection;
    }

    /// <summary>
    /// This build expression tree for a func that is equivalent to 
    ///     Func<IServiceProvider, Func<TType>> factory = serviceProvider => new Func<TType>(serviceProvider.GetService<TType>);
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static Func<IServiceProvider, object> FuncBuilder(Type type)
    {
        var serviceProvider = Expression.Parameter(typeof(IServiceProvider), "serviceProvider");
        var method = GetServiceMethod.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 Func<IServiceProvider, object>;
        return factory;
    }
}
公共静态类ServiceCollectionExtensions
{
私有静态MethodInfo GetServiceMethod;
静态ServiceCollectionExtensions()
{
Func getServiceMethod=ServiceProviderServiceExtensions.GetService;
GetServiceMethod=GetServiceMethod.Method.GetGenericMethodDefinition();
}
/// 
///将构造函数中的所有函数注册到ServiceCollection-在所有注册后调用很重要
/// 
/// 
/// 
公共静态IServiceCollection AddFactorys(此IServiceCollection集合)
{
//获取regigstered类型的构造函数中使用的所有函数的列表
var funcTypes=newhashset(collection.Where(x=>x.ImplementationType!=null)
.选择(x=>x.ImplementationType)
.SelectMany(x=>x.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
.SelectMany(x=>x.GetParameters())
.选择(x=>x.ParameterType)
其中(x=>x.IsGenericType&&x.GetGenericTypeDefinition()==typeof(Func));
//获取已注册Func的列表,并将其从哈希集中删除
var registeredFuncs=collection.Select(x=>x.ServiceType)
其中(x=>x.IsGenericType&&x.GetGenericTypeDefinition()==typeof(Func));
funcTypes.ExceptWith(registeredFuncs);
//每个func都为其建立工厂
foreach(funcTypes中的var funcType)
{
var type=funcType.GetGenericArguments().First();
AddTransient(funcType,FuncBuilder(type));
}
回收;
}
/// 
///此func的生成表达式树等效于
///Func factory=serviceProvider=>newfunc(serviceProvider.GetService);
/// 
/// 
/// 
专用静态Func FuncBuilder(类型)
{
变量
public static class ServiceCollectionExtensions
{
    private static MethodInfo GetServiceMethod;

    static ServiceCollectionExtensions()
    {
        Func<IServiceProvider, object> getServiceMethod = ServiceProviderServiceExtensions.GetService<object>;
        GetServiceMethod = getServiceMethod.Method.GetGenericMethodDefinition();
    }

    /// <summary>
    /// Registers all Funcs in constructors to the ServiceCollection - important to call after all registrations
    /// </summary>
    /// <param name="collection"></param>
    /// <returns></returns>
    public static IServiceCollection AddFactories(this IServiceCollection collection)
    {

        // Get a list of all Funcs used in constructors of regigstered types
        var funcTypes = new HashSet<Type>(collection.Where(x => x.ImplementationType != null)
            .Select(x => x.ImplementationType)
            .SelectMany(x => x.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
            .SelectMany(x => x.GetParameters())
            .Select(x => x.ParameterType)
            .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>)));

        // Get a list of already registered Func<> and remove them from the hashset
        var registeredFuncs = collection.Select(x => x.ServiceType)
            .Where(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(Func<>));
        funcTypes.ExceptWith(registeredFuncs);

        // Each func build the factory for it
        foreach (var funcType in funcTypes)
        {
            var type = funcType.GetGenericArguments().First();
            collection.AddTransient(funcType, FuncBuilder(type));
        }

        return collection;
    }

    /// <summary>
    /// This build expression tree for a func that is equivalent to 
    ///     Func<IServiceProvider, Func<TType>> factory = serviceProvider => new Func<TType>(serviceProvider.GetService<TType>);
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static Func<IServiceProvider, object> FuncBuilder(Type type)
    {
        var serviceProvider = Expression.Parameter(typeof(IServiceProvider), "serviceProvider");
        var method = GetServiceMethod.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 Func<IServiceProvider, object>;
        return factory;
    }
}
// Add to your Service Collection.
services.AddTransient(typeof(Lazy<>), typeof(LazyServiceFactory<>));

class LazyServiceFactory<T> : Lazy<T>
{
    public LazyServiceFactory(IServiceProvider serviceProvider)
        : base(() => serviceProvider.GetRequiredService<T>())
    {
    }
}
// And some tests...
[TestMethod]
[DataTestMethod]
[DataRow(ServiceLifetime.Transient)]
[DataRow(ServiceLifetime.Scoped)]
[DataRow(ServiceLifetime.Singleton)]
public void Resolve_GivenLazyilyRegisteredService_CanResolve(ServiceLifetime serviceLifetime)
{
    // Arrange
    IServiceProvider serviceProvider = CreateServiceProvider(serviceLifetime);
    using IServiceScope scope = serviceProvider.CreateScope();

    // Act
    Func<Lazy<ClassHello>> result = () => scope.ServiceProvider.GetRequiredService<Lazy<ClassHello>>();

    // Assert
    result
        .Should()
        .NotThrow()
        .And
        .Subject()
        .Value
        .Should()
        .NotBeNull();
}

static IServiceProvider CreateServiceProvider(ServiceLifetime serviceLifetime)
{
    IServiceCollection services = new ServiceCollection();

    services.Add(new ServiceDescriptor(typeof(Lazy<>), typeof(LazyServiceFactory<>), serviceLifetime));

    services.Add(new ServiceDescriptor(typeof(ClassHello), typeof(ClassHello), serviceLifetime));

    return services.BuildServiceProvider(true);
}