6

我正在经历一场职业生涯中期的哲学建筑危机。我看到客户端代码(UI、Web 服务、MVC、MVP 等)和服务层之间的界限非常清晰。然而,从服务层回来的界限越来越模糊。这一切都始于使用 Linq 查询代码的能力和延迟加载的概念。

我创建了一个由合同和实施组成的业务层。然后,实现可以依赖于其他合同等。这是通过带有 DI 的 IoC 容器处理的。有一个服务可以处理 DataAccess,它所做的只是返回一个 UnitOfWork。这个 UnitOfWork 在扩展时创建一个事务,并在 Commit 方法上提交数据。[查看本文(可测试性和实体框架 4.0) ]:

public interface IUnitOfWork : IDisposable {
   IRepository<T> GetRepository<T>() where T : class;
   void Commit();
}

Repository 是通用的,适用于两种实现(EF4 和 InMemory DataStore)。T 由从数据库架构或 EF4 映射生成的 POCO 组成。可测试性内置于存储库设计中。我们可以利用内存中的实现来断言具有预期的结果。

public interface IRepository<T> where T : class {
   IQueryable<T> Table { get; }
   void Add(T entity);
   void Remove(T entity);
}

虽然数据源是抽象的,但 IQueryable 仍然使我能够在业务逻辑中的任何位置创建查询。这是一个例子。

public interface IFoo {
   Bar[] GetAll();
}

public class FooImpl : IFoo {
   IDataAccess _dataAccess;
   public FooImpl(IDataAccess dataAccess) {
      _dataAccess = dataAccess;
   }

   public Bar[] GetAll() {
      Bar[] output;
      using (var work = _dataAccess.DoWork()) {
          output = work.GetRepository<Bar>().Table.ToArray();
      }
      return output;
   }
}

现在您可以看到当您使用复杂的过滤器执行连接时,查询如何变得更加复杂。

因此,我的问题是:

  1. BLL 和 DAL 之间没有明确的区别是否重要?.
  2. 在类似于 InMemory 抽象的 Repository 层后面时,可查询性是否被视为数据访问或业务逻辑?

补充:我想得越多,也许第二个问题是唯一应该问的问题。

4

3 回答 3

6

我认为回答您的问题的最佳方式是退后一步,考虑一下为什么将业务逻辑层和数据访问层分开是推荐的做法。

在我看来,原因很简单:将业务逻辑与数据层分开,因为业务逻辑是价值所在,数据层和业务逻辑将需要随着时间的推移或多或少地相互独立,并且业务逻辑需要可读,而不必详细了解所有数据访问层的功能。

因此,您的查询体操的试金石可以归结为:

  1. 您能否在不破坏大部分业务逻辑的情况下更改系统中的数据模式?
  2. 您和其他 C# 开发人员是否可以阅读您的业务逻辑?
于 2010-09-28T23:50:11.770 回答
1

1.只有当你更关心哲学而不是完成工作时。:)

2.我会说这是业务逻辑,因为您在两者之间有一个抽象。我会称 DAL 的存储库层的一部分,以及任何使用它的东西,BL。

但是,是的,这对我来说也很模糊。我不认为这很重要。使用这样的模式的目的是编写一个干净、可用的代码,同时易于通信,并且无论哪种方式都可以实现这个目标。

于 2010-09-28T23:45:47.617 回答
1

1.BLL和DAL之间没有明确的区别有关系吗?

这确实很重要!任何使用您的 Table 属性的程序员都需要了解后果(数据库往返、查询翻译、对象跟踪)。这也适用于阅读业务逻辑类的程序员。

2.在类似于 InMemory 抽象的 Repository 层之后,可查询性是否被视为数据访问或业务逻辑?

抽象是我们隐藏问题的毯子。

如果您的抽象是完美的,那么查询可以抽象地被认为是针对内存中的集合进行操作,因此它们不是数据访问。

但是,抽象泄漏。如果您想要在数据世界中有意义的查询,则必须努力超越抽象。这种额外的努力(它破坏了抽象)产生了数据访问代码。


一些例子:

output = work.GetRepository<Bar>().Table.ToArray(); 

这是代码(抽象地)很好。但是在数据世界中,它会导致扫描整个表并且(至少通常)是愚蠢的!


badquery = work.GetRepository<Customer>().Table.Where(c => c.Name.Contains("Bob")).ToArray(); 
goodquery = work.GetRepository<Customer>().Table.Where(c => c.Name.StartsWith("Bob")).ToArray(); 

当有索引时,好查询比坏查询好Customer.Name。但是,除非我们解除抽象,否则我们无法获得这一事实。


badquery = work.GetRepository<Customer>().Table
  .GroupBy(c => c.Orders.Count())
  .Select(g => new
  {
    TheCount = g.Key,
    TheCustomers = g.ToList()
  }).ToArray();
goodquery = work.GetRepository<Customer>().Table
  .Select(c => new {Customer = c, theCount = c.Orders.Count())
  .ToArray()
  .GroupBy(x => x.theCount)
  .Select(g => new
  {
    TheCount = g.Key,
    TheCustomers = g.Select(x => x.Customer).ToList()
  })
  .ToArray();

goodquery 比 bad query 好,因为 badquery 将针对每个组按组键重新查询数据库(更糟糕的是,极不可能有索引来帮助过滤客户c.Orders.Count())。


可测试性内置于存储库设计中。我们可以利用 InMemory 实现来断言具有预期的结果。

如果您确实针对内存中的集合运行查询,请不要幻想您的查询正在被测试。除非涉及数据库,否则这些查询是不可测试的。

于 2010-09-29T03:50:04.840 回答