繁体   English   中英

与 EF Core 的一对一关系

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

我想要实现的是有一个模型CommentsThread ,它可以附加到任何其他需要评论的模型,无论是ChapterBlogPostUserProfile ,还是你有什么。 基本上,我需要的结构是

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)

但是我不知道如何在 EF 核心中正确配置它,而不必向CommentsThread模型添加对ChapterBlogPostUserProfile可为空引用。

我的CommentsThread代码如下:

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

Chapter ,省略了任何不相关的属性,是

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

    public CommentsThread CommentsThread { get; set; }
}

使用以下 Fluent 配置(再次跳过不相关的位):

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

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

dotnet ef migrations add X将此输出抛出到控制台。


更新:

所以,我尝试明确添加public int CommentsThreadId { get; set; } public int CommentsThreadId { get; set; } public int CommentsThreadId { get; set; }Chapter和改变配置来

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

这确实使迁移通过了,但在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,有两种方法可以配置 1-1 关系:

  1. 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

第一个帖子需要每个模型的参考。 但你提到:

无需添加可为空引用

那么,只有第二个概念(每个类型的表)可以达到您的目标,但是该帖子对于 EF Core 来说太旧了。

以下是新的数据模型:

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; }
}

这是示例上下文:

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));
    }
}

这是生成的迁移代码:

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");
    }
}

以下是结果截图:

在此处输入图片说明

你的数据库迁移错误;

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.

是由于向已包含数据的表添加了非空列。 如果您想为每种记录类型强制存在一个CommentsThread ,您还需要编写一个迁移脚本来为现有数据插入这些记录。

或者只是将外键列类型更改为int? 所以它将允许空值。

暂无
暂无

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

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