通过通用接口/抽象类实现使.NET Core DI自动解析类

时间:2018-02-18 13:03:33

标签: c# dependency-injection .net-core inversion-of-control

.NET Core中是否有办法注册通用接口,并使其解析与某个实现相匹配的类。

例如,我有以下界面:

public interface IMapper<TFrom, TTo>
{
}

我还有一个抽象类:

public abstract class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
    protected Mapper()
    {
        // some generic stuff
    }

    public abstract TTo Map(TFrom);
}

然后我可以创建一个类似的实现:

public class UserMapper : Mapper<Domain.User, Entity.User>
{
    public override Entity.User Map(Domain.User from)
    {
        // do mapping
    }
}

有没有办法,使用默认的.NET Core DI注册IMapper<,>,让它自动解析类?

因此,如果我在代码中的某处执行此操作:

class SomeClass
{
    public SomeClass(IMapper<Domain.User, Entity.User> mapper) {}
}

它以某种方式知道它应该解析类UserMapper<Domain.User, Entity.User>

原因是手动注册每个映射器有点冗长,特定于实现。所以我希望Microsoft.DependencyInjection足够智能,能够以某种方式自动解决其实现。

3 个答案:

答案 0 :(得分:6)

目前设计的唯一方法是使用Reflection:

Assembly assembly = typeof(UserMapper).Assembly;

foreach (var type in assembly.GetTypes()
    .Where(t => t.IsClass && !t.IsAbstract))
{
    foreach (var i in type.GetInterfaces())
    {
        if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IMapper<,>))
        {
            // NOTE: Due to a limitation of Microsoft.DependencyInjection we cannot 
            // register an open generic interface type without also having an open generic 
            // implementation type. So, we convert to a closed generic interface 
            // type to register.
            var interfaceType = typeof(IMapper<,>).MakeGenericType(i.GetGenericArguments());
            services.AddTransient(interfaceType, type);
        }
    }
}
  

注意:您可以通过在IServiceCollection上创建一个包含AddTransientAddSingleton等各种重载的扩展方法来简化它。

如果更改设计,请使用非抽象泛型作为实现类型:

public class Mapper<TFrom, TTo> : IMapper<TFrom, TTo>
{
    //...
}

然后你可以像这样注册:

services.AddTransient(typeof(IMapper<,>), typeof(Mapper<,>));

答案 1 :(得分:1)

您已准备好使用助手:

通过通用界面

services.AddAllGenericTypes(typeof(IGenericRepository<>), new[] {typeof(MyDbContext).GetTypeInfo().Assembly});

通过界面

services.AddAllTypes<IGenerator>(new[] { typeof(FileHandler).GetTypeInfo().Assembly },

扩展自: https://gist.github.com/GetoXs/5caf0d8cfe6faa8a855c3ccef7c5a541

答案 2 :(得分:0)

这是更通用的解决方案(默认范围内的生命周期)

首先创建此扩展方法

  public static void RegisterAllTypes(this IServiceCollection services, Assembly assembly, Type baseType,
            ServiceLifetime lifetime = ServiceLifetime.Scoped)
        {
            foreach (var type in assembly.GetTypes()
                .Where(t => t.IsClass && !t.IsAbstract))
            {
                foreach (var i in type.GetInterfaces()
                    .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == baseType))
                {

                    var interfaceType = baseType.MakeGenericType(i.GetGenericArguments());
                    services.Add(new ServiceDescriptor(interfaceType, type, lifetime));
                }
            }
        }

然后将其添加到startup.cs

 services.RegisterAllTypes(typeof(AClass).Assembly, typeof(IMapper<>));