我有几个类具有ILogger
类型的依赖关系。 ILogger
的实现需要知道它是记录器的类型,即ILogger
的{{1}}将是Foo
,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;
}
}
这related question显示了我正在尝试做的事情,接受的答案正是我正在寻找的那种......但它适用于NInject,而不是Unity。
答案 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