【问题标题】:Many-to-many with TPH inheritance in EF CoreEF Core 中的多对多与 TPH 继承
【发布时间】:2021-11-16 20:49:59
【问题描述】:

如果连接表的一侧是按层次结构设置的表中的派生类,我在使用 EF 核心设置多对多连接表时遇到问题。

设置如下:

class Chore
{
    Guid Id;
}

class LaundryChore : Chore
{
    // PROBLEMATIC
    List<Clothing> ManyClothing;   
}

class FoldingChore : Chore
{
    Clothing SingleClothing;   
}

class Clothing
{
    Guid Id;
    
    // PROBLEMATIC
    List<Chore> Chores;
}

我用鉴别器设置了 TPH,一切正常。如果 ManyClothing 字段在 Chore 类上,那么我可以这样做:

builder.Entity<Clothing>().HasMany(clothing => clothing.Chores)
    .WithMany(chore => chore.ManyClothing);

这按预期工作。

但由于 ManyClothing 字段在 LaundryChore 类上,我会收到上面的 DNE 错误。

我试着换个方向:

builder.Entity<LaundryChore>().HasMany(chore => clothing.ManyClothing)
    .WithMany(clothing => clothing.Chores);

我得到一个转换错误:

无法将类型“System.Collections.Generic.List”隐式转换为“System.Collections.Generic.IEnumerable”

如果我改为:

class Clothing
{
    Guid Id;

    List<LaundryChore> Chores;
}

那么我得到的错误是:

无法为实体类型“LaundryChore”指定过滤器表达式 ...。过滤器只能应用于根实体类型“杂项”

任何指导将不胜感激 - 谢谢!

【问题讨论】:

  • Clothing 中的集合导航属性必须为 List&lt;LaundryChore&gt;。那么过滤器错误是一个不同的问题,是由您没有显示的代码引起的。

标签: c# entity-framework-core many-to-many ef-fluent-api table-per-hierarchy


【解决方案1】:

一个导航属性只能参与一个关系。

不是限制类型,而是关系限制。例如,如果您只有 Chore 和 Clothing 类:

public class Chore
{
    public int Id { get; set; }
    public List<Clothing> ManyClothingToLaundry { get; set; }
    public Clothing SingleClothingToFolding { get; set; }
}

public class Clothing
{
    public int Id { get; set; }
    public List<Chore> Chores { get; set; }
}

现在,您想为服装添加杂项:

clothing.Chores.Add(chore);

这会给洗衣或折叠带来麻烦吗?在这种情况下 EF Core 无法知道。

在您的特定情况下,EF Core 可以从类型中检测到关系,但未实现此功能。

如果 Clothing 有两个不同的关系,那么 Clothing 需要两个导航属性:

public class Clothing
{
    public int Id { get; set; }
    public List<Chore> FoldingChores { get; set; }
    public List<Chore> LaundryChores { get; set; }
    public IEnumerable<Chore> Chores => FoldingChores.Union(LaundryChores);
}

...

protected override void OnModelCreating(ModelBuilder builder)
{
    ...
    builder.Entity<Clothing>().Ignore(c => c.Chores);
    builder.Entity<Clothing>()
        .HasMany<LaundryChore>(nameof(Clothing.LaundryChores))
        .WithMany(chore => chore.ManyClothing);
    builder.Entity<Clothing>()
        .HasMany<FoldingChore>(nameof(Clothing.FoldingChores))
        .WithOne(chore => chore.SingleClothing);
}

Clothing.FoldingChores 是基础实体Chore 的集合,但也可以直接是结局类型FoldingChore 的集合。与Clothing.LaundryChores 同上:

public class Clothing
{
    public int Id { get; set; }
    public List<FoldingChore> FoldingChores { get; set; }
    public List<LaundryChore> LaundryChores { get; set; }
    public IEnumerable<Chore> Chores => FoldingChores.Cast<Chore>().Union(LaundryChores.Cast<Chore>());
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 2021-03-06
    • 2019-11-07
    • 1970-01-01
    • 2022-10-25
    • 1970-01-01
    相关资源
    最近更新 更多