Instrukcje: Tworzenie usługi transakcyjnej
W tym przykładzie pokazano różne aspekty tworzenia usługi transakcyjnej i korzystania z transakcji zainicjowanej przez klienta w celu koordynowania operacji usług.
Tworzenie usługi transakcyjnej
Utwórz kontrakt usługi i dodaj adnotację do operacji z żądanym ustawieniem z TransactionFlowOption wyliczenia, aby określić wymagania dotyczące transakcji przychodzących. Należy pamiętać, że można również umieścić element TransactionFlowAttribute w zaimplementowanej klasie usługi. Umożliwia to pojedynczą implementację interfejsu w celu korzystania z tych ustawień transakcji zamiast każdej implementacji.
[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); }
Utwórz klasę implementacji i użyj elementu , ServiceBehaviorAttribute aby opcjonalnie określić element TransactionIsolationLevel i .TransactionTimeout Należy pamiętać, że w wielu przypadkach wartość domyślna TransactionTimeout to 60 sekund i wartość domyślna
Unspecified
TransactionIsolationLevel są odpowiednie. Dla każdej operacji można użyć atrybutu OperationBehaviorAttribute , aby określić, czy praca wykonywana w metodzie powinna odbywać się w zakresie zakresu transakcji zgodnie z wartością atrybutu TransactionScopeRequired . W takim przypadku transakcja używana dlaAdd
metody jest taka sama jak obowiązkowa transakcja przychodząca, która jest przepływany z klienta, a transakcja używana dlaSubtract
metody jest taka sama jak transakcja przychodząca, jeśli została ona przepływna z klienta, lub nowa niejawnie i lokalnie utworzona transakcja.[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. } }
Skonfiguruj powiązania w pliku konfiguracji, określając, że kontekst transakcji powinien być przepływany, a protokoły, które mają być używane do tego celu. Aby uzyskać więcej informacji, zobacz ServiceModel Transaction Configuration (Konfiguracja transakcji ServiceModel). W szczególności typ powiązania jest określony w atrybucie elementu
binding
punktu końcowego. Element <punktu końcowego>bindingConfiguration
zawiera atrybut, który odwołuje się do konfiguracji powiązania o nazwietransactionalOleTransactionsTcpBinding
, jak pokazano w poniższej przykładowej konfiguracji.<service name="CalculatorService"> <endpoint address="net.tcp://localhost:8008/CalcService" binding="netTcpBinding" bindingConfiguration="transactionalOleTransactionsTcpBinding" contract="ICalculator" name="OleTransactions_endpoint" /> </service>
Przepływ transakcji jest włączony na poziomie konfiguracji przy użyciu atrybutu
transactionFlow
, a protokół transakcji jest określony przy użyciu atrybututransactionProtocol
, jak pokazano w poniższej konfiguracji.<bindings> <netTcpBinding> <binding name="transactionalOleTransactionsTcpBinding" transactionFlow="true" transactionProtocol="OleTransactions"/> </netTcpBinding> </bindings>
Obsługa wielu protokołów transakcji
Aby uzyskać optymalną wydajność, należy użyć protokołu OleTransactions w scenariuszach obejmujących klienta i usługę napisaną przy użyciu programu Windows Communication Foundation (WCF). Jednak protokół WS-AtomicTransaction (WS-AT) jest przydatny w scenariuszach, gdy wymagana jest współdziałanie z stosami protokołów innych firm. Usługi WCF można skonfigurować tak, aby akceptowały oba protokoły, udostępniając wiele punktów końcowych z odpowiednimi powiązaniami specyficznymi dla protokołu, jak pokazano w poniższej przykładowej konfiguracji.
<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>
Protokół transakcji jest określony przy użyciu atrybutu
transactionProtocol
. Jednak ten atrybut jest nieobecny w podanymwsHttpBinding
przez system , ponieważ to powiązanie może używać tylko protokołu WS-AT.<bindings> <wsHttpBinding> <binding name="transactionalWsatHttpBinding" transactionFlow="true" /> </wsHttpBinding> <netTcpBinding> <binding name="transactionalOleTransactionsTcpBinding" transactionFlow="true" transactionProtocol="OleTransactions"/> </netTcpBinding> </bindings>
Kontrolowanie zakończenia transakcji
Domyślnie operacje programu WCF automatycznie kończą transakcje, jeśli nie są zgłaszane żadne nieobsługiwane wyjątki. To zachowanie można zmodyfikować przy użyciu TransactionAutoComplete właściwości i SetTransactionComplete metody . Gdy operacja jest wymagana do wykonania w ramach tej samej transakcji co inna operacja (na przykład operacja debetowa i kredytowa), można wyłączyć zachowanie autouzupełniania, ustawiając TransactionAutoComplete właściwość na ,
false
jak pokazano w poniższymDebit
przykładzie operacji. TransakcjaDebit
używa operacji nie zostanie ukończona, dopóki metoda z właściwością TransactionAutoComplete ustawioną natrue
jest wywoływana, jak pokazano w operacjiCredit1
, lub gdy SetTransactionComplete metoda jest wywoływana, aby jawnie oznaczyć transakcję jako ukończoną, jak pokazano w operacjiCredit2
. Należy pamiętać, że dwie operacje kredytowe są wyświetlane w celach ilustracyjnych i że pojedyncza operacja kredytowa byłaby bardziej typowa.[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; } }
Na potrzeby korelacji transakcji ustawienie TransactionAutoComplete właściwości
false
wymaga użycia powiązania sesji. To wymaganie jest określone z właściwościąSessionMode
w obiekcie 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); }
Kontrolowanie okresu istnienia wystąpienia usługi transakcyjnej
Program WCF używa ReleaseServiceInstanceOnTransactionComplete właściwości , aby określić, czy bazowe wystąpienie usługi jest zwalniane po zakończeniu transakcji. Ponieważ ta wartość domyślna to
true
, chyba że skonfigurowano inaczej, program WCF wykazuje wydajne i przewidywalne zachowanie aktywacji typu "just in time". Wywołania usługi w kolejnej transakcji zapewniają nowe wystąpienie usługi bez pozostałości stanu poprzedniej transakcji. Chociaż jest to często przydatne, czasami możesz chcieć zachować stan w wystąpieniu usługi poza ukończeniem transakcji. Przykładem może być to, gdy wymagany stan lub uchwyty do zasobów są kosztowne do pobrania lub ponownego utworzenia. Można to zrobić, ustawiając ReleaseServiceInstanceOnTransactionComplete właściwość nafalse
. W przypadku tego ustawienia wystąpienie i dowolny skojarzony stan będą dostępne w kolejnych wywołaniach. W przypadku korzystania z tej funkcji należy dokładnie rozważyć, kiedy i jak stan i transakcje zostaną wyczyszczone i ukończone. W poniższym przykładzie pokazano, jak to zrobić, utrzymując wystąpienie za pomocą zmiennejrunningTotal
.[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 } }
Uwaga
Ponieważ okres istnienia wystąpienia jest zachowaniem wewnętrznym usługi i kontrolowanym za pośrednictwem ServiceBehaviorAttribute właściwości, nie jest wymagana żadna modyfikacja konfiguracji usługi lub kontraktu usługi w celu ustawienia zachowania wystąpienia. Ponadto przewody nie będą zawierać żadnej reprezentacji tego.