简体   繁体   English

与 EF Core 的一对一关系

[英]One-way one-to-one relationship with EF Core

What I want to achieve, is have a model CommentsThread that can be attached to any other model that needs comments, be it Chapter , BlogPost , UserProfile , or what have you.我想要实现的是有一个模型CommentsThread ,它可以附加到任何其他需要评论的模型,无论是ChapterBlogPostUserProfile ,还是你有什么。 Basically, the structure I need is基本上,我需要的结构是

CommentsThread
  int ID

Chapter
  int ID
  int Thread FK(CommentsThread.ID)

BlogPost
  int ID
  int Thread FK(CommentsThread.ID)

UserProfile
  int ID
  int Thread FK(CommentsThread.ID)

however I have no idea how to configure it properly in EF core, without having to add nullable references to Chapter , BlogPost and UserProfile to the CommentsThread model.但是我不知道如何在 EF 核心中正确配置它,而不必向CommentsThread模型添加对ChapterBlogPostUserProfile可为空引用。

My code for CommentsThread is as follows:我的CommentsThread代码如下:

public class CommentsThread
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public ICollection<Comment> Comments { get; set; }
}

And Chapter , omitting any irrelevant properties, isChapter ,省略了任何不相关的属性,是

public class Chapter
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public CommentsThread CommentsThread { get; set; }
}

With the following Fluent config (again, skipping the irrelevant bits):使用以下 Fluent 配置(再次跳过不相关的位):

builder.Entity<CommentsThread>()
    .HasMany(ct => ct.Comments)
    .WithOne()
    .OnDelete(DeleteBehavior.Cascade);

builder.Entity<Chapter>()
    .HasOne(c => c.CommentsThread)
    .WithOne()
    .OnDelete(DeleteBehavior.Cascade);

On dotnet ef migrations add X it throws this output into the console.dotnet ef migrations add X将此输出抛出到控制台。


Update:更新:

So, I tried explicitly adding public int CommentsThreadId { get; set; }所以,我尝试明确添加public int CommentsThreadId { get; set; } public int CommentsThreadId { get; set; } public int CommentsThreadId { get; set; } to the Chapter and changing the config to public int CommentsThreadId { get; set; }Chapter和改变配置来

builder.Entity<Chapter>()
    .HasOne(c => c.CommentsThread)
    .WithOne()
    .HasForeignKey<Chapter>(c => c.CommentsThreadId)
    .OnDelete(DeleteBehavior.Cascade);

That did make the migration go through, but failed on database update with这确实使迁移通过了,但在database update时失败了

The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.Chapters' and the index name 'IX_Chapters_CommentsThreadId'. The duplicate key value is (0).
The statement has been terminated.

AFAIK, there are two ways to configure 1-1 relationship: AFAIK,有两种方法可以配置 1-1 关系:

  1. https://www.entityframeworktutorial.net/efcore/one-to-one-conventions-entity-framework-core.aspx https://www.entityframeworktutorial.net/efcore/one-to-one-conventions-entity-framework-core.aspx
  2. https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt

the 1st post requires reference from each model.第一个帖子需要每个模型的参考。 but you mentioned:但你提到:

without having to add nullable references无需添加可为空引用

then, only the 2nd concept (Table Per Type) can reach your goal, but the post is too old code to EF Core.那么,只有第二个概念(每个类型的表)可以达到您的目标,但是该帖子对于 EF Core 来说太旧了。

Here are new data models:以下是新的数据模型:

public class CommentsThread
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

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

    public ICollection<ThreadComment> Comments { get; set; }
}

[Table(nameof(Chapter))]
public class Chapter
{
    [Key]
    [Required]
    [ForeignKey(nameof(CommentsThread))]
    public int Id { get; set; }

    public int Number { get; set; }

    public CommentsThread CommentsThread { get; set; }
}
[Table(nameof(BlogPost))]
public class BlogPost
{
    [Key]
    [Required]
    [ForeignKey(nameof(CommentsThread))]
    public int Id { get; set; }

