C# 使用Ninject填充Log4Net依赖关系
我在应用程序中使用Ninject作为DI容器。为了与日志库松散耦合,我使用了如下界面:C# 使用Ninject填充Log4Net依赖关系,c#,log4net,ninject,C#,Log4net,Ninject,我在应用程序中使用Ninject作为DI容器。为了与日志库松散耦合,我使用了如下界面: public interface ILogger { void Debug(string message); void Debug(string message, Exception exception); void Debug(Exception exception); void Info(string message);
public interface ILogger
{
void Debug(string message);
void Debug(string message, Exception exception);
void Debug(Exception exception);
void Info(string message);
...you get the idea
我的实现如下所示
public class Log4NetLogger : ILogger
{
private ILog _log;
public Log4NetLogger(ILog log)
{
_log = log;
}
public void Debug(string message)
{
_log.Debug(message);
}
... etc etc
具有日志依赖项的示例类
public partial class HomeController
{
private ILogger _logger;
public HomeController(ILogger logger)
{
_logger = logger;
}
当实例化Log4Net实例时,您应该为其提供将要记录的类的名称。这对Ninject来说是一个挑战
目标是在实例化HomeController时,Ninject应使用“名称”为“HomeController”实例化ILog
这是我的配置
public class LoggingModule : NinjectModule
{
public override void Load()
{
Bind<ILog>().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x)))
.InSingletonScope();
Bind<ILogger>().To<Log4NetLogger>()
.InSingletonScope();
}
private string GetParentTypeName(IContext context)
{
return context.Request.ParentContext.Request.ParentContext.Request.Service.FullName;
}
}
公共类日志模块:ninject模块
{
公共覆盖无效负载()
{
Bind().ToMethod(x=>LogManager.GetLogger(GetParentTypeName(x)))
.InSingletonScope();
将()绑定到()上
.InSingletonScope();
}
私有字符串GetParentTypeName(IContext上下文)
{
返回context.Request.ParentContext.Request.ParentContext.Request.Service.FullName;
}
}
然而,传递给ILog的“名称”并不是我所期望的。我也找不出任何押韵或理由,有时是对的,但大多数时候不是。我看到的名称是其他类的名称,这些类也依赖于ILogger。ILog和ILogger的范围需要是暂时的,否则它只会重用它创建的第一个记录器。感谢@Meryln Morgan Graham帮我找到了它。
Bind().ToMethod(x=>LogManager.GetLogger(GetParentTypeName(x)))
Bind<ILog>().ToMethod(x => LogManager.GetLogger(GetParentTypeName(x)))
.InSingletonScope();
.InSingletonScope();
您当前绑定在Singleton范围内,因此只创建了一个记录器,它将使用第一个创建的记录器的名称。而是使用
InTransientScope()
Ninject.Extension.Logging扩展已经提供了您自己实现的所有功能。包括对log4net、NLog和NLog2的支持
您还希望使用以下作为记录器类型:
context.Request.ParentRequest.ParentRequest.Target.Member.DeclaringType
否则,您将获得服务类型的记录器,而不是实现类型的记录器。我个人没有兴趣抽象出我的记录器,因此我的实现模块直接引用
log4net.dll
,我的构造函数根据需要请求一个ILog
为了实现这一点,在我的静态void RegisterServices(IKernel内核)
的末尾,使用Ninject v3的单行注册如下所示:
启动应用程序时会产生以下结果:
<datetime> INFO MeApp.App_Start.NinjectWebCommon - Start
<datetime> DEBUG MeApp.App_Start.NinjectWebCommon+LogCanary - Debug Logging Canary message
<datetime> INFO MeApp.App_Start.NinjectWebCommon+LogCanary - Logging Canary message
INFO MeApp.App_Start.NinjectWebCommon-Start
DEBUG MeApp.App_Start.NinjectWebCommon+LogCanary-调试日志记录Canary消息
INFO MeApp.App_Start.NinjectWebCommon+LogCanary-记录Canary消息
也许我的答案晚了,但我使用的是以下格式:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ILog>()
.ToMethod(c => LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType))
.InSingletonScope();
}
私有静态无效注册服务(IKernel内核)
{
kernel.Bind()
.ToMethod(c=>LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType))
.InSingletonScope();
}
我非常喜欢将Log4Net包装到我自己的接口中的想法。我不想依赖于Ninject的实现,因为对我来说,这只意味着我在整个应用程序中都依赖Ninject,我认为这与依赖注入的用途完全相反。与第三方服务分离。因此,我采用了最初的海报代码,但我更改了以下代码以使其正常工作
private string GetParentTypeName(IContext context)
{
var res = context.Request.ParentRequest.ParentRequest.Service.FullName;
return res.ToString();
}
我必须调用ParentRequest.ParentRequest,这样当我打印布局%logger时,它将打印调用Log4Net log方法的类,而不是调用log方法的方法的Log4Net类。对于仍在寻找正确答案的所有人,正确的实现是:
public class LoggingModule : NinjectModule
{
public override void Load()
{
Bind<ILog>().ToMethod(x => LogManager.GetLogger(x.Request.Target.Member.DeclaringType));
Bind<ILogger>().To<Log4NetLogger>()
.InSingletonScope();
}
}
当它不对的时候,它是什么样子的?你能模拟一个类关系,这样你就可以给一些假名字,至少解释一下你看到的名字之间的关系吗?或者只是复制一些代码?我看到的名称是依赖于
ILogger
的其他类的名称。例如,在我的HomeController
中,它得到一个名为SomethingRepository
的记录器。实际上,我想是你让我解决了这个问题。ILog/ILOGER的作用域不正确。它是为一个实例更新它们,然后重用它。他们应该是范围瞬态。这看起来很完美,我一定会检查!唯一的缺点是,即使我只想要ILogger接口(例如:模拟单元测试的依赖项),我仍然必须引用该程序集。我想这不会伤害任何东西。绝对值得这么做,因为我可以用一个简单的nuget add处理所有这些工作。为什么它有两个级别的。ParentRequest
?我是唯一一个依赖IoC框架的人吗?我喜欢原始海报的想法。正如Remo所提到的,Ninject已经有一些组件可以完全满足您的需要,从您所展示的“GetParentTypeName”实现来看,Ninject扩展可能更健壮。@user1068352谢谢;该死,现在你提醒我,出于某种原因,我喜欢你的方法context.Request.Target对我来说是空的。希望我能得到这种方法working@JoshBerke猜测这是因为您正在执行一个测试请求(而不是像我的示例代码中那样间接地解析日志依赖关系)。您应该能够通过查看debugger@RubenBartelink这实际上是将ninject拉入一些没有根对象的遗留代码的副作用。我们使用的是服务定位器模式,所以目标就是请求它的人。一旦我将ninject配置为创建通用的asp.net处理程序,我们都很好。这正是我今天所使用的(这个问题现在已经有4年了),同样的原因是
private string GetParentTypeName(IContext context)
{
var res = context.Request.ParentRequest.ParentRequest.Service.FullName;
return res.ToString();
}
public class LoggingModule : NinjectModule
{
public override void Load()
{
Bind<ILog>().ToMethod(x => LogManager.GetLogger(x.Request.Target.Member.DeclaringType));
Bind<ILogger>().To<Log4NetLogger>()
.InSingletonScope();
}
}
x.Request.Target.Member.DeclaringType