如何使用被解析的类型来解析依赖

How to use the type being resolved to resolve a dependency

我有几个 类 依赖类型 ILoggerILogger 的实现需要知道它是记录器的类型,即 FooILogger 将是 new Logger(typeof(Foo))Bar 将是new Logger(typeof(Bar)),等等

我希望 Unity 自动注入适当的记录器;换句话说,当我调用 container.Resolve<Foo>() 时,我希望将 new Logger(typeof(Foo)) 注入到 Foo 实例中。

如何在 Unity 中进行设置?有没有办法将正在解析的类型传递给依赖项?

(在我的真实代码中,我实际上有一个 ILoggerFactory 和一个 Create 方法,它也将类型作为参数。所以我可以将工厂传递给我的 类,他们会自己调用 Create 来获得合适的记录器,但它并不像我想要实现的那样优雅)


一些使事情更清楚的代码:

interface ILogger
{
    ...
}

class Logger : ILogger
{
    private readonly Type _type;
    public Logger(Type type)
    {
        _type = type;
    }
    ...
}

class Foo
{
    private readonly ILogger _logger;
    public Foo(ILogger logger) // here I want a Logger with its type set to Foo
    {
        _logger = logger;
    }
}

这个 准确地展示了我正在尝试做的事情,而被接受的答案正是我正在寻找的东西......但它是针对 NInject,而不是 Unity .

正如@spotco 所建议的,我也推荐仿制药。统一注册将使用 "open generics" 完成,如此处所示。这种单一注册使您能够解析 Logger 的泛型参数中的任何类型。

unityContainer.RegisterType(typeof(ILogger<>), typeof(Logger<>));

使用该注册,您可以像这样使用构造函数注入来解析实例。

public class MyClass 
{
    public MyClass(ILogger<MyClass> logger) { ... }
}

More info here.

这是一个容器扩展,它将 Logger 构造函数的 Type 参数设置为 ILogger 注入的类型。

瞬态 IBuilderContext.Policies 用于存储 ILogger 注入的类型。

也许它比需要的更复杂,但这似乎有效

public class LoggerExtension : UnityContainerExtension
{
    public static NamedTypeBuildKey LoggerBuildKey = new NamedTypeBuildKey<Logger>();

    protected override void Initialize()
    {
        Context.Strategies.Add(new LoggerTrackingPolicy(), UnityBuildStage.TypeMapping);
        Context.Strategies.Add(new LoggerBuildUpStrategy(), UnityBuildStage.PreCreation);
    }
}

public class LoggerTrackingPolicy : BuilderStrategy
{
    public LoggerTrackingPolicy()
    {
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        if (context.BuildKey.Type != typeof(Logger))
        {
            var loggerPolicy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
            if (loggerPolicy == null)
            {
                loggerPolicy = new LoggerPolicy();
                context.Policies.Set<ILoggerPolicy>(loggerPolicy, LoggerExtension.LoggerBuildKey);
            }

            loggerPolicy.Push(context.BuildKey.Type);
        }
    }
}

public class LoggerBuildUpStrategy : BuilderStrategy
{
    public LoggerBuildUpStrategy()
    {
    }

    public override void PreBuildUp(IBuilderContext context)
    {
        if (context.BuildKey.Type == typeof(Logger))
        {
            var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
            Type type = policy.Peek();
            if (type != null)
            {
                context.AddResolverOverrides(new ParameterOverride("type", new InjectionParameter(typeof(Type), type)));
            }
        }
    }

    public override void PostBuildUp(IBuilderContext context)
    {
        if (context.BuildKey.Type != typeof(Logger))
        {
            var policy = context.Policies.Get<ILoggerPolicy>(LoggerExtension.LoggerBuildKey);
            policy.Pop();
        }
    }
}

public interface ILoggerPolicy : IBuilderPolicy
{
    void Push(Type type);
    Type Pop();
    Type Peek();
}

public class LoggerPolicy : ILoggerPolicy
{
    private Stack<Type> types = new Stack<Type>();

    public void Push(Type type)
    {
        types.Push(type);
    }

    public Type Peek()
    {
        if (types.Count > 0)
        {
            return types.Peek();
        }

        return null;
    }

    public Type Pop()
    {
        if (types.Count > 0)
        {
            return types.Pop();
        }

        return null;
    }
}

它的工作原理是:当尝试解析不是 Logger 的类型时,在 TypeMapping 阶段(在任何创建之前),该类型被压入堆栈。稍后,在创建之前,如果类型是 Logger,那么它被注入的类型将从堆栈中窥视,并且该类型用作解析器覆盖。创建后,如果类型不是Logger,则出栈。

还有一些代码来确保它正常工作(我向记录器添加了一个类型 属性 只是为了验证它是否设置正确):

class Program
{
    static void Main(string[] args)
    {
        IUnityContainer container = new UnityContainer();

        container.RegisterType<ILogger, Logger>();
        container.AddNewExtension<LoggerExtension>();

        var a = container.Resolve<A>();
        var b = container.Resolve<B>();
        var c = container.Resolve<C>();
        var d = container.Resolve<D>();
        var x = container.Resolve<X>();
    }
}

public interface ILogger
{
    Type Type { get; }
}

public class Logger : ILogger
{
    private readonly Type _type;
    public Logger(Type type)
    {
        _type = type;
    }

    public Type Type { get { return _type; } }
}

public class A
{
    public A(ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(A));
    }
}

public class B
{
    public B(ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(B));
    }
}

public class C
{
    public C(A a, D d, B b, ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(C));
    }
}

public class D
{
    public D()
    {
    }
}

public class X
{
    public X(Y y)
    {
    }
}

public class Y
{
    public Y(ILogger logger)
    {
        System.Diagnostics.Debug.Assert(logger.Type == typeof(Y));
    }
}