Linq to sql 如何使用LinqToSQL/Entity Framework/NHibernate实现管道和过滤器模式?

Linq to sql 如何使用LinqToSQL/Entity Framework/NHibernate实现管道和过滤器模式?,linq-to-sql,design-patterns,linq-to-entities,pipes-filters,Linq To Sql,Design Patterns,Linq To Entities,Pipes Filters,在构建DAL存储库时,我偶然发现了一个称为管道和过滤器的概念。我在这里读到了这篇文章,并看到了一段视频。我仍然不知道如何实施这种模式。从理论上讲,这一切听起来都不错,但我们如何在企业场景中真正实现这一点呢 如果您有任何资源、提示或示例,请在问题中提到的数据映射器/ORM的上下文中解释此模式,我将不胜感激 提前谢谢 归根结底,IEnumerable上的LINQ是一个管道和过滤器实现IEnumerable是一种流式API——这意味着数据会按照您的要求(通过迭代器块)缓慢返回,而不是一次加载所有内容并

在构建DAL存储库时,我偶然发现了一个称为管道和过滤器的概念。我在这里读到了这篇文章,并看到了一段视频。我仍然不知道如何实施这种模式。从理论上讲,这一切听起来都不错,但我们如何在企业场景中真正实现这一点呢

如果您有任何资源、提示或示例,请在问题中提到的数据映射器/ORM的上下文中解释此模式,我将不胜感激


提前谢谢

归根结底,
IEnumerable
上的LINQ是一个管道和过滤器实现
IEnumerable
是一种流式API——这意味着数据会按照您的要求(通过迭代器块)缓慢返回,而不是一次加载所有内容并返回大量记录缓冲区

这意味着您的查询:

var qry = from row in source // IEnumerable<T>
          where row.Foo == "abc"
          select new {row.ID, row.Name};
当您对此进行枚举时,它将惰性地消耗数据。你可以用Jon Skeet的图表看到这一点。唯一能打破管道的东西是强迫缓冲的东西
OrderBy
GroupBy
,等等。对于大量工作,Jon和我自己致力于在这种情况下进行无缓冲聚合

IQueryable
(大多数ORM工具都公开了——LINQ到SQL、实体框架、LINQ到NHibernate)是一个稍微不同的beast;因为数据库引擎将完成大部分繁重的工作,所以大多数步骤很可能已经完成了—剩下的就是使用
IDataReader
并将其投影到对象/值—但这通常仍然是一个管道(
IQueryable
实现
IEnumerable
)除非调用
.ToArray()
.ToList()

关于在企业中使用。。。使用
IQueryable
在存储库中编写可组合查询是可以的,但它们不应该离开存储库,因为这会使存储库的内部操作受制于调用方,因此,您将无法正确地进行单元测试/配置文件/优化/等等。我已经开始在存储库中做一些聪明的事情,但返回列表/数组。这也意味着我的存储库不知道实现

这是一种耻辱——因为从存储库方法“返回”
IQueryable
的诱惑相当大;例如,这将允许调用者添加分页/过滤器/等等,但请记住,他们还没有实际使用数据。这使得资源管理成为一个难题。此外,在MVC等中,您需要确保控制器调用
.ToList()
或类似的调用,以便它不是控制数据访问的视图(否则,您无法正确地对控制器进行单元测试)

DAL中过滤器的安全(IMO)使用如下:

public Customer[] List(string name, string countryCode) {
     using(var ctx = new CustomerDataContext()) {
         IQueryable<Customer> qry = ctx.Customers.Where(x=>x.IsOpen);
         if(!string.IsNullOrEmpty(name)) {
             qry = qry.Where(cust => cust.Name.Contains(name));
         }
         if(!string.IsNullOrEmpty(countryCode)) {
             qry = qry.Where(cust => cust.CountryCode == countryCode);
         }
         return qry.ToArray();
     }
}
突然,我们的DAL开始失败(无法将
someunappedfunction
转换为TSQL,等等)。不过,您仍然可以在存储库中做很多有趣的事情

这里唯一的痛点是,它可能会迫使您使用一些重载来支持不同的调用模式(有/没有分页等)。在到达之前,我发现最好的答案是在接口上使用扩展方法;这样,我只需要一个具体的存储库实现:

class CustomerRepository {
    public Customer[] List(
        string name, string countryCode,
        int? pageSize, int? pageNumber) {...}
}
interface ICustomerRepository {
    Customer[] List(
        string name, string countryCode,
        int? pageSize, int? pageNumber);
}
static class CustomerRepositoryExtensions {
    public static Customer[] List(
          this ICustomerRepository repo,
          string name, string countryCode) {
       return repo.List(name, countryCode, null, null); 
    }
}
现在我们在
icCustomerRepository
上有了虚拟重载(作为扩展方法),因此我们的调用者可以使用
repo.List(“abc”、“def”)
,而无需指定分页



最后,如果没有LINQ,使用管道和过滤器会变得更加痛苦。您将编写某种基于文本的查询(TSQL、ESQL、HQL)。显然,您可以附加字符串,但它不是非常“管道/过滤器”-ish。“Criteria API”稍微好一点,但没有LINQ那么优雅。

如果出于任何原因决定否决该问题,请添加评论。该问题有问题吗??
 var custs = customerRepository.GetCustomers()
       .Where(x=>SomeUnmappedFunction(x));
class CustomerRepository {
    public Customer[] List(
        string name, string countryCode,
        int? pageSize, int? pageNumber) {...}
}
interface ICustomerRepository {
    Customer[] List(
        string name, string countryCode,
        int? pageSize, int? pageNumber);
}
static class CustomerRepositoryExtensions {
    public static Customer[] List(
          this ICustomerRepository repo,
          string name, string countryCode) {
       return repo.List(name, countryCode, null, null); 
    }
}