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
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);
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事务回滚”错误。您可以跟踪应用程序执行的数据库调用吗?