Compensating Resource Managers (CRMs)
A compensating resource manager (CRM) is a service provided by COM+ that enables you to include nontransactional objects in Microsoft Distributed Transaction Coordinator (DTC) transactions. Although CRMs do not provide the capabilities of a full resource manager, they do provide transactional atomicity (all-or-nothing behavior) and durability through the recovery log.
The following example has two parts, a client and a server. The example shows how to create a custom CRM by using a Worker class, a Compensator class, and a Clerk object, and demonstrates how to perform a commit and an abort.
Server
Imports System Imports System.IO Imports System.Reflection Imports System.EnterpriseServices Imports System.EnterpriseServices.CompensatingResourceManager <assembly: ApplicationActivation(ActivationOption.Server)> <assembly: ApplicationCrmEnabled> <assembly: AssemblyKeyFile("crm.key")> Namespace CrmServer ' Create a Worker class. <Transaction> Public Class CRMWorker Inherits Servicedcomponent Public Sub CRMMethod(filename As String, bCommit As Boolean) ' Create the clerk object. Dim myclerk As Clerk=New Clerk(GetType(CRMCompensator), "CRMCompensator", CompensatorOptions.AllPhases) myclerk.WriteLogRecord(filename) myclerk.ForceLog() If bCommit=true Then ContextUtil.SetComplete() Else ContextUtil.SetAbort() End If End Sub End Class ' Create a class derived from the Compensator class. Public Class CRMCompensator Inherits Compensator Dim bBeginPrepareCalled As Boolean = False Dim bPrepareRecordCalled As Boolean = False Dim bBeginCommitCalled As Boolean = False Dim bCommitRecordCalled As Boolean = False Dim bBeginAbortCalled As Boolean = False Dim bAbortRecordCalled As Boolean = False Dim _filename as String Public Overrides Sub BeginPrepare() bBeginPrepareCalled = True End Sub Public Overrides Function PrepareRecord(rec As LogRecord) As Boolean dim o as Object = rec.Record _fileName = o.ToString() bPrepareRecordCalled = True Return False End Function Public Overrides Function EndPrepare() As Boolean if not bBeginPrepareCalled then Return False if not bPrepareRecordCalled then Return False if _fileName="" then Return False ' This is a Prepare Phase success. Return True End Function Public Overrides Sub BeginCommit(fRecovery As Boolean) bBeginCommitCalled = True End Sub Public Overrides Function CommitRecord(rec As LogRecord) As Boolean bCommitRecordCalled = True Return True End Function Public Overrides Sub EndCommit() if not bBeginCommitCalled then Return if not bCommitRecordCalled then Return if _fileName="" then Return ' This is a Commit Phase success. End Sub Public Overrides Sub BeginAbort(fRecovery As Boolean) bBeginAbortCalled = True End Sub Public Overrides Function AbortRecord(rec As LogRecord) As Boolean bAbortRecordCalled = True dim o as Object = rec.Record _fileName = o.ToString() Return True End Function Public Overrides Sub EndAbort() if not bBeginAbortCalled then Return if not bAbortRecordCalled then Return if _fileName="" then Return ' This is an Abort Phase success. End Sub End Class End Namespace [C#] using System; using System.IO; using System.Reflection; using System.EnterpriseServices; using System.EnterpriseServices.CompensatingResourceManager; [assembly: ApplicationActivation(ActivationOption.Server)] [assembly: ApplicationCrmEnabled] [assembly: AssemblyKeyFile("crm.key")] namespace CrmServer { [Transaction] // Create a Worker class. public class CRMWorker:ServicedComponent { public void CRMMethod(string fileName, bool bCommit) { // Create clerk object. Clerk clerk = new Clerk(typeof(CRMCompensator), "CRMCompensator", CompensatorOptions.AllPhases); clerk.WriteLogRecord(fileName); clerk.ForceLog(); if (bCommit) ContextUtil.SetComplete(); else ContextUtil.SetAbort(); } } // Create class derived from Compensator class. public class CRMCompensator:Compensator { bool bBeginPrepareCalled = false; bool bPrepareRecordCalled = false; bool bBeginCommitCalled = false; bool bCommitRecordCalled = false; bool bBeginAbortCalled = false; bool bAbortRecordCalled = false; String _fileName; public override void BeginPrepare() { bBeginPrepareCalled = true; } public override bool PrepareRecord(LogRecord rec) { Object o = rec.Record; _fileName = o.ToString(); bPrepareRecordCalled = true; return false; } public override bool EndPrepare() { if (!bBeginPrepareCalled) {return false;} if (!bPrepareRecordCalled) {return false;} if (_fileName==null) {return false;} // This is a Prepare Phase success. return true; } public override void BeginCommit(bool fRecovery) { bBeginCommitCalled = true; } public override bool CommitRecord(LogRecord rec) { bCommitRecordCalled = true; return true; } public override void EndCommit() { if (!bBeginCommitCalled) {return;} if (!bCommitRecordCalled) {return;} if (_fileName==null) {return;} // This is a Commit Phase success. } public override void BeginAbort(bool fRecovery) { bBeginAbortCalled = true; } public override bool AbortRecord(LogRecord rec) { bAbortRecordCalled = true; Object o = rec.Record; _fileName = o.ToString(); return true; } public override void EndAbort() { if (!bBeginAbortCalled) {return;} if (!bAbortRecordCalled) {return;} if (_fileName==null) {return;} // This is an Abort Phase success. } } }
Client
Imports System Imports System.IO Imports System.EnterpriseServices Imports CrmServer Imports System.Runtime.InteropServices Public Class CRM Public Shared Sub Main() dim logfilename As String = "crm.log" Console.WriteLine("Creating a managed CRM worker object...") dim crmworker As CRMWorker = new CRMWorker() Console.WriteLine("Demonstrating a worker commit...") crmworker.CRMMethod(logfilename, True) Console.WriteLine("Demonstrating a worker abort...") crmworker.CRMMethod(logfilename, False) Console.WriteLine("DONE!") Return End Sub End Class [C#] using System; using System.IO; using System.EnterpriseServices; using CrmServer; using System.Runtime.InteropServices; class CRM { public static int Main() { string logfilename = "crm.log"; Console.WriteLine("Creating a managed CRM worker object..."); CRMWorker crmworker = new CRMWorker(); Console.WriteLine("Demonstrating a worker commit..."); crmworker.CRMMethod(logfilename, true); Console.WriteLine("Demonstrating a worker abort..."); crmworker.CRMMethod(logfilename, false); Console.WriteLine("DONE!"); return 0; } }
Makefile.bat
You can compile the server and client program as follows:
sn –k crm.key vbc /t:library /r:System.EnterpriseServices.dll crm.vb vbc /r:crm.dll /r:System.EnterpriseServices.dll crmclient.vb [C#] sn –k crm.key csc /t:library /r:System.EnterpriseServices.dll crm.cs csc /r:crm.dll crmclient.cs
See Also
Summary of Available COM+ Services | System.EnterpriseServices Namespace