如何使用正在解析的类型来解析依赖关系

Tho*_*que 8 c# dependency-injection unity-container

我有几个类依赖于类ILogger.实现ILogger需要知道它是记录器的类型,即ILoggerfor Foo将是new Logger(typeof(Foo)),因为Bar它将是new Logger(typeof(Bar)),等等.

我希望Unity自动注入合适的记录器; 换句话说,当我打电话时container.Resolve<Foo>(),我希望将a 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这个相关的问题显示了我正在尝试做的事情,接受的答案正是我正在寻找的那种......但它适用于NInject,而不是Unity.

Ran*_*ica 4

这是一个容器扩展,它将 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

它的工作原理是:当尝试解析不是 Logger 的类型时,在 TypeMapping 阶段(在任何创建之前),该类型会被推送到堆栈上。稍后,在创建之前,如果该类型是 Logger,则将从堆栈中查看它所注入的类型,并将该类型用作解析器覆盖。创建后,如果类型不是 Logger,则会从堆栈中弹出。

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

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));
    }
}
Run Code Online (Sandbox Code Playgroud)