Gör så här: Skapa en transaktionstjänst
Det här exemplet visar olika aspekter av att skapa en transaktionstjänst och användningen av en klientinitierad transaktion för att samordna tjänståtgärder.
Skapa en transaktionstjänst
Skapa ett tjänstkontrakt och kommentera åtgärderna med önskad inställning från TransactionFlowOption uppräkningen för att ange de inkommande transaktionskraven. Observera att du också kan placera TransactionFlowAttribute på den tjänstklass som implementeras. Detta gör att en enda implementering av ett gränssnitt kan använda dessa transaktionsinställningar i stället för varje implementering.
[ServiceContract] public interface ICalculator { [OperationContract] // Use this to require an incoming transaction [TransactionFlow(TransactionFlowOption.Mandatory)] double Add(double n1, double n2); [OperationContract] // Use this to permit an incoming transaction [TransactionFlow(TransactionFlowOption.Allowed)] double Subtract(double n1, double n2); }
Skapa en implementeringsklass och använd ServiceBehaviorAttribute för att ange en TransactionIsolationLevel och en TransactionTimeout. Observera att i många fall är standardvärdet TransactionTimeout 60 sekunder och standardvärdet TransactionIsolationLevel
Unspecified
för lämpligt. För varje åtgärd kan du använda OperationBehaviorAttribute attributet för att ange om arbete som utförs inom metoden ska ske inom omfånget för ett transaktionsomfång enligt värdet för TransactionScopeRequired attributet. I det här fallet är transaktionen som används förAdd
metoden samma som den obligatoriska inkommande transaktionen som flödas från klienten, och transaktionen som används förSubtract
metoden är antingen samma som den inkommande transaktionen om en har flödat från klienten eller en ny implicit och lokalt skapad transaktion.[ServiceBehavior( TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable, TransactionTimeout = "00:00:45")] public class CalculatorService : ICalculator { [OperationBehavior(TransactionScopeRequired = true)] public double Add(double n1, double n2) { // Perform transactional operation RecordToLog($"Adding {n1} to {n2}"); return n1 + n2; } [OperationBehavior(TransactionScopeRequired = true)] public double Subtract(double n1, double n2) { // Perform transactional operation RecordToLog($"Subtracting {n2} from {n1}"); return n1 - n2; } private static void RecordToLog(string recordText) { // Database operations omitted for brevity // This is where the transaction provides specific benefit // - changes to the database will be committed only when // the transaction completes. } }
Konfigurera bindningarna i konfigurationsfilen och ange att transaktionskontexten ska flöda och vilka protokoll som ska användas för att göra det. Mer information finns i ServiceModel Transaction Configuration. Mer specifikt anges bindningstypen i slutpunktselementets
binding
attribut. Slutpunktselementet <>innehåller ettbindingConfiguration
attribut som refererar till en bindningskonfiguration med namnettransactionalOleTransactionsTcpBinding
, enligt följande exempelkonfiguration.<service name="CalculatorService"> <endpoint address="net.tcp://localhost:8008/CalcService" binding="netTcpBinding" bindingConfiguration="transactionalOleTransactionsTcpBinding" contract="ICalculator" name="OleTransactions_endpoint" /> </service>
Transaktionsflödet aktiveras på konfigurationsnivå med hjälp
transactionFlow
av attributet och transaktionsprotokollet anges med hjälp avtransactionProtocol
attributet, enligt följande konfiguration.<bindings> <netTcpBinding> <binding name="transactionalOleTransactionsTcpBinding" transactionFlow="true" transactionProtocol="OleTransactions"/> </netTcpBinding> </bindings>
Stöd för flera transaktionsprotokoll
För optimala prestanda bör du använda OleTransactions-protokollet för scenarier som involverar en klient och tjänst som skrivits med Hjälp av Windows Communication Foundation (WCF). WS-AtomicTransaction-protokollet (WS-AT) är dock användbart för scenarier när samverkan med protokollstackar från tredje part krävs. Du kan konfigurera WCF-tjänster för att acceptera båda protokollen genom att tillhandahålla flera slutpunkter med lämpliga protokollspecifika bindningar, enligt följande exempelkonfiguration.
<service name="CalculatorService"> <endpoint address="http://localhost:8000/CalcService" binding="wsHttpBinding" bindingConfiguration="transactionalWsatHttpBinding" contract="ICalculator" name="WSAtomicTransaction_endpoint" /> <endpoint address="net.tcp://localhost:8008/CalcService" binding="netTcpBinding" bindingConfiguration="transactionalOleTransactionsTcpBinding" contract="ICalculator" name="OleTransactions_endpoint" /> </service>
Transaktionsprotokollet anges med hjälp av attributet
transactionProtocol
. Det här attributet saknas dock i systemetwsHttpBinding
eftersom den här bindningen endast kan använda WS-AT-protokollet.<bindings> <wsHttpBinding> <binding name="transactionalWsatHttpBinding" transactionFlow="true" /> </wsHttpBinding> <netTcpBinding> <binding name="transactionalOleTransactionsTcpBinding" transactionFlow="true" transactionProtocol="OleTransactions"/> </netTcpBinding> </bindings>
Kontrollera slutförandet av en transaktion
Som standard slutför WCF-åtgärder automatiskt transaktioner om inga ohanterade undantag utlöses. Du kan ändra det här beteendet med hjälp TransactionAutoComplete av egenskapen och SetTransactionComplete metoden. När en åtgärd krävs för att utföra samma transaktion som en annan åtgärd (till exempel en debet- och kreditåtgärd) kan du inaktivera funktionen för automatisk komplettering genom att ange TransactionAutoComplete egenskapen till
false
enligt följandeDebit
åtgärdsexempel. Transaktionen somDebit
åtgärden använder har inte slutförts förrän en metod med TransactionAutoComplete egenskapen inställdtrue
på anropas, enligt vad som visas i åtgärdenCredit1
, eller när SetTransactionComplete metoden anropas för att uttryckligen markera transaktionen som slutförd, som du ser i åtgärdenCredit2
. Observera att de två kreditåtgärderna visas i illustrationssyfte och att en enda kreditåtgärd är mer typisk.[ServiceBehavior] public class CalculatorService : IAccount { [OperationBehavior( TransactionScopeRequired = true, TransactionAutoComplete = false)] public void Debit(double n) { // Perform debit operation return; } [OperationBehavior( TransactionScopeRequired = true, TransactionAutoComplete = true)] public void Credit1(double n) { // Perform credit operation return; } [OperationBehavior( TransactionScopeRequired = true, TransactionAutoComplete = false)] public void Credit2(double n) { // Perform alternate credit operation OperationContext.Current.SetTransactionComplete(); return; } }
För transaktionskorrelation kräver inställningen TransactionAutoComplete av egenskapen att
false
en sessionskänslig bindning används. Det här kravet anges medSessionMode
egenskapen på ServiceContractAttribute.[ServiceContract(SessionMode = SessionMode.Required)] public interface IAccount { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void Debit(double n); [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void Credit1(double n); [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void Credit2(double n); }
Kontrollera livslängden för en transaktionstjänstinstans
WCF använder ReleaseServiceInstanceOnTransactionComplete egenskapen för att ange om den underliggande tjänstinstansen släpps när en transaktion slutförs. Eftersom det här standardvärdet är
true
, såvida det inte har konfigurerats på annat sätt, uppvisar WCF ett effektivt och förutsägbart "just-in-time"-aktiveringsbeteende. Anrop till en tjänst för en efterföljande transaktion säkerställs att en ny tjänstinstans inte innehåller några rester av den tidigare transaktionens tillstånd. Detta är ofta användbart, men ibland kanske du vill behålla tillståndet i tjänstinstansen efter transaktionens slutförande. Exempel på detta skulle vara när det krävs tillstånd eller referenser till resurser är dyra att hämta eller återskapa. Du kan göra detta genom att ange ReleaseServiceInstanceOnTransactionComplete egenskapen tillfalse
. Med den inställningen blir instansen och alla associerade tillstånd tillgängliga för efterföljande anrop. När du använder detta bör du noga överväga när och hur tillstånd och transaktioner ska rensas och slutföras. Följande exempel visar hur du gör detta genom att underhålla instansen med variabelnrunningTotal
.[ServiceBehavior(TransactionIsolationLevel = [ServiceBehavior( ReleaseServiceInstanceOnTransactionComplete = false)] public class CalculatorService : ICalculator { double runningTotal = 0; [OperationBehavior(TransactionScopeRequired = true)] public double Add(double n) { // Perform transactional operation RecordToLog($"Adding {n} to {runningTotal}"); runningTotal = runningTotal + n; return runningTotal; } [OperationBehavior(TransactionScopeRequired = true)] public double Subtract(double n) { // Perform transactional operation RecordToLog($"Subtracting {n} from {runningTotal}"); runningTotal = runningTotal - n; return runningTotal; } private static void RecordToLog(string recordText) { // Database operations omitted for brevity } }
Kommentar
Eftersom instansens livslängd är ett beteende som är internt för tjänsten och styrs via ServiceBehaviorAttribute egenskapen krävs ingen ändring av tjänstkonfigurationen eller tjänstkontraktet för att ange instansbeteendet. Dessutom innehåller tråden ingen representation av detta.