有三个.NET类库的循环引用——可能的,也可能是愚蠢的



我在这里读过关于循环引用的其他问题,但我找不到问题的答案。

我有三个类库:身份验证、电子邮件服务和异常服务。

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

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

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

  • 我希望EmailService能够使用ExceptionService的功能,以便报告EmailService中的任何错误。理论上,这可能意味着ExcpetionService会回电给EmailService发送报告电子邮件,这可能会引发同样的错误,所以我必须编写一个EmailService只使用的方法,该方法不发送电子邮件,只记录电子邮件。

  • ExceptionService仍应引用EmailService。

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

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

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

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

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

非常感谢您的帮助:)

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

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

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

您的情况是循环引用的有效情况。这两个类只是出于逻辑上的原因而相互需要。

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

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

问题是您使用的是具体的类,而不是接口。因此,我的建议是引入两个接口,即:IExceptionServiceIESailService>,并将它们放在单独的项目中,例如:服务。包含这两个服务的实现的项目将引用这个新项目。由于ExceptionService可以使用IEmailServicemailServiceIE异常服务。同时,可以在不同的程序集中定义ExceptionServiceEmailService

重要的是ExceptionServiceEmailService不应该知道这些接口背后的内容。具体的实现应该以某种方式注入。为了做到这一点,您可以使用依赖注入容器。如果您不想使用另一个新库,也可以实现简单的服务定位器。

(我现在忽略AuthenticationService,因为它只会混淆问题——您所拥有的是两个服务之间的简单循环依赖关系——Exception和Email)。

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

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

使用某种控制反转机制,EmailService获得对IExceptionService的引用,反之亦然,因此圆被打破。

一个简单的IoC机制是ServiceLocator模式。创建这个(简化的)对象:

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

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

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

相关内容

最新更新