代码之家  ›  专栏  ›  技术社区  ›  Vivendi

制作NET核心DI通过通用接口/抽象类实现自动解析类

  •  3
  • Vivendi  · 技术社区  · 7 年前

    有办法进去吗。NET内核注册一个通用接口,并使其解析一个与特定实现匹配的类。

    例如,我有以下界面:

    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 回复  |  直到 7 年前
        1
  •  8
  •   NightOwl888 Jabrwoky    7 年前

    当前设计的唯一方法是使用反射:

    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 使用各种重载 AddTransient , AddSingleton

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

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

    然后,您可以像这样简单地注册:

    services.AddTransient(typeof(IMapper<,>), typeof(Mapper<,>));
    
        2
  •  3
  •   Ben Croughs    6 年前

    下面是一个更通用的解决方案(默认范围的生存期)

    首先创建此扩展方法

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

    然后将其添加到启动中。反恐精英

     services.RegisterAllTypes(typeof(AClass).Assembly, typeof(IMapper<>));
    
        3
  •  1
  •   Mateusz Przybylek    4 年前

    这里有现成的帮助程序:

    按通用接口

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

    按接口

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

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