SQL Server【转】Repository 返回 IQueryable?还是 IEnumerable?

当下是一个深有趣的题材,我们同步一步来探讨,首先得明确两独概念(来自
MSDN):

  • IQueryable:提供针对性无指定数据类型的特定数据源的询问进行测算的法力。
  • IEnumerable:公开枚举数,该枚举数支持以非泛型集合上展开简要迭代。

IQueryable 继承自 IEnumerable,它们俩极端酷之分别是,IQueryable
是表达式树处理,可以缓查询,而 IEnumerable
只能查询在该地内存中,Repository 的定义就不多说了,在“伪
DDD”设计中,你可以拿它们当作是多少访问层。

脚我们事先实现 Repository 返回 IEnumerable:

public interface IBookRepository  
{
    Book GetById();
    IEnumerable<Book> GetAllBooks();
    IEnumerable<Book> GetBy....();
    void Add(Book book);
    void Delete(Book book);
    void SaveChanges();
}

  

地方是咱的貌似接口设计,包含查询、增加、删除操作,你发现并没改,其实我们得以先通过
GetById 操作,然后抱 Book 对象,进行修改,最后执行 SaveChanges
就可了,在持久化数据库的时节,会咬定实体状态值的定义,最后进行动改变。

GetBy….() 代表了同一类似查询艺术,因为我们的事体比较复杂,对 Book
的查询会千奇百怪,所以,没有主意,我们只能加各类查询方式来满足需求,最后可能致的结果是,一个
Where 对应一个查询办法,IBookRepository
会充斥着个查询艺术,并且这些查询方式一般就会受一个 Application
方法调用,如果您翻下 GetBy….()
方法实现,会发觉实际都大同小异,不同的就是 Where
条件,这样的结果就是会见促成代码变得杀之交汇。

对地方的问题,怎么惩罚也?因为 IEnumerable
是查询在本地内存中,所以没有艺术,我们只好这样处理,那如何下
IQueryable 会是哪的为?我们看下代码:

 

public interface IBookRepository  
{
    IQueryable<Book> GetBooks();
    void Add(Book book);
    void Delete(Book book);
    void SaveChanges();
}

单单生一个 GetBooks 查询,那它能够满足各类查询需要为?我们看下 Application
中调用的代码:

public class BookApplication : IBookApplication  
{
    private IBookRepository _bookRepository;

    public BookApplication(IBookRepository bookRepository)
    {
        _bookRepository = bookRepository;
    }

    public IEnumerable<Book> GetAllBooks()
    {
        return _bookRepository.GetBooks().AsEnumerable();
    }

    public IEnumerable<Book> GetBooksByUser(int userId)
    {
        return _bookRepository.GetBooks().Where(b => b.UserId == userId).AsEnumerable();
    }

    //....
}

因为 IQueryable 是推查询,只有当执行 AsEnumerable
的时候,才见面真正去询问,也堪这么说,BookApplication
可以因要求任意构建查询表达式树,就如咱于 SQL Server 中写查询
SQL,SELECT * FORM Books 在 BookRepository
中进行构建,WHERE ... 操作在 BookApplication 中展开构建,最后的 F5
执行也以 BookApplication 中。

从地方的代码中,我们好看看,IQueryable 很好之解决了运用 IEnumerable
所出现的问题,一个询问好答应针对千变万化的动查询,IQueryable
看起好像是那么的强有力,其实 IQueryable
的兵不血刃并无压制此,上面说之凡查询表达式,那长、修改及去操作,可以利用其进行好吗?修改和去是得的,添加并无能够,具体可以参见
dudu
的及时篇博文:付出笔记:基于EntityFramework.Extended用EF实现指定字段的创新。

有关 EntityFramework.Extended
的壮大,需要记录下,因为此东西确实不行好,改变了我们事先的无数写法和题材,比如,在事先以
EF 进行改动和去实体,我们一般会这些写:

 

public class BookApplication : IBookApplication  
{
    private IBookRepository _bookRepository;

    public BookApplication(IBookRepository bookRepository)
    {
        _bookRepository = bookRepository;
    }

    public void UpdateNameById(int bookId, string bookName)
    {
        var book = _bookRepository.GetById(bookId);
        book.BookName = bookName;
        _bookRepository.SaveChanges();
    }

    public void UpdateNameByIds(int[] bookIds, string bookName)
    {
        var books = _bookRepository.GetBooksByIds(bookIds);
        foreach (var book in books)
        {
            book.BookName = bookName;
        }
        _bookRepository.SaveChanges();
    }

    public void Delete(int id)
    {
        var book = _bookRepository.GetById(id);
        _bookRepository.Delete(book);//context.Books.Remove(book);
        _bookRepository.SaveChanges();
    }
}

地方的写法有啊问题也?其实最老的题材便,我们若开展改动和去,必须先行取得之实体,也就是是事先查询再拓展改动及去,这个就算多少多余了,尤其是
UpdateNameByIds 中的批量改,先拿走 Book
对象列表,然后再次遍历修改,最后保存,是匪是发生点 XXX
的感到吗,仔细考虑,还非使不用 EF 来的大概,因为一个 Update SQL
就可搞定,简单又性能又大,为什么还要用 EF 呢?这是一个坑?其实以
EF 也足以执行 SQL,但立刻就比如换了只马甲,没有呀卵用。

