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