如何在内置依赖注入中使用Func <t>

时间:2016-03-02 00:06:04

标签: dependency-injection asp.net-core

使用asp.net 5我希望我的控制器注入Func<T>代替T

例如:

public HomeController(Func<Interfaces.IUnitOfWork> uow)

而不是

public HomeController(Interfaces.IUnitOfWork uow)

是否可以使用内置DI或我被迫移动到外部DI?

6 个答案:

答案 0 :(得分:6)

默认情况下

Func<T>没有注册或解决,但没有什么能阻止您自己注册。

e.g。

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

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

答案 1 :(得分:4)

据我所知,使用ASP.NET Core中的当前默认IoC容器无法推迟这样的依赖。我还是无法让它工作!

要推迟这样的依赖关系的初始化,您需要实现一个现有的,功能更丰富的IoC容器。

答案 2 :(得分:2)

您可以注册Func<T>或具有ServiceCollection的代表。我建议使用一个委托,因为它可以让您区分具有相同签名的不同方法。

这是一个例子。

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);
    }
}

这几乎看起来像是服务定位符,因为我们要解析的功能实际上是IServiceCollection.GetService<ThingINeedFactory>()。但这隐藏在合成根目录中。注入此委托的类取决于委托,而不取决于实现。

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

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);
    }
}

这是一种扩展方法,可以(也许)使它更容易一些:

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);

答案 3 :(得分:1)

您可以使用几种选择,第一种是您可以切换使用令人难以置信的Lamar(使用ASP.NET Core integration)。

在大多数情况下,切换到Lamar只需几行代码,您就可以整天解析Func<>Lazy<>

我已经在大型的基于微服务的平台上大规模使用了它一段时间,我们对它*感到完全满意。

如果您不想转移到Lamar,可以使用它来解决Lazy<>(很抱歉,我已经尝试了很多次,但无法与{{1}一起使用) }:

Func<>

为了完整起见,这也是一个测试。

// Add to your Service Collection.
services.AddTransient(typeof(Lazy<>), typeof(LazyServiceFactory<>));

class LazyServiceFactory<T> : Lazy<T>
{
    public LazyServiceFactory(IServiceProvider serviceProvider)
        : base(() => serviceProvider.GetRequiredService<T>())
    {
    }
}

由于我现在非常专心地使用Lamar,因此我没有完全按照这个进度进行操作,但这对于较小的/一次性项目很方便。

*我唯一的小问题是它不支持// 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); } yet

答案 4 :(得分:0)

.net核心的默认依赖项注入中没有内置的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;
    }
}

在AddFactories中,我们获得所有已注册的concreate类型的列表,然后检查其构造函数中是否有Func <>。从该列表中删除之前已注册的所有Func。使用一些expressiontrees,我们构建所需的Funcs。

codereview中的代码也结束了,减去了对已注册功能的检查。

答案 5 :(得分:0)

我写了一些扩展方法,用于注册服务和工厂(Func<T>):

public static class IServiceCollectionExtension
{
    public static IServiceCollection AddFactory<TService, TServiceImplementation>(this IServiceCollection serviceCollection) 
        where TService : class
        where TServiceImplementation : class, TService
    {
        return serviceCollection
            .AddTransient<TService, TServiceImplementation>();
            .AddSingleton<Func<TService>>(sp => sp.GetRequiredService<TService>);
    }
}

用法

serviceCollection
   .AddFactory<IMyInterface, MyImplementation>()