ASP.NET Core MVC Code-First带枚举与索引的复杂关系配置问题
解决方案
前置实体调整
先删除InputFile类中int Id属性上的[Key]特性,避免单主键和后续配置的联合主键冲突;如果你不需要单独的自增Id字段,也可以直接移除该属性。
OnModelCreating 完整配置
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 1. 配置InputFile联合主键 modelBuilder.Entity<InputFile>() .HasKey(i => new { i.InputFileType, i.ScraperUploadClassId }); // 可选:如果保留原Id字段,配置为自增非主键字段 // modelBuilder.Entity<InputFile>() // .Property(i => i.Id) // .ValueGeneratedOnAdd(); // 2. 配置一对多关联 // ConfirmationFiles 对应 ConfirmationFile 类型的InputFile modelBuilder.Entity<ScraperUploadClass>() .HasMany(s => s.ConfirmationFiles) .WithOne(i => i.ScraperUploadClass) .HasForeignKey(i => i.ScraperUploadClassId) .HasFilter($"{nameof(InputFile.InputFileType)} = {(int)InputFileTypes.ConfirmationFile}") .OnDelete(DeleteBehavior.Restrict); // PrestationFiles 对应 PrestationFile 类型的InputFile modelBuilder.Entity<ScraperUploadClass>() .HasMany(s => s.PrestationFiles) .WithOne(i => i.ScraperUploadClass) .HasForeignKey(i => i.ScraperUploadClassId) .HasFilter($"{nameof(InputFile.InputFileType)} = {(int)InputFileTypes.PrestationFile}") .OnDelete(DeleteBehavior.Restrict); // 3. 配置一对一关联 // EocChoiceFile 对应 EOCChoiceFile 类型 modelBuilder.Entity<ScraperUploadClass>() .HasOne(s => s.EocChoiceFile) .WithOne() .HasPrincipalKey<InputFile>(i => i.ScraperUploadClassId) .HasForeignKey<ScraperUploadClass>(s => s.ID) .HasFilter($"{nameof(InputFile.InputFileType)} = {(int)InputFileTypes.EOCChoiceFile}") .OnDelete(DeleteBehavior.Restrict); // EocReplaceFile 对应 EOCReplaceFile 类型 modelBuilder.Entity<ScraperUploadClass>() .HasOne(s => s.EocReplaceFile) .WithOne() .HasPrincipalKey<InputFile>(i => i.ScraperUploadClassId) .HasForeignKey<ScraperUploadClass>(s => s.ID) .HasFilter($"{nameof(InputFile.InputFileType)} = {(int)InputFileTypes.EOCReplaceFile}") .OnDelete(DeleteBehavior.Restrict); // CareStaffFile 对应 CareStaffFile 类型 modelBuilder.Entity<ScraperUploadClass>() .HasOne(s => s.CareStaffFile) .WithOne() .HasPrincipalKey<InputFile>(i => i.ScraperUploadClassId) .HasForeignKey<ScraperUploadClass>(s => s.ID) .HasFilter($"{nameof(InputFile.InputFileType)} = {(int)InputFileTypes.CareStaffFile}") .OnDelete(DeleteBehavior.Restrict); // JobCreationFile 对应 JobCreationFile 类型 modelBuilder.Entity<ScraperUploadClass>() .HasOne(s => s.JobCreationFile) .WithOne() .HasPrincipalKey<InputFile>(i => i.ScraperUploadClassId) .HasForeignKey<ScraperUploadClass>(s => s.ID) .HasFilter($"{nameof(InputFile.InputFileType)} = {(int)InputFileTypes.JobCreationFile}") .OnDelete(DeleteBehavior.Restrict); // NurseTitlesFile 对应 NurseTitleFile 类型 modelBuilder.Entity<ScraperUploadClass>() .HasOne(s => s.NurseTitlesFile) .WithOne() .HasPrincipalKey<InputFile>(i => i.ScraperUploadClassId) .HasForeignKey<ScraperUploadClass>(s => s.ID) .HasFilter($"{nameof(InputFile.InputFileType)} = {(int)InputFileTypes.NurseTitleFile}") .OnDelete(DeleteBehavior.Restrict); }
注意事项
- 所有关联都设置了
DeleteBehavior.Restrict,避免循环级联删除导致迁移报错 - 过滤条件的语法适配SQL Server,如果你使用MySQL等其他数据库,需要将字段名的包裹符从方括号改为反引号
- 如果之前已经生成过迁移文件,需要删除旧的迁移文件和数据库快照,重新执行
Add-Migration命令生成新的迁移,避免历史配置冲突
内容的提问来源于stack exchange,提问作者Luc Van den Borre
相关产品推荐
相关产品推荐

