Entity framework 4.1 存储库是否应返回IEnumerable<;T>,IQueryable<;T>;或列表<;T>;?

Entity framework 4.1 存储库是否应返回IEnumerable<;T>,IQueryable<;T>;或列表<;T>;?,entity-framework-4.1,repository,appfabric,azure-appfabric,appfabric-beta-2,Entity Framework 4.1,Repository,Appfabric,Azure Appfabric,Appfabric Beta 2,我希望使我的应用程序尽可能灵活,但不要让我的界面过于具体而使自己陷入困境 存储库的最佳对象类型是什么?IEnumerable、IQueryable还是List 我正在考虑使用的技术是 Azure应用程序结构缓存 实体框架4.1 可能是Windows Server AppFabric 这取决于您是否希望将来对实体执行任何查询,以及这些查询是否应在内存中: 如果将来有查询,DB应该执行该工作,则返回IQueryable 如果将来有查询并且要在内存中执行,则返回IEnumerable 如果没有进一

我希望使我的应用程序尽可能灵活,但不要让我的界面过于具体而使自己陷入困境

存储库的最佳对象类型是什么?IEnumerable、IQueryable还是List

我正在考虑使用的技术是

  • Azure应用程序结构缓存

  • 实体框架4.1

  • 可能是Windows Server AppFabric


这取决于您是否希望将来对实体执行任何查询,以及这些查询是否应在内存中:

  • 如果将来有查询,DB应该执行该工作,则返回IQueryable
  • 如果将来有查询并且要在内存中执行,则返回IEnumerable
  • 如果没有进一步的查询,并且需要读取所有数据,则返回ILST、ICollection等

您需要从DAL返回
IEnumerable
(非集合)的自定义实现的可能性有多大?(要回答这个问题,请查看您以前的项目,并计算您周围有多少个此类项目或
收益率项目。)


如果答案是“不太”,我只会返回
ICollection
甚至数组(如果您想防止查询结果被无意修改的话)。必要时,如果您需要将查询更改为带有自定义
IEnumerable
的“流式”结果,您总是可以让旧方法调用新方法,并将结果具体化,以保持与旧客户端的兼容性。

我想说的是,使用IQueryable构建DAL,并传递它,确保您的对象上下文生命周期是请求。通过这种方式,您将获得延迟执行的好处,但也会面临查询数据库效率低下的风险

然后确保对应用程序(或至少是最有可能获得流量的部分)进行性能测试,并查看数据访问模式。在DAL中创建专门的方法来检索完全物化的对象,并将这些查询作为预编译的查询

存储库接口的示例如下

  public interface IDataContext
  {
        void Add<T>(T entity) where T : BaseEntity;
        void Delete<T>(T entity) where T : BaseEntity;
        IQueryable<T> Find<T>(Expression<Func<T, bool>> where) where T : BaseEntity;
   }
Expression
会将整个表达式传递到存储库,而不仅仅是Func,因为EF使用Expression生成SQL查询,典型的用法是

ICollection<WFGroup> wgGroups = this.dataContext.Find<WFGroup>((w) => true).ToList();
ICollection wgGroups=this.dataContext.Find((w)=>true.ToList();

WFGroup是从BaseEntity派生的类,我通常使用延迟加载和代理,不将对象分离/附加到上下文。

最近有一篇非常好的文章介绍了这一点,即标题为“返回IQueryable的存储库”。它是这样说的:

我们使用repository模式的原因之一是封装fat查询。这些查询使得ASP.NET MVC控制器中的操作难以读取、理解和测试。此外,随着应用程序的增长,在多个位置重复fat查询的可能性也会增加。使用repository模式,我们将这些查询封装在存储库类中。其结果是更纤细、更清洁、更易于维护和测试操作。考虑这个例子:

var orders = context.Orders
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);
在这里,我们直接使用DbContext,而不使用存储库模式。当您的存储库方法返回IQueryable时,其他人将获得该IQueryable并在其上组合查询。结果如下:

var orders = repository.GetOrders()
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);
你能看到这两段代码之间的区别吗?唯一的区别是第一行。在第一个示例中,我们使用context.Orders,在第二个示例中,我们使用repository.GetOrders()。那么,这个存储库解决了什么问题?没什么

存储库应该返回域对象。因此,GetOrders()方法应该返回一个IEnumerable。这样,第二个示例可以重写为:

var orders=repository.GetOrders(1234)

看到区别了吗

因此,我在团队中添加了以下编码约定:

对于存储库类方法,永远不要返回IQueryable对象。始终首先枚举或转换它(例如,ToArrayToList可计算的


理由是,IQueryable将允许调用方在此基础上进行构建,并最终修改在数据库上执行的SQL查询。这在DB性能方面可能是危险的,但更多的是关于SoC。调用方不关心数据源;它只需要数据。

在哪里可以根据过去的DAL发出查询?我应该将IQueryable传递给控制器吗?视图?如果使用
IEnumerable
,您仍然可以使查询在数据库中运行,
IQueryable
的文档称其旨在实现查询提供程序,而不是进行此区分。我更喜欢在查询数据库完成后不使用IEnumerable,因为使用集合类型可以确保返回的是实际结果,而不是错误返回的未评估查询。这类问题要么与性能调整有关,在这种情况下,应使用所有考虑的选项来衡量性能(并且选择了性能最好的)或者是架构方面的考虑。对于后者,我建议向控制器公开IQueryable是可以接受的(因为控制器对域对象的知识是可以接受的),但是视图/视图模型应该是哑的,并且不知道这些事情……但这只是我的观点!@makerofthings7:在严格的分层体系结构中,在DAL之外的任何地方发出DB支持的查询都是不合适的。在内存中执行查询可以在任何地方转换数据结构,而且您无论如何也不能阻止它,因为所有的收集ns可以在内存中查询。我认为IQueryable永远都不应该被返回,而且我已经发布了一个关于它的声明
var orders = repository.GetOrders()
    .Include(o => o.Details)
    .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);