Unity容器中IEnumerable<T>与Log4NetExtension结合的日志名称异常问题
我之前遇到过一模一样的问题,踩过这个坑!核心原因是Unity.log4net扩展默认是基于当前解析上下文的目标类型来创建ILog实例的:当你通过IEnumerable<IActivity>注入一批命名注册的实例时,Unity是在解析ActivityExecutor的构造函数参数时去遍历这些注册项,这时候上下文的目标类型是ActivityExecutor,所以所有IActivity实例都会拿到对应ActivityExecutor的日志实例,而不是它们自身类的。
下面给你几个可行的解决方案,从简单到进阶:
方案1:手动为每个命名注册的Activity指定对应ILog
这是最直接的方式,在注册每个IActivity实现时,显式注入对应自身类的ILog实例:
container.RegisterType<IActivity, ActivityA>(nameof(ActivityA), new InjectionConstructor(LogManager.GetLogger(typeof(ActivityA)))); container.RegisterType<IActivity, ActivityB>(nameof(ActivityB), new InjectionConstructor(LogManager.GetLogger(typeof(ActivityB))));
这样每个ActivityA/ActivityB实例在创建时,都会被注入对应自身类的ILog,日志输出时就会显示正确的类名了。
方案2:用InjectionFactory自动绑定具体类型的ILog
如果你有很多IActivity实现,手动写每个构造函数注入太繁琐,可以用工厂方法统一处理:
container.RegisterType<IActivity, ActivityA>(nameof(ActivityA), new InjectionFactory(c => new ActivityA(LogManager.GetLogger(typeof(ActivityA))))); container.RegisterType<IActivity, ActivityB>(nameof(ActivityB), new InjectionFactory(c => new ActivityB(LogManager.GetLogger(typeof(ActivityB)))));
本质和方案1一样,只是用工厂方式简化了实例创建逻辑,确保每个具体类型都拿到自己的日志实例。
方案3:扩展Unity.log4net实现自动匹配命名注册的ILog
如果你的项目中有大量命名注册的类型需要注入正确的ILog,可以自定义扩展来自动化这个过程:
首先创建一个自定义的Log4Net扩展,监听注册事件,自动为命名注册的类型创建对应名称的ILog实例:
public class PerConcreteTypeLogExtension : Log4NetExtension { protected override void Initialize() { base.Initialize(); // 监听注册事件,为命名注册的具体类型创建对应ILog Context.Registering += (sender, e) => { if (string.IsNullOrEmpty(e.Name) || e.TypeTo == null) return; // 以注册名称为键,注册对应具体类型的ILog Container.RegisterInstance<ILog>(e.Name, LogManager.GetLogger(e.TypeTo), new ContainerControlledLifetimeManager()); }; } }
然后替换原来的Log4NetExtension:
// 替换掉原来的container.AddNewExtension<Log4NetExtension>(); container.AddNewExtension<PerConcreteTypeLogExtension>();
最后注册IActivity时,通过名称解析对应的ILog:
container.RegisterType<IActivity, ActivityA>(nameof(ActivityA), new InjectionConstructor(new ResolvedParameter<ILog>(nameof(ActivityA)))); container.RegisterType<IActivity, ActivityB>(nameof(ActivityB), new InjectionConstructor(new ResolvedParameter<ILog>(nameof(ActivityB))));
这样后续新增命名注册的IActivity时,只需要按这个模式注册,扩展会自动帮你创建对应的ILog实例。
验证你的Activity构造函数
确保你的ActivityA/ActivityB是通过构造函数接收ILog的:
public class ActivityA : IActivity { private readonly ILog _logger; public ActivityA(ILog logger) { _logger = logger; } public void Execute() { _logger.Info("Executing ActivityA"); // 现在日志名称会是ActivityA } }
以上三种方案都能解决你的问题,优先推荐方案1或2,简单直接不需要修改扩展;如果有大量类型需要处理,方案3会更高效。
内容的提问来源于stack exchange,提问作者DeMaki

