我有一个名为MyClass
的类,它使用名为Logger1
和Logger2
的两个不同的记录器。我使用log4net进行日志记录,并希望使用StructureMap进行DI。
public class MyClass
{
private static readonly ILog Logger1 = LogManager.GetLogger("Logger1"); // Loggers are configured in a config file
private static readonly ILog Logger2 = LogManager.GetLogger("Logger2");
public void DoSomething()
{
...
Logger1.Info("did something");
...
Logger2.Info("need to log this elsewhere");
}
}
引入DI,使用StructureMap(使用v3.0.3),我将使记录器实例成员,并将它们注入构造函数,如下所示:公共类MyClass{private readonly ilogloger1;private readonly ILog Logger2;
myClass(ILog logger1, ILog logger2)
{
this.Logger1 = logger1;
this.Logger2 = logger2;
}
public void DoSomething()
{
...
Logger1.Info("did something");
...
Logger2.Info("need to log this elsewhere");
}
}
问题是,我不能让StructureMap为我正确地连接这个。我尝试像这样连接记录器:
For<ILog>.Use(()=> LogManager.GetLogger("Logger1")).Named("Logger1");
For<ILog>.Use(()=> LogManager.GetLogger("Logger2")).Named("Logger2");
Doing this让我空(未配置的)记录器)。用Add()
替换Use()
给了我一个异常,因为没有为ILog注册默认实例。
有人知道我该怎么做吗?
如果日志记录器执行不同的角色,那么我将创建两个继承ILog的接口来反映这一点。这样配置StructureMap来处理它们就没有问题了。
我最终做了以下事情:我按照Rob的建议创建了两个接口:ILogger1
和ILogger2
。由于两者具有相同的API,因为我需要相同的类型的功能,它们都继承自相同的接口-尽管不是 log4net.ILog
,正如Steven的建议:
interface IMyLog
{
void Info(object message);
void Info(string format, params object[] args);
}
interface ILogger1 : IMyLog { }
interface ILogger2 : IMyLog { }
此外,由于此API的实现与我的需求相同,因此我有一个具体的类MyLogger
,实现ILogger1
和ILogger2
。如果我需要不同的实现,那么使用显式接口实现或单独的类对我来说很容易。只有My Logger
依赖于log4net,因为它在实现中使用它:
enum LoggerType { Logger1, Logger2 }
internal class MyLogger : ILogger1, ILogger2
{
private readonly ILog _log;
public MyLogger(LoggerType loggerName)
{
switch (loggerName)
{
case LoggerType.Logger1:
_log = LogManager.GetLogger("first-log");
break;
case LoggerType.Logger2:
_log = LogManager.GetLogger("second-log");
break;
default:
throw new ArgumentException("Invalid logger name", "loggerName");
}
}
public void Info(object message)
{
_log.Info(message);
}
public void Info(string format, params object[] args)
{
_log.InfoFormat(format, args);
}
}
为了将它注册到StructureMap,我在注册表中使用了以下代码:
For<ILogger1>().Use<MyLogger>().Ctor<LoggerType>("loggerName").Is(LoggerType.Logger1).Singleton(); // I want only one logger per type
For<ILogger2>().Use<MyLogger>().Ctor<LoggerType>("loggerName").Is(LoggerType.Logger2).Singleton();
一切都很好。所以,感谢史蒂文和罗布的建议。我真的学到了一些东西。我希望我能不止一次地为一个答案和一个回应投票。
总结一下,我:
- 为每种记录器创建了单独的界面(甚至现在听起来很直观)。
- 为记录器接口创建了一个基本接口,因为对于我的需求它们具有相同的API (YMMV)
- 创建一个实现两个接口的具体记录器适配器,因为它适合我的需求(再次YMMV)
- 由于上述原因,共享接口API的实现
- 注册每个接口来创建一个具体的记录器,并在构造函数中传递不同的类型
- 用log4net配置具体的记录器,使用工厂方法来确定使用哪个记录器
作为上述答案的替代方案,这可以在StructureMap中单独解决(我相信对于问题中使用的SM版本有效)。您可以通过在注册表中包含以下内容来准确地告诉StructureMap如何构建MyClass
。
For<MyClass>().Use(x => new MyClass(x.GetInstance<ILog>("Logger1"), x.GetInstance<ILog>("Logger2")));