【问题标题】:How can I wire StructureMap with 2 different objects (log4net loggers) of the same interface如何将 StructureMap 与同一接口的 2 个不同对象(log4net 记录器)连接起来
【发布时间】:2014-08-16 07:08:41
【问题描述】:

我有一个名为MyClass 的类,它使用两个不同的记录器,分别名为Logger1Logger2。我使用 log4net 进行日志记录,并希望将 StructureMap 用于 DI。

如果没有 StructureMap,我的课程将如下所示:

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 { 私有只读 ILog Logger1; 私有只读 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");

这样做会让我清空(未配置的)记录器)。由于没有为 ILog 注册默认实例,将 Use() 替换为 Add() 会给我一个异常。

有人知道我该怎么做吗?

【问题讨论】:

  • 您是打算将相同的信息写入这两个记录器,还是需要将不同的信息记录到两个记录器?
  • 不同记录器的不同信息。我已经知道我可以使用不同的附加程序将相同的信息转到不同的文件。 :)

标签: c# log4net structuremap structuremap3


【解决方案1】:

如果记录器执行不同的角色,那么我将创建两个从 ILog 继承的接口来反映这一点。这样你就可以毫无问题地配置 StructureMap 来处理它们。

【讨论】:

  • OP 使用 log4net 进行日志记录,我认为不可能从 ILog 继承。
  • 该接口只允许 OP 按照他们的意愿定位命名记录器实例。
  • @kpa6uk,OP 不应该让他的应用程序直接依赖于这个框架定义的接口。接口应该由客户端定义。
  • 这似乎不起作用。虽然我可以从ILog 接口继承,但当我尝试这样配置注册表时:For&lt;IExecutionLog&gt;().Use(()=&gt; LogManager.GetLogger("execution-log"));,它说 类型“log4net.ILog”不能用作泛型中的类型参数“T”类型或方法“StructureMap.Configuration.DSL.Expressions.CreatePluginFamilyExpression.Use(System.Linq.Expressions.Expression>)”。没有从“log4net.ILog”到“StructureMapAndLog4Net.IExecutionLog”的隐式引用转换
  • @AssafStone,我同意 Rob 的观点,您需要两个抽象,因为它们在您的应用程序中具有不同的功能。我不同意从 ILog 继承,因为 1. 这仍然迫使您的应用程序依赖 log4net 和 2. ILog 抽象是一大违反接口隔离原则的行为。定义您自己的两个狭窄抽象,理想情况下只使用一种方法。
【解决方案2】:

我最终做了以下事情:按照 Rob 的建议,我创建了两个接口:ILogger1ILogger2。由于两者都有相同的 API,因为我需要它们的相同 种类 功能,它们都从相同的接口继承 - 尽管 not log4net.ILog 根据 Steven 的建议:

interface IMyLog
{
    void Info(object message);
    void Info(string format, params object[] args);
}

interface ILogger1 : IMyLog { }
interface ILogger2 : IMyLog { }

另外,由于这个 API 的实现与我的需求相同,我有一个具体的类MyLogger,实现了ILogger1ILogger2。如果我需要不同的实现,那么我很容易拥有显式接口实现或单独的类。只有 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();

这一切都非常好。所以,感谢 Steven 和 Rob 的建议。我真的学到了一些东西。我希望我可以不止一次地对一个答案和一个回复进行投票。

所以,总结一下,我:

  • 为每种记录器创建了一个单独的界面(即使现在说它听起来很直观)。
  • 为记录器接口创建了一个基本接口,因为满足我的需要它们具有相同的 API (YMMV)
  • 创建了一个实现这两个接口的具体记录器适配器,因为它适合 我的需要(再次 YMMV)
  • 分享接口API的实现,原因就在上面
  • 注册每个接口以创建具有在构造函数中传递的不同类型的具体记录器
  • 使用Factory Method 使用 log4net 配置具体记录器以确定要使用的记录器

【讨论】:

    【解决方案3】:

    作为上述答案的替代方案,这可以仅在 StructureMap 中解决(我相信适用于问题中使用的 SM 版本)。您还可以通过在注册表中包含以下内容来准确地告诉 StructureMap 如何构建 MyClass

    For<MyClass>().Use(x => new MyClass(x.GetInstance<ILog>("Logger1"), x.GetInstance<ILog>("Logger2")));
    

    【讨论】:

      猜你喜欢
      • 2011-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多