【问题标题】:Circular referencing with three .NET class libraries - possible or stupid?使用三个 .NET 类库进行循环引用 - 可能还是愚蠢?
【发布时间】:2014-01-19 21:57:26
【问题描述】:

我已在此处阅读有关循环引用的其他问题,但我找不到我的问题的答案。

我有三个类库:Authentication、EmailService 和 ExceptionService。

身份验证控制用户登录到各种应用程序,EmailService 发送电子邮件,ExceptionService 将错误/异常记录到数据库。

目前,Authentication 引用了 EmailService 和 ExceptionService,以使用它们的功能,这很有效。 ExceptionService 引用 EmailService 来发送报告电子邮件。一切都很好。

我想知道的是以下是否可行/可取/愚蠢,以及是否有更好的方法:

  • 我希望 EmailService 能够使用 ExceptionService 的功能,以便报告 EmailService 中的任何错误。从理论上讲,这可能意味着 ExcpetionService 然后会回调到 EmailService 以发送报告电子邮件,这可能会触发相同的错误,因此我必须编写一个仅由 EmailService 使用的方法,它不发送电子邮件,只记录了它。

  • ExceptionService 仍应引用 EmailService。

  • 身份验证类库还应该使用其他两个服务。

这一切听起来非常复杂和循环,这就是为什么我认为这可能不是一件好事。但是我应该怎么做呢?

我尝试在 EmailService 中引用 ExceptionService,但是当我创建私有 ExceptionService 对象并尝试使用它时,它不会编译。

我想我真正想要的是让我的任何应用程序引用 EmailService 和 ExceptionService,但它们也可以相互引用。

到目前为止,我发现解决此问题的唯一方法是忘记在 EmailService 中报告异常。

非常感谢您的帮助:)

【问题讨论】:

  • 为什么不让像 Log4net 这样的第三方实用程序来处理您的异常日志记录?您可以将 log4net 配置为将电子邮件作为日志输出发送。
  • 使用 NLog 记录它的最佳选项,它还可以发送电子邮件
  • 如果服务直接依赖其他服务来实现非核心功能,则设计存在缺陷。最好使用服务总线在服务之间进行通信。详细解释超出了评论的范围,但请查看 MassTransit 或 NServiceBus。基本上每个服务都向服务总线发送消息,并且可以从该总线读取消息。例如,错误服务可以向总线发送错误消息,然后电子邮件服务将接收该错误消息以创建有关它的电子邮件消息并发送它。这样服务之间就没有直接的依赖关系了。

标签: c# asp.net-mvc reference class-library


【解决方案1】:

您遇到问题的原因是因为您将类紧密耦合,并且当您尝试创建循环耦合时编译器会非常明显地感到不安。您可以通过为您的服务创建接口并将一个服务的接口实例提供给另一个服务的实现来解决这个问题,反之亦然。

一个更好的解决方案是停止重新发明轮子并使用现有的日志框架。 NLogLog4Net 都可以满足您的日志记录和电子邮件发送需求。

【讨论】:

  • 我也向她提出了同样的建议..但是 NLog 更好,请建议更多
  • 所以最好的办法是让我的 ExcpetionService 使用 NLog(或类似的),这样如果我们将来要更改为不同的记录器,我就不必更改所有应用程序,并使用用于发送电子邮件报告的电子邮件。然后我的 EmailService 将仅用于其他电子邮件。这样,只有 EmailService 需要引用 ExceptionService (NLog),反之亦然。
  • @tekiegirl 在不知道全部细节的情况下,我不能肯定地说,但这至少听起来是不错的第一步。
  • @DavidArno 我已经开始研究 NLog,因为我认为这是正确的方法。我正在创建一个类库作为 NLog 的包装器,它不需要引用 EmailService,但 EmailService 可以使用它来记录自己的错误。非常感谢。
【解决方案2】:

合并程序集。将项目拆分为尽可能多的程序集是一个常见的错误。这增加了管理负担,并在循环引用的情况下造成麻烦。紧耦合是要避免的,但不能完全避免。接受它。

您的情况是循环引用的有效案例。这两个类只是出于逻辑原因需要彼此。

您可以通过接口解决循环引用问题,但依赖关系在运行时仍然存在。接口不会提高代码质量,它们只是关闭编译器警告。

