【问题标题】:EF Core and SQL multiple cascadeEF Core 和 SQL 多级联
【发布时间】:2021-09-21 22:41:49
【问题描述】:

我正在尝试使用 SQL Server 在 ef-core 中对付款进行建模。支付有一个从方和一个到方,在这种情况下说每个用户或参与者。我有一个与 FromActor 和 ToActor 有关系的事务类。生成的迁移在数据库中更新失败,说明可能有多个级联路径。我已经读到我需要在 db 上下文中覆盖 OnModelCreating 方法中的默认级联,但我不确定如何。对 EF 和迁移相当陌生,我只是关注 https://docs.microsoft.com/en-us/ef/core/get-started/overview/first-app?tabs=visual-studio

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; }
}

在表“事务”上引入 FOREIGN KEY 约束“FK_Transactions_Actors_ToActorId”可能会导致循环或多个级联路径。指定 ON DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。

您能告诉我如何在 db 上下文中进行这项工作吗?还是我需要对我的域进行不同的建模?

protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        ??
    }

更新:根据我在这里收到的所有 cmets 是我最终的工作解决方案 -

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,   // hardcoded for illustration
              FromActorId = 1,  // hardcoded for illustration. Using id from actor seed file.
              ToActorId = 2,    // hardcoded for illustration. Using id from actor seed file.
            };
            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 引入 2 个集合(从和到)。而且我无法在保存期间设置事务的 FromActor 和 ToActor 属性;只设置ID。设置对象会在事务保存期间尝试插入到 Actors 表中。不直观。

感谢您的帮助。

【问题讨论】:

    标签: .net entity-framework-core


    【解决方案1】:

    您可以使用 modelBuilder 以您想要的方式为每个实体修改模型构建器的属性是的:

    如果我要给你举个演员的例子,可能是:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
                base.OnModelCreating(modelBuilder);
    
                modelBuilder.Entity<Transaction>(entity =>
                {
                    entity.HasOne(t => t.FromActor)
                        .WithMany(a => a.TransactionsFrom)
                        .HasForeignKey(t => t.FromActorId)
                        .OnDelete(DeleteBehavior.NoAction)
                        .HasConstraintName("fk_transaction_from_id_from_actor");
                    entity.HasOne(t => t.ToActor)
                        .WithMany(a => a.TransactionsTo)
                        .HasForeignKey(t => t.ToActorId)
                        .OnDelete(DeleteBehavior.NoAction)
                        .HasConstraintName("fk_transaction_to_id_to_actor");
                });
        }
    

    或类似的东西。如您所见,语法非常不言自明。

    public class Actor
    {
        public int ActorId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    
        #region InverseProperty
        public ICollection<Transaction> TransactionsFrom {get; set; }
        public ICollection<Transaction> TransactionsTo {get; set; }
        #endregion InverseProperty
    }
    
    public class Transaction
    {
        public int TransactionId { get; set; }
        public DateTime TimestampUtc { get; set; }
        public decimal Amount { get; set; }
      
        public int FromActorId { get; set; }
        public int ToActorId { get; set; }
    
        #region ForeignKey
        public virtual Actor ToActor { get; set; }
        public virtual Actor FromActor { get; set; }
        #endregion ForeignKey
    }
    

    实体Transaction有一个Actor FromActor,那个FromActor有一个交易列表(TransactionsFrom)。外键应该是 FromActorId,并且在删除时,您不对实体执行任何操作。约束的名称应该是“fk_transaction_id_from_actor”。

    这允许您随意更改约束、行为和命名。如果需要,它还可以允许您通过迁移在模型中添加数据:

    modelBuilder.Entity<Actor>().HasData(
                    new
                    {
                        ActorId = 1,
                        FirstName = "John",
                        LastName = "Doe"
                    },
                    new
                    {
                        ActorId = 2,
                        FirstName = "Jane",
                        LastName = "Doe"
                    });
    

    当您执行“dotnet ef migrations add myMigration”时,上下文中的每个更改都将在下一次迁移中注册,因此如果您在 HasData 中添加数据,并且您不是单独处理项目,请务必跟踪 id 否则迁移将失败! 我希望这足以帮助你。

    【讨论】:

    • 现在出现此错误:无法在“Actor.Transactions”和“Transaction.ToActor”之间创建关系,因为“Actor.Transactions”和“Transaction.FromActor”之间已经存在关系。导航属性只能参与单个关系。如果您想在“OnModelCreating”中首先在导航“Transaction.ToActor”上覆盖现有关系调用“Ignore”。
    • 这是因为您在 Actors 上的“Transactions”属性当前是 EF Core 认为的“两个不同事物”的聚合,这意味着参与者是受益人或发行人的交易。我从来没有见过这样的聚合,很不幸我无法回答你的问题。解决此问题的一种方法是拥有两个“TransactionsFrom”和“TransactionTo”,但这可能不是您在概念上想要/需要的。 Serge 似乎在我之前就发现了这个问题 =)
    • 好的,我将更新我的 Actor 模型,使其具有 FromTransactions 和 ToTransactions。似乎 EF 仍然有办法在现实世界中用作 ORM,而不需要用户适应它而不是相反。
    • 更新:在将 From 和 To 事务添加到 Actor 模型后,我可以更新数据库。我什至在 OnModelCreating 中播种 Actor,没问题。但是,当我简单地使用数据库中的 2 个现有演员创建一个新的 Transaction 对象时,保存更改正试图插入到 ACTORS 中?这显然失败了。听起来我仍然被困住了。当 IDENTITY_INSERT 设置为 OFF 时,无法为表“Actors”中的标识列插入显式值。
    • Update2:我将新的 Transcation() 更改为仅添加 FromActorId 和 ToActorId 并省略了实际的 FromActor 和 ToActor 对象。这似乎已经成功了,现在我可以保存交易了。结合我创建了 FromActor 和 ToActor 虚拟属性。
    【解决方案2】:

    你必须为你的类添加一些导航属性

    public class Actor
    {
        public int ActorId { get; set; }
         .....
    
        public virtual List<Transaction> FromActors {get; set;} 
       public virtual List<Transaction> ToActors { get; set; }
    
    }
    
    public class Transaction
    {
        public int TransactionId { get; set; }
        ....
    
        public int? FromActorId { get; set; }
        public  virtual Actor FromActor { get; set; }
    
        public int? ToActorId { get; set; }
        public virtual Actor ToActor { get; set; }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-29
      • 2018-08-03
      • 1970-01-01
      • 1970-01-01
      • 2018-06-21
      • 2018-07-19
      • 1970-01-01
      相关资源
      最近更新 更多