Udostępnij za pośrednictwem


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

  1. 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);  
    }  
    
  2. 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 UnspecifiedTransactionIsolationLevel 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 dla Add metody jest taka sama jak obowiązkowa transakcja przychodząca, która jest przepływany z klienta, a transakcja używana dla Subtract 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.  
        }  
    }  
    
  3. 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 nazwie transactionalOleTransactionsTcpBinding, 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 atrybutu transactionProtocol , jak pokazano w poniższej konfiguracji.

    <bindings>  
      <netTcpBinding>  
        <binding name="transactionalOleTransactionsTcpBinding"  
          transactionFlow="true"  
          transactionProtocol="OleTransactions"/>  
      </netTcpBinding>  
    </bindings>  
    

Obsługa wielu protokołów transakcji

  1. 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 podanym wsHttpBindingprzez 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

  1. 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ższym Debit przykładzie operacji. Transakcja Debit używa operacji nie zostanie ukończona, dopóki metoda z właściwością TransactionAutoComplete ustawioną na true jest wywoływana, jak pokazano w operacji Credit1, lub gdy SetTransactionComplete metoda jest wywoływana, aby jawnie oznaczyć transakcję jako ukończoną, jak pokazano w operacji Credit2. 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;  
        }  
    }  
    
  2. 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

  1. 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ść na false. 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ą zmiennej runningTotal .

    [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.