Repository应该返回IEnumerable <T>,IQueryable <T>还是List <T>?

goo*_*ate 16 repository appfabric appfabric-beta-2 azure-appfabric entity-framework-4.1

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

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

我正在考虑使用的技术是

  • Azure App Fabric缓存

  • 实体框架4.1

  • 可能是Windows Server AppFabric

Ric*_*lly 9

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

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

  • 这类问题要么与性能调整有关,在这种情况下,应该使用所有考虑的选项(以及所选择的最高性能)或者根据架构考虑来衡量性能.在后者的情况下,我建议将一个IQueryable暴露给控制器是可以接受的(因为控制器对域对象的知识是可以的),但是视图/视图模型应该是愚蠢的,并且不了解这些事情......但是这个只是我的意见! (2认同)
  • @ makerofthings7:在严格的分层体系结构中,不能在DAL之外的任何地方发出数据库支持的查询。可以进行内存中查询以将数据结构转换到任何地方,而且您也无法阻止它,因为所有集合都可以在内存中查询。 (2认同)

Neo*_*Neo 6

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

\n\n
\n

我们使用存储库模式的原因之一是封装胖查询。这些查询使得阅读、理解和测试 ASP.NET MVC 控制器中的操作变得困难。此外,随着应用程序的增长,在多个位置重复胖查询的机会也会增加。通过存储库模式,我们将这些查询封装在存储库类中。结果是更精简、更干净、更易于维护且更易于测试的操作。考虑这个例子:

\n\n
var orders = context.Orders\n    .Include(o => o.Details)\n    .ThenInclude(d => d.Product)\n    .Where(o => o.CustomerId == 1234);\n
Run Code Online (Sandbox Code Playgroud)\n\n

这里我们直接使用 DbContext,而不使用存储库模式。当您的存储库方法返回 IQueryable 时,其他人将获取该 IQueryable 并在其上编写查询。这里\xe2\x80\x99是结果:

\n\n
var orders = repository.GetOrders()\n    .Include(o => o.Details)\n    .ThenInclude(d => d.Product)\n    .Where(o => o.CustomerId == 1234);\n
Run Code Online (Sandbox Code Playgroud)\n\n

你能看出这两个代码片段之间的区别吗?唯一的区别在于第一行。在第一个示例中,我们使用context.Orders,在第二个示例中,我们使用repository.GetOrders()。那么,这个存储库解决了什么问题?没有什么!

\n\n

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

\n\n

var 订单=repository.GetOrders(1234);

\n\n

看到不同?

\n
\n\n

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

\n\n
\n

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

\n
\n\n

原因是IQueryable将允许调用者在此基础上进行构建并最终修改在数据库上执行的 SQL 查询。这对于数据库性能而言可能存在潜在危险,但更多的是与 SoC 相关。调用者不关心数据源;它只想要数据。

\n


np-*_*ard 5

我想说使用IQueryable构建你的DAL,并传递它,确保你的对象争用生命周期是请求.这样您就可以获得延迟执行的好处,但是会面临数据库查询效率低下的风险.

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

存储库接口的示例就像

  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;
   }
Run Code Online (Sandbox Code Playgroud)

其中BaseEntity是我们所有类的基类,看起来,这个类没有映射到db中的任何表

public abstract class BaseEntity
{
        public int Id { get; set; }
        public DateTime CreateDateTime { get; set; }
        public string CreateUser { get; set; }
        public DateTime ModDateTime { get; set; }
        public string ModUser { get; set; }
        public byte[] RowVersion { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

Expression<Func<T, bool>> 将整个表达式传递给您的存储库而不仅仅是Func,因为EF在表达式上工作以生成sql查询,典型的用途是

ICollection<WFGroup> wgGroups = this.dataContext.Find<WFGroup>((w) => true).ToList();
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为这个答案现在已经过时和错误的建议。**IQueryable** 应该**不** 出于多种原因,其中一个原因是潜在的 DB 滥用是危险的,但更多的是与 SoC 相关。我已经发布了一个 [answer](/sf/answers/3251333081/) 解释了我的意思,并参考了最近关于此事的文章。 (2认同)