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

使用Dapper更新SQL Server中列值不同的交易数据行的实现方案

实现仅更新Transaction对象差异列的SQL Server方法

嘿,针对你开发的银行对账单PDF转SQL Server工具里的「仅更新Transaction对象差异列」需求,我整理了几个实用的实现方案,你可以根据自己的技术栈来选:

方案1:手动构建动态SQL(简单场景首选)

如果你的Transaction字段不多,手动对比字段并生成只包含变化列的UPDATE语句是最直观的方式,完全可控:

public void UpdateTransactionIfChanged(Transaction updatedTransaction)
{
    // 1. 从数据库获取当前交易记录
    var existingTransaction = _dbContext.Transactions
        .FirstOrDefault(t => t.TransactionID == updatedTransaction.TransactionID);
    
    if (existingTransaction == null)
    {
        // 可根据业务需求改为插入新记录或抛出异常
        throw new ArgumentException("目标交易记录不存在于数据库中");
    }

    // 2. 逐个字段对比,收集需要更新的内容
    var updateClauses = new List<string>();
    var sqlParameters = new List<SqlParameter>();

    // 对比账号
    if (existingTransaction.accountNumber != updatedTransaction.accountNumber)
    {
        updateClauses.Add("accountNumber = @AccountNumber");
        sqlParameters.Add(new SqlParameter("@AccountNumber", updatedTransaction.accountNumber));
    }

    // 对比交易日期(注意处理DateTime精度差异,比如截断毫秒)
    if (existingTransaction.TransDate != updatedTransaction.TransDate)
    {
        updateClauses.Add("TransDate = @TransDate");
        sqlParameters.Add(new SqlParameter("@TransDate", updatedTransaction.TransDate));
    }

    // 对比交易标题(忽略大小写的字符串对比)
    if (!string.Equals(existingTransaction.Title, updatedTransaction.Title, StringComparison.OrdinalIgnoreCase))
    {
        updateClauses.Add("Title = @Title");
        sqlParameters.Add(new SqlParameter("@Title", updatedTransaction.Title));
    }

    // 继续对比Summary、category(注意你代码里的拼写是catagory,建议修正)、typeOfTransaction、金额等字段...

    // 3. 有需要更新的列才执行SQL
    if (updateClauses.Count > 0)
    {
        var updateSql = $"UPDATE Transactions SET {string.Join(", ", updateClauses)} WHERE TransactionID = @TransactionID";
        sqlParameters.Add(new SqlParameter("@TransactionID", updatedTransaction.TransactionID));
        
        _dbContext.Database.ExecuteSqlRaw(updateSql, sqlParameters.ToArray());
    }
}

优缺点:

  • ✅ 逻辑清晰,完全控制更新内容,避免不必要的字段写入
  • ❌ 字段较多时代码冗余,需要手动维护每个字段的对比逻辑

方案2:利用Entity Framework Core的变更跟踪(EF Core用户首选)

如果你用EF Core作为ORM框架,它自带的变更跟踪机制会自动检测实体属性的变化,生成只包含修改列的UPDATE语句,无需手动写SQL:

public void UpdateTransactionWithEFChangeTracker(Transaction updatedTransaction)
{
    // 附加主键实体到上下文,告知EF需要跟踪该记录
    var trackedTransaction = _dbContext.Transactions.Attach(new Transaction { TransactionID = updatedTransaction.TransactionID });
    
    // 用映射工具(比如AutoMapper)复制变化的属性,或者手动赋值
    // 示例:手动复制差异属性
    trackedTransaction.Entity.accountNumber = updatedTransaction.accountNumber;
    trackedTransaction.Entity.TransDate = updatedTransaction.TransDate;
    trackedTransaction.Entity.Title = updatedTransaction.Title;
    // 其他字段同理...

    // EF会自动生成仅包含修改列的UPDATE语句
    _dbContext.SaveChanges();
}

如果用AutoMapper,可以更简洁地处理属性映射:

// 提前配置AutoMapper:只映射非空/有变化的属性
_mapper.Map(updatedTransaction, trackedTransaction.Entity);

优缺点:

  • ✅ 代码简洁,无需手动写SQL,自动处理变更检测
  • ✅ 无需维护字段对比逻辑,新增字段时自动适配
  • ❌ 依赖EF Core框架,若用原生ADO.NET则不适用

方案3:反射自动对比字段(多字段场景优化)

如果Transaction字段很多,手动写对比代码太繁琐,可以用反射遍历属性,自动收集差异列,减少重复代码:

public void UpdateTransactionWithReflection(Transaction updatedTransaction)
{
    var existingTransaction = _dbContext.Transactions
        .FirstOrDefault(t => t.TransactionID == updatedTransaction.TransactionID);
    
    if (existingTransaction == null)
    {
        throw new ArgumentException("目标交易记录不存在");
    }

    // 获取Transaction的所有公共实例属性(排除主键TransactionID)
    var properties = typeof(Transaction).GetProperties(BindingFlags.Public | BindingFlags.Instance)
        .Where(p => p.Name != nameof(Transaction.TransactionID));

    var updateClauses = new List<string>();
    var sqlParameters = new List<SqlParameter>();

    foreach (var prop in properties)
    {
        var existingValue = prop.GetValue(existingTransaction);
        var updatedValue = prop.GetValue(updatedTransaction);

        // 处理值类型、引用类型和null的对比
        if (!Equals(existingValue, updatedValue))
        {
            updateClauses.Add($"{prop.Name} = @{prop.Name}");
            sqlParameters.Add(new SqlParameter($"@{prop.Name}", updatedValue ?? DBNull.Value));
        }
    }

    if (updateClauses.Count > 0)
    {
        var updateSql = $"UPDATE Transactions SET {string.Join(", ", updateClauses)} WHERE TransactionID = @TransactionID";
        sqlParameters.Add(new SqlParameter("@TransactionID", updatedTransaction.TransactionID));
        
        _dbContext.Database.ExecuteSqlRaw(updateSql, sqlParameters.ToArray());
    }
}

额外提示:

  • 注意修正代码中catagory的拼写为标准的category,避免后续维护坑
  • 对于DateTime类型,建议对比时截断毫秒(比如existingTransaction.TransDate.Truncate(TimeSpan.FromMilliseconds(1))),避免数据库与.NET的精度差异导致误更新
  • 反射可以缓存属性信息,减少重复反射的性能开销

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 08:32:16