假设我有一个假设的应用程序有3层(程序集):Data
,Biz
和Gui
。
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
的引用,则会出现编译时错误。
所以我的问题
Gui
需要引用Data
? GetDataObject()
是内部的,所以我不明白为什么需要参考才能编译。澄清:
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对象的任何内容添加对它们的引用。
答案 0 :(得分:1)
在Biz
图层中查看此类型:
public class Foo: BizBase<Data.Bar>
这会将您的Data
类型(至少是这一类,但您只需要一条)暴露为Biz
类型的部分。因此,使用Biz
的任何内容都需要引用Data
才能了解Foo
是什么,因为Foo
对Data.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
引用Biz
和Data
Gui
引用Biz
和DI
Gui
将初始化DI
中的依赖注入(通常在依赖注入框架的应用程序启动时),然后使用该系统获取基于接口的实现。任何时候Gui
都不会知道(或关心)实现是什么。它不需要对这些库的引用。
当然,运行时输出仍然需要这些库。但是Gui
中的代码永远不会&#34;看到&#34; (以任何设计时或编译时的方式)依赖项(Data
)。它只会初始化DI
,然后通过Biz
执行所有逻辑,为其提供从DI
获取的实例。
对于.NET中这种体系结构的一些具体示例,请查看(有点旧)presentation I put together here。演示文稿are available here中引用的代码示例。 (注意:我真的应该更新这些示例以使用实体框架。是的,它已经很久了。我确实有一些简单的代码示例in an article here,它演示了一些应用于EF的相同概念。)