【问题标题】:How to avoid repeating ToLists when using LINQ and Entity Framework?使用 LINQ 和实体框架时如何避免重复 ToList?
【发布时间】:2021-05-11 08:45:28
【问题描述】:

我有一个DbSet<T>,我需要将其投影到不同的类型上。对于投影,我使用Select

如果我直接在我的DbSet<T> 上拨打Select 就像:

private IEnumerable<PersonPOCO> _getPersons(ILocator loc)
{
    using(var service = loc.GetService())
    {
        return service.GetPersons().Select(p => Mapper.ToPoco(p));
    }
}

这将抛出NotSupportedException,因为实体框架无法识别Select(这很明显)。

所以我需要通过调用ToList() 来“具体化”列表:

private IEnumerable<PersonPOCO> _getPersons(ILocator loc)
{
    using(var service = loc.GetService())
    {
        return service.GetPersons()
                      .ToList()
                      .Select(p => Mapper.ToPoco(p));
    }
}

如果我这样离开它,Select 是否“懒惰”只会在真正枚举列表时才被评估。但是到那时,服务使用的 Context 将被释放(在using 语句之外),所以我将拥有一个DbContextDisposedException

所以我需要直接枚举using里面的列表

private IEnumerable<PersonPOCO> _getPersons(ILocator loc)
{
    using(var prov = loc.GetService())
    {
        return prov.GetPersons()
            .ToList()
            .Select(p => Mapper.ToPOCO(p))
            .ToList();
    }
}

有没有办法避免在同一条指令中调用ToList() 两次?

【问题讨论】:

  • EF 确实“识别”Select,它不知道如何处理 Mapper.ToPoco。那是什么?
  • 第二个ToList 是不必要的,因为第一个会立即枚举集合。只有 Select 被延迟评估,此时它将在内存中的列表上运行。
  • SelectAsEnumerable(),而不是ToList() 之前,技术上你需要的(如果你不能重写Select 以通过用映射表达式替换映射器调用来翻译)。跨度>
  • @JohnathanBarclay 很好,是的,但是如果你要在本地缓冲数据,你不妨投影之后再做,这样如果它被多次消耗,投影就不会重复
  • @MarcGravell 是的,你说得对。评论更多是因为 OP 认为省略第二个 ToList 会导致异常。

标签: c# entity-framework linq


【解决方案1】:

这里的问题是 EF 不知道如何处理 DataViewMapper.ToPOCO。您可以使用AsEnumerable() 切换到 LINQ-to-Objects:

using (var prov = arg.GetProvider())
{
    return prov.GetParamTypes()
        .AsEnumerable()
        .Select(p => DataViewMapper.ToPOCO(p))
        .ToList();
}

这避免了中间列表/数组/等分配。

【讨论】:

  • 问题是我看到AsEnumerable 就像ToList 一样的样板,所以它不会让我免于额外的查询来进行查询。我会建议您更改DataViewMapper.ToPOCO 返回Expression,这实际上可以为我节省一条指令。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-20
  • 1970-01-01
  • 2020-07-21
  • 2020-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多