EF Core对接SQL Server多级联路径报错的配置解决方法
我尝试在EF Core中使用SQL Server搭建支付模型,一笔支付包含转出方和转入方,双方均为用户(Actor实体)。我定义的Transaction类分别关联FromActor和ToActor两个属性,生成迁移更新数据库时失败,提示存在多条级联路径。我查到需要在数据库上下文的OnModelCreating方法中重写默认级联规则,但不清楚具体实现方式。我对EF和迁移操作比较陌生,此前仅跟着官方教程操作。
初始实体代码如下:
public class Actor { public int ActorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public List<Transaction> Transactions = new List<Transaction>(); } public class Transaction { public int TransactionId { get; set; } public DateTime TimestampUtc { get; set; } public decimal Amount { get; set; } public int FromActorId { get; set; } public Actor FromActor { get; set; } public int ToActorId { get; set; } public Actor ToActor { get; set; } }
执行迁移时收到报错:
在Transactions表上引入外键约束'FK_Transactions_Actors_ToActorId'可能会导致循环或多条级联路径。请指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他外键约束。
请问我该如何在数据库上下文中配置解决该问题?还是需要调整我的领域模型设计?
我当前的OnModelCreating方法还是空的:
protected override void OnModelCreating(ModelBuilder modelBuilder) { ?? }
最终解决方案
根据大家的建议,我最终的可用实现如下:
public class Actor { public int ActorId { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public virtual List<Transaction> FromTransactions { get; set; } public virtual List<Transaction> ToTransactions { get; set; } } public class Transaction { public int TransactionId { get; set; } public DateTime TimestampUtc { get; set; } public double Amount { get; set; } public int FromActorId { get; set; } public virtual Actor FromActor { get; set; } public int ToActorId { get; set; } public virtual Actor ToActor { get; set; } } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Transaction>(entity => { entity.HasOne(t => t.FromActor) .WithMany(a => a.FromTransactions) .HasForeignKey(t => t.FromActorId) .OnDelete(DeleteBehavior.NoAction) .HasConstraintName("FK_Transaction_Id_From_Actor"); entity.HasOne(t => t.ToActor) .WithMany(a => a.ToTransactions) .HasForeignKey(t => t.ToActorId) .OnDelete(DeleteBehavior.NoAction) .HasConstraintName("FK_Transaction_Id_To_Actor"); }); Seed(modelBuilder); } private void Seed(ModelBuilder modelBuilder) { using (var reader = new StreamReader("Data/Actors.csv")) using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture)) { var actors = csv.GetRecords<Actor>(); modelBuilder.Entity<Actor>().HasData(actors.ToArray()); } } protected async Task<Transaction> SaveTransactionAsync() { using (var context = contextFactory.CreateDbContext()) { var transaction = new Transaction() { TimestampUtc = DateTime.UtcNow, Amount = 20.45, // 为演示硬编码 FromActorId = 1, // 为演示硬编码,使用Actor种子文件中的id ToActorId = 2, // 为演示硬编码,使用Actor种子文件中的id }; context.Add(transaction); await context.SaveChangesAsync(); await context.Entry(transaction).Reference(t => t.FromActor).LoadAsync(); await context.Entry(transaction).Reference(t => t.ToActor).LoadAsync(); logger.LogDebug("Saved transaction to database."); return transaction; } }
经验总结
除了禁用级联删除外,我得到的主要经验是必须为Actor实体引入两个集合(转出交易集合和转入交易集合)。另外保存Transaction时不能直接设置FromActor和ToActor导航属性,只能设置对应的id,直接设置对象会导致保存交易时尝试向Actors表插入新数据,这点不符合直觉。
内容的提问来源于stack exchange,提问作者Heems
相关产品推荐
相关产品推荐

