网站设计模板素材,具有价值的响应式网站,网络网站设计培训,深圳品牌学校网站建设这是一个很有意思的问题#xff0c;我们一步一步来探讨#xff0c;首先需要明确两个概念#xff08;来自 MSDN#xff09;#xff1a; IQueryable#xff1a;提供对未指定数据类型的特定数据源的查询进行计算的功能。IEnumerable#xff1a;公开枚举数#xff0c;该枚举…这是一个很有意思的问题我们一步一步来探讨首先需要明确两个概念来自 MSDN IQueryable提供对未指定数据类型的特定数据源的查询进行计算的功能。IEnumerable公开枚举数该枚举数支持在非泛型集合上进行简单迭代。IQueryable 继承自 IEnumerable它们俩最大的区别是IQueryable 是表达式树处理可以延迟查询而 IEnumerable 只能查询在本地内存中Repository 的概念就不多说了在“伪 DDD”设计中你可以把它看作是数据访问层。 下面我们先实现 Repository 返回 IEnumerable public interface IBookRepository
{Book GetById();IEnumerableBook GetAllBooks();IEnumerableBook 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
{IQueryableBook 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 IEnumerableBook GetAllBooks(){return _bookRepository.GetBooks().AsEnumerable();}public IEnumerableBook GetBooksByUser(int userId){return _bookRepository.GetBooks().Where(b b.UserId userId).AsEnumerable();}//....
} 因为 IQueryable 是延迟查询只有在执行 AsEnumerable 的时候才会真正去查询也可以这么说BookApplication 可以根据需求任意构建查询表达式树就像我们在 SQL Server 中写查询 SQLSELECT * 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){IQueryableBook books _bookRepository.GetBooks();books books.Where(b b.bookId bookId);books.UpdateBook(b new Book { BookName bookName });}public void UpdateNameByIds(int[] bookIds, string bookName){IQueryableBook books _bookRepository.GetBooks();books books.Where(b bookIds.Contains(bookIds));books.UpdateBook(b new Book { BookName bookName });}public void Delete(int id){IQueryableBook books _bookRepository.GetBooks();books books.Where(b b.bookId id);books.DeleteBook();}
} 有没有发现什么不同呢原来 IQueryable 还可以这样写这货居然不只是用于查询也可以用于删除和修改另外通过追踪生成的 SQL 代码你会发现没有了 SELECT和我们直接写 SQL 是一样的效果在执行修改和删除之前我们需要对查询表达树进行过滤也就是说的当我们最后应用修改的时候会是在这个过滤的查询表达树基础上的比如上面的 Delete 操作我们先通过 bookId 进行过滤然后直接进行 Delete 就可以了哇塞原来是这样的简单。 当 BookApplication 操作变的简单的时候BookRepository 也会相应变的简单 public interface IBookRepository
{IQueryableBook GetBooks();void SaveChanges();//只用于Books.Add(book);
} 一个 IQueryable 表达树一个 SaveChanges 操作就可以满足 BookApplication 中的所有操作。 既然 IQueryable 是这么的强大那用它就好了为什么还要讨论呢如果你 Google 搜索“Repository IQueryable”关键词会发现大量的相关文章我先贴出几个非常赞的讨论 Should Repositories return IQueryable?Repository Return IQueryableShould 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但讨论的却非常有意思比如有个老外这样感叹Im still not convinced that returning IQueryable is a bad idea, but at least Im far more aware of the arguments against it. 大致意思是我仍然不相信返回 IQueryable 是一个坏主意但至少我更了解他们的反对理由是不是很有意思呢 关于 Repository 返回 IQueryable 的讨论我大致总结下 好处 延迟执行。减少 Repository 重复代码GetBy...。IQueryable 提供更好的灵活性。...坏处 隔离单元测试。数据访问在 Repository 之外完成。数据访问异常在 Repository 之外抛出。该领域层将充斥着这些相当详细查询。...好处就不多说了因为我们上面已经实践过了关于坏处“隔离单元测试”是什么意思呢也就是说我们不能很好的对 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, youre 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 就没啥问题了。 转载于:https://www.cnblogs.com/xishuai/p/repository-return-iqueryable-or-ienumerable.html