Using Autofac, I can register a class to resolve against an interface using property injection, using the following code:
使用Autofac,我可以使用以下代码注册一个类,通过属性注入来解析一个接口:
builder.RegisterType<Log4NetAdapter>()
.As<ILogger>()
.PropertiesAutowired()
.InstancePerDependency();
However, my Log4NetAdapter
class has a constructor parameter that requires the name of the calling class. This way, I can log events based upon the name of the calling class.
但是,我的Log4NetAdapter类有一个构造函数参数,它需要调用类的名称。通过这种方式,我可以基于调用类的名称来记录事件。
public class Log4NetAdapter : ILogger
{
private readonly ILog _logger;
public Log4NetAdapter(string logName)
{
_logger = LogManager.GetLogger(logName);
}
...
}
How can I inject the name (i.e. typeof(dependency).Name
) of the dependency into the property-injected class' constructor given that each dependency will have its own Log4NetAdapter
instance?
如果每个依赖项都有自己的Log4NetAdapter实例,那么如何将依赖项的名称(即类型(dependency).Name)注入到属性注入类的构造函数中呢?
2 个解决方案
#1
3
Update: Building on the LogInjectionModule sample and how Autofac does property injection, I have extended the module to do both constructor and property injection.
更新:在LogInjectionModule示例和Autofac如何进行属性注入的基础上,我扩展了模块来完成构造函数和属性注入。
Note: I've fixed the type passed to LogManager
in OnComponentPreparing
to use the declaring type. This makes e.g. Resolve<Func<Service>>
use the correct log type.
注意:我已经修复了在oncomponentprepareto中传递给LogManager的类型,准备使用声明类型。这使得Resolve
using System.Linq;
using log4net;
public class LogInjectionModule : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
{
registration.Preparing += OnComponentPreparing;
registration.Activating += OnComponentActivating;
}
private static void OnComponentActivating(object sender, ActivatingEventArgs<object> e)
{
InjectLogProperties(e.Context, e.Instance, false);
}
private static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
e.Parameters = e.Parameters.Union(new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(ILog),
(p, i) => LogManager.GetLogger(p.Member.DeclaringType))
});
}
private static void InjectLogProperties(IComponentContext context, object instance, bool overrideSetValues)
{
if (context == null) throw new ArgumentNullException("context");
if (instance == null) throw new ArgumentNullException("instance");
var instanceType = instance.GetType();
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(pi => pi.CanWrite && pi.PropertyType == typeof(ILog));
foreach (var property in properties)
{
if (property.GetIndexParameters().Length != 0)
continue;
var accessors = property.GetAccessors(false);
if (accessors.Length == 1 && accessors[0].ReturnType != typeof(void))
continue;
if (!overrideSetValues &&
accessors.Length == 2 &&
(property.GetValue(instance, null) != null))
continue;
ILog propertyValue = LogManager.GetLogger(instanceType);
property.SetValue(instance, propertyValue, null);
}
}
}
On how to use the module, here's a sample:
关于如何使用这个模块,这里有一个例子:
public class Service
{
public Service(ILog log) { ... }
}
var cb = new ContainerBuilder();
cb.RegisterModule<LogInjectionModule>();
cb.RegisterType<Service>();
var c = cb.Build();
var service = c.Resolve<Service>();
#2
1
You only use logName
to effectively resolve by name an ILog
, so why not just inject an ILog
?
您只使用logName来有效地解析ILog,那么为什么不直接注入ILog呢?
public class Log4NetAdapter : ILogger
{
private readonly ILog _logger;
public Log4NetAdapter(ILog logger)
{
_logger = logger;
}
...
}
OK, so now I've just moved the problem a bit, but I've also made this less coupled to other classes, namely the LogManager
.
好了,现在我把问题移了一点,但我也让它与其他类的耦合更小,也就是LogManager。
So if I was using unity, I would then do this to ensure I get the right logger:
如果我使用unity,我就会这样做,以确保我找到正确的日志记录器:
var childContainer = container.CreateChildContainer();
childContainer.RegisterInstance<ILog>(LogManager.GetLogger(logName));
var adaptor = childContainer.Resolve<Log4NetAdapter>();
The child container prevents any other code getting access to that ILog
. You can do this as high up as you like, I don't know any more about your code.
子容器防止任何其他代码访问该ILog。你可以在你喜欢的高度做这个,我不知道你的代码。
#1
3
Update: Building on the LogInjectionModule sample and how Autofac does property injection, I have extended the module to do both constructor and property injection.
更新:在LogInjectionModule示例和Autofac如何进行属性注入的基础上,我扩展了模块来完成构造函数和属性注入。
Note: I've fixed the type passed to LogManager
in OnComponentPreparing
to use the declaring type. This makes e.g. Resolve<Func<Service>>
use the correct log type.
注意:我已经修复了在oncomponentprepareto中传递给LogManager的类型,准备使用声明类型。这使得Resolve
using System.Linq;
using log4net;
public class LogInjectionModule : Module
{
protected override void AttachToComponentRegistration(IComponentRegistry registry, IComponentRegistration registration)
{
registration.Preparing += OnComponentPreparing;
registration.Activating += OnComponentActivating;
}
private static void OnComponentActivating(object sender, ActivatingEventArgs<object> e)
{
InjectLogProperties(e.Context, e.Instance, false);
}
private static void OnComponentPreparing(object sender, PreparingEventArgs e)
{
e.Parameters = e.Parameters.Union(new[]
{
new ResolvedParameter(
(p, i) => p.ParameterType == typeof(ILog),
(p, i) => LogManager.GetLogger(p.Member.DeclaringType))
});
}
private static void InjectLogProperties(IComponentContext context, object instance, bool overrideSetValues)
{
if (context == null) throw new ArgumentNullException("context");
if (instance == null) throw new ArgumentNullException("instance");
var instanceType = instance.GetType();
var properties = instanceType
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(pi => pi.CanWrite && pi.PropertyType == typeof(ILog));
foreach (var property in properties)
{
if (property.GetIndexParameters().Length != 0)
continue;
var accessors = property.GetAccessors(false);
if (accessors.Length == 1 && accessors[0].ReturnType != typeof(void))
continue;
if (!overrideSetValues &&
accessors.Length == 2 &&
(property.GetValue(instance, null) != null))
continue;
ILog propertyValue = LogManager.GetLogger(instanceType);
property.SetValue(instance, propertyValue, null);
}
}
}
On how to use the module, here's a sample:
关于如何使用这个模块,这里有一个例子:
public class Service
{
public Service(ILog log) { ... }
}
var cb = new ContainerBuilder();
cb.RegisterModule<LogInjectionModule>();
cb.RegisterType<Service>();
var c = cb.Build();
var service = c.Resolve<Service>();
#2
1
You only use logName
to effectively resolve by name an ILog
, so why not just inject an ILog
?
您只使用logName来有效地解析ILog,那么为什么不直接注入ILog呢?
public class Log4NetAdapter : ILogger
{
private readonly ILog _logger;
public Log4NetAdapter(ILog logger)
{
_logger = logger;
}
...
}
OK, so now I've just moved the problem a bit, but I've also made this less coupled to other classes, namely the LogManager
.
好了,现在我把问题移了一点,但我也让它与其他类的耦合更小,也就是LogManager。
So if I was using unity, I would then do this to ensure I get the right logger:
如果我使用unity,我就会这样做,以确保我找到正确的日志记录器:
var childContainer = container.CreateChildContainer();
childContainer.RegisterInstance<ILog>(LogManager.GetLogger(logName));
var adaptor = childContainer.Resolve<Log4NetAdapter>();
The child container prevents any other code getting access to that ILog
. You can do this as high up as you like, I don't know any more about your code.
子容器防止任何其他代码访问该ILog。你可以在你喜欢的高度做这个,我不知道你的代码。