解决循环引用(C#)

4

我一直在处理与循环引用/依赖相关的问题,已经花了一整天的时间。我的思维过程肯定有问题,我真的不明白。

这是我的项目:

Flip.Main     (ASP.NET MVC)
Flip.Domain   (C# DLL)
Flip.Services (C# DLL)
Flip.Utility  (C# DLL)

当前参考/依赖项:
Flip.Main ->     Flip.Domain, Flip.Services, Flip.Utility
Flip.Services -> Flip.Domain, Flip.Utility
Flip.Domain ->   Flip.Utility

我希望在项目中进行结构化处理,使得服务项目拥有所有服务,领域项目包含模型、存储库和“流畅”扩展以查询模型,而主要和实用项目则相当自解释。

遇到的问题:

1)在Flip.Services项目中有一个EmailService,需要发送本地化邮件。所有本地化都在Flip.Main的App_GlobalResources中完成。但是不知道如何将强类型的电子邮件和其他本地化资源传递到我的服务层,因为Flip.Main已经依赖于服务层,因此无法让其再次依赖Main项目。

2)我有业务类(例如CustomerSearchFilter),代表强类型搜索查询。我想将这些业务类放在Flip.Domain项目之外,因为它们不是领域模型的一部分。然而,在我的CustomerSearchFilter类中,我有领域类实例(例如CustomerGroup),因此它需要了解领域类。同时,我的Flip.Domain项目中的流畅接口也需要知道CustomerSearchFilter是什么,以便将其应用于我的IQueryable接口。又是循环引用。

3)我有一个自定义的[AuthorizeSessionState]属性,用于装饰ASP.NET MVC Flip.Main项目中的特定控制器操作。这是一个ActionFilterAttribute,需要实例化我的SessionService,该服务位于Flip.Services项目中。但我不能将其放入我的Utility类中(因为Flip.Services已经引用了Flip.Utility)。我认为它们也不应该在Flip.Main中 - 我需要为此再创建另一个项目吗?

(还有20个问题)

我觉得我在某个地方犯了错误,特别是当我读到其他人通常不会遇到循环引用问题时。请帮帮我?

6个回答

6

对于所有非平凡的类,请使用接口。将接口放置在与实现不同的程序集中。


2
这就是我要为第二项建议的内容。如果你觉得两个兄弟程序集需要相互了解彼此的类,只需拆分出一个接口并让它们都使用即可。 - Spencer Ruport
你可以考虑使用带有虚成员的基础类型而不是接口。这样,你可以为宿主应用程序提供一个功能默认实现,这可以使测试更容易,并与实现共享一些逻辑。 - mcintyre321

0
问题在于你通过命名空间和动态链接库(DLL)分离什么。如果你有充分的理由保持所有东西都是模块化的,那么你必须非常努力地工作。但是,如果每个DLL中只有一个或两个类,也许你可以将它们合并在一起?

这些DLL相当大。在我的Flip.Services DLL中,我有12个不同的服务(可能会增长到>20); 在我的Flip.Domain DLL中,有模型、模型部分类扩展、存储库类和流畅扩展方法;在Flip.Utility中有加密和业务支持类,在Flip.Main中则是网站。对我来说这似乎很合理? - Alex
命名空间是用来组织代码的。当你想要更新程序中的某些部分或者重复使用代码时,可以使用程序集。但是,如果你只有一个应用程序或一小部分共享功能,则建议将所有内容放在一起。 - Spence

0
花几分钟时间整理流程...为每个项目创建一个标识符(FM,FS,FD,FU)。在页面上列出每个公开可访问的过程,然后添加一个项目标识符,如果该项目使用该过程...
然后您可以看到哪些过程需要在哪个项目中(或可访问)。希望这有所帮助!

正如我之前所写的,问题在于通常有两个项目需要相互了解。您的方法将如何解决这个问题? - Alex
如果你尝试这个建议,你会发现(我相信还有其他的事情),你的一些程序并不适合放在最合适的项目中...例如,在主要项目中拥有本地化而不是工具类似乎不太合适。根据你所追求的模块化程度,你可能会发现语言管理最适合完全独立。 - Robert French

0
  1. 您可以将本地化的电子邮件字符串放在Flip.Services中。缺点是您需要维护两个本地化资源的位置。您也可以有一个单独的dll来存储所有资源,以最小化编辑资源的位置。
  2. 您需要将流畅接口移动到另一个dll或使CustomerSearchFilter成为域的一部分。
  3. 您需要添加更多项目或重新排列结构,并使用命名空间创建分离。

把CustomerSearchFilter(以及其他没有数据表示的业务对象)纳入领域模型中,这样做算是正常的吗? - Alex

0

听起来你正在基于具体实现而不是接口/契约进行构建。正如Ima所建议的那样,定义描述某个类应该能够做什么的接口。在声明属性、参数等时使用此接口。将接口与实现分开,并且实现和使用接口的项目都可以引用接口项目。

然后您可以使用依赖注入的好处,使您的代码更容易测试。


你有类似的项目结构示例文章吗?这种结构常见吗? - Alex

0
在一个域的“层次结构”中,存储库和服务处于同一逻辑级别,在基础设施角色上高于域。我建议将您的存储库实现(查询等)移出域本身。这至少解决了问题#2。

那会是什么样的项目呢?(技术术语?) - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接