测试是否存在开放式并发冲突的方法有若干种。 其中一种涉及到在表中包含时间戳列。 数据库通常会提供时间戳功能,该功能可用于标识上次更新记录的日期和时间。 当使用这种方法时,将在表定义中包含时间戳列。 每当更新记录时,时间戳都将得到更新,以反映当前的日期和时间。 在测试是否存在开放式并发冲突时,对表内容的任何查询都会返回时间戳列。 当试图执行更新时,数据库中的时间戳值将与所修改行中包含的原始时间戳值进行比较。 如果两者匹配,则会执行更新,并用当前时间更新时间戳列以反映更新。 如果两者不匹配,则发生了开放式并发冲突。
测试是否存在开放式并发冲突的另一种方法是验证某行中的所有原始列值是否仍匹配数据库中的相应值。 例如,考虑以下查询:
SELECT Col1, Col2, Col3 FROM Table1
若要在更新 Table1 中的某行时测试是否存在开放式并发冲突,请发出以下 UPDATE 语句:
UPDATE Table1 Set Col1 = @NewCol1Value,
Set Col2 = @NewCol2Value,
Set Col3 = @NewCol3Value
WHERE Col1 = @OldCol1Value AND
Col2 = @OldCol2Value AND
Col3 = @OldCol3Value 只要原始值匹配数据库中的值,就会执行更新。 如果已修改某个值,由于 WHERE 子句找不到匹配项,更新将不会修改该行。
请注意,建议始终在查询中返回唯一的主键值。 否则,以上 UPDATE 语句会更新多个行,这可能会有悖于您的意图。
如果数据源中的列允许空值,则可能需要扩展 WHERE 子句,以查找本地表和数据源中的匹配空引用。 例如,以下 UPDATE 语句验证本地行中的空引用是否仍匹配数据源中的空引用,或者本地行中的值是否匹配数据源中的值。
UPDATE Table1 Set Col1 = @NewVal1
WHERE (@OldVal1 IS NULL AND Col1 IS NULL) OR Col1 = @OldVal1
当使用开放式并发模型时,也可以选择应用限制较少的条件。 例如,如果只在 WHERE 子句中使用主键列,那么无论自上次查询以来是否已更新其他列,数据都将被重写。 也可以只将 WHERE 子句应用于特定列,除非自上次查询特定字段以来已将其更新,否则数据也会被重写。
DataAdapter.RowUpdated 事件
DataAdapter 对象的 RowUpdated 事件可以与上述方法一起用来向应用程序提供有关开放式并发冲突的通知。 在每次尝试从 DataSet 更新 Modified 行之后,将发生 RowUpdated。 它使您能够添加特殊的处理代码,包括在发生异常时进行处理,添加自定义错误信息,添加重试逻辑等。 RowUpdatedEventArgs 对象为表中已修改的行返回 RecordsAffected 属性,其中包含特定更新命令所影响的行数。 通过设置更新命令来测试是否存在开放式并发,当开放式并发冲突已发生时,因为没有更新任何记录,所以 RecordsAffected 属性最终将返回值 0。 如果是这种情况,则将引发异常。 RowUpdated 事件使您能够通过设置合适的 RowUpdatedEventArgs.Status 值(例如 UpdateStatus.SkipCurrentRow)来处理这种情况并避免异常。 有关 RowUpdated 事件的更多信息,请参见处理 DataAdapter 事件 (ADO.NET)。
或者,可以在调用 Update 之前将 DataAdapter.ContinueUpdateOnError 设置为 true,并在完成 Update 后响应存储在特定行的 RowError 属性中的错误信息。 有关更多信息,请参见行错误信息。