Manually creating DTC Transactions

I have seen this article somewhere before. I am not able to find it. I modified the code little bit and reposting it here.

Scenario: There are cases where your application has to update your database and queue messages to MSMQ in a single atomic transaction scope. In .NETV1.0, the only way to achieve this is to use COM+ through System.EnterpriseServices. In .NET V1.1 there is another way to do this without using System.EnterpriseServices.

The idea here is to create a DTC transaction through TransactionDispenser component provided by DTC. Once you get the transaction object, you can pass the transaction object to your SQL provider as well as MSMQ APIs to participate in a single transaction. DTC takes care of two phase commit.

Sample code here explains how to obtain a transaction from DTC. Once you get the ITransaction interface, you can pass it on to any resource managers that support ITransaction. You have to use Interop to get the DTC Transaction.

using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.EnterpriseServices;

namespace Transactions
{

 [Flags]
 public enum ISOLATIONLEVEL
 {
  //ISOLATIONLEVEL_UNSPECIFIED      = 0xFFFFFFFF,
  ISOLATIONLEVEL_CHAOS            = 0x00000010,
  ISOLATIONLEVEL_READUNCOMMITTED  = 0x00000100,
  ISOLATIONLEVEL_BROWSE           = 0x00000100,
  ISOLATIONLEVEL_READCOMMITTED    = 0x00001000,
  ISOLATIONLEVEL_CURSORSTABILITY  = 0x00001000,
  ISOLATIONLEVEL_REPEATABLEREAD   = 0x00010000,
  ISOLATIONLEVEL_SERIALIZABLE     = 0x00100000,
  ISOLATIONLEVEL_ISOLATED         = 0x00100000
 }

 public enum ISOFLAG
 {
  ISOFLAG_RETAIN_COMMIT_DC  =  1,
  ISOFLAG_RETAIN_COMMIT     =  2,
  ISOFLAG_RETAIN_COMMIT_NO  =  3,
  ISOFLAG_RETAIN_ABORT_DC   =  4,
  ISOFLAG_RETAIN_ABORT      =  8,
  ISOFLAG_RETAIN_ABORT_NO   = 12,
  ISOFLAG_RETAIN_DONTCARE   =  7,
  ISOFLAG_RETAIN_BOTH       = 10,
  ISOFLAG_RETAIN_NONE       = 13,
  ISOFLAG_OPTIMISTIC        = 16,
  ISOFLAG_READONLY          = 32
 }

 //0x3A6AD9E1, 0x23B9, 0x11cf, 0xAD, 0x60, 0x00, 0xAA, 0x00, 0xA7, 0x4C, 0xCD);
 [Guid("3A6AD9E1-23B9-11cf-AD60-00AA00A74CCD")]
 [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
 public interface ITransactionDispenser
 {
  
  ushort GetOptionsObject([MarshalAs(UnmanagedType.IUnknown)]ref object options);

  int BeginTransaction(
   [MarshalAs(UnmanagedType.IUnknown)] object pUnknownOuter,
   ISOLATIONLEVEL isoLevel,
   ISOFLAG isoFlag,
   [MarshalAs(UnmanagedType.IUnknown)] object transactionOptions,
   [MarshalAs(UnmanagedType.Interface)] ref ITransaction pTransaction);
     
 }

 public class NativeSafeDTC
 {
  
  public static Guid DispenserIID = new Guid("3A6AD9E1-23B9-11cf-AD60-00AA00A74CCD");

  [DllImport("xolehlp.dll")]
  public static extern int DtcGetTransactionManager(
    IntPtr hostName,//[MarshalAsAttribute(UnmanagedType.LPStr)]string hostName,
    IntPtr tmName,//[MarshalAsAttribute(UnmanagedType.LPSTR)]string tmName,
    ref Guid iid,
    UInt32 dwReserved1,
    UInt16 wReserved2,
    IntPtr pvReserved,
    [MarshalAsAttribute(UnmanagedType.Interface)]ref ITransactionDispenser txnDispenser);
  
 }
}

Client Code

class Class1
 {
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [STAThread]
  static void Main(string[] args)
  {
   //
   // TODO: Add code to start application here
   //

   
   ITransactionDispenser oDispenser = null;

   int hResult = NativeSafeDTC.DtcGetTransactionManager(IntPtr.Zero,//null,
         IntPtr.Zero,//null,
         ref NativeSafeDTC.DispenserIID,
         0, 0, IntPtr.Zero,
         ref oDispenser);
   
   if (oDispenser != null)
   {
    ITransactionDispenser dispenser = oDispenser;

    Console.WriteLine("Got a good dispenser");

    
    for (int i=0; i < 100000; i++)
    {
     ITransaction transaction = null;
     dispenser.BeginTransaction(null, ISOLATIONLEVEL.ISOLATIONLEVEL_SERIALIZABLE,
      ISOFLAG.ISOFLAG_RETAIN_DONTCARE, null, ref transaction);
     if (transaction != null)
     {
      //Console.WriteLine("Obtained good transaction object");
      transaction.Commit(0,0,0);
     }
    }
   }
   

  }
 }