[英]One-way one-to-one relationship with EF Core
我想要实现的是有一个模型CommentsThread
,它可以附加到任何其他需要评论的模型,无论是Chapter
、 BlogPost
、 UserProfile
,还是你有什么。 基本上,我需要的结构是
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
模型添加对Chapter
、 BlogPost
和UserProfile
可为空引用。
我的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 关系:
第一个帖子需要每个模型的参考。 但你提到:
无需添加可为空引用
那么,只有第二个概念(每个类型的表)可以达到您的目标,但是该帖子对于 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.