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

如何在NHibernate中添加嵌套事务?求.NET多配置事务回滚代码示例

嘿,得先给你捋清楚一个核心点:常规的数据库事务一旦提交,就没法再回滚了。所以你想要的“后续配置项处理失败时,回滚所有已经提交的独立事务”,直接用NHibernate的普通嵌套事务是实现不了的——因为那些已经提交的事务已经把数据持久化到数据库里了。不过我们有两种可行的方案来满足你的业务需求,下面给你详细拆解:

方案1:使用分布式事务(DTC)

如果你的数据库支持分布式事务协调器(DTC),可以把所有配置项的操作纳入一个全局事务中。这样每个配置项的操作虽然看起来是独立执行,但实际上都属于同一个全局事务的一部分,只有当所有操作都成功时才会统一提交,任何一步出错都会全局回滚。

NHibernate可以配合.NET的TransactionScope来实现这个逻辑,代码示例如下:

using System.Transactions;
using NHibernate;
using System.Collections.Generic;

public class ConfigService
{
    private readonly ISessionFactory _sessionFactory;

    public ConfigService(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public void ProcessMultipleConfigs(List<ConfigItem> configItems)
    {
        // 创建全局事务范围,自动管理全局事务的生命周期
        using (var scope = new TransactionScope(TransactionScopeOption.Required))
        {
            foreach (var configItem in configItems)
            {
                try
                {
                    using (var session = _sessionFactory.OpenSession())
                    {
                        // 处理单个配置项的业务逻辑
                        ProcessSingleConfig(session, configItem);
                    }
                }
                catch (Exception ex)
                {
                    // 抛出异常触发全局回滚
                    throw new InvalidOperationException($"处理配置项{configItem.Id}失败", ex);
                }
            }

            // 所有操作无异常,提交全局事务
            scope.Complete();
        }
    }

    private void ProcessSingleConfig(ISession session, ConfigItem configItem)
    {
        var existingConfig = session.Get<ConfigItem>(configItem.Id);
        if (existingConfig != null)
        {
            existingConfig.Value = configItem.Value;
            session.Update(existingConfig);
        }
        else
        {
            session.Save(configItem);
        }
        // 刷新会话确保操作纳入全局事务
        session.Flush();
    }
}

// 示例配置项实体
public class ConfigItem
{
    public virtual int Id { get; set; }
    public virtual string Key { get; set; }
    public virtual string Value { get; set; }
}

注意:使用DTC需要确保你的数据库和应用服务器都开启了DTC服务,这种方案在高并发场景下可能有性能开销,因为全局事务的锁持有时间更长。

方案2:补偿事务模式

如果你的场景不适合用DTC(比如性能要求高、数据库不支持DTC),可以采用补偿事务的思路:每个配置项执行成功后,记录对应的“补偿操作”;如果后续步骤出错,就执行这些补偿操作来撤销之前的修改。

代码示例如下:

using NHibernate;
using System.Collections.Generic;

public class ConfigService
{
    private readonly ISessionFactory _sessionFactory;
    // 存储已执行操作的补偿逻辑
    private readonly List<Action<ISession>> _compensations = new List<Action<ISession>>();

    public void ProcessMultipleConfigs(List<ConfigItem> configItems)
    {
        using (var session = _sessionFactory.OpenSession())
        {
            try
            {
                foreach (var configItem in configItems)
                {
                    ProcessSingleConfigWithCompensation(session, configItem);
                }

                // 所有操作成功,提交最终事务
                using (var tx = session.BeginTransaction())
                {
                    tx.Commit();
                }
                // 清空补偿列表,无需再执行
                _compensations.Clear();
            }
            catch (Exception ex)
            {
                // 执行补偿操作,回滚已完成的修改
                using (var tx = session.BeginTransaction())
                {
                    foreach (var compensation in _compensations)
                    {
                        compensation(session);
                    }
                    tx.Commit();
                }
                throw new InvalidOperationException("处理配置项失败,已执行补偿回滚", ex);
            }
        }
    }

    private void ProcessSingleConfigWithCompensation(ISession session, ConfigItem configItem)
    {
        using (var tx = session.BeginTransaction())
        {
            var existingConfig = session.Get<ConfigItem>(configItem.Id);
            if (existingConfig != null)
            {
                // 记录原有值,作为补偿逻辑
                var originalValue = existingConfig.Value;
                _compensations.Add(s => 
                {
                    var configToRollback = s.Get<ConfigItem>(configItem.Id);
                    configToRollback.Value = originalValue;
                    s.Update(configToRollback);
                });

                // 更新配置项
                existingConfig.Value = configItem.Value;
                session.Update(existingConfig);
            }
            else
            {
                // 新增配置的补偿逻辑:删除该配置
                _compensations.Add(s => 
                {
                    var configToDelete = s.Get<ConfigItem>(configItem.Id);
                    if (configToDelete != null)
                    {
                        s.Delete(configToDelete);
                    }
                });

                session.Save(configItem);
            }

            tx.Commit();
        }
    }
}

这个方案的核心是:每完成一个配置项的修改,就把“如何撤销这个修改”的逻辑存起来;一旦后续出错,就逐个执行这些撤销操作,把数据恢复到之前的状态。

注意:补偿事务需要你自己确保补偿逻辑的正确性,而且如果在执行补偿操作时又出错,可能需要额外的重试或人工介入机制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:28:01