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

时间:2016-02-09 16:37:36

标签: c# dependency-injection unity-container

我有几个类具有ILogger类型的依赖关系。 ILogger的实现需要知道它是记录器的类型,即ILogger的{​​{1}}将是Foonew Logger(typeof(Foo))将是Bar new Logger(typeof(Bar))等等。

我希望Unity能够自动注入合适的记录器;换句话说,当我致电container.Resolve<Foo>()时,我希望将new Logger(typeof(Foo))注入Foo个实例。

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

(在我的实际代码中,我实际上有一个ILoggerFactoryCreate方法,它也将一个类型作为参数。所以我可以将工厂传递给我的类,他们会自己打电话给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;
    }
}

related question显示了我正在尝试做的事情,接受的答案正是我正在寻找的那种......但它适用于NInject,而不是Unity。

2 个答案:

答案 0 :(得分:4)

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

瞬态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,则从堆栈中弹出。

还有一些代码可以确保它正常工作(我在记录器中添加了一个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));
    }
}

答案 1 :(得分:0)

正如@spotco建议的那样,我也会推荐泛型。统一注册将使用“open generics”完成,如此处所示。通过此单一注册,您可以解析Logger的泛型参数中的任何类型。

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

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

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

https://media.readthedocs.org/pdf/identityserver4/release/identityserver4.pdf