如何将文件写入磁盘并在单个事务中插入数据库记录?

And*_*son 11 .net c# sql-server transactions file

我试图将文件写入磁盘以及通过原子事务中的存储过程将数据插入数据库.即如果这两个操作中的任何一个失败(文件无法写入磁盘或存储过程失败)我想什么也不做,只是将一个异常抛回给调用者.

关于如何最好地解决文件写入和数据库插入的原子事务的任何建议?

附加信息:我正在使用C#.NET和MS SQL Server中的存储过程,但是不一定适合这些技术的一般解决方案也很好.

更新:在回顾下面的所有答案并研究其他人之后,我写了这篇文章,关于如何使用3种不同的方法来解决这个问题.

Rem*_*anu 7

您需要使用新的TxF,Vista,Windows 7和Windows Server 2008中引入的Transacted NTFS.这是一篇很好的介绍性文章:使用文件系统事务增强您的应用程序.它包含一个将文件操作注册到系统事务中的小型托管示例:

// IKernelTransaction COM Interface
[Guid("79427A2B-F895-40e0-BE79-B57DC82ED231")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IKernelTransaction
{
    int GetHandle(out IntPtr pHandle);
}

[DllImport(KERNEL32, 
   EntryPoint = "CreateFileTransacted",
   CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern SafeFileHandle CreateFileTransacted(
   [In] string lpFileName,
   [In] NativeMethods.FileAccess dwDesiredAccess,
   [In] NativeMethods.FileShare dwShareMode,
   [In] IntPtr lpSecurityAttributes,
   [In] NativeMethods.FileMode dwCreationDisposition,
   [In] int dwFlagsAndAttributes,
   [In] IntPtr hTemplateFile,
   [In] KtmTransactionHandle hTransaction,
   [In] IntPtr pusMiniVersion,
   [In] IntPtr pExtendedParameter);

....

using (TransactionScope scope = new TransactionScope())
{
   // Grab Kernel level transaction handle
   IDtcTransaction dtcTransaction = 
      TransactionInterop.GetDtcTransaction(managedTransaction);
   IKernelTransaction ktmInterface = (IKernelTransaction)dtcTransaction;

   IntPtr ktmTxHandle;
   ktmInterface.GetHandle(out ktmTxHandle);

   // Grab transacted file handle
   SafeFileHandle hFile = NativeMethods.CreateFileTransacted(
      path, internalAccess, internalShare, IntPtr.Zero,
      internalMode, 0, IntPtr.Zero, ktmTxHandle,
      IntPtr.Zero, IntPtr.Zero);

   ... // Work with file (e.g. passing hFile to StreamWriter constructor)

   // Close handles
}
Run Code Online (Sandbox Code Playgroud)

您需要在同一事务中注册SQL操作,该事务将在TransactionScope下自动进行.但我强烈建议您覆盖默认的TransactionScope 选项以使用ReadCommitted隔离级别:

using (TransactionScope scope = new TransactionScope(
     TransactionScope.Required, 
     new TransactionOptions 
         { IsolationLevel = IsolationLEvel.ReadCommitted}))
{
...
}
Run Code Online (Sandbox Code Playgroud)

如果没有这个,你将获得默认的Serializable隔离级别,这对大多数情况来说都是过度杀伤.


p.c*_*ell 5

这个问题和答案似乎是答案的一部分.它涉及Transactional NTFS.SLaks链接到MSDN上托管的Transactional NTFS.NET托管包装器.

你可以尝试使用TransactionScope.