当前位置:  开发笔记 > 后端 > 正文

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

如何解决《如何使用LinqToSQL/EntityFramework/NHibernate实现管道和过滤器模式?》经验,为你挑选了1个好方法。

在DAL Repository构建时,我偶然发现了一个名为Pipes and Filters的概念.我读到它在这里,在这里看到了一个截屏来自这里.我仍然不确定如何实现这种模式.从理论上讲,所有听起来都不错,但我们如何在企业场景中真正实现这一点呢?

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

提前致谢!!



1> Marc Gravell..:

最终,LINQ on IEnumerable 一个管道和过滤器实现.IEnumerable是一个 API - 意味着数据是懒惰的返回,因为你要求它(通过迭代器块),而不是一次加载所有内容,并返回一个大的记录缓冲区.

这意味着您的查询:

var qry = from row in source // IEnumerable
          where row.Foo == "abc"
          select new {row.ID, row.Name};

是:

var qry = source.Where(row => row.Foo == "abc")
            .Select(row = > new {row.ID, row.Name});

当你对此进行枚举时,它将懒惰地使用数据.你可以用Jon Skeet的Visual LINQ以图形方式看到这个.打破管道的唯一因素是强制缓冲的东西; OrderBy,GroupBy等等.对于数据量较大的工作,乔恩和我对工作推LINQ做骨料没有这样的场景缓冲.

IQueryable(大多数ORM工具暴露 - LINQ-to-SQL,实体框架,LINQ-to-NHibernate)是一个略有不同的野兽; 因为数据库引擎将完成大部分繁重工作,很可能已经完成了大部分步骤 - 剩下的就是使用IDataReader并将其投影到对象/值 - 但这通常仍然是一个管道(IQueryable实现IEnumerable),除非你打电话.ToArray(),.ToList()等等.

关于在企业中的使用... 我的观点是,可以使用IQueryable在存储库中编写可组合查询,但它们不应该离开存储库 - 因为这会使存储库的内部操作受调用者的影响,所以你将无法正确地进行单元测试/配置文件/优化/等等.我已经采取了在存储库中做一些聪明的事情,但返回列表/数组.这也意味着我的存储库不会意识到实现.

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

一个安全的DAL(IMO)使用过滤器会之类的东西:

public Customer[] List(string name, string countryCode) {
     using(var ctx = new CustomerDataContext()) {
         IQueryable 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();
     }
}

在这里我们已经添加了过滤器,但在我们调用之前没有任何反应ToArray.此时,获取并返回数据(在过程中处理数据上下文).这可以完全进行单元测试.如果我们做了类似但返回的事情IQueryable,调用者可能会执行以下操作:

 var custs = customerRepository.GetCustomers()
       .Where(x=>SomeUnmappedFunction(x));

突然间,我们的DAL开始失败(无法转换SomeUnmappedFunction为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); 
    }
}

现在我们有虚拟重载(作为扩展方法)ICustomerRepository- 所以我们的调用者可以使用repo.List("abc","def")而无需指定分页.


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

推荐阅读
携手相约幸福
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有