    public string Author { get; set; }

    public CommentsThread CommentsThread { get; set; }
}

[Table(nameof(UserProfile))]
public class UserProfile
{
    [Key]
    [Required]
    [ForeignKey(nameof(CommentsThread))]
    public int Id { get; set; }
    public string Bio { get; set; }

    public CommentsThread CommentsThread { get; set; }
}

public class ThreadComment
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

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

    public CommentsThread CommentsThread { get; set; }
}

Here is sample context:这是示例上下文:

public class ApplicationDbContext : IdentityDbContext
{
    public virtual DbSet<CommentsThread> CommentsThreads { get; set; }
    public virtual DbSet<Chapter> Chapters { get; set; }
    public virtual DbSet<BlogPost> BlogPosts { get; set; }
    public virtual DbSet<UserProfile> UserProfiles { get; set; }
    public virtual DbSet<ThreadComment> ThreadComments { get; set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }


    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Chapter>().ToTable(nameof(Chapter));
        modelBuilder.Entity<BlogPost>().ToTable(nameof(BlogPost));
        modelBuilder.Entity<UserProfile>().ToTable(nameof(UserProfile));
    }
}

Here is generated migration code:这是生成的迁移代码:

public partial class AddCommentsThread : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "CommentsThreads",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Title = table.Column<string>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_CommentsThreads", x => x.Id);
            });

        migrationBuilder.CreateTable(
            name: "BlogPost",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false),
                Author = table.Column<string>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_BlogPost", x => x.Id);
                table.ForeignKey(
                    name: "FK_BlogPost_CommentsThreads_Id",
                    column: x => x.Id,
                    principalTable: "CommentsThreads",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

        migrationBuilder.CreateTable(
            name: "Chapter",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false),
                Number = table.Column<int>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Chapter", x => x.Id);
                table.ForeignKey(
                    name: "FK_Chapter_CommentsThreads_Id",
                    column: x => x.Id,
                    principalTable: "CommentsThreads",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

        migrationBuilder.CreateTable(
            name: "ThreadComments",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Content = table.Column<string>(nullable: false),
                CommentsThreadId = table.Column<int>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_ThreadComments", x => x.Id);
                table.ForeignKey(
                    name: "FK_ThreadComments_CommentsThreads_CommentsThreadId",
                    column: x => x.CommentsThreadId,
                    principalTable: "CommentsThreads",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Restrict);
            });

        migrationBuilder.CreateTable(
            name: "UserProfile",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false),
                Bio = table.Column<string>(nullable: true)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_UserProfile", x => x.Id);
                table.ForeignKey(
                    name: "FK_UserProfile_CommentsThreads_Id",
                    column: x => x.Id,
                    principalTable: "CommentsThreads",
                    principalColumn: "Id",
                    onDelete: ReferentialAction.Cascade);
            });

        migrationBuilder.CreateIndex(
            name: "IX_ThreadComments_CommentsThreadId",
            table: "ThreadComments",
            column: "CommentsThreadId");
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "BlogPost");

        migrationBuilder.DropTable(
            name: "Chapter");

        migrationBuilder.DropTable(
            name: "ThreadComments");

        migrationBuilder.DropTable(
            name: "UserProfile");

        migrationBuilder.DropTable(
            name: "CommentsThreads");
    }
}

Here is screenshot for result:以下是结果截图:

在此处输入图片说明

Your database migration error;你的数据库迁移错误;

The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.Chapters' and the index name 'IX_Chapters_CommentsThreadId'. The duplicate key value is (0).
The statement has been terminated.

Is due to adding a non-null column to a table that already has data in it.是由于向已包含数据的表添加了非空列。 If you want to force a CommentsThread to exist for each record type, you'll also need to write a migration script to insert these records for existing data.如果您想为每种记录类型强制存在一个CommentsThread ,您还需要编写一个迁移脚本来为现有数据插入这些记录。

Or just change the foreign key column type to int?或者只是将外键列类型更改为int? so it will allow nulls.所以它将允许空值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM