Dela via


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

  1. 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);  
    }  
    
  2. 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 TransactionIsolationLevelUnspecified 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ör Add metoden samma som den obligatoriska inkommande transaktionen som flödas från klienten, och transaktionen som används för Subtract 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.  
        }  
    }  
    
  3. 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 ett bindingConfiguration attribut som refererar till en bindningskonfiguration med namnet transactionalOleTransactionsTcpBinding, 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 av transactionProtocol attributet, enligt följande konfiguration.

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

Stöd för flera transaktionsprotokoll

  1. 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 systemet wsHttpBindingeftersom 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

  1. 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öljande Debit åtgärdsexempel. Transaktionen som Debit åtgärden använder har inte slutförts förrän en metod med TransactionAutoComplete egenskapen inställd true på anropas, enligt vad som visas i åtgärden Credit1, eller när SetTransactionComplete metoden anropas för att uttryckligen markera transaktionen som slutförd, som du ser i åtgärden Credit2. 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;  
        }  
    }  
    
  2. För transaktionskorrelation kräver inställningen TransactionAutoComplete av egenskapen att false en sessionskänslig bindning används. Det här kravet anges med SessionMode 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

  1. 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 till false. 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 variabeln 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  
        }  
    }  
    

    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.