配置/构建包含两个id指向同一个实体的表



我正在努力用ef core构建和配置一个表。

我有/想要一个实体/表,显示两个实体之间的关系。

所以我有Book2,它是Book1的续集,Book2与Book3有相同的字符,等等。

我有一个简单的想法,使用下面的模型,它只保存两个项目的id,然后有定义关系的属性和可选的描述。

public class MangaRelation : BaseEntity
{
public int ParentId { get; set; }
public int ChildId { get; set; }
public virtual Manga? Parent { get; set; }
public virtual Manga? Child { get; set; }
public RelationType RelationType { get; set; }
public string? Description { get; set; }
}

我还有另一个模型,我想要推荐,其中的概念类似,我们也有两个id指向同一个表。

Book1被推荐为Book2,然后是用户xy认为如果你阅读Book1,应该推荐Book2的原因。

模型是这样的:

public class MangaRecommendation : BaseEntity
{
public int MangaId { get; set; }
public int RecommendedMangaId { get; set; }
public virtual Manga? Manga { get; set; }
public virtual Manga? RecommendedManga { get; set; }
public string? Reason { get; set; }
}

我缩短了模型,只显示与这个问题相关的属性。

public partial class Manga : BaseEntity
{
public int MangaId { get; set; }
public virtual ICollection<MangaRelation> Relations { get; set; } = new HashSet<MangaRelation>();
public virtual ICollection<MangaRecommendation> Recommendations { get; set; } = new HashSet<MangaRecommendation>();
}

在DbContext中,我这样配置表:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
RegisterDbFunctions(modelBuilder);
modelBuilder.Entity<Manga>(e =>
{
e.HasKey(p => p.MangaId);
e.HasMany(p => p.RelationShips).WithOne(m => m.Parent).OnDelete(DeleteBehavior.Cascade);
e.HasMany(p => p.Recommendations).WithOne(m => m.Manga).OnDelete(DeleteBehavior.Cascade);
});       
modelBuilder.Entity<MangaRelation>(e =>
{
e.HasKey(k => new { k.MangaParentId, k.MangaChildId });
e.HasIndex(p => p.MangaParentId);
e.HasIndex(p => p.MangaChildId);
e.Property(p => p.MangaParentId);
e.Property(p => p.MangaChildId);
e.HasOne(p => p.Parent).WithMany().HasForeignKey(k => k.MangaParentId).HasConstraintName("FK_Manga_Parent")
.OnDelete(DeleteBehavior.NoAction);
e.HasOne(p => p.Child).WithMany().HasForeignKey(k => k.MangaChildId).HasConstraintName("FK_Manga_Child")
.OnDelete(DeleteBehavior.NoAction);
e.Property(p => p.RelationType).HasConversion<int>();
e.Property(p => p.Description);
});
modelBuilder.Entity<MangaRecommondation>(e =>
{
e.HasKey(k => new { k.MangaId, k.RecommendedMangaId });
e.HasIndex(p => p.MangaId);
e.HasIndex(p => p.RecommendedMangaId);
e.Property(p => p.Reason);
e.Property(p => p.MangaId);
e.HasOne(p => p.Manga).WithMany().HasForeignKey(k => k.MangaId).HasConstraintName("FK_MangaParent_Rec")
.OnDelete(DeleteBehavior.NoAction);
e.HasOne(p => p.RecommendedManga).WithMany().HasForeignKey(k => k.RecommendedMangaId)
.HasConstraintName("FK_MangaRecParent_Rec").OnDelete(DeleteBehavior.NoAction);
e.Property(p => p.RecommendedMangaId);
});
}

如果我使用ef core工具来创建迁移文件,它说不能按照约定配置关系,然后工具执行错误,并给出这个奇怪的堆栈跟踪和这个奇怪的消息(所有项目都启用了空上下文):

System.InvalidOperationException: Cannot scaffold C# literals of type 'System.Reflection.NullabilityInfoContext'. The provider should implement CoreTypeMapping.GenerateCodeLiteral to support using it at design time.
at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.UnknownLiteral(Object value)
at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.<Fragment>g__AppendMethodCall|52_0(IMethodCallCodeFragment current, <>c__DisplayClass52_0&)
at Microsoft.EntityFrameworkCore.Design.Internal.CSharpHelper.Fragment(IMethodCallCodeFragment fragment, Int32 indent)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.GenerateAnnotations(String builderName, IAnnotatable annotatable, IndentedStringBuilder stringBuilder, Dictionary`2 annotations, Boolean inChainedCall, Boolean leadingNewline)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpSnapshotGenerator.Generate(String modelBuilderName, IModel model, IndentedStringBuilder stringBuilder)
at Microsoft.EntityFrameworkCore.Migrations.Design.CSharpMigrationsGenerator.GenerateMetadata(String migrationNamespace, Type contextType, String migrationName, String migrationId, IModel targetModel)
at Microsoft.EntityFrameworkCore.Migrations.Design.MigrationsScaffolder.ScaffoldMigration(String migrationName, String rootNamespace, String subNamespace, String language)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Cannot scaffold C# literals of type 'System.Reflection.NullabilityInfoContext'. The provider should implement CoreTypeMapping.GenerateCodeLiteral to support using it at design time.

不是直接答案,但似乎Manga属性不应该被标记为可空(即Manga?)。我认为,只有当两种关系都存在时,在这些表中保存记录才有意义。它也与Id的空属性(Parent/Child, Manga/recommendmanga,这些属性都是不可空的)相矛盾:

public class MangaRelation : BaseEntity
{
public int ParentId { get; set; }
public int ChildId { get; set; }
public virtual Manga Parent { get; set; }
public virtual Manga Child { get; set; }
public RelationType RelationType { get; set; }
public string? Description { get; set; }
}
public class MangaRecommendation : BaseEntity
{
public int MangaId { get; set; }
public int RecommendedMangaId { get; set; }
public virtual Manga Manga { get; set; }
public virtual Manga RecommendedManga { get; set; }
public string? Reason { get; set; }
}

最新更新