使用子类通用基类时避免引用其他程序集

时间:2015-12-02 01:20:36

标签: .net generics inheritance

假设我有一个假设的应用程序有3层(程序集):DataBizGui。 My Biz程序集有一个通用基类,几乎所有其他Biz类都继承自:

namespace Biz
{
   // Base Class
   public abstract class BizBase<T>
   {
      public string GetDataName()
      {
         return typeof(T).Name;
      }

      abstract internal protected T GetDataObject();
   }

   // Child Class
   public class Foo: BizBase<Data.Bar>
   {
       override internal protected Data.Bar GetDataObject()
       {
          Data.Bar ret = null;
          ... //Do stuff
          return ret;
       }
   }
}

显然这是一个非常简单的例子,但我们假设BizBase中有很多依赖于GetDataObect()的逻辑,并且Gui直接使用Biz对象。

namespace Gui
{
    public static void Main(params string[] args)         
    {
        Biz.Foo foo = new Biz.Foo();
        Console.WriteLine(foo.GetDataName);    
    }
}

问题

我希望我的Biz程序集引用我的Data程序集,我希望我的Gui能够引用Biz,但我不想要我和#34; Gui&#34;汇编以引用我的&#34;数据&#34;部件。不幸的是,如果我在Data中未包含对Gui的引用,则会出现编译时错误。

所以我的问题

  1. 为什么Gui需要引用DataGetDataObject()是内部的,所以我不明白为什么需要参考才能编译。
  2. 我如何避免让我的&#34; Gui&#34;汇编需要参考&#34;数据&#34;?
  3. 澄清

    • Gui图层正如您所想。在我的WPF案例中,它可以很容易地成为Console应用程序的Main方法。
    • Data类实际上只是实体(在我的情况下使用EF生成的POCO)。实际的DbContext位于一个单独的程序集中,该程序集未直接引用(除非使用IModule s向我的IoC容器注册它。)
    • Biz类主要将我的大部分业务和验证逻辑以及每个地图存储到单个Data类(Biz.Color继承自Biz.BizBase<Data.Color>)。 Biz类使用IDataStore服务(它是Dependency-Injected DbContext)来检索/保存它们使用的特定Data对象。

    最初,我有一个非通用的BizBase类,但我发现我在每个Biz类中复制了很多CRUD逻辑,它们与IDataStore以及它们使用的特定实体类型进行交互。 BizBase有几个抽象方法(除了所使用的特定实体类型)在每个子类中都是相同的。

    我能够通过使它成为通用的方式将几乎所有这些逻辑移动到基类,但突然我的Biz类的任何消费者都需要直接引用我的Data类(我的实体)。虽然我的实体实际上并不包含与数据库交互的任何逻辑,但我不愿意为需要使用Biz对象的任何内容添加对它们的引用。

1 个答案:

答案 0 :(得分:1)

Biz图层中查看此类型:

public class Foo: BizBase<Data.Bar>

这会将您的Data类型(至少是这一类,但您只需要一条)暴露为Biz类型的部分。因此,使用Biz的任何内容都需要引用Data才能了解Foo是什么,因为FooData.Bar具有内在依赖性。< / p>

为了实现您正在寻找的分离,Biz根本不应该引用Data。这意味着它肯定不应公开公开Data中的类型,因此要求所有消费者引用Data

您对Data程序集的引用是向后的。引用应该向内指向业务逻辑,而不是向外来自业务逻辑。 (参见&#34;依赖规则&#34; in this article。)

Stack Overflow问题的细节可能有点宽泛,但基本上是:

  • Data应参考Biz
  • Gui应参考Biz
  • Biz不应该引用任何内容。

当然,Biz需要一种方法来调用Data中的操作。这就是依赖注入的来源。如果Data实现Biz中的接口,那么Biz可以编码这些接口。依赖注入器(它不一定是框架,但也可能是)将为需要的那些接口提供实现的实例。

至少从环境配置的角度来看,应用层的责任是知道它需要哪些依赖项。 (例如,在App.config中配置依赖项注入器。)在足够简单的应用程序中,依赖项注入可以是应用程序本身的一部分。在这些情况下,Gui 应该引用Data,因为它需要知道哪些实现用于接口。

但是,即使在简单的应用程序中,也有 希望Gui引用Data的愿望。毕竟,至少会带来Gui中代码直接在Data中调用代码的风险,这会破坏架构。

相反,考虑与其他三个层正交的第四层。 DI图层。你拥有的是:

  • Biz没有提及任何内容
  • Data引用Biz
  • DI引用BizData
  • Gui引用BizDI

Gui将初始化DI中的依赖注入(通常在依赖注入框架的应用程序启动时),然后使用该系统获取基于接口的实现。任何时候Gui都不会知道(或关心)实现是什么。它不需要对这些库的引用。

当然,运行时输出仍然需要这些库。但是Gui中的代码永远不会&#34;看到&#34; (以任何设计时或编译时的方式)依赖项(Data)。它只会初始化DI,然后通过Biz执行所有逻辑,为其提供从DI获取的实例。

对于.NET中这种体系结构的一些具体示例,请查看(有点旧)presentation I put together here。演示文稿are available here中引用的代码示例。 (注意:我真的应该更新这些示例以使用实体框架。是的,它已经很久了。我确实有一些简单的代码示例in an article here,它演示了一些应用于EF的相同概念。)