在Linq Select中创建元组

dohp0rv5  于 2023-05-20  发布在  其他
关注(0)|答案(7)|浏览(124)

我正在使用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)”方法,并且此方法无法转换为存储表达式。
问题出在哪里?

cbeh67ev

cbeh67ev1#

虽然octavioccl的答案是可行的,但最好先将查询结果投影到匿名类型,然后切换到enumerable并将其转换为元组。这样,您的查询将只从数据库中检索所需的字段。

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

**注:**以上规则适用于EF 6。EF Core通过元组构造器自然地支持元组(在投影中或作为连接/组键),例如原始查询仅起作用

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

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

bqf10yzr

bqf10yzr2#

这只是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。
更多关于choosing-between-anonymous-and-tuple的文档

jdzmm42g

jdzmm42g3#

试试这个:

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

已被告知这在LINQ to实体中不接受。
另一种选择是在选择之前将结果拉入内存。如果你要这样做,我建议你在.AsEnumerable()之前完成所有的过滤,因为这意味着你只会拉回你想要的结果,而不是拉回整个表然后过滤。

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

如果您想让代码在元组类型中更显式一些,Tuple.Create(c.Id,c.Flag)也可以更改为newTuple(c.Id,c.Flag)

mf98qq94

mf98qq944#

linq to entities中,你可以投影到匿名类型或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而不是ToList的好处是AsEnumerable不执行查询,它保留了延迟执行。在调用这些方法之前,最好先过滤数据。

b5lpy0ml

b5lpy0ml5#

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

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);
iaqfqrcu

iaqfqrcu6#

我的两分钱:这让我在类型名称上遇到了几次麻烦:
举几个简单的例子:

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);
    }

问候。

p5fdfcr1

p5fdfcr17#

您也可以使用record
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/records

private record Code(string Id, byte Flag);

那就像

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

稍后,您可以使用命名属性而不是.Item1、.Item2

var code = codes.First();
...code.Id; 
...code.Flag;

相关问题