在Linq Select中创建元组


87

我正在使用C#和.NET Framework 4.5.1,使用Entity Framework 6.1.3从SQL Server数据库中检索数据。

我有这个:

codes = codesRepo.SearchFor(predicate)
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

当我运行它时,我收到以下消息:

LINQ to Entities仅支持无参数构造函数和初始化程序。

我不知道如何创建元组,因为我发现的所有示例大多都与此类似。

我已经试过了:

codes = codesRepo.SearchFor(predicate)
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

并得到这个错误:

LINQ to Entities无法识别方法'System.Tuple`2 [System.String,System.Byte] Create [String,Byte](System.String,Byte)'方法,并且该方法无法转换为商店表达式。

哪里有问题?


看起来您需要创建一个强类型的对象。是的,很好的问题。赞成。
frenchie 2015年

Answers:


118

答案octavioccl的作品,这是最好的第一个项目的查询结果为匿名类型,然后切换到枚举并将其转换为元组。这样,您的查询将仅从数据库中检索所需的字段。

codes = codesRepo.SearchFor(predicate)
    .Select(c => new { c.Id, c.Flag })
    .AsEnumerable()
    .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
    .ToList();

注意:以上规则适用于EF6。EF Core通过元组构造函数自然地支持元组(以投影或作为联接/组键),例如,原始查询就可以正常工作

codes = codesRepo.SearchFor(predicate)
  .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
  .ToList();

但不是Tuple.Create方法(EF Core 2.x)。


很好的解决方案-谢谢!...如果我必须将此解决方案扩展为另一个可为空的值怎么办?.Select(c => new { c.Id, c.Flag, c.Foo?.Code })不起作用。
skyfrog

2
@skyfrog?.表达式树中不支持该运算符。但是除此之外,您可以使用所需的任意数量的值扩展匿名类型-别忘了;不要忘记在需要时命名它们:)例如c => new { c.Id, c.Flag, Code = (int?)c.Foo.Code }
Ivan Stoev

1
大!非常感谢@Ivan的回复。我好近!...但是回头看总是很容易说
一遍

很好的答案,可以与EF实体一起使用。例如dbCtx.MyEntity.Where()。Select(..到匿名对象...)。etc ...
joedotnot

44

只是C#7的更新答案,现在您可以使用更简单的语法来创建ValueTuples。

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag })
.AsEnumerable()
.Select(c => (c.Id, c.Flag))
.ToList();

您甚至可以立即命名元组的属性:

codes = codesRepo.SearchFor(predicate)
.Select(c => new { c.Id, c.Flag }) // anonymous type
.AsEnumerable()
.Select(c => (Id: c.Id, Flag: c.Flag)) // ValueTuple
.ToList();

因此,您可以将其用作ID或Flag而不是将其用作Item1或Item2。

有关在匿名和元组之间选择的更多文档


11

试试这个:

codes = codesRepo.SearchFor(predicate)
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

被告知这在LINQ中不接受实体。

另一个选择是在选择之前将结果拉入内存。如果要执行此操作,我建议您在.AsEnumerable()之前进行所有过滤,因为这意味着您只回退了想要的结果,而不是回退整个表然后进行过滤。

codes = codesRepo.SearchFor(predicate).AsEnumerable()
  .Select(c => Tuple.Create(c.Id, c.Flag))
  .ToList();

如果您想使代码在元组类型中更加明确,则可以将Tuple.Create(c.Id,c.Flag)更改为新的Tuple(c.Id,c.Flag)


对不起,它不起作用。我已经用更多详细信息更新了我的问题。
VansFannel 2015年

11

linq to实体中,您可以投影到匿名类型或DTO上。要避免该问题,可以使用AsEnumerable扩展方法:

codes = codesRepo.SearchFor(predicate).AsEnumerable().
      .Select(c => new Tuple<string, byte>(c.Id, c.Flag))
      .ToList();

此方法使您可以使用Linq to Object而不是Linq to Entities进行操作,因此在调用它之后,您可以根据需要投影查询结果。使用AsEnumerable相反的优势ToListAsEnumerable不执行查询,它保留了延迟执行。最好在调用这些方法之一之前先过滤数据。


5

我找到了答案:

codes = codesRepo.SearchFor(predicate)
      .ToList()
      .Select(c => Tuple.Create(c.Id, c.Flag))
      .ToList();

不,这将产生SELECT *
Mihai Bratulescu

1

使用此方法执行此操作并使用异步。

var codes = await codesRepo.SearchFor(predicate)
                    .Select(s => new
                    {
                        Id = s.Id,
                        Flag = s.Flag
                    }).FirstOrDefaultAsync();

                var return_Value = new Tuple<string, byte>(codes.Id, codes.Flag);

0

只是我的两分钱:这让我迷恋了好几次:

一些点头的例子:

    private Tuple<string, byte> v1()
    {
        return new Tuple<string, byte>("", 1);
    }

    private (string, int) v2()
    {
        return ("", 1);
    }

    private (string Id, byte Flag) v3()
    {
        return ("", 1);
    }

问候。


您发布的语法不起作用。您可能想写的是public (string Id, byte Flag) SearchFor(Expression predicate),但这是无关紧要的。两美分不是答案,而是评论。
M.Stramm '19

2
我更新了我的答案-我应该在发布前检查它。我不同意; 所有信息对放置在此页面上的所有访问者都是有用的,无论其放置方式如何。评论不能传达意图,也不能通过答案来回答。
IbrarMumtaz

我确实同意添加的内容不错,注释不能很好地满足代码示例的要求。感谢您的编辑,现在很明显,这不是OP的问题的答案(但可能有助于解决与元组有关的问题)。
M.Stramm '19
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.