如何解决特定的循环依赖:DAL和日志记录

时间:2022-09-04 11:04:00

Some "high risk" data operations need to be logged. In this case, the "high risk" operations are defined as writes to our ERP system. It happens that we are logging those events to our SQL Server database.

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

Pseudo-code:

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);
  }
}

What is the best practice to eliminate this tight coupling?

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

7 个解决方案

#1


Hmm, IMHO logging is an infrastructure concern. You can use it in your DAL, but your logger should not use your DAL.

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

If you remove the dependency your logger has on your DAL, then you should be able to use your logger in other projects as well.

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

#2


You can create a custom TraceListener (System.Diagnostics) to insert into your company's SQL Server database. Then use Trace / TraceSource (System.Diagnostics) for your logging in your application's code. You can then use standard .NET configuration to use your custom TraceListener at design time. That way, if you ever need to change your event logging, you just have to change the TraceListener. Plus you could reuse the TraceListener in other applications.

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

You could also use the Logging Application Block of the Enterprise Library and many other 3rd party logging solutions.

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

#3


I've decoupled this situation before in two ways: Status changes and Log events.

我之前以两种方式解耦了这种情况:状态更改和日志事件。

First way is to create an IHaveStatus interface like such:

第一种方法是创建一个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; }
}

As your object does stuff, you set your Status property. You can configure your property setter to fire the StatusChanged event when you set it. Whoever uses your objects can listen to your status changed event and log everything that happens.

当您的对象执行操作时,您可以设置Status属性。您可以将属性设置器配置为在设置StatusChanged事件时触发它。使用您的对象的人可以收听您的状态更改事件并记录发生的所有事情。

Another version of this is to add a Log events to your objects.

另一个版本是向对象添加Log事件。

public event EventHandler<LogEventArgs> Log;

The principal is pretty much the same, only your objects would be less chatty than a Status-driven log (you only fire the event when you specifically want to log something).

校长几乎是相同的,只有你的对象比状态驱动的日志更少繁琐(你只在你特别想记录某事时才触发事件)。

The idea is that its the responsibility of callers outside of your DAL to hook these events up to the proper log (hopefully set by using DI). Your DAL is oblivious to who or what is consuming these events, which separates these concerns well.

这个想法是,你的DAL之外的调用者有责任将这些事件挂钩到正确的日志(希望通过使用DI设置)。你的DAL不知道消费这些事件的人或者是什么,这些事件很好地区分了这些问题。

#4


The common thread among the responses seems to be the suggestion to implement something like the observer pattern.

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

(Let me know if there's a better summary statement. I'll update accordingly.)

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

#5


Maybe you could have your logging component, moved to a seperate assembly (I'm assuming this is C# code), raise an event, that the caller could register for before calling Logging.Write(). After Logging.Write() returns, unregister from the event. In the event handler you could execute your MyCompany.DAL.ExecuteSQL("Insert INTO EventLog VALUES " + msg) call.

也许你可以拥有你的日志记录组件,移动到一个单独的程序集(我假设这是C#代码),引发一个事件,调用者可以在调用Logging.Write()之前注册。 Logging.Write()返回后,从事件中取消注册。在事件处理程序中,您可以执行MyCompany.DAL.ExecuteSQL(“插入INTO EventLog VALUES”+ msg)调用。

#6


Actually, if by high-risk data you mean criticial/important to know it is how it is supposed to be data, and also if you need to have the logs in the database (some kind of meta-data), then the solution should be completely different as what others have suggested.

实际上,如果高风险数据你的意思是批评/重要的是知道它是如何被认为是数据,并且如果你需要在数据库中有日志(某种元数据),那么解决方案应该是与其他人的建议完全不同。

The situation I described would mean that the result of a database transaction should have both the logging data and the data itself in the database at any given time. One should not be done independently from the other.

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

As a result, this kind of "logging" should be done as part a single database transaction and the DAL should make sure that both items are inserted correctly at the same time in the same transaction.

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

Failure to do so could have the following side effect:

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

  • Having only one of the data or log inserted in the db.
  • 只有一个数据或日志插入数据库中。

  • Having only one of the data or log inserted in the db before the other, meaning that a system relying on the fact that both must be present at any given time might randomly fail in specific circumstances.
  • 只有一个数据或日志在另一个之前插入数据库中,这意味着系统依赖于在任何给定时间必须存在的事实可能在特定情况下随机失败。

#7


To avoid the circular dependency DAL -> Logger -> DAL, I'd propose you have two layers of DAL: "simple DAL", and "logging DAL".

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

The "simple DAL" ist just a DAL. The "logging DAL" builds on the "simple DAL"; it manipulates the DB using the simple DAL, and also logs stuff, again using the simple DAL. So you have:

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

[application logic] --uses--> [logging DAL] --uses--> [simple DAL] --uses--> DB

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

If you want to do stuff to the DB that does not need to be logged ("low risk" operations ;-)) you could use "simple DAL" directly and bypass the logging DAL.

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

#1


Hmm, IMHO logging is an infrastructure concern. You can use it in your DAL, but your logger should not use your DAL.

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

If you remove the dependency your logger has on your DAL, then you should be able to use your logger in other projects as well.

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

#2


You can create a custom TraceListener (System.Diagnostics) to insert into your company's SQL Server database. Then use Trace / TraceSource (System.Diagnostics) for your logging in your application's code. You can then use standard .NET configuration to use your custom TraceListener at design time. That way, if you ever need to change your event logging, you just have to change the TraceListener. Plus you could reuse the TraceListener in other applications.

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

You could also use the Logging Application Block of the Enterprise Library and many other 3rd party logging solutions.

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

#3


I've decoupled this situation before in two ways: Status changes and Log events.

我之前以两种方式解耦了这种情况:状态更改和日志事件。

First way is to create an IHaveStatus interface like such:

第一种方法是创建一个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; }
}

As your object does stuff, you set your Status property. You can configure your property setter to fire the StatusChanged event when you set it. Whoever uses your objects can listen to your status changed event and log everything that happens.

当您的对象执行操作时,您可以设置Status属性。您可以将属性设置器配置为在设置StatusChanged事件时触发它。使用您的对象的人可以收听您的状态更改事件并记录发生的所有事情。

Another version of this is to add a Log events to your objects.

另一个版本是向对象添加Log事件。

public event EventHandler<LogEventArgs> Log;

The principal is pretty much the same, only your objects would be less chatty than a Status-driven log (you only fire the event when you specifically want to log something).

校长几乎是相同的,只有你的对象比状态驱动的日志更少繁琐(你只在你特别想记录某事时才触发事件)。

The idea is that its the responsibility of callers outside of your DAL to hook these events up to the proper log (hopefully set by using DI). Your DAL is oblivious to who or what is consuming these events, which separates these concerns well.

这个想法是,你的DAL之外的调用者有责任将这些事件挂钩到正确的日志(希望通过使用DI设置)。你的DAL不知道消费这些事件的人或者是什么,这些事件很好地区分了这些问题。

#4


The common thread among the responses seems to be the suggestion to implement something like the observer pattern.

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

(Let me know if there's a better summary statement. I'll update accordingly.)

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

#5


Maybe you could have your logging component, moved to a seperate assembly (I'm assuming this is C# code), raise an event, that the caller could register for before calling Logging.Write(). After Logging.Write() returns, unregister from the event. In the event handler you could execute your MyCompany.DAL.ExecuteSQL("Insert INTO EventLog VALUES " + msg) call.

也许你可以拥有你的日志记录组件,移动到一个单独的程序集(我假设这是C#代码),引发一个事件,调用者可以在调用Logging.Write()之前注册。 Logging.Write()返回后,从事件中取消注册。在事件处理程序中,您可以执行MyCompany.DAL.ExecuteSQL(“插入INTO EventLog VALUES”+ msg)调用。

#6


Actually, if by high-risk data you mean criticial/important to know it is how it is supposed to be data, and also if you need to have the logs in the database (some kind of meta-data), then the solution should be completely different as what others have suggested.

实际上,如果高风险数据你的意思是批评/重要的是知道它是如何被认为是数据,并且如果你需要在数据库中有日志(某种元数据),那么解决方案应该是与其他人的建议完全不同。

The situation I described would mean that the result of a database transaction should have both the logging data and the data itself in the database at any given time. One should not be done independently from the other.

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

As a result, this kind of "logging" should be done as part a single database transaction and the DAL should make sure that both items are inserted correctly at the same time in the same transaction.

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

Failure to do so could have the following side effect:

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

  • Having only one of the data or log inserted in the db.
  • 只有一个数据或日志插入数据库中。

  • Having only one of the data or log inserted in the db before the other, meaning that a system relying on the fact that both must be present at any given time might randomly fail in specific circumstances.
  • 只有一个数据或日志在另一个之前插入数据库中,这意味着系统依赖于在任何给定时间必须存在的事实可能在特定情况下随机失败。

#7


To avoid the circular dependency DAL -> Logger -> DAL, I'd propose you have two layers of DAL: "simple DAL", and "logging DAL".

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

The "simple DAL" ist just a DAL. The "logging DAL" builds on the "simple DAL"; it manipulates the DB using the simple DAL, and also logs stuff, again using the simple DAL. So you have:

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

[application logic] --uses--> [logging DAL] --uses--> [simple DAL] --uses--> DB

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

If you want to do stuff to the DB that does not need to be logged ("low risk" operations ;-)) you could use "simple DAL" directly and bypass the logging DAL.

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