You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制填充DataRow?C# DataSet实现“最后保存获胜”方法

没问题,要实现你说的「最后保存获胜」——也就是让本地修改强制覆盖数据库中已经变更的数据,解决并发冲突的问题,你可以试试这几种方案,根据你的实际代码场景选就行:

方案1:通过ConflictOption配置自动生成更新命令

如果你是用OracleCommandBuilder自动生成Update/Insert/Delete命令的话,最简单的方式就是修改它的ConflictOption属性为OverwriteChanges。

默认情况下,CommandBuilder生成的Update语句会把所有原始字段值都放到WHERE子句里(用来对比数据库当前值,判断是否有并发修改),而设置OverwriteChanges后,WHERE子句只会包含主键列,这样不管数据库里的其他字段有没有变化,只要主键匹配就会用本地新值覆盖。

代码示例:

// 假设你已经有了查询用的OracleCommand和OracleConnection
var selectCmd = new OracleCommand("SELECT UserId, Name, Age FROM Users", yourConnection);
var adapter = new OracleDataAdapter(selectCmd);

// 创建CommandBuilder并设置ConflictOption
var builder = new OracleCommandBuilder(adapter);
builder.ConflictOption = ConflictOption.OverwriteChanges;

// 后续Fill和Update操作不变
var ds = new DataSet();
adapter.Fill(ds, "Users");

// 修改DataSet中的行...

// 执行更新,此时会直接覆盖数据库中对应主键的行
adapter.Update(ds, "Users");
方案2:手动构造UpdateCommand(更灵活可控)

如果你不想依赖CommandBuilder,或者需要自定义更新逻辑,可以手动写Update语句,确保WHERE子句只包含主键,不对比其他原始字段值。

这样更新时只会根据主键定位行,直接用本地新值覆盖数据库数据:

var adapter = new OracleDataAdapter();
adapter.SelectCommand = new OracleCommand("SELECT UserId, Name, Age FROM Users", yourConnection);

// 手动构造UpdateCommand
var updateCmd = new OracleCommand(
    "UPDATE Users SET Name = :NewName, Age = :NewAge WHERE UserId = :OriginalUserId",
    yourConnection);

// 绑定参数:新值用当前行的Current版本,主键用Original版本(防止主键被修改)
updateCmd.Parameters.Add(":NewName", OracleType.VarChar, 50, "Name").SourceVersion = DataRowVersion.Current;
updateCmd.Parameters.Add(":NewAge", OracleType.Int32, 0, "Age").SourceVersion = DataRowVersion.Current;
updateCmd.Parameters.Add(":OriginalUserId", OracleType.Int32, 0, "UserId").SourceVersion = DataRowVersion.Original;

adapter.UpdateCommand = updateCmd;

// 后续Fill、修改、Update操作正常执行即可
方案3:在RowUpdated事件中捕获冲突并强制覆盖

如果你已经在处理RowUpdated事件,想在捕获到并发冲突时再强制执行覆盖操作,可以在事件处理逻辑里判断冲突,然后执行一个不检查原始值的更新命令。

通常并发冲突会表现为e.RecordsAffected == 0(因为默认的WHERE子句找不到匹配的行),或者抛出对应的Oracle异常(比如ORA-01403)。处理逻辑示例:

var adapter = new OracleDataAdapter(selectCmd);
// 假设你已经有默认的CommandBuilder或UpdateCommand

// 绑定RowUpdated事件
adapter.RowUpdated += (sender, e) =>
{
    if (e.Status == UpdateStatus.ErrorsOccurred)
    {
        var oracleEx = e.Exception as OracleException;
        // 判断是否是并发冲突:要么是没有找到行,要么是对应的异常码
        if ((oracleEx != null && oracleEx.Number == 1403) || e.RecordsAffected == 0)
        {
            // 构造强制覆盖的更新命令
            var forceUpdate = new OracleCommand(
                "UPDATE Users SET Name = :NewName, Age = :NewAge WHERE UserId = :UserId",
                e.Command.Connection);
            
            // 从当前行获取新值和原始主键
            forceUpdate.Parameters.Add(":NewName", OracleType.VarChar, 50).Value = e.Row["Name", DataRowVersion.Current];
            forceUpdate.Parameters.Add(":NewAge", OracleType.Int32).Value = e.Row["Age", DataRowVersion.Current];
            forceUpdate.Parameters.Add(":UserId", OracleType.Int32).Value = e.Row["UserId", DataRowVersion.Original];
            
            // 执行强制更新
            int affected = forceUpdate.ExecuteNonQuery();
            if (affected > 0)
            {
                // 标记冲突已处理,跳过默认的错误流程
                e.Status = UpdateStatus.SkipCurrentRow;
                e.Exception = null;
            }
        }
    }
};

// 后续Fill、修改、Update操作正常执行

注意事项

  • 「最后保存获胜」逻辑会直接覆盖数据库中的最新数据,一定要确保业务场景允许这种操作,避免不可逆的数据丢失。
  • 如果你的表主键允许被修改(非常不建议这么设计),那方案中主键的取值要特别注意,确保能正确定位到目标行。

内容的提问来源于stack exchange,提问作者Danny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:33:40