如何使用 Entity Framework Core 2.1 查询多对多关系?

How do you query a many-to-many relationship using Entity Framework Core 2.1?

我正在尝试使用 EF Core(Code First)select 来自多对多关系的数据,但出现以下错误:

Unhandled Exception: System.ArgumentException: must be reducible node

我认为我没有做任何特别奇怪的事情,我可以愉快地 运行 在 LinqPad 中使用 LinqToSQL 进行相关查询。希望有人能指出我哪里出错了?

我在控制台应用程序中创建了一个最小的(人为的)复制品,可以找到 here. Basically I have modelled the many to many relationship using a joining table as follows:

public class Foo
{
    [Key]
    public int Id { get; set; }

    public ICollection<FooBar> FooBars { get; set; } = new List<FooBar>();
}

public class Bar
{
    [Key]
    public int Id { get; set; }

    public string Value { get; set; }

    public ICollection<FooBar> FooBars { get; set; } = new List<FooBar>();
}

public class FooBar
{
    [Required]
    public int FooId { get; set; }

    [ForeignKey(nameof(FooId))]
    public Foo Foo { get; set; }

    [Required]
    public int BarId { get; set; }

    [ForeignKey(nameof(BarId))]
    public Bar Bar { get; set; }
}

我想从 Bar 对象创建 Value 的查找,使用 FooId 作为键,使用以下 query :

ILookup<int, string> data = 
    this.context.Foos
        .SelectMany(f => f.FooBars.Select(fb => new { f.Id, fb.Bar.Value }))
        .ToLookup(fb => fb.Id, fb => fb.Value);

在错误发生之前,我在控制台中收到以下警告:

Microsoft.EntityFrameworkCore.Query[20500] The LINQ expression 'from <>f__AnonymousType02 <generated>_0 in {from FooBar fb in value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1[ReducibleNodeDemo.FooBar]) join Bar fb.Bar in value(Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable1[ReducibleNodeDemo.Bar]) on Property([fb], "BarId") equals Property([fb.Bar], "Id") where ?= (Property([f], "Id") == Property([fb], "FooId")) =? select new <>f__AnonymousType02(Id = [f].Id, Value = [fb.Bar].Value)}' could not be translated and will be evaluated locally.

我对 EF Core 比较陌生,但多年来一直使用 EF6,并且从未遇到过像这样的简单查询的问题!

显然您遇到了当前的 EF Core 实现错误,因此请考虑将其报告给他们的问题跟踪器。

好像是SelectMany里面的Select和内部lambda里面使用了外参数f造成的。解决方法是对元素选择器使用 SelectMany 重载:

.SelectMany(f => f.FooBars, (f, fb) => new { f.Id, fb.Bar.Value })

我相信当您使用等效的 LINQ 查询语法时,C# 编译器会使用它

(from f in this.context.Foos
 from fb in f.FooBars
 select new { f.Id, fb.Bar.Value })

在这种特殊情况下,另一个 workaround/solution 是直接从 FooBar 开始查询(具有显式连接实体的多对多的优点之一)

this.context.Set<FooBar>()
    .Select(fb => new { fb.Foo.Id, fb.Bar.Value });