oracle无原生rowversion,其乐观并发依赖原始值比对,常见因uitodatarow将null转为空字符串导致where匹配失败;推荐加version列或关闭自动校验并手写sql。
oracle 没有原生的 rowversion 或 timestamp 列类型(sql server 那种自增二进制版本号),直接套用 .net 的 datarowversion.original 乐观并发逻辑会失败。 你看到的“并发冲突”异常,大概率不是数据库层面锁住了,而是 oracle.dataaccess.dll 在提交时拿 datarow 里缓存的原始值去比对数据库当前值——而这个原始值早在 ui 赋值阶段就被悄悄污染了(比如空字符串覆盖了 dbnull)。
为什么 Oracle.DataAccess.dll 的 UpdateDataSet 会报“并发冲突”
它默认启用乐观并发检查:执行 UPDATE 时 WHERE 条件里会带上所有原始列值(例如 WHERE id = :id AND name = :original_name AND count = :original_count)。只要数据库中任意一列值和 DataRow["col", DataRowVersion.Original] 不一致,整条 UPDATE 就不生效,驱动抛出“并发冲突”异常。
- 常见污染源:
***UIToDataRow()方法把空字符串""写进原本是DBNull的字段,导致原始值从NULL变成"" - Oracle 中
''和NULL不等价,WHERE 匹配失败 - 即使你没改这列,别人改了,也会触发
-
System.Data.OracleClient(已废弃)行为不同,不强制比对全部原始列
不依赖 ROWVERSION,用 Oracle 原生方式做乐观并发
Oracle 没有 ROWVERSION,但可以用 ORA_ROWSCN 或显式 VERSION 列模拟。推荐后者,可控、明确、兼容性好。
- 在业务表加一列:
VERSION NUMBER DEFAULT 0 NOT NULL - 所有
UPDATE语句必须带条件:WHERE id = :id AND VERSION = :original_version - 成功更新后,
UPDATE ... SET ..., VERSION = VERSION + 1 -
OracleDataAdapter的UpdateCommand需手动配置这个 WHERE 和 SET 逻辑,不能靠自动生成 - 捕获
OracleException错误码ORA-00001(唯一约束)或影响行数为 0,即判定并发失败
绕过驱动层并发检查的实操开关
如果你确认业务上可以接受“后写覆盖前写”,且不想改表结构,就关掉 OracleDataAdapter 的自动并发校验。
- 设置
adapter.ContinueUpdateOnError = true—— 不因单行失败中断整个 DataSet 更新 - 清空
adapter.UpdateCommand.Parameters,手写 SQL,WHERE 只保留主键:UPDATE table3 SET name = :name, count = :count WHERE id = :id - 不要调用
dataSet.AcceptChanges()前就手动把DataRow["col", DataRowVersion.Original]改回正确值(如把""改回DBNull.Value) - 注意:这等于放弃乐观并发保护,需业务层兜底(比如记录修改人、时间戳)
真正容易被忽略的点
问题往往不出在数据库或驱动,而出在 ***UIToDataRow() 这类数据映射方法里——它把 UI 层的空字符串、零值、未填字段无差别转成 DataRow 的当前值,顺手也污染了原始值缓存。没人会去 audit 这个几十行的工具方法,但它决定了整条乐观并发链是否可靠。上线前务必用 DataRow.GetColumnError("name") 或日志打印原始值/当前值对比,验证映射逻辑是否保真。