不要使用程序集管理依赖项。使用命名空间和文件夹。程序集是部署单元,而不是依赖管理工具。

【讨论】:

  • “这些接口并没有提高代码质量,它们只是关闭了编译器警告”。接口减少耦合,从而提高代码质量。
  • @DavidArno 减少耦合意味着能够修改代码的一部分而无需另一部分。具有单个实现者的接口对此无济于事。如果你不同意,它有什么帮助?此外,如果接口也能起到这样的作用,为什么不进行大规模重构并将所有内容都转换为接口呢?这显然没有用。
  • 如果你因为“只是接受”而设计了一个紧密耦合的系统,那么代码很可能在很多方面质量都很差。因此,仅仅重构以添加接口不会神奇地使它成为好的代码。使用松散耦合设计的代码很可能在其他方面设计得很好:使用更小的类和方法、良好的单元测试、良好的抽象和数据隐藏等。与单个实现者的接口极大地帮助了单元测试,例如,因为可以很容易地模拟系统的某些部分以简化测试。
  • @DavidArno 您刚才所说的一切都是正确的。 OP 的情况是接口无法解决任何运行时耦合的情况。他们通过关闭循环检查来“解决”编译时耦合。两个类是使用 call 指令还是 vcall 指令相互调用,它们对彼此的行为和期望没有任何影响。耦合保持不变,只是现在隐藏了。邮件和日志相互使用是他要求的基础。顺便说一句,在我的一个项目中,我的情况完全相同。
【解决方案3】:

问题在于您使用的是具体类,而不是接口。所以,我的建议是引入两个接口,即:IExceptionServiceIEmailService,并将它们放在单独的项目中,例如:Services。包含这两个服务实现的项目将引用这个新项目。感谢 ExceptionService 可以使用 IEmailService 并且 EmailService 可以使用 IExceptionService。同时ExceptionServiceEmailService可以定义在不同的程序集中。

什么是重要的 ExceptionServiceEmailService 不应该知道这些接口背后是什么。具体的实现应该以某种方式注入到它们中。为此,您可以使用依赖注入容器。如果不想使用其他新库,也可以实现简单的服务定位器。

【讨论】:

    【解决方案4】:

    (我现在忽略 AuthenticationService,因为它只会混淆问题 - 你所拥有的是两个服务之间的简单循环依赖 - 异常和电子邮件)。

    解决这些循环依赖问题的最佳方法是使用接口层和存储库。

    假设您有两个类,EmailServiceExceptionService。它们不能相互引用对方的 DLL,所以你要做的是创建第三个程序集 Interfaces,并为它们创建两个接口 IEmailServiceIExceptionService。现在您的两个类都只能引用该共享接口程序集。

    使用某种Inversion of Control 机制,您的EmailService 获得对IExceptionService 的引用,反之亦然,因此循环被打破。

    一种简单的 IoC 机制是服务定位器模式。创建这个(简化的)对象:

    public class ServiceLocator
    {
         public static IEmailService EmailService {get;set;}
         public static IExceptionService ExceptionService {get;set;}
    }
    

    现在,您的 EmailService 在启动时可以向 ServiceLocator 注册自身,并允许其他类获取对它的引用,而无需依赖其程序集。

    当然,大多数 IoC 解决方案的功能远不止这些,但这是基本思想 - 通过将共享接口提取到共享程序集中并仅引用它来防止循环依赖。

    【讨论】:

    • “依赖注入机制是服务定位器模式”。不。 DI 意味着 注入 依赖项,而不是通过诸如服务定位器模式之类的“全局变量”公开。
    • 感谢您的帮助。如果我的服务是一个类库中的类,我可以看到这将如何工作,但它们是分开的。在这种情况下,接口将是一个单独的类库,需要同时引用电子邮件和异常库,因此会创建更多圈子。还是我错了?
    • @tekiegirl 接口将位于一个单独的类库中,但它不需要对电子邮件和异常库的任何引用 - 只有它们会引用它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 2023-03-17
    • 1970-01-01
    • 2013-08-14
    • 1970-01-01
    • 2013-02-04
    • 2020-05-13
    相关资源
    最近更新 更多