无法确定类型 * 的导航 * 表示的关系。手动配置关系,或忽略此 属性 使用

Unable to determine the relationship represented by navigation * of type *. Either manually configure the relationship, or ignore this property using

我有 .NET 5.0.0 控制台应用程序,它使用 Microsoft.EntityFrameworkCore.Sqlite 5.0.0。我已经安装了 Microsoft.EntityFrameworkCore.Sqlite 5.0.0、Microsoft.AspNetCore.Identity.EntityFrameworkCore 5.0.0(对于 IdentityUser<>)和 Microsoft.EntityFrameworkCore.Design 5.0.0(对于 dotnet-ef 迁移 命令)nuget 包。

我的模型代表一组测试,其中用户 select 其他用户,在其他一些测试用户中做同样的事情并留下一些评论(为了测试目的大大简化了模型)。所以我的 TestAnswer classes 使用三级继承:

public class TestAnswerBase
{
   public Int64 Id { get; set; }
   [Required]
   public Int64 UserId { get; set; }
   public virtual User User { get; set; }
}


public partial class TestAnswerChild : TestAnswerBase
{
   [Required]
   public Int64 ChosenId { get; set; }
   public virtual User Chosen { get; set; }
}

//If I use this TestAnswerGrandChild variant - it shows an error while creating a migration:
//"Unable to determine the relationship represented by navigation 'TestAnswerChild.Chosen' 
//of type 'User'. Either manually configure the relationship, or ignore this property using 
//the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'."
public partial class TestAnswerGrandChild : TestAnswerChild
{
   public string Comment { get; set; }
}    

我的用户class(它有用户select编辑某人的测试集合和某人选择用户的集合):

public partial class User : IdentityUser<Int64>
{
    public User()
    {
        TestAnswerChildChosen = new HashSet<TestAnswerChild>();
        TestAnswerChildUser = new HashSet<TestAnswerChild>();
        TestAnswerGrandChildChosen = new HashSet<TestAnswerGrandChild>();
        TestAnswerGrandChildUser = new HashSet<TestAnswerGrandChild>();
        
    }

    [Required]
    public string Name { get; set; }

    [InverseProperty(nameof(TestAnswerChild.Chosen))]
    public virtual ICollection<TestAnswerChild> TestAnswerChildChosen { get; set; }

    [InverseProperty(nameof(TestAnswerChild.User))]
    public virtual ICollection<TestAnswerChild> TestAnswerChildUser { get; set; }

    [InverseProperty(nameof(TestAnswerGrandChild.Chosen))]
    public virtual ICollection<TestAnswerGrandChild> TestAnswerGrandChildChosen { get; set; }

    [InverseProperty(nameof(TestAnswerGrandChild.User))]
    public virtual ICollection<TestAnswerGrandChild> TestAnswerGrandChildUser { get; set; }
}

我的 DbContext 相当简单,但我在此处包含了它的代码:

public partial class MyDbContext : IdentityDbContext<User, IdentityRole<Int64>, Int64>
{
    public MyDbContext()
    {
    }

    public MyDbContext(DbContextOptions<MyDbContext> options)
        : base(options)
    {
    }
   
    public virtual DbSet<TestAnswerChild> TestAnswersChild { get; set; }
    public virtual DbSet<TestAnswerGrandChild> TestAnswersGrandChild { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlite("data source=test.db");
        }
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //is necessary because -> "The entity type 'IdentityUserLogin<long>' requires a primary key to be defined"
        base.OnModelCreating(modelBuilder);
    }
}

因此,当我调用 dotnet-ef migrations add InitialCreate 命令时,我得到一个错误:

Unable to determine the relationship represented by navigation 'TestAnswerChild.Chosen' of type 'User'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.

如果我像这样更改 TestAnswerGrandChild 代码(只有两级继承)- 它会创建一个没有错误的迁移:

//If I use that TestAnswerGrandChild variant - migration is created with no errors
    
public partial class TestAnswerGrandChild : TestAnswerBase
{
   [Required]
   public Int64 ChosenId { get; set; }
   public virtual User Chosen { get; set; }
   public string Comment { get; set; }
}

那么我的问题是什么以及如何解决它(我需要多级继承 - 大大简化了测试项目)?

我已经上传了我的测试项目here(运行更新migrations.cmd出现错误)

提前谢谢你。

So what is my problem and how to solve it

该问题是由多个因素和一些当前的 EF Core 缺点和实现细节共同引起的。

主要问题是,默认情况下,当某些基 class 用作 entity 时,EF Core 假定它应该使用可用的一种映射到数据库database inheritance 策略(目前为 TPH 和 TPT)。然而,正如我所理解的那样,你不希望这样——你使用对象层次结构只是为了代码的可重用性,最终是一些 polymorphic/generic 类型的处理,但你希望它们被 完全 映射到单独的表。

部分问题是没有数据注释。您必须流畅地指定:

modelBuilder.Entity<TestAnswerGrandChild>()
    .HasBaseType((Type)null);

但是,约定和数据注释在之前 OnModelCreating 应用。由于在这种情况下通过导航属性和注释定义的关系对任何现有数据库继承无效,因此 EF Core 会简单地忽略它们并生成异常要求您手动执行此操作。

不幸的是,这就是您应该做的 - 除了上述内容之外,之后,手动指定和配对导航属性,从而完全重新定义关系:

modelBuilder.Entity<TestAnswerGrandChild>()
    .HasBaseType((Type)null);

modelBuilder.Entity<TestAnswerChild>()
    .HasOne(e => e.Chosen)
    .WithMany(e => e.TestAnswerChildChosen);

modelBuilder.Entity<TestAnswerChild>()
    .HasOne(e => e.User)
    .WithMany(e => e.TestAnswerChildUser);

modelBuilder.Entity<TestAnswerGrandChild>()
    .HasOne(e => e.Chosen)
    .WithMany(e => e.TestAnswerGrandChildChosen);

modelBuilder.Entity<TestAnswerGrandChild>()
    .HasOne(e => e.User)
    .WithMany(e => e.TestAnswerGrandChildUser);