对地方的问题,该怎么缓解吗?很简单,使用 EntityFramework.Extended 和
IQueryable 就足以,我们改造下者的代码:

 

using EntityFramework.Extensions;

public class BookApplication : IBookApplication  
{
    private IBookRepository _bookRepository;

    public BookApplication(IBookRepository bookRepository)
    {
        _bookRepository = bookRepository;
    }

    public void UpdateNameById(int bookId, string bookName)
    {
        IQueryable<Book> books = _bookRepository.GetBooks();
        books = books.Where(b => b.bookId == bookId);
        books.Update<Book>(b => new Book { BookName = bookName });
    }

    public void UpdateNameByIds(int[] bookIds, string bookName)
    {
        IQueryable<Book> books = _bookRepository.GetBooks();
        books = books.Where(b => bookIds.Contains(bookIds));
        books.Update<Book>(b => new Book { BookName = bookName });
    }

    public void Delete(int id)
    {
        IQueryable<Book> books = _bookRepository.GetBooks();
        books = books.Where(b => b.bookId == id);
        books.Delete<Book>();
    }
}

 

来没出察觉什么两样呢?原来 IQueryable
还足以如此写?这货还不只是用于查询,也可用来去和修改,另外,通过追踪生成的
SQL 代码,你会意识,没有了 SELECT,和我们直接写 SQL
是平的意义,在履修改和去之前,我们得针对查询表达树进行过滤,也就是说的,当我们最后动用修改的时节,会是在此过滤的询问表达树基础及的,比如上面的
Delete 操作,我们先行经过 bookId 进行过滤,然后径直进行 Delete
就可了,哇塞,原来是这么的简练。

当 BookApplication 操作变的略的当儿,BookRepository 也会见相应变的简约:

 

public interface IBookRepository  
{
    IQueryable<Book> GetBooks();
    void SaveChanges();//只用于Books.Add(book);
}

一个 IQueryable 表达树,一个 SaveChanges 操作,就好满足
BookApplication 中之享有操作。


既然 IQueryable 是这样的有力,那用她就是吓了,为什么还要讨论也?如果您
Google 搜索“Repository
IQueryable”关键词,会发现大量之有关文章,我事先贴发几单可怜赞赏的座谈:

  • Should Repositories return
    IQueryable?
  • Repository Return
    IQueryable
  • Should you return IQueryable from Your
    Repositories?
  • What are alternatives to using IQueryable in Repository
    Pattern?
  • IQueryable vs List: What Should Your Repository
    Return?
  • Should my repository expose
    IQueryable?
  • Repository Pattern and
    IQueryable(精简而强
  • Why the Repository Pattern Is Still
    Valid

方才是一些,关于这好像的章,老外写的挺多,而且评论被之议论吗生强烈,因为英语实在差,我大约看了有的,出乎我预料之外的是,很多人还非赞同
Repository 返回
IQueryable,但座谈的也死有意思,比如来个老外这样感叹:I’m still not
convinced that returning IQueryable is a bad idea, but at least I’m far
more aware of the arguments against it. 大致意思是:我仍不信赖返回
IQueryable
是一个要命主意,但最少自己再也了解她们之不予理由,是不是异常风趣啊?

至于 Repository 返回 IQueryable 的议论,我大致总结下:

好处:

  1. 延期执行。
  2. 压缩 Repository 重复代码(GetBy…)。
  3. IQueryable 提供再好之油滑。

坏处:

  1. 断单元测试。
  2. 数访问于 Repository 之外就。
  3. 多少访问十分在 Repository 之外抛出。
  4. 拖欠领域层将充满着这些相当详细查询。

补就是未多说了,因为咱们地方已履行过了,关于坏处,“隔离单元测试”是啊意思呢?也就是说我们无能够非常好的指向
Repository 进行单元测试,一方面是因 IRepository
是那么的简单(就零星只措施),另一方面 IQueryable
是查询表达树,它并无是就时,只有以切实可行调用的时节才见面询问好,所以,对于
Repository 的单元测试,显然是不曾其余意义之。

关于 Repository Pattern and
IQueryable 这篇博文,我眷恋还说一下,因为是老外的看法大赞赏,首先,它是因
Repository 模式概念基础及说的,所以,我们一致开始说:在“伪
DDD”设计被,你可以将 Repository
看作是多少访问层。这是简单个不同的前提,我再也盖总结下此老外的见地:

  • However the mistake is not the IQueryable itself, but its
    purpose.(不是 IQueryable 本身的荒唐,而是它的目的。)
  • The point is that using IQueryable, you’re asking for a query
    builder and not for a model.(问题之要是,使用 IQueryable
    是一个查询生成器,而非是一个模型。)
  • we want to specify what to get, not how to get
    it.(我们怀念透过则得到其,而非是何等去取得。)
  • tell it the what, not the how.

关押了面,是免是有点豁然开朗的感觉到啊,其实从 Repository
的模式概念方面考虑,使用 IQueryable
确实不是特别有分寸,但不可否认的是,IQueryable
又这么强大以及有利于,怎么收拾为?就比如博文同开始强调的那么:Repository
的定义就不多说了,在“伪 DDD”设计中,你可拿它们看做是数量访问层。

因而也,如果您的种是“伪 DDD”,并且 Repository
是让公作为“数据访问层”,那么用 IQueryable 就不曾啥问题了。

 

 

原文:http://www.cnblogs.com/xishuai/p/repository-return-iqueryable-or-ienumerable.html

相关文章