Procedure: Een transactionele service maken
In dit voorbeeld ziet u verschillende aspecten van het maken van een transactionele service en het gebruik van een door de client geïnitieerde transactie om servicebewerkingen te coördineren.
Een transactionele service maken
Maak een servicecontract en annotaeer de bewerkingen met de gewenste instelling uit de TransactionFlowOption opsomming om de vereisten voor binnenkomende transacties op te geven. U kunt de TransactionFlowAttribute serviceklasse ook implementeren. Hierdoor kan één implementatie van een interface deze transactie-instellingen gebruiken in plaats van elke implementatie.
[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); }
Maak een implementatieklasse en gebruik de ServiceBehaviorAttribute opdracht om een en een TransactionTimeoutoptioneel op te gevenTransactionIsolationLevel. Houd er rekening mee dat in veel gevallen de standaardwaarde TransactionTimeout van 60 seconden en de standaardwaarde
Unspecified
TransactionIsolationLevel geschikt zijn. Voor elke bewerking kunt u het OperationBehaviorAttribute kenmerk gebruiken om op te geven of werk dat binnen de methode wordt uitgevoerd, moet plaatsvinden binnen het bereik van een transactiebereik op basis van de waarde van het TransactionScopeRequired kenmerk. In dit geval is de transactie die wordt gebruikt voor deAdd
methode hetzelfde als de verplichte binnenkomende transactie die van de client wordt gestroomd en de transactie die voor deSubtract
methode wordt gebruikt, hetzelfde is als de binnenkomende transactie als er een is gestroomd vanaf de client, of een nieuwe impliciete en lokaal gemaakte transactie.[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. } }
Configureer de bindingen in het configuratiebestand, waarbij u opgeeft dat de transactiecontext moet worden gestroomd en welke protocollen hiervoor moeten worden gebruikt. Zie ServiceModel Transaction Configuration voor meer informatie. Het bindingstype wordt specifiek opgegeven in het kenmerk van
binding
het eindpuntelement. Het <eindpuntelement> bevat eenbindingConfiguration
kenmerk dat verwijst naar een bindingsconfiguratie met de naamtransactionalOleTransactionsTcpBinding
, zoals wordt weergegeven in de volgende voorbeeldconfiguratie.<service name="CalculatorService"> <endpoint address="net.tcp://localhost:8008/CalcService" binding="netTcpBinding" bindingConfiguration="transactionalOleTransactionsTcpBinding" contract="ICalculator" name="OleTransactions_endpoint" /> </service>
Transactiestroom wordt ingeschakeld op configuratieniveau met behulp van het
transactionFlow
kenmerk en het transactieprotocol wordt opgegeven met behulp van hettransactionProtocol
kenmerk, zoals wordt weergegeven in de volgende configuratie.<bindings> <netTcpBinding> <binding name="transactionalOleTransactionsTcpBinding" transactionFlow="true" transactionProtocol="OleTransactions"/> </netTcpBinding> </bindings>
Ondersteuning voor meerdere transactieprotocollen
Voor optimale prestaties moet u het OleTransactions-protocol gebruiken voor scenario's met betrekking tot een client en service die is geschreven met behulp van Windows Communication Foundation (WCF). Het WS-AtomicTransaction-protocol (WS-AT) is echter handig voor scenario's wanneer interoperabiliteit met protocolstacks van derden vereist is. U kunt WCF-services configureren om beide protocollen te accepteren door meerdere eindpunten met de juiste protocolspecifieke bindingen te bieden, zoals wordt weergegeven in de volgende voorbeeldconfiguratie.
<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>
Het transactieprotocol wordt opgegeven met behulp van het
transactionProtocol
kenmerk. Dit kenmerk is echter niet aanwezig in het systeem,wsHttpBinding
omdat deze binding alleen het WS-AT-protocol kan gebruiken.<bindings> <wsHttpBinding> <binding name="transactionalWsatHttpBinding" transactionFlow="true" /> </wsHttpBinding> <netTcpBinding> <binding name="transactionalOleTransactionsTcpBinding" transactionFlow="true" transactionProtocol="OleTransactions"/> </netTcpBinding> </bindings>
De voltooiing van een transactie beheren
WCF-bewerkingen voltooien standaard automatisch transacties als er geen onverwerkte uitzonderingen worden gegenereerd. U kunt dit gedrag wijzigen met behulp van de TransactionAutoComplete eigenschap en de SetTransactionComplete methode. Wanneer een bewerking moet worden uitgevoerd binnen dezelfde transactie als een andere bewerking (bijvoorbeeld een debit- en kredietbewerking), kunt u het gedrag van automatisch aanvullen uitschakelen door de TransactionAutoComplete eigenschap
false
in te stellen op zoals wordt weergegeven in het volgendeDebit
voorbeeld van de bewerking. De transactie die door deDebit
bewerking wordt gebruikt, wordt pas voltooid als een methode waarop de TransactionAutoComplete eigenschap is ingesteldtrue
, wordt aangeroepen, zoals wordt weergegeven in de bewerkingCredit1
, of wanneer de SetTransactionComplete methode wordt aangeroepen om de transactie expliciet als voltooid te markeren, zoals wordt weergegeven in de bewerkingCredit2
. Houd er rekening mee dat de twee kredietbewerkingen ter illustratie worden weergegeven en dat één kredietbewerking meer typisch is.[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; } }
Voor transactiecorrelatie moet de TransactionAutoComplete eigenschap worden ingesteld op
false
het gebruik van een sessieful binding. Deze vereiste wordt opgegeven met deSessionMode
eigenschap op de 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); }
De levensduur van een transactionele service-instantie beheren
WCF gebruikt de ReleaseServiceInstanceOnTransactionComplete eigenschap om op te geven of het onderliggende service-exemplaar wordt vrijgegeven wanneer een transactie is voltooid. Aangezien dit standaard is ingesteld
true
op , tenzij anders geconfigureerd, vertoont WCF een efficiënt en voorspelbaar 'Just-In-Time'-activeringsgedrag. Aanroepen naar een service op een volgende transactie zijn verzekerd van een nieuw service-exemplaar zonder restanten van de status van de vorige transactie. Hoewel dit vaak nuttig is, wilt u soms de status binnen het service-exemplaar behouden na de voltooiing van de transactie. Voorbeelden hiervan zijn wanneer de vereiste status of ingangen naar resources duur zijn om op te halen of opnieuw te reconstitueren. U kunt dit doen door de ReleaseServiceInstanceOnTransactionComplete eigenschap in te stellen opfalse
. Met deze instelling zijn het exemplaar en de bijbehorende status beschikbaar voor volgende aanroepen. Wanneer u dit gebruikt, moet u zorgvuldig nadenken over wanneer en hoe status en transacties worden gewist en voltooid. In het volgende voorbeeld ziet u hoe u dit doet door het exemplaar bij te houden met derunningTotal
variabele.[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 } }
Notitie
Omdat de levensduur van het exemplaar een gedrag is dat intern is voor de service en wordt beheerd via de ServiceBehaviorAttribute eigenschap, is er geen wijziging van de serviceconfiguratie of het servicecontract vereist om het gedrag van het exemplaar in te stellen. Bovendien bevat de draad geen representatie hiervan.