C# 除了BLL实例化DAL之外,还有什么选项允许在n层解决方案中进行单元测试而不将DAL暴露于UI或BLL暴露于DAL?

C# 除了BLL实例化DAL之外,还有什么选项允许在n层解决方案中进行单元测试而不将DAL暴露于UI或BLL暴露于DAL?,c#,data-access-layer,n-tier-architecture,bll,C#,Data Access Layer,N Tier Architecture,Bll,我有一个分层解决方案,如下所示: 用户界面 BLL(业务逻辑层) DAL(数据访问层) 共享身份(仅具有实体POCO的VS项目) 我希望BLL有一个名为GetProductList()的服务,该服务在我的DAL层中实现。我考虑在BLL和DAL实现中定义一个接口,如下所示: 备选方案A: // Interface defined in BLL public interface IDataServices { List<Product> GetProductList(); }

我有一个分层解决方案,如下所示:

  • 用户界面
  • BLL(业务逻辑层)
  • DAL(数据访问层)
  • 共享身份(仅具有实体POCO的VS项目)
我希望BLL有一个名为GetProductList()的服务,该服务在我的DAL层中实现。我考虑在BLL和DAL实现中定义一个接口,如下所示:

备选方案A:

// Interface defined in BLL 
public interface IDataServices
{
  List<Product> GetProductList();
}

// Interface implemented in DAL
public class DataServices : IDataServices
{
    Public List<Product> GetProductList()
    {
      return //do some database work here and return List<Product>;
    }
}
//在BLL中定义的接口
公共接口IDataServices
{
List GetProductList();
}
//DAL中实现的接口
公共类数据服务:IDataServices
{
公共列表GetProductList()
{
return//在这里做一些数据库工作并返回列表;
}
}
如果我想在DAL中实现这一点,那么我必须让DAL项目引用BLL项目,以便查看IDataServices的接口定义。或者,我可以在DAL中复制接口定义,但随后我将得到要维护的重复代码(BLL和DAL中的相同接口定义)

备选案文B: 我可以这样做的另一种方法是忘记接口思想,在BLL中进行以下具体的类和方法调用,UI可以使用这些调用:

// Concrete class defined in the BLL
public class DataServices
{
    Public List<Product> GetProductList()
    {
         DAL aDAL = new DAL();
         Return (aDAL.GetProductList());
    }
}
//在BLL中定义的具体类
公共类数据服务
{
公共列表GetProductList()
{
DAL aDAL=新DAL();
Return(aDAL.GetProductList());
}
}
这很简单,但是BLL看到DAL并且有一个参考,但是这真的是件坏事吗?只要BLL不使用任何数据库对象(即数据源、连接字符串等)来满足请求,并且DAL符合我在BLL DataServices类中定义的服务的名称,这还不够吗?我听到的关于在另一个数据库引擎中进行交换的所有讨论都可以通过确保下一个DAL提供与BLL在DataServices类中标识的服务相同的服务来完成,例如GetProductList()。在此设置中,用户界面仍然不知道DAL的任何信息,DAL也不知道BLL的任何信息。如果我考虑使用依赖注入来避免在BLL中实例化DAL,那就意味着在UI中实例化DAL并将其传递到BLL中。我不想这样做,因为这样会使UI能够访问DAL方法

备选案文C: 我简短地看了一下Unity容器,但是该工具建议在入口点注册所有的接口和具体的类,入口点本来是UI,而最终为BLL和DAL提供了UI可见性,这看起来更糟。我看到了一个使用MEF和Unity来解决入口点查看所有层的问题的参考,但也看到如果这样做,就无法真正对这样的配置进行单元测试。与选项B相比,似乎需要大量的工作和复杂性

备选案文D: 我考虑过的另一个选择是在BLL和DAL之间创建一个facade层(另一个VS项目)。这似乎没有多大意义,除非我最终用了很多DAL方法,这些方法与BLL无关,所以它们必须隐藏起来;允许DAL外观仅显示BLL需要的内容。如果我要在另一个数据库中进行交换,我仍然必须创建facade根据BLL的需要所需的方法,正如我在选项B中提到的那样

所以基于这些,我想选择B,但我希望这里有一些社区的意见。我还可以做什么来满足以下条件:

  • 1) 不让UI看到DAL
  • 2) 不让DAL看到BLL
  • 3) 该解决方案仍然允许对所有层进行单元测试

应在DAL中定义IDataServices<代码>但是BLL看到DAL并有一个对它的引用,这是很自然的方法。项目可以参照其下方的图层。如果不允许向下引用,则可能根本没有引用

请注意,反射引用仍然是引用,因为如果不更改上面的层,则无法更改下面的层。依赖项不是一个仅限于编译时的概念。方案B没有增加任何好处。它删除compiletime依赖项,但不删除运行时依赖项

将依赖项从a移到B的要点是,您可以在不更改a的情况下更改B。这就是全部要点。运行时依赖项仍然有效

关于C:您可以让UI要求BLL注册其依赖项。BLL可以要求DAL注册。这样,用户界面就不受DAL的影响

D:我不知道这会有什么效果

通过使DAL定义
IDataServices

您的选项A(BLL中的接口,DAL中的实现)+和IoC容器是最好的方法,您可以轻松满足约束条件

当设计一个复杂的软件解决方案时,你必须把它分成更小的部分

其中一些内容对解决方案至关重要。它们将是您开发软件的原因,而不仅仅是购买现有的解决方案。你必须关注他们。这些部分将是复杂且难以实现的。你对此无能为力。必须尽可能最好地实施这些措施

也会有简单的作品。易于实现的功能,甚至可以购买。试着用尽可能少的努力来制作这个零件。他们是必要的,但他们不是你受雇的目的

首先关注硬的部分。那里的业务逻辑将非常复杂。业务逻辑将不依赖于您选择的存储解决方案,因此不要让它依赖于系统中的DAL项目。在业务逻辑层中定义实体存储库的接口(如果遵循DDD,则聚合)

你是谁