C# 交换单元格数据会导致唯一的约束冲突,尽管整体更改是有效的

C# 交换单元格数据会导致唯一的约束冲突,尽管整体更改是有效的,c#,oracle,datagridview,C#,Oracle,Datagridview,以下用例给出了不必要的唯一约束冲突,很可能是因为更改是逐行检查的,而不是全部检查的: 给定:Oracle数据库中的表,其形式如下: 名称|顺序 名称1 | 1 名称2 | 2 其中订单具有唯一约束 此表使用DataGridView呈现给用户,如下所示: this.myDataGridView.DataSource = this.mydataSet; this.myTableAdapter.Fill(this.myDataSet.myTableName); this.myDataSet.Accep

以下用例给出了不必要的唯一约束冲突,很可能是因为更改是逐行检查的,而不是全部检查的:

  • 给定:Oracle数据库中的表,其形式如下:
  • 名称|顺序

    名称1 | 1

    名称2 | 2

    其中订单具有唯一约束

  • 此表使用DataGridView呈现给用户,如下所示:

    this.myDataGridView.DataSource = this.mydataSet;
    this.myTableAdapter.Fill(this.myDataSet.myTableName);
    this.myDataSet.AcceptChanges();
    
    using (TransactionScope scope = new TransactionScope())
    {
      this.myTableAdapter.Update(this.myDataSet.myTableName);
      scope.Complete();
    }
    
    DataTable dtChanges = this.myDataSet.myTableName.GetChanges(DataRowState.Modified);
    DataRowCollection changedRows = dtChanges.Rows;
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.BeginEdit();
    }
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.EndEdit();
    }
    
    this.myDataSet.EnforceConstraints = false;
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    this.myDataSet.EnforceConstraints = true;
    
  • 用户交换表中的两个值。该表现在如下所示:

  • this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    名称|顺序

    名称1 | 2

    名称2 | 1

  • 用户尝试将更改保存到数据库中。相应的代码如下所示:

    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
  • 结果:我得到一个唯一的约束冲突(ORA-00001)

    我已经尝试了以下解决方案:

    A) 在如下事务中重新填充命令:

    this.myDataGridView.DataSource = this.mydataSet;
    this.myTableAdapter.Fill(this.myDataSet.myTableName);
    this.myDataSet.AcceptChanges();
    
    using (TransactionScope scope = new TransactionScope())
    {
      this.myTableAdapter.Update(this.myDataSet.myTableName);
      scope.Complete();
    }
    
    DataTable dtChanges = this.myDataSet.myTableName.GetChanges(DataRowState.Modified);
    DataRowCollection changedRows = dtChanges.Rows;
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.BeginEdit();
    }
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.EndEdit();
    }
    
    this.myDataSet.EnforceConstraints = false;
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    this.myDataSet.EnforceConstraints = true;
    
    B) 对所有行或所有更改的行使用BeginEdit和EndEdit,如下所示:

    this.myDataGridView.DataSource = this.mydataSet;
    this.myTableAdapter.Fill(this.myDataSet.myTableName);
    this.myDataSet.AcceptChanges();
    
    using (TransactionScope scope = new TransactionScope())
    {
      this.myTableAdapter.Update(this.myDataSet.myTableName);
      scope.Complete();
    }
    
    DataTable dtChanges = this.myDataSet.myTableName.GetChanges(DataRowState.Modified);
    DataRowCollection changedRows = dtChanges.Rows;
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.BeginEdit();
    }
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.EndEdit();
    }
    
    this.myDataSet.EnforceConstraints = false;
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    this.myDataSet.EnforceConstraints = true;
    
    C) 关闭约束检查,如下所示:

    this.myDataGridView.DataSource = this.mydataSet;
    this.myTableAdapter.Fill(this.myDataSet.myTableName);
    this.myDataSet.AcceptChanges();
    
    using (TransactionScope scope = new TransactionScope())
    {
      this.myTableAdapter.Update(this.myDataSet.myTableName);
      scope.Complete();
    }
    
    DataTable dtChanges = this.myDataSet.myTableName.GetChanges(DataRowState.Modified);
    DataRowCollection changedRows = dtChanges.Rows;
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.BeginEdit();
    }
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    foreach (DataRow currRow in dtChanges.Rows)
    {
      currRow.EndEdit();
    }
    
    this.myDataSet.EnforceConstraints = false;
    
    this.myTableAdapter.Update(this.myDataSet.myTableName);
    
    this.myDataSet.EnforceConstraints = true;
    
    没有一个解决了我的问题,例外不断出现

    是否有人知道如何使这种交换成为可能,如果可能的话,而不完全删除约束

    更新1:

    更奇怪的是:即使我使用

    this.myDataSet.myTableName.Constraints.Clear();
    

    我仍然受到约束违反。是否数据库根本不关心我在代码中指定的内容?

    Oracle不是逐行验证约束,而是基于语句验证约束。
    因此,如果通过单个UPDATE语句进行交换,则不会出现错误:

    -- table with constraint
    CREATE TABLE tab (NAME VARCHAR2(200), order_ NUMBER);
    ALTER TABLE tab ADD (CONSTRAINT test_uk UNIQUE (order_));
    -- initial data population
    INSERT INTO tab VALUES ('name1',1);
    INSERT INTO tab VALUES ('name2',2);
    COMMIT;
    
    您可以检查约束是否有效;)

    现在,您可以通过单个UPDATE语句交换值:

    UPDATE tab SET order_ = DECODE(order_,1,2,2,1) WHERE order_ IN (1,2);
    
    这很好用

    SELECT * FROM tab;
    
    延迟约束检查
    您也可以检查约束,不是针对每个语句,而是针对整个事务。
    在这种情况下,您需要将约束重新创建为可延迟:

    ALTER TABLE tab DROP CONSTRAINT test_uk CASCADE;
    ALTER TABLE tab ADD (CONSTRAINT test_uk UNIQUE (order_) DEFERRABLE);
    
    让我们重新填充这张桌子

    TRUNCATE TABLE tab;
    INSERT INTO tab VALUES ('name1',1);
    INSERT INTO tab VALUES ('name2',2);
    COMMIT;
    
    由于以下限制,该声明将失败:

    UPDATE tab SET order_ = 2 WHERE NAME = 'name1';
    
    但若您将约束设置为deferred,那个么在提交之前,同一语句不会失败

    SET CONSTRAINT test_uk DEFERRED;
    UPDATE tab SET order_ = 2 WHERE NAME = 'name1';
    
    提交将验证约束,并将失败

    COMMIT; -- this will fail now
    
    但如果我们交换订单,提交会成功,因为现在不会违反约束:

    SET CONSTRAINT test_uk DEFERRED;
    UPDATE tab SET order_ = 2 WHERE NAME = 'name1';
    UPDATE tab SET order_ = 1 WHERE NAME = 'name2';
    COMMIT;
    
    并给出了预期的结果:

    SELECT * FROM tab;
    

    我试图将逻辑保留在C#中,而不是Oracle中-我必须以何种方式更改Update调用以使Update成为一条语句?将约束类型更改为DEREFERED ONLY不会更改结果,异常仍处于启用状态。我遗漏了什么?检查约束的实际状态。因为它最初可以是即时的,也可以是延迟的。如果它最初是立即的,那么在启动DML之前需要将其设置为延迟。另外,请阅读延迟约束,因为它们会带来一些您可能不知道的后果。感谢您提供的提示-阅读了一些有关此问题的内容,在接下来的几天中,您将做更多的工作。关于问题本身:如果我将约束更改为deferred,initially deferred,我仍然会得到相同的错误-附加了一个“ORA-02091事务回滚”错误。您可以跟踪应用程序执行的数据库调用吗?