【问题标题】:Entity Framework: How do I delete table rows that reference each other?实体框架:如何删除相互引用的表行?
【发布时间】:2022-01-04 08:37:20
【问题描述】:

如何在不出现此异常的情况下删除订单?

UserLicenses 引用 SerialOnOrderDetails 反之亦然:

DELETE 语句与 REFERENCE 约束“FK_SerialsOnOrderDetail_UserLicenses”冲突。冲突发生在数据库“sales”、表“dbo.SerialsOnOrderDetail”、列“UserLicenseId”中。

删除确认的控制器动作代码:

[Authorize(Roles = "admin")]    
[HttpPost, ActionName("Delete")]
public async Task<ActionResult> DeleteConfirmed(int id)
{
    Order order = GetOrderById(id);

    if (order.UserLicenses.Count > 0)
    {
        context.UserLicenses.RemoveRange(order.UserLicenses);
    }

    if (order.SerialsOnOrderDetails.Count > 0)
    {
        context.SerialsOnOrderDetails.RemoveRange(order.SerialsOnOrderDetails);
    }

    context.Orders.Remove(order);

    context.SaveChanges(); // Exception here !!!
}

[编辑] 添加实时数据

实时数据(Id = UserLicenseId):

其他类:

public partial class UserLicense
{   
    public string Id { get; set; }
    public int OrderId { get; set; }
    public string ProductId { get; set; }
    public int CustomerId { get; set; }
    public string AssignedUserId { get; set; }
    public bool IsActive { get; set; }

    public virtual AspNetUser AspNetUser { get; set; }
    public virtual Customer Customer { get; set; }
    public virtual Order Order { get; set; }
    public virtual Product Product { get; set; }

    public virtual ICollection<SerialsOnOrderDetail> SerialsOnOrderDetails { get; set; }
}

public partial class SerialsOnOrderDetail
{
   public int orderID { get; set; }
   public string serial { get; set; }
   public string productID { get; set; }
   public string UserLicenseId { get; set; }
   public int customerID { get; set; }

   public virtual Product Product { get; set; }
   public virtual Serial Serial1 { get; set; }
   public virtual Order Order { get; set; }
   public virtual UserLicense UserLicense { get; set; }
   public virtual Customer Customer { get; set; }
}

public partial class Order
{
    public Order()
    {
        this.OrderDetails = new HashSet<OrderDetail>();
        this.SerialsOnOrderDetails = new HashSet<SerialsOnOrderDetail>();
        this.UserLicenses = new HashSet<UserLicense>();
    }

    public int orderID { get; set; }
    public int customerID { get; set; }
    public string promoCodeID { get; set; }
    public System.DateTime date { get; set; }
    public Nullable<int> resellerID { get; set; }
    public string invoiceID { get; set; }
    public string poNumber { get; set; }
    public Nullable<System.DateTime> paymentDate { get; set; }
    public Nullable<bool> validated { get; set; }
    public string resellerOrderID { get; set; }
    public Nullable<int> parentOrderID { get; set; }
    public int months { get; set; }

    public virtual Customer Customer { get; set; }
    public virtual ICollection<OrderDetail> OrderDetails { get; set; }
    public virtual PromoCode PromoCode { get; set; }
    public virtual Reseller Reseller { get; set; }
    public virtual ICollection<SerialsOnOrderDetail> SerialsOnOrderDetails { get; set; }
    public virtual Order ParentOrder { get; set; }
    public virtual ICollection<UserLicense> UserLicenses { get; set; }
}

【问题讨论】:

  • 这个问题不受你使用的IDE的影响,所以[visual-studio]标签在这里无关紧要。请仅使用 [visual-studio] 标记解决有关使用 Visual Studio 的问题(如 its tag description 所示)。
  • 你最初是如何插入这些行的?其中一个必须在另一行存在之前首先插入,因此当时无法引用它。无论在插入过程中应用什么机制/排序,在删除过程中反向执行相同的步骤。

标签: c# asp.net-mvc entity-framework


【解决方案1】:

您引用的约束消息来自底层 SQL Server。这不是 EF 错误,而是通过了。

当我在 TSQL 中遇到此类问题时,解决方案通常是在单个事务中删除两个相互引用的行。

虽然我对 EF 不是很熟悉,但通过快速搜索可以找到这个将多个操作放入单个事务上下文的示例,而不是每个操作单个事务的默认行为。

{
    context.Database.Log = Console.WriteLine;
    using (DbContextTransaction transaction = context.Database.BeginTransaction())
    {
        try
        {
            Author author1 = new Author() {    Name = "Mark" };
            Author author2 = new Author() {    Name = "John" };
            
            context.Authors.Add(author1);
            context.SaveChanges();
            
            context.Authors.Add(author2);
            context.SaveChanges();
            transaction.Commit();
        }
        catch (Exception ex)
        {
            transaction.Rollback();
        }
    }
}

我注意到另一位受访者描述了其他可能错误的事情。完全有可能我们都是对的,并且有多个问题需要解决。

【讨论】:

  • 不,即使添加多个 context.SaveChanges() 呼叫也无济于事,抱歉。
  • 如果两行存储在同一个表中,您只能同时删除它们。否则,这无济于事,因为 SQL Server 不支持对外键的延迟检查(在其他产品中,这将允许仅在提交事务时进行检查)。您必须有两条语句影响这两个表,并且第一条语句会发生错误。
【解决方案2】:

您是否验证了 UserLicenses 和 SerialsOnOrderDetails 集合是否正确加载且不为空?你确定 removerange 是正确的方法吗?如果你不习惯的话,我建议你阅读一些关于 EF 的教程。

也许您必须使用 .Include("....") 指令更新 GetOrderById 才能加载这些集合,或者手动加载相关项目。

【讨论】:

  • 是的,UserLicenses 集合包含 2 项,而 SerialsOnOrderDetails 4 项。我怀疑该错误与数据库约束有关。
  • 我认为这与 EF 无关。您是否尝试在纯 SQL 中准确查看受影响的行,以及如果要删除的 UserLicense 未在另一个 SerialsOnOrderDetail 中使用(例如)?它将解释违反约束。
  • 我刚刚编辑了 OP 以添加实时数据,是否有助于理解问题?
  • 您是否验证了这些用户许可证是否未在另一个 SerialsOnOrderDetails 记录中使用?您的屏幕截图没有告诉它,因为它专注于订单而不是要删除的行。另外,正如已经建议的那样,尝试在纯 sql 中运行删除(如果您不想实际删除行,则在最后带有 ROLLBACK 的事务中)。
  • 是的,UserLicenseId gx2rdtch 也用于104458。这解释了删除记录时的错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-19
  • 1970-01-01
相关资源
最近更新 更多