【问题标题】:How to resolve specific circular dependency: DAL & Logging如何解决特定的循环依赖:DAL & Logging
【发布时间】:2009-01-20 19:12:18
【问题描述】:

需要记录一些“高风险”数据操作。在这种情况下,“高风险”操作被定义为写入我们的 ERP 系统。碰巧我们正在将这些事件记录到我们的 SQL Server 数据库中。

伪代码:

Public Class MyCompany.DAL.ERP {
  Public void WriteToERP(string msg) {
    // ... do the write
    MyCompany.Logging.Write("Wrote msg: " + msg);
  }
}

Public Class MyCompany.Logging {
  Public void Write(string msg) {
    MyCompany.DAL.ExecuteSQL("Insert INTO EventLog VALUES " + msg);
  }
}

消除这种紧密耦合的最佳做法是什么?

【问题讨论】:

    标签: logging data-access-layer decoupling


    【解决方案1】:

    嗯,恕我直言,日志记录是基础架构问题。 您可以在 DAL 中使用它,但您的记录器不应使用您的 DAL。

    如果您删除了您的记录器对 DAL 的依赖,那么您应该也可以在其他项目中使用您的记录器。

    【讨论】:

      【解决方案2】:

      您可以创建自定义 TraceListener (System.Diagnostics) 以插入公司的 SQL Server 数据库。然后使用 Trace / TraceSource (System.Diagnostics) 登录应用程序代码。然后,您可以使用标准 .NET 配置在设计时使用您的自定义 TraceListener。这样,如果您需要更改事件日志记录,您只需更改 TraceListener。另外,您可以在其他应用程序中重用 TraceListener。

      您还可以使用企业库的日志记录应用程序块和许多其他第三方日志记录解决方案。

      【讨论】:

      • +1 - 由于 EntLibs 很大程度上受配置驱动,因此您可以拥有一个一致的日志记录子系统,该子系统与您的应用程序完全分离(在 DAL 等领域);您可以将内容记录到 n 应用程序数据库或其他地方 - 它们都是松散耦合的。
      【解决方案3】:

      我之前通过两种方式解耦了这种情况:状态变化和记录事件。

      第一种方法是创建一个 IHaveStatus 接口,如下所示:

      /// <summary>
      /// Interface for objects that have a status message 
      /// that describes their current state.
      /// </summary>
      public interface IHaveStatus
      {
          /// <summary>
          /// Occurs when the <seealso cref="Status"/> property has changed.
          /// </summary>
          event EventHandler<StatusChangedEventArgs> StatusChanged;
          /// <summary>
          /// The current status of the object.  When this changes, 
          /// <seealso cref="StatusChanged"/> fires.
          /// </summary>
          string Status { get; }
      }
      

      当你的对象做事时,你设置你的 Status 属性。您可以将属性设置器配置为在设置时触发 StatusChanged 事件。使用您的对象的任何人都可以监听您的状态更改事件并记录发生的所有事情。

      另一个版本是向您的对象添加日志事件。

      public event EventHandler<LogEventArgs> Log;
      

      主体几乎相同,只是您的对象会比状态驱动的日志更健谈(只有在您特别想记录某些内容时才会触发该事件)。

      这个想法是 DAL 之外的调用者有责任将这些事件连接到正确的日志(希望通过使用 DI 设置)。您的 DAL 不知道是谁或什么在消耗这些事件,从而很好地区分了这些问题。

      【讨论】:

        【解决方案4】:

        响应中的共同点似乎是建议实现类似于观察者模式的东西。

        (如果有更好的总结性声明,请告诉我。我会相应更新。)

        【讨论】:

          【解决方案5】:

          也许您可以将日志记录组件移至单独的程序集(我假设这是 C# 代码),引发一个事件,调用者可以在调用 Logging.Write() 之前注册该事件。在 Logging.Write() 返回后,从事件中注销。在事件处理程序中,您可以执行 MyCompany.DAL.ExecuteSQL("Insert INTO EventLog VALUES " + msg) 调用。

          【讨论】:

            【解决方案6】:

            实际上,如果您所说的高风险数据是指关键/重要的是要知道它应该如何成为数据,并且如果您需要在数据库中保存日志(某种元数据),那么解决方案应该与其他人建议的完全不同。

            我描述的情况意味着数据库事务的结果应该在任何给定时间都包含日志数据和数据库中的数据本身。一个不应该独立于另一个完成。

            因此,这种“日志记录”应作为单个数据库事务的一部分完成,DAL 应确保在同一事务中同时正确插入两个项目。

            不这样做可能会产生以下副作用:

            • 只有一个数据或日志插入到数据库中。
            • 只有一个数据或日志在另一个之前插入到数据库中,这意味着依赖于在任何给定时间都必须存在的事实的系统可能会在特定情况下随机失败。

            【讨论】:

            • 这不是元数据。我们正在处理对缓慢且容易出错的系统的写入。将日志记录到快速、可靠的数据库中,作为一种回退,以识别“坏”数据库的问题并从中恢复。整个问题源于一个数据库相对不可靠的事实。
            • 好的,那么我将保留您添加的信息的答案,但不适用于您的情况。
            【解决方案7】:

            为了避免循环依赖 DAL -> Logger -> DAL,我建议你有两层 DAL:“simple DAL”和“logging DAL”。

            “简单 DAL”只是一个 DAL。 “日志记录 DAL”建立在“简单 DAL”之上;它使用简单的 DAL 操作数据库,并再次使用简单的 DAL 记录内容。所以你有:

            [应用逻辑] --uses--> [logging DAL] --uses--> [simple DAL] --uses--> DB

            如果您想对不需要记录的数据库执行操作(“低风险”操作 ;-)),您可以直接使用“简单 DAL”并绕过记录 DAL。

            【讨论】:

              猜你喜欢
              • 2014-11-13
              • 2012-03-15
              • 2016-09-21
              • 2020-11-12
              相关资源
              最近更新 更多