使用 Unity 动态注册接口的多个实现(来自 AutoMapper 的 IValueResolver

Dynamically registering multiple implementations of an interface with Unity (IValueResolver from AutoMapper

我有代码可以动态扫描程序集以查找 AutoMapper 配置文件的实现 class 并构建一个映射器以与 Unity DI 一起使用。最近发现需要用到IValueResolver接口。 class 需要在其中注入资源依赖性才能执行模型之间的转换。

我可以在自动映射器配置中添加一行,强制它使用统一来解析 IValueResolvers。

    cfg.ConstructServicesUsing(type => container.Resolve(type));

我无法弄清楚如何使用 Unity 动态注册通用接口的多个实例。

我可以硬编码

    container.RegisterType<IValueResolver<Class1, Class2, string>,Class1ToClass2ValueResolver>();

但是,我宁愿扫描并动态注册它们。

可能有更好的方法,这种方法可能有点天真,可能无法处理所有嵌套类型层次结构或同一通用接口的多个实现,但这种方法确实适用于已发布的案例。也许可以通过使用某些按约定注册的功能来清理它。

var resolverTypes = .AppDomain.CurrentDomain.GetAssemblies()
    .Where(a => !a.IsDynamic) // Exclude dynamic assemblies
    .Select(a => a.GetTypes())
    .SelectMany(t => t) // flatten Types for each assembly into one
    .Select(t => t.GetTypeInfo())
    .Where(t => 
        t.ImplementedInterfaces.Any(i =>
            i.IsGenericType && 
            i.GetGenericTypeDefinition().IsAssignableFrom(typeof(IValueResolver<,,>))))
    .Select(t => new
    {
        Inteface = t.ImplementedInterfaces.First(i =>
            i.IsGenericType && i.GetGenericTypeDefinition()
                             .IsAssignableFrom(typeof(IValueResolver<,,>))),
        Type = t
    });

var container = new UnityContainer();

foreach (var resolverType in resolverTypes)
{
    container.RegisterType(resolverType.Inteface, resolverType.Type);
    var resolver = container.Resolve(typeof(IValueResolver<Class1, Class2, string>));
    Debug.Assert(resolver.GetType() == typeof(Class1ToClass2ValueResolver));
}

艰苦的工作是获得正确的类型: 首先获取所有程序集,排除动态程序集,并扁平化为可枚举类型(而不是 Enumerable<Type[]>)。接下来仅选择具有通用接口并匹配开放通用 IValueResolver<,,> 接口的 类 和 return 具有匹配的封闭通用接口的类型。

一旦我们获得了该信息,只需注册从接口到实现的映射即可。在这种情况下 IValueResolver<ClassA, ClassB, string>Class1ToClass2ValueResolver。以下是示例类和使用的接口:

public class Class1 { }

public class Class2 { }

public interface IValueResolver<U, V, W> { }

public class Class1ToClass2ValueResolver : IValueResolver<Class1, Class2, string> { }