How to: Create a Compensating Resource Manager (CRM)
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.
To create a Compensating Resource Manager
Import the EnterpriseServices and CompensatingResourceManager namespaces.
Imports System.EnterpriseServices Imports System.EnterpriseServices.CompensatingResourceManager
using System.EnterpriseServices; using System.EnterpriseServices.CompensatingResourceManager;
Enable CRM support for the assembly.
<assembly: ApplicationCrmEnabled>
[assembly: ApplicationCrmEnabled]Define a CRM worker class that derives from the ServicedComponent class. For example, the following code shows a class CRMWorker that derives directly from ServicedComponent.
<Transaction> Public Class CRMWorker Inherits Servicedcomponent End Class
[Transaction] public class CRMWorker:ServicedComponent { }
Implement a public method that creates a Clerk object and commits or aborts the transaction. The method must update the CRM log by using the Clerk object. For example, the following code shows a method CRMMethod that updates the CRM log and commits or aborts the transaction.
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
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(); }
Define a class that derives from the Compensation class.
JustInTimeActivation() Public Class CRMCompensator Inherits Compensator End Class
[JustInTimeActivation] public class CRMCompensator:Compensator { }
Conclusion
Note: |
|---|
You must apply the JustInTimeActivation attribute to the compensator; otherwise abort is called twice. |
Override the BeginPrepare, PrepareRecord, EndPrepare, BeginCommit, CommitRecord, EndCommit, BeginAbort, AbortRecord, and EndAbort members of the Compensator class.
Create a client application to test the CRM Worker and Compensator components.
Import the required names, such as the System.EnterpriseServices and the namespace that implements the CRM Worker and Compensator classes.
Imports System Imports System.IO Imports System.EnterpriseServices Imports CrmServer Imports System.Runtime.InteropServices
using System; using System.IO; using System.EnterpriseServices; using CrmServer; using System.Runtime.InteropServices;
Define a class and implement the Main method to create an instance of the CRM worker class, and call that method that creates the CRM Clerk object. For example, the following code creates an object of the CRMWorker type and calls the method CRMMethod to create the CRM Clerk object.
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!") End Sub End Class
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; } }
Generate a strong key and compile the following example.
sn –k crm.key vbc /t:library /r:System.EnterpriseServices.dll crm.vb vbc /r:crm.dll /r:System.EnterpriseServices.dll crmclient.vb
sn –k crm.key csc /t:library /r:System.EnterpriseServices.dll crm.cs csc /r:crm.dll crmclient.cs
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. JustInTimeActivation() 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
Note: