AFAIK, существует два способа настройки отношения 1-1:
- https://www.entityframeworktutorial.net/efcore/one-to-one-conventions-entity-framework-core.aspx
- https://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-2-table-per-type-tpt
1-й пост требует ссылки от каждой модели. но вы упомянули:
без добавления пустых ссылок
тогда только 2-я концепция (таблица на тип) может достичь вашей цели, но пост слишком старый код для 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");
}
}
Вот скриншот для результата:
