Biztalk Pipeline and File System Transaction (TxF)

With Vista and Windows Server 2008 comes and new amazing feature: the possibility to include file system operations into a transaction. The functionnality is implemented as the Kernel Transaction Manager (KTM).

Requirements: Biztalk must be installed on a Vista box or Windows Server 2008 (which is not supported afaik).

I had to implement this functionnality inside in a pipeline component in a Biztalk 2006 R2 solution . I started with the wrapper provided on Codeplex and it did not worked as expected. As I have spent a bit of time on that problem and I have not found the information anywhere, here is how I solve the case.

I have found several blogs discussing about it but never within a Biztalk context. I have added the sources I have used at the end of this article. For further information on the new feature, please visit those sites.

To make it simple in a common situation, to use KTM integration we just have to use a TransactionScope. If a transaction already exists, the TransactionScope will hook to that transaction in the other case a new transaction will be created.

When using a pipeline component, the best way to use transaction is to use the context transaction which derived from Systems.SystemEnterprise.ITransaction and which is an MSDTC transaction. The trick here is that the wrapper is retrieving directly the current transaction from a transaction scope.

Here is the best way to hook to the current transaction in a receive pipeline :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
  {
  //Using MSDTC Transaction
  // Casting the current context to IPipelineContextEx to extract a transaction.
  IPipelineContextEx txContext = (IPipelineContextEx)pContext;
  
  if (null != txContext)
  {
      //Get a transaction (unmanaged)
      ITransaction tx = (ITransaction)txContext.GetTransaction();
      if (null != tx)
      {
          //TODO: Enlist the operation in the DTC Transaction created.
          // ex: sqlConnection.EnlistDistributedTransaction(tx)
          //TODO: Implement transaction work
      }
      else
      {
          //No transaction available
          //TODO: perform required steps
      }
  }
  else
  {
      //No transactional aware context available
      //TODO: perform required steps
  }
  
  // Return the message
  return pInMsg;
  }

Transaction are only available in Receive Pipelines. For a send Pipeline the use of a TransactionScope may be the best option, but I haven’t tested this yet.

So I had to do 2 things:

  • Add a method to the NativeKTMMethods to return a KTM Transaction Handle from the MSDTC Transaction:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    public static KtmTransactionHandle CreateKtmTransactionHandle (ITransaction iTransaction)
    {
        IDtcTransaction dtcTransaction = (IDtcTransaction)(iTransaction );
        IKernelTransaction ktmInterface = (IKernelTransaction)dtcTransaction;
        IntPtr ktmTxHandle;
        
        int hr = ktmInterface.GetHandle(out ktmTxHandle);
        HandleError(hr);
        
        return new KtmTransactionHandle(ktmTxHandle);
    }
  • Add the transaction retrieved from the pipeline context as a parameter to the file operation methods and remove the TransactionScope operations.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
    public static bool Copy(string sourceFileName, string destFileName, bool overwrite, ITransaction iTransaction)
    {
        //using (TransactionScope scope = new TransactionScope())
        using (KtmTransactionHandle ktmTx =
        KtmTransactionHandle.CreateKtmTransactionHandle(iTransaction))
        {
            // Creating Transacted File.
            NativeMethods.CopyFileFlags copyFlags = NativeMethods.CopyFileFlags.COPY_FILE_FAIL_IF_EXISTS;
            if (overwrite)
            {
                copyFlags = 0;
            }
            
            bool pbCancel = false;
            bool status = NativeMethods.CopyFileTransacted(
            sourceFileName,
            destFileName,
            IntPtr.Zero,
            IntPtr.Zero,
            ref pbCancel,
            copyFlags,
            ktmTx);
            
            if (!status)
            {
                NativeMethods.HandleCOMError(Marshal.GetLastWin32Error());
            }
            
            return status;
            }
        }
        
        public static Int32 WriteStreamToTransactedFile(string path, Stream streamedFile, ITransaction iTransaction)
        {
            //using (TransactionScope scope = new TransactionScope())
            using (KtmTransactionHandle ktmTx =
            KtmTransactionHandle.CreateKtmTransactionHandle(iTransaction))
            {
                // Creating Transacted File.
                NativeMethods.FileMode internalMode = TranslateFileMode(FileMode.Create);
                NativeMethods.FileShare internalShare = TranslateFileShare(FileShare.Read);
                NativeMethods.FileAccess internalAccess =
                TranslateFileAccess(FileAccess.ReadWrite);
                

From now on when a message is incoming in the pipeline, the file attached is saved on the file system, further processes are done on other components, like database update, and so on. If anything goes wrong in any component, and an exception is raised the transaction is rolled back and all operations canceled.

The transaction is shared between all the pipeline components of the pipeline. You don’t have to worry about committing or aborting the transaction, the pipeline will do it.

I am expecting this to work under Biztalk 2009 on a Windows Server 2008. I hope I will soon have the opportunity to test it and see how it can be implemented on a send pipeline.

Sources: Wrapper for the transactional NTFS

MSDN sources : Transactional Enhancements Samples Excellent Articles from Bart Desmet on Transactionnal functionnalities with Vista and Windows server 2008. (there are more article about reansactions and registry) Windows Vista - Introducing TxF in C# (part 1) - Transacted file delete Windows Vista - Introducing TxF in C# (part 2) - Using System.Transactions and the DTC Windows Vista - Introducing TxF in C# (part 3) - CreateFileTransacted demo Original Post