Net6 EfCore數據庫遷移時報: 可能會導致循環或多重級聯路徑。請指定 ON DELETE NO ACTION ...錯誤

十年河東,十年河西,莫欺少年窮

學無止境,精益求精

操作EfCore時,數據遷移執行update-database時報如下錯誤

 將 FOREIGN KEY 約束 'FK_S_Books_S_Companys_companyId' 引入表 'S_Books' 可能會導致循環或多重級聯路徑。請指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 約束。 無法創建約束或索引。請參閱前面的錯誤。

怎麼解決呢?

有這樣一個需求,公司、公司內有多個員工,每個員工可以寫多本書

using System;
using System.Collections.Generic;
using System.Text;

namespace CoreDbContext.DbDtos
{
    public class S_Company
    {
        public string uid { get; set; }
        public string companyName { get; set; }
    } 

    public class S_Author
    {
        public string uid { get; set; }
        public string authorName { get; set; }
        public string companyId { get; set; }
        public virtual S_Company Company { get; set; }
    }

    public class S_Book
    {
        public string uid { get; set; }
        public string bookName { get; set; }
        public string authorId { get; set; }
        public virtual S_Author Author { get; set; }
        public string companyId { get; set; }
        public virtual S_Company Company { get; set; }
    }
}
View Code

其配置關係如下:

using System;
using System.Collections.Generic;
using System.Text;
using CoreDbContext.DbDtos;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

namespace CoreDbContext.DbConfigs
{
    internal class S_CompanyConfig : IEntityTypeConfiguration<S_Company>
    {
        public void Configure(EntityTypeBuilder<S_Company> builder)
        {
            builder.ToTable("S_Companys");
            builder.HasKey(A => A.uid);
            builder.Property(A => A.uid).HasColumnType("varchar(64)").HasComment("主鍵");
            builder.Property(A => A.companyName).HasMaxLength(128).HasComment("公司名稱");  
        }
    }

    internal class S_AuthorConfig : IEntityTypeConfiguration<S_Author>
    {
        public void Configure(EntityTypeBuilder<S_Author> builder)
        {
            builder.ToTable("S_Authors");
            builder.HasKey(A => A.uid);
            builder.Property(A => A.uid).HasColumnType("varchar(64)").HasComment("主鍵");
            builder.Property(A => A.companyId).HasColumnType("varchar(64)").HasComment("外鍵-所屬公司");
            builder.Property(A => A.authorName).HasMaxLength(32).HasComment("作者姓名");

            //一對多配置
            builder.HasOne<S_Company>(A => A.Company).WithMany().HasForeignKey(A => A.companyId);

        }
    }


    internal class S_BookConfig : IEntityTypeConfiguration<S_Book>
    {
        public void Configure(EntityTypeBuilder<S_Book> builder)
        {
            builder.ToTable("S_Books");
            builder.HasKey(A => A.uid);
            builder.Property(A => A.uid).HasColumnType("varchar(64)").HasComment("主鍵");
            builder.Property(A => A.companyId).HasColumnType("varchar(64)").HasComment("外鍵-所屬公司");
            builder.Property(A => A.authorId).HasColumnType("varchar(64)").HasComment("外鍵-作者");
            builder.Property(A => A.bookName).HasMaxLength(32).HasComment("書名"); 
            //一對多配置
            builder.HasOne<S_Company>(A => A.Company).WithMany().HasForeignKey(A => A.companyId);
            builder.HasOne<S_Author>(A => A.Author).WithMany().HasForeignKey(A => A.authorId);

        }
    }
}
View Code

此時,使用Add-Migration 和 Update-Database 命令做數據庫遷移時,就會出現上述錯誤。解決這個錯誤之前,應先了解EfCore刪除關聯實體的7種策略,也稱之爲EfCore 級聯刪除規則,大家可自行百度,必應

 微軟官方文檔地址爲:https://learn.microsoft.com/zh-cn/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-6.0

 級聯刪除:https://learn.microsoft.com/zh-cn/ef/core/saving/cascade-delete

 EfCore 默然開啓了級聯刪除,默認爲:Cascade

1、DeleteBehavior.Restrict 

實際項目中使用較多的是軟刪除,因此,建議使用Restrict關閉級聯刪除

框架不執行任何操作,由開發人員決定關聯實體的行爲

將efcore的默認策略改成自定義行爲

 

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //關閉級聯刪除
            var foreignKeys = modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()).Where(fk => fk.DeleteBehavior == DeleteBehavior.Cascade);
            foreach (var fk in foreignKeys)
            {
                fk.DeleteBehavior = DeleteBehavior.Restrict;
            }
            //建立索引 並 增加唯一性約束
            modelBuilder.Entity<T_CabinetProduce>().HasIndex(u => u.batchNo).IsUnique();

            base.OnModelCreating(modelBuilder);
            //從當前程序集命名空間加載所有的IEntityTypeConfiguration
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
        }
View Code

2、DeleteBehavior.SetNull

使用setNull時,外鍵和導航屬性要設置爲可爲NULL,因此,實體變更如下

    public class S_Company
    {
        public string uid { get; set; }
        public string companyName { get; set; }
    } 


    public class S_Author
    {
        public string uid { get; set; }
        public string authorName { get; set; }
        public string? companyId { get; set; }
        public virtual S_Company? Company { get; set; }
    }

    public class S_Book
    {
        public string uid { get; set; }
        public string bookName { get; set; }
        public string? authorId { get; set; }
        public virtual S_Author? Author { get; set; }
        public string? companyId { get; set; }
        public virtual S_Company? Company { get; set; }
    }
View Code

關係配置時顯式聲明爲SetNull

    internal class S_CompanyConfig : IEntityTypeConfiguration<S_Company>
    {
        public void Configure(EntityTypeBuilder<S_Company> builder)
        {
            builder.ToTable("S_Companys");
            builder.HasKey(A => A.uid);
            builder.Property(A => A.uid).HasColumnType("varchar(64)").HasComment("主鍵");
            builder.Property(A => A.companyName).HasMaxLength(128).HasComment("公司名稱");  
        }
    }

    internal class S_AuthorConfig : IEntityTypeConfiguration<S_Author>
    {
        public void Configure(EntityTypeBuilder<S_Author> builder)
        {
            builder.ToTable("S_Authors");
            builder.HasKey(A => A.uid);
            builder.Property(A => A.uid).HasColumnType("varchar(64)").HasComment("主鍵");
            builder.Property(A => A.companyId).HasColumnType("varchar(64)").HasComment("外鍵-所屬公司");
            builder.Property(A => A.authorName).HasMaxLength(32).HasComment("作者姓名");

            //一對多配置
            builder.HasOne<S_Company>(A => A.Company).WithMany().HasForeignKey(A => A.companyId).OnDelete(DeleteBehavior.SetNull);

        }
    }

    internal class S_BookConfig : IEntityTypeConfiguration<S_Book>
    {
        public void Configure(EntityTypeBuilder<S_Book> builder)
        {
            builder.ToTable("S_Books");
            builder.HasKey(A => A.uid);
            builder.Property(A => A.uid).HasColumnType("varchar(64)").HasComment("主鍵");
            builder.Property(A => A.companyId).HasColumnType("varchar(64)").HasComment("外鍵-所屬公司");
            builder.Property(A => A.authorId).HasColumnType("varchar(64)").HasComment("外鍵-作者");
            builder.Property(A => A.bookName).HasMaxLength(32).HasComment("書名"); 
            //一對多配置
            builder.HasOne<S_Company>(A => A.Company).WithMany().HasForeignKey(A => A.companyId).OnDelete(DeleteBehavior.SetNull);
            builder.HasOne<S_Author>(A => A.Author).WithMany().HasForeignKey(A => A.authorId).OnDelete(DeleteBehavior.SetNull);

        }
    }
View Code

1.1、數據遷移

add-migration addtable
--
update-database

 

 插入測試數據

insert into [dbo].[S_Companys] values('001','江蘇出版社')
insert into [dbo].[S_Authors]  values('author01','陳臥龍','001')
insert into [dbo].[S_Authors]  values('author02','陳大六','001')
insert into [dbo].[S_Books] values('book01','平凡的世界','author01','001')
insert into [dbo].[S_Books] values('book02','鋼鐵是怎樣煉成的','author01','001')
insert into [dbo].[S_Books] values('book03','心語','author02','001')
insert into [dbo].[S_Books] values('book04','林語','author02','001')

select *from [S_Companys]
select *from [S_Authors]
select *from [S_Books]


delete from [S_Companys]
delete from [S_Authors]
delete from [S_Books]
View Code

1.2、測試

        [AllowAnonymous]
        [HttpGet]
        [Route("Test")]
        public IActionResult Test()
        {
            var ef = context.Authors.Find("author02");
            context.Authors.Remove(ef);
            context.SaveChanges();
            return Ok();
        }

上述代碼刪除其中名稱爲陳大六的作者,按照setNull的規定,當主表刪除時,從表關聯記錄外鍵會被賦值爲NULL

 

繼續將公司刪除

        [AllowAnonymous]
        [HttpGet]
        [Route("Test")]
        public IActionResult Test()
        {
            var ef = context.Companys.Find("001");
            context.Companys.Remove(ef);
            context.SaveChanges();
            return Ok();
        }

將公司刪除,所有從表外鍵companyId均會被賦值爲NULL

@天才臥龍的博科人

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章