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

如何在TransactionScope中实现MoveFileTransacted并同步数据库提交?

在TransactionScope中实现MoveFileTransacted并关联数据库提交

咱们要实现的是把MoveFileTransacted和TransactionScope绑定到同一个事务里,让文件移动和数据库提交要么全成、要么全败——核心就是让文件操作加入到TransactionScope管理的事务中,下面一步步来落地:

第一步:补全必要的P/Invoke定义和辅助类型

你找到的MoveFileTransacted定义不完整,咱们先把它补全,还要加上需要的枚举和获取事务句柄的方法:

using System;
using System.Runtime.InteropServices;
using System.Transactions;

// MoveFileTransacted的参数枚举,按需选择使用
[Flags]
public enum MoveFileFlags : uint
{
    MOVEFILE_REPLACE_EXISTING = 0x00000001,
    MOVEFILE_COPY_ALLOWED = 0x00000002,
    MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004,
    MOVEFILE_WRITE_THROUGH = 0x00000008,
    MOVEFILE_CREATE_HARDLINK = 0x00000010,
    MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020
}

// MoveFileTransacted要求的回调函数,不需要的话可以传null
public delegate bool MoveFileProgressRoutine(
    IntPtr lpSourceFile,
    IntPtr lpDestinationFile,
    IntPtr lpProgressData,
    UInt64 ulTotalFileSize,
    UInt64 ulTotalBytesTransferred);

// 完整的MoveFileTransacted P/Invoke定义
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool MoveFileTransacted(
    [MarshalAs(UnmanagedType.LPWStr)] string lpExistingFileName,
    [MarshalAs(UnmanagedType.LPWStr)] string lpNewFileName,
    MoveFileProgressRoutine lpProgressRoutine,
    IntPtr lpProgressData,
    MoveFileFlags dwFlags,
    IntPtr hTransaction); // 关键参数:传入事务句柄

另外,咱们需要从TransactionScope的事务中获取内核级的事务句柄,用TransactionInterop类来实现:

private static IntPtr GetKernelTransactionHandle()
{
    var currentTransaction = Transaction.Current;
    if (currentTransaction == null)
        throw new InvalidOperationException("当前没有活跃的事务");

    // 获取DTC事务句柄,再转换为内核事务句柄
    var dtcTransaction = TransactionInterop.GetDtcTransaction(currentTransaction);
    IntPtr kernelHandle;
    var result = Ole32.DuplicateHandle(
        GetCurrentProcess(),
        dtcTransaction,
        GetCurrentProcess(),
        out kernelHandle,
        0,
        false,
        0x00000002); // DUPLICATE_SAME_ACCESS

    if (!result)
        throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());

    return kernelHandle;
}

// 补充需要的辅助P/Invoke
[DllImport("ole32.dll", SetLastError = true)]
private static extern bool DuplicateHandle(
    IntPtr hSourceProcessHandle,
    IntPtr hSourceHandle,
    IntPtr hTargetProcessHandle,
    out IntPtr lpTargetHandle,
    uint dwDesiredAccess,
    bool bInheritHandle,
    uint dwOptions);

[DllImport("kernel32.dll")]
private static extern IntPtr GetCurrentProcess();

第二步:在TransactionScope中整合文件移动和数据库操作

现在可以写出完整的业务逻辑了,把文件移动和数据库提交放在同一个TransactionScope里,确保两者事务一致:

public void MoveFileAndCommitDb(string sourcePath, string destPath)
{
    // 初始化TransactionScope,默认使用可提升的事务
    using (var scope = new TransactionScope(TransactionScopeOption.Required))
    {
        try
        {
            // 获取当前事务的内核句柄
            var transactionHandle = GetKernelTransactionHandle();

            // 调用MoveFileTransacted,传入事务句柄绑定事务
            bool moveSuccess = MoveFileTransacted(
                sourcePath,
                destPath,
                null, // 不需要进度回调,传null即可
                IntPtr.Zero,
                MoveFileFlags.MOVEFILE_REPLACE_EXISTING, // 根据你的需求设置标志
                transactionHandle);

            if (!moveSuccess)
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }

            // 执行你的数据库提交操作,替换成实际业务代码
            CommitDatabaseChanges();

            // 提交整个事务,只有调用Complete后,事务才会真正生效
            scope.Complete();
        }
        catch (Exception ex)
        {
            // 任何一步出错,TransactionScope会自动回滚整个事务
            // 包括文件移动和数据库操作都会被撤销
            Console.WriteLine($"操作失败:{ex.Message}");
            throw;
        }
    }
}

// 示例数据库提交方法,替换成你实际的数据库操作逻辑
private void CommitDatabaseChanges()
{
    // 比如用EF Core提交:
    // using (var dbContext = new YourDbContext())
    // {
    //     // 你的数据库变更操作
    //     dbContext.SaveChanges();
    // }

    // 或者用ADO.NET:
    // using (var conn = new SqlConnection("你的连接字符串"))
    // {
    //     conn.Open();
    //     var cmd = conn.CreateCommand();
    //     cmd.CommandText = "你的提交SQL";
    //     cmd.ExecuteNonQuery();
    // }
}

关键注意事项

  • 事务兼容性:确保你的数据库支持分布式事务(比如SQL Server),因为当你把文件系统操作和数据库操作放在同一个TransactionScope时,事务会自动升级为分布式事务(DTC),需要先确认DTC服务已经开启。
  • 权限要求:执行MoveFileTransacted需要足够的文件系统权限,同时进程要有访问DTC的权限。
  • 句柄管理:示例里通过DuplicateHandle拿到的内核句柄,一般不需要手动释放,TransactionScope结束后事务会自动清理;如果要严格管控,可以在使用完后调用CloseHandle来释放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 07:41:44