如果存储库返回IEnumerable <t>,IQueryable <t>或List <t>?</t> </t> </t>

时间:2011-11-12 02:39:56

标签: entity-framework-4.1 repository appfabric azure-appfabric appfabric-beta-2

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

  

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

我正在考虑使用的技术是

  • Azure App Fabric缓存

  • 实体框架4.1

  • 可能是Windows Server AppFabric

4 个答案:

答案 0 :(得分:9)

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

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

答案 1 :(得分: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;
   }

其中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; }
}

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

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

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

答案 2 :(得分:4)

您有多大可能需要从DAL返回IEnumerable(不是集合)的自定义实现? (要回答这个问题,请查看以前的项目,并计算您有多少人或yield return个人。)

如果答案是“不是很”,我只会返回ICollection甚至数组(如果你想阻止无意中修改查询结果。)在紧要关头,如果你需要改变使用自定义IEnumerable“流式”结果的查询,您始终可以使用旧方法调用新方法并实现结果以保持与旧客户端的兼容性。

答案 3 :(得分:1)

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

  

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

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 对象。始终首先枚举或转换它(例如, ToArray ToList AsEnumerable )。

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