通过EF/Linq投影到KeyValuePair

本文关键字:KeyValuePair 投影 Linq EF 通过 | 更新日期: 2023-09-27 18:19:55

我正试图从EF/Linq查询中加载KeyValuePairs列表,如下所示:

return (from o in context.myTable 
select new KeyValuePair<int, string>(o.columnA, o.columnB)).ToList();

我的问题是,这导致错误

"中只支持无参数构造函数和初始值设定项LINQ to实体。"

有什么简单的方法可以解决这个问题吗?我知道我可以为此创建一个自定义类,而不是使用KeyValuePair,但这似乎是在重新发明轮子。

通过EF/Linq投影到KeyValuePair

只从表中选择列A和列B,并在内存中移动进一步的处理:

return context.myTable
              .Select(o => new { o.columnA, o.columnB }) // only two fields
              .AsEnumerable() // to clients memory
              .Select(o => new KeyValuePair<int, string>(o.columnA, o.columnB))
              .ToList();

还可以考虑创建包含KeyValuePairs的字典:

return context.myTable.ToDictionary(o => o.columnA, o => o.columnB).ToList();

由于LINQ to Entities不支持KeyValuePair,因此您应该先使用AsEnumerable转到LINQ to Object:

return context.myTable
              .AsEnumerable()
              .Select(new KeyValuePair<int, string>(o.columnA, o.columnB))
              .ToList();

对于最新版本的EF Core,选择KeyValuePair似乎可以很好地工作。我还验证了(在MS SQL Server上)SQL只选择两个相关字段;选择";实际翻译为:

return context.myTable 
    .Select(x => new KeyValuePair<int, string>(o.columnA, o.columnB))
    .ToList();
SELECT [m].[columnA], [m].[columnB]
FROM [myTable] AS [m]

生成的SQL与匿名类相同,只是只为后者("例如as A")进行重命名:

return context.myTable 
    .Select(x => new {A = o.columnA, B = o.columnB})
    .ToList();
SELECT [m].[columnA] AS [A], [m].[columnB] AS [B]
FROM [myTable] AS [m]

使用.NET 6进行了测试。

请注意,与KeyValuePairs列表相比,使用Dictionary(如其他答案中所建议的)将引入额外的约束:

  • Dictionary条目的顺序未定义。因此,虽然一个(当前)通常具有相同的元素顺序来填充和枚举Dictionary,但这并不能保证(我已经看到过重新排序的情况),并且可能在未来发生更改而不另行通知
  • Dictionary中的键值必须是唯一的(这可能适用于也可能不适用于数据库中的columnA,如果columnA不适用,则会导致异常)
  • 字典不允许";空";对于键(这只是为了完整性,因为OP无论如何都使用"int"类型作为键)
  • Dictionary保留其键的索引,因此设置它可能需要比只拥有KeyValuePairs列表更长的时间(尽管这是微观优化…)

根据OP的用例(我无法从这个问题中推断出),其中一些约束可能是可取的,而Dictionary实际上可能是解决方案。

还有一种选择,当您想为一个键存储多个值时,存在一种称为查找的东西

表示键的集合,每个键映射到一个或多个值。

这是一些官方文件。

更多超过的查找似乎比Dictionary<TKey,列表<T值>>