【问题标题】:Returning IEnumerable from Repository using ToList() vs using IList使用 ToList() 从存储库返回 IEnumerable 与使用 IList
【发布时间】:2014-01-30 04:26:30
【问题描述】:

2017 年 3 月 31 日更新

自从这篇文章之后我又学到了一些东西,所以我想给出一个重要的理由,从存储库返回时使用 ToList - 调用 ToList 将(使用 IQueryable 时)在数据库上执行翻译后的 SQL而不是将记录拉入内存然后过滤。我不相信隐式转换为 IEnumerable 或 IList 会这样做。


按照 MSDN 网站上的一些教程,我在我的应用程序中使用了通用存储库层。这个存储层由我的服务层调用,而服务层又由控制器调用。

查看通用存储库,获取数据并通过调用ToList() 返回。但是,该方法的返回类型是IEnumerable,这意味着服务层必须接受IEnumerable,并且在返回到控制器之前,它必须再次IEnumerable 上调用ToList()

示例 - 存储库:

public IEnumerable<TEntity> Get(
    Expression<Func<TEntity, bool>> filter = null,
    Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
    string includeProperties = "")
{
    IQueryable<TEntity> query = dbSet;

    if (filter != null)
    {
        query = query.Where(filter);
    }

    foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
    {
        query = query.Include(includeProperty);
    }

    return orderBy != null ? orderBy(query).ToList() : query.ToList();
}

示例 - 服务层

public IList<DOC> SearchDocuments(string docInfo)
{
    // build the predicate and logic, etc

    // grab all the matching documents with the relationships
    IEnumerable<DOC> documents = _unitOfWork.DocumentRepository.Get(
        predicate,
        p => p.OrderBy(d => d.DocInfo),
        "DocRelationship, DocRelationship.OtherDocTable");

    return documents.ToList();
} 

从我所做的阅读来看,最好向控制器提供它需要的对象的List,而不是IEnumerableIQueryable

因此,我认为最好不要返回IEnumerable,而是返回IList。我在这里遗漏了什么吗?为什么 MSDN 团队选择IEnumerable 进行设计并致电ToList?这在哪里有用??在我看来,多次进行这种转换似乎效率低下且毫无意义,尤其是在处理大量数据时。

如果不清楚或已经有答案,我提前道歉,但我一直在搜索和阅读其他帖子 IEnumerable vs IQueryable vs IList,但我仍然不明白这个特定问题。

【问题讨论】:

  • 它们是不同的类型/接口,实体框架中的语义也不同(在 IQueryable 上调用 ToList() 会在此时具体化查询,因此如果您对查询有进一步的改进,它们'正在内存中而不是在数据库中完成)。

标签: c# asp.net-mvc performance repository-pattern


【解决方案1】:

返回适用于您的特定情况的最小接口

通常IEnumerable&lt;T&gt; 就足够了并且提供了更大的灵活性,但是如果您发现自己需要经常使用.ToList()(并且出于正当理由)返回IList&lt;T&gt; 甚至List&lt;T&gt;

注意事项

  • IEnumerable&lt;T&gt; 允许更轻松的惰性评估和单元测试(因为它比 IList 小 - 所以尽量坚持下去。
  • 您的样本没有显示调用.ToList() 的正当理由 - 结合IEnumerable&lt;T&gt; 直到您知道您正在寻找什么甚至可以让您获得更好的性能(即,如果经过多次检查您决定只需要一个元素但@ 987654329@ 将强制您获取 10000)。

【讨论】:

  • 使用IListList 的正当理由是什么?
  • @Cody 通常,一个好的方法是在面向公众的 API 中使用 IList(如果需要,并且需要列表语义),然后在内部使用 List 来实现 API。这允许您更改为 IList 的不同实现,而不会破坏使用您的类的代码。
  • 那么,在我的存储库层中,我是否应该改用 AsEnumerable 而不是 ToList 来返回 IEnumerable
  • @Cody 就您个人而言,我会从您的存储库层返回 IEnumerable。您没有理由在 IMO 提供的代码中使用 ToList
  • 好点!我坚持使用 IEnumerable 而不是 IList 的另一个原因是我们使用 Composite 和 Specification 模式,并且为了设置整个查询,让它返回 IQueryable 是一种简单的调试方法,然后调用 ToList IQueryable 在实际命中数据库之后。
【解决方案2】:

你很好。现在发生的主要事情是 repo 中的 .ToList() 实际上是对数据库执行查询。但是将该列表返回为 IEnumerable,您真正要做的就是将其装箱,以便结果是通用的且不可更改的。稍后拆箱会有一些开销,但由于存储库的职责是返回 REQUESTED 数据,因此您应该像这样返回它 imo。

我认为这可能是团队的想法。您要求提供此数据,所以在这里。如果您需要对其进行进一步编辑,则必须再次枚举它才能对该列表进行任何修改。这没什么不好。事实上,它支持 SoC。

通常,我的 repos 返回 IEnumerable,然后如果我的服务层需要返回 dto 或 viewmodel 之类的东西,我将使用该 IEnum 来构建新对象:

var myEnumerable = _uow.MyJunk.Get(j => j.Stuff.Where(a => a.OtherJunk == something);
var myDtos = myEnumerable.Select(obj => new DtoClass { Foo = obj.Foo, Bar = obj.Bar };
return myDtos;

【讨论】:

  • 你在说什么“拳击”?你可能有不同的意思,但它读起来就像你在某种程度上指的是值类型的装箱......
  • 我的错。我真的想不出另一个术语来作为界面返回某些东西。我知道“拳击”可能会误导某些人。
  • 澄清一下,我的意思是选角,但是在传达真实想法的同时,将“this as that”融入描述性句子中更难。
猜你喜欢
  • 1970-01-01
  • 2020-10-13
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 2021-12-12
  • 1970-01-01
  • 2014-05-08
  • 2011-03-11
相关资源
最近更新 更多