ServiceObject的TransactionMode有三个取值:不需要交易,需要交易,需要新的交易。ServiceObject的方法被调用时会建立一个TTransactionContext,并随着子元件,子子元件的调用传递,子元件,子子元件只是对这个Transaction投票,是需要Commit还是Rollback,根元件根据最后结果来决定最后该Commit还是Rollback。
property DataObject:TCustomServiceObject read GetDataObject;
property DataService:IDBService read GetDataService;
property Context:TTransactionContext read GetContext;
property IsInTransaction:Boolean read GetIsInTransaction;
property Status:TServiceObjectStatus read FStatus write SetStatus;
end;
function TCustomServiceObject.Call(const AMethodName: string;
const AParams: OLEVariant;
const AASyncCallContent:TASyncCallContent=nil;
const AClientContent:TClientContent=nil): OLEVariant;
const
UseDTCTransaction=false;
var
//单个Connection执行“BEGIN DISTRIBUTED TRANSACTION”并不能将多个Connection纳入分布式事务
IsRoot:Boolean;
procedure CheckTransactionStart;
begin
case GetTransactionMode of
tmNeedNew:
begin
//开始新的activity,这个交易沿新的activity传递
if not FContextCreated then FContext:=nil;
//不使用activity传下来的DataOject
if not FDataObjectCreated then FDataObject:=nil;
if not IsInTransaction then
begin
if UseDTCTransaction then
begin
Context.FTransaction:=DTCTransaction.DTCTransactionStart;
DataService.JoinDistributeTransaction(Context.FTransaction);
end
else
DataService.StartTransaction;
end;
end;
tmNeed:
begin
if not IsInTransaction then
begin //根元件启动交易
IsRoot:=true;
if UseDTCTransaction then
begin
Context.FTransaction:=DTCTransaction.DTCTransactionStart;
DataService.JoinDistributeTransaction(Context.FTransaction);
end
else
DataService.StartTransaction;
end;
end;
end;
end;
procedure CheckTransactionCommit;
begin
if GetTransactionMode=tmNeedNew then
begin
if UseDTCTransaction then
DTCTransaction.DTCTransactionCommit(Context.FTransaction)
else
DataService.CommitTransaction;
end
else if IsRoot then
begin
//根元件根据activity的投票结果,决定确认交易还是回滚交易
if Context.FVoteTransactionRollbacked then
begin
if UseDTCTransaction then
DTCTransaction.DTCTransactionRollback(Context.FTransaction)
else
DataService.RollbackTransaction;
end
else
begin
if UseDTCTransaction then
DTCTransaction.DTCTransactionCommit(Context.FTransaction)
else
DataService.CommitTransaction;
end;
end;
//投票确认activity交易
VoteCommitTransaction;
end;
procedure CheckTransactionRollback;
begin
if GetTransactionMode=tmNeedNew then
begin
if IsInTransaction then
if UseDTCTransaction then
DTCTransaction.DTCTransactionCommit(Context.FTransaction)
else
DataService.RollbackTransaction;
end
else if IsRoot then
begin
if IsInTransaction then
if UseDTCTransaction then
DTCTransaction.DTCTransactionCommit(Context.FTransaction)
else
DataService.RollbackTransaction;
end;
//表决回滚activity的交易
VoteRollbackTransaction;
end;
procedure CheckClearTransaction;
begin
if (GetTransactionMode=tmNeedNew) or IsRoot then
begin
if IsInTransaction then
if UseDTCTransaction then
DataService.UnJoinDistributeTransaction;
ConText.FTransaction:=nil;
ConText.FVoteTransactionRollbacked:=false;
end;
end;
var
ATime:TDateTime;
begin
Result:=null;
ATime:=Now;
if ServiceLog<>nil then
ServiceLog.ServiceObjectBeforeMethodCalled(FServiceName,AMethodName);
IsRoot:=false;
try
try
CheckTransactionStart;
Result:=CallMethod(AMethodName,AParams,AAsyncCallContent,AClientContent);
CheckTransactionCommit;
except
CheckTransactionRollback;
Raise;
end;
finally
CheckClearTransaction;
if ServiceLog<>nil then
ServiceLog.ServiceObjectAfterMethodCalled(
FServiceName,AMethodName,Round((Now-ATime)*86400000));
end;
end;
如果是多个数据库的分散式Transaction,需要调用MSDTC来控制。
interface
uses
SysUtils,OleDB,ComObj,Windows;
function DTCTransactionStart:ITransaction;
procedure DTCTransactionCommit(ATransaction:ITransaction);
procedure DTCTransactionRollback(ATransaction:ITransaction);
const
TransactionISOLATIONLEVEL=ISOLATIONLEVEL_SERIALIZABLE;
CLSID_MSDASQL :TGUID='{C8B522CB-5CF3-11CE-ADE5-00AA0044773D}'; //oledb for ODBC
CLSID_SQLOLEDB :TGUID='{0C7FF16C-38E3-11D0-97AB-00C04FC2AD98}'; //oledb for sql server
implementation
function DtcGetTransactionManager(
hostName:PChar;
tmName:PChar;
iid:Pointer;//pointer of TGUID;
dwReserved1:DWord;
wReserved2:Word;
pvReserved:Pointer;
out txnDispenser:ITransactionDispenser):HResult;cdecl;external 'xolehlp.dll';
function DTCTransactionStart:ITransaction;
var
ID:ITransactionDispenser;
begin
Result:=nil;
OLECheck(DtcGetTransactionManager(
nil,
nil,
@IID_ITransactionDispenser,
0,0,
nil,
ID));
OLECheck(ID.BeginTransaction(nil,
TransactionISOLATIONLEVEL,
ISOFLAG_RETAIN_DONTCARE,nil,Result));
end;
procedure DTCTransactionCommit(ATransaction:ITransaction);
begin
OLECheck(ATransaction.Commit(false,0,0));
end;
procedure DTCTransactionRollback(ATransaction:ITransaction);
var
B:BOID;
begin
OLECheck(ATransaction.Abort(@B,false,false));
end;
end.