c# - 作为装饰者登录 vs. 依赖注入(inject)——如果我需要在类里面登录怎么办?

标签 c# logging dependency-injection inversion-of-control cross-cutting-concerns

(我最初在 this comment 中提出这个问题,但 Mark Seemann 让我创建一个新问题。)

我正在启动一个新应用(.NET Core,如果这很重要),现在我正在尝试决定如何进行日志记录。

普遍的共识似乎是日志记录是一个横切关注点,因此不应将记录器直接注入(inject)到应该记录日志的类中。

通常,有一个例子像下面的类如何去做:

public class BadExample : IExample
{
    private readonly ILogger logger;

    public BadExample(ILogger logger)
    {
        this.logger = logger;
    }

    public void DoStuff()
    {
        try
        {
            // do the important stuff here
        }
        catch (Exception e)
        {
            this.logger.Error(e.ToString());
        }
    }
}

相反,具有业务逻辑的类不应该知道记录器(SRP)并且应该有一个单独的类来执行日志记录:

public class BetterExample : IExample
{
    public void DoStuff()
    {
        // do the important stuff here
    }
}

public class LoggingBetterExample : IExample
{
    private readonly IExample betterExample;
    private readonly ILogger logger;

    public LoggingBetterExample(IExample betterExample, ILogger logger)
    {
        this.betterExample = betterExample;
        this.logger = logger;
    }

    public void DoStuff()
    {
        try
        {
            this.betterExample.DoStuff();
        }
        catch (Exception e)
        {
            this.logger.Error(e.ToString());
        }
    }
}

每当需要 IExample 时,DI 容器都会返回一个 LoggingBetterExample 的实例,它使用 BetterExample(其中包含实际的业务逻辑)在幕后。

这种方法的一些来源:

Mark Seemann 的博文:

博文和 SO 回答 Steven :


我的问题:

显然,LoggingBetterExample 方法仅在日志记录可以在实际类之外完成时才有效。
(如上例所示:从外部捕获 BetterExample 抛出的任何异常)

我的问题是我想在实际类(class)中记录其他内容。
马克·西曼 suspected here如果有人需要这样做,可能是所讨论的方法做得太多了。

正如我之前所说,我正处于新应用程序的规划阶段,所以我没有太多代码可以展示,但我现在考虑的用例是这样的:

我的应用程序将有一个包含一些可选值的配置文件。
用户可能决定省略可选值,但这是一个重要的决定。
所以我想在缺少某些可选值时记录警告,以防万一它是错误发生的。
(尽管省略这些值完全没问题,所以我不能抛出异常并停止)

这意味着我将有一个类读取配置值并需要做这样的事情(伪代码):

var config = ReadConfigValues("path/to/config.file");

if (config.OptionalValue == null)
{
    logger.Warn("Optional value not set!");
}

无论 ReadConfigValues 属于此类还是其他类,我认为此类都不会违反 SRP。

当我无法使用装饰器在实际类之外登录时,是否有比注入(inject)记录器更好的解决方案?

我知道我可以在内部类中读取配置文件,但在装饰器中检查值(并记录警告)。但是 IMO 检查值是业务逻辑而不是基础设施,所以对我来说它属于读取配置文件的同一个类。

最佳答案

checking the value is business logic and not intfastructure, so to me it belongs in the same class where the config file is read.

显然,我不太了解您的领域,无法质疑该断言的真实性,但日志记录 是领域模型的一部分对我来说听起来很奇怪。不管怎样,为了争论,让我们假设是这种情况。

不过,应该是这样的,读取配置文件是域逻辑。从文件中读取和操作数据很容易成为域逻辑,而读取文件则是 I/O。

most common approach to Inversion of Control in application architecture is to employ the Ports & Adapters architecture .这种架构的全部意义在于将域模型与 I/O 和其他不确定性源分离。海报示例旨在展示如何将域模型与其数据库访问分离,但文件访问也完全属于该类别。

在这种特殊情况下,这应该意味着您无论如何都需要一些 IConfigurationReader 接口(interface)。这意味着您可以应用装饰器:

public class ValidatingConfigurationReader : IConfigurationReader
{
    private readonly IConfigurationReader reader;
    private readonly ILogger logger;

    public ValidatingConfigurationReader(IConfigurationReader reader, ILogger logger)
    {
        this.reader = reader;
        this.logger = logger;
    }

    public MyConfiguration ReadConfigValues(string filePath)
    {
        var config = this.reader.ReadConfigValues(filePath);

        if (config.OptionalValue == null)
        {
            this.logger.Warn("Optional value not set!");
        }

        return config;
    }
}

这个 ValidatingConfigurationReader 类可以在域模型中实现,即使底层的文件读取 IConfigurationReader 实现属于某个 I/O 层。

关于c# - 作为装饰者登录 vs. 依赖注入(inject)——如果我需要在类里面登录怎么办?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34635740/

相关文章:

c# - 与泛型类中的方法不同的返回类型

logging - 记录到消息队列是个好主意吗?

gwt - 如何在 GWT 日志记录中配置特定于类的日志级别?

java - 如何将 spring 注入(inject)的依赖项绑定(bind)到 wicket 页面?

c# - 我可以使用 Ninject 将 IKernel 注入(inject)类吗

javascript - AngularJS 服务注入(inject)时未定义

C# - XML - 压缩

c# - 用于挂载azure云驱动器的控制台应用程序

c# - Sitecore 从媒体库加载 XML 文件?

go - elogrus 未找到事件连接