【问题标题】:Dependency-Inversion-Principal: Should I use an extra Project/DLL for interfaces?Dependency-Inversion-Principal:我应该为接口使用额外的项目/DLL吗?
【发布时间】:2021-10-27 05:33:35
【问题描述】:

如果我有这样的分层架构: 业务层 -> 数据访问层,如何正确实现依赖反转主体?

作为主体状态,较低级别 (DAL) 使用的接口应由较高级别定义。但是如果我在业务层 DLL 中定义接口,我会得到一个循环依赖。将接口移动到供双方使用的单独 DLL 中是否是个好主意?

【问题讨论】:

    标签: c# architecture layer


    【解决方案1】:

    在我看来 - 是的。那么解决方案中的项目会是这样的(不过只是一个例子,我不知道你的具体情况):

    • DependencyInjectionProject.InjectInterfaces(对所有项目的引用) - 该项目包括解决接口依赖关系的逻辑
    • Domain.YourBusinessLogic(对接口的引用)
    • 基础设施.接口
    • Infrastructure.Implementations(参考接口)

    最后,在您的终端/客户端应用程序中,您可以引用 DependencyInjectionProject.InjectInterfaces 来解决业务逻辑中的依赖关系。

    【讨论】:

    • 请记住,Infrastructure.Interfaces 还需要定义要交换的对象 - 除非它们位于所有程序集引用的第四个程序集中。
    【解决方案2】:

    一般来说,将抽象分开并没有什么坏处。它们可以在多个不同的项目中以多种方式实现/扩展。您只会引用需要依赖项的抽象项目。不一定知道实现。 这种方法有更多的好处,而不是项目数量增加等一些小缺点。

    【讨论】:

    • 同样这样,维护解决方案和代码库会容易得多。如果将来您决定重构您的代码库并使用不同的架构,那么您也会从中受益。
    【解决方案3】:

    @ElConrado 没有错,但它有点开放。是的,您可以使用单独的程序集,但您不必这样做; DI 不仅仅是一个程序集/包级别的东西 - 但这是有很多价值的地方,因为它是您可以通过重新部署隔离的 DLL 来交换系统部分的方式,而无需重新部署(或编译)整个东西。

    通常我有一个Common 程序集/命名空间,并在其中有我的 DTO 和接口,因为大多数时候我正在处理的应用程序的大小和复杂性都很好。您可以将抽象接口放在单独的程序集中,但我看不出这会给您带来什么实际价值。

    就如何在 C# 中做到这一点而言:

    程序集:MyApp.Common,命名空间:MyApp.Common.Utilities

    // Utility that instantiates concrete providers 
    // (classes that implement the abstractions / interfaces) 
    // like IContentDataProvider.
    public class ProviderLoader
    {
      public static object CreateInstance(string fullTypeNameConfigKey)
      {
         ...
      }
    
      public static object CreateInstance(string fullTypeName)
      {
         ...
      }
    }
    

    程序集:MyApp.Common,命名空间:MyApp.Common.Interfaces.IDataProvider

    // Defines abstract data access methods
    public interface IContentDataProvider
    {
      ContentInfo Content_SELECT_ByContentID(int contentId);
    }
    

    程序集:MyApp.Common,命名空间:MyApp.Common.Info

    // Dumb DTO's / POCO's, used by the interfaces / abstractions 
    // as a way of exchanging data between layers, whilst still 
    // maintaining a clean separation.
    public class ContentInfo
    {
      ...
    }
    

    程序集:MyApp.SQLDataProvider,命名空间:MyApp.SQLDataProvider

    参考 MyApp.Common

    // Concrete data provider
    public class ContentDataProvider : IContentDataProvider
    {
      public ContentInfo Content_SELECT_ByContentID(int contentId)
      {
        ...
      }
    }
    

    现在是有趣的部分,将它们组合在一起:

    程序集:MyApp.Business,命名空间:MyApp.Business

    参考 MyApp.Common

    // Concrete data provider
    public class ContentPublisher
    {
        // Not strictly necessary, but Lazy-Load is one 
        // way to instantiate the provider where you need it.
        private static MyApp.Common.Interfaces.IDataProvider.IContentDataProvider _iContentDataProvider;
        private static MyApp.Common.Interfaces.IDataProvider.IContentDataProvider IContentDataProvider
        {
            get
            {
                if (_iContentDataProvider== null)
                {
                    // Here the type of concrete provider would 
                    // be loaded based on a value set in a config file 
                    // so the parameter here would be a config-key.
                    // But you could use other approaches. 
                    _iContentDataProvider= MyApp.Common.Utilities.ProviderLoader.CreateInstance(...) as MyApp.Common.Interfaces.IDataProvider.IContentDataProvider;
                }
                return _iContentDataProvider;
            }
        }
    
        // Use the IContentDataProvider to access the concrete implementation:
        public static ContentItem GetContentItemById(int contentId)
        {
            ContentInfo contentInfo = IContentDataProvider.Content_SELECT_ByContentID(contentId);
            ...
            // Take the lowly ContentInfo DTO and use it, 
            // sprinkled with some other Business Layer magic, 
            // to return ContentItem.
        }
    }
    

    虽然看起来有点矫枉过正,但您没有理由不采用相同的方法(使用接口、提供程序加载器等)并将其全部放在一个程序集中,并让您的应用程序的用户利用它 - 例如选择用于某种目的的算法。他们可以通过配置设置来做到这一点,甚至可以通过 UI 更改应用程序在内存中的状态。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-04-27
      • 1970-01-01
      • 2010-10-18
      • 2022-01-06
      • 1970-01-01
      • 2013-08-19
      • 2015-04-15
      • 1970-01-01
      相关资源
      最近更新 更多