Поделиться через


Направление транзакций в службы рабочего процесса и из них

Службы и клиенты рабочих процессов могут использоваться в транзакциях. Чтобы сделать операцию службы частью внешней транзакции, поместите действие Receive в действие TransactedReceiveScope. Все вызовы, выполненные действием Send или SendReply в области TransactedReceiveScope, также будут выполнены во внешней транзакции. Клиентское приложение рабочего процесса может создавать внешнюю транзакцию с помощью действия TransactionScope и вызывать операции службы с помощью внешних транзакций. В данном разделе описывается создание службы рабочего процесса и клиента рабочего процесса, которые участвуют в транзакции.

Предупреждение

Если экземпляр службы рабочего процесса загружается в транзакцию, а рабочий процесс содержит Persist действие, экземпляр рабочего процесса блокируется до истечения времени ожидания транзакции.

Внимание

При использовании области TransactedReceiveScope рекомендуется размещать все операции получения в рабочем процессе в действиях, связанных с областью TransactedReceiveScope.

Внимание

Если используется область TransactedReceiveScope, а сообщения поступают в неверном порядке, рабочий процесс будет прерван при попытке доставки первого сообщения, пришедшего в неправильном порядке. Убедитесь, что рабочий процесс всегда находится в согласованном состоянии при освобождении рабочего процесса. Это позволит перезапустить рабочий процесс с прежней сохраненной точки в случае, если он был прерван.

Создание общей библиотеки

  1. Создайте новое пустое решение Visual Studio.

  2. Добавьте новый проект библиотеки классов с именем Common. Добавьте ссылки на следующие сборки:

    • System.Activities.dll

    • System.ServiceModel.dll

    • System.ServiceModel.Activities.dll

    • System.Transactions.dll

  3. Добавьте новый класс с именем PrintTransactionInfo к проекту Common. Класс является производным от NativeActivity и перегружает метод Execute.

    using System;  
    using System;  
    using System.Activities;  
    using System.Transactions;  
    
    namespace Common  
    {  
        public class PrintTransactionInfo : NativeActivity  
        {  
            protected override void Execute(NativeActivityContext context)  
            {  
                RuntimeTransactionHandle rth = context.Properties.Find(typeof(RuntimeTransactionHandle).FullName) as RuntimeTransactionHandle;  
    
                if (rth == null)  
                {  
                    Console.WriteLine("There is no ambient RuntimeTransactionHandle");  
                }  
    
                Transaction t = rth.GetCurrentTransaction(context);  
    
                if (t == null)  
                {  
                    Console.WriteLine("There is no ambient transaction");  
                }  
                else  
                {  
                    Console.WriteLine("Transaction: {0} is {1}", t.TransactionInformation.DistributedIdentifier, t.TransactionInformation.Status);  
                }  
            }  
        }  
    
    }  
    

    Это собственное действие, при выполнении которого отображаются данные о внешней транзакции, и оно используется в службе и клиенте рабочих процессов, описываемых в этом разделе. Создайте решение, чтобы сделать это действие доступным в разделе "Общие" панели элементов.

Реализация службы рабочего процесса

  1. Добавьте в проект новую службу WorkflowServiceCommon рабочих процессов WCF. Для этого щелкните проект правой Common кнопкой мыши, выберите "Добавить", "Создать элемент", выберите "Рабочий процесс" в разделе "Установленные шаблоны" и выберите "Служба рабочих процессов WCF".

    Adding a Workflow Service

  2. Удалите заданные по умолчанию действия ReceiveRequest и SendResponse.

  3. Перетащите действие с именем WriteLine в Sequential Service. Задайте значение для свойства текста "Workflow Service starting ...", как показано в следующем примере.

    ! [Добавление действия WriteLine в действие последовательной службы(./media/flowing-transactions-in-and-out-workflow-services/add-writeline-sequential-service.jpg)

  4. Перетащите действие TransactedReceiveScope и поместите его после действия WriteLine. Действие TransactedReceiveScope можно найти в разделе "Обмен сообщениями" панели элементов. Действие TransactedReceiveScope состоит из двух разделов "Запрос " и "Текст". Раздел "Запрос" содержит Receive действие. Раздел " Текст" содержит действия, выполняемые в рамках транзакции после получения сообщения.

    Adding a TransactedReceiveScope activity

  5. TransactedReceiveScope Выберите действие и нажмите кнопку "Переменные". Добавьте следующие переменные.

    Adding variables to the TransactedReceiveScope

    Примечание.

    Можно удалить переменную данных, заданную по умолчанию. Также можно использовать существующую переменную обработки.

  6. Перетащите Receive действие в разделе TransactedReceiveScope "Запрос" действия. Задайте следующие свойства:

    Свойство Значение
    CanCreateInstance True (установите флажок)
    OperationName StartSample
    ServiceContractName ITransactionSample

    Рабочий процесс должен выглядеть так:

    Adding a Receive activity

  7. Щелкните ссылку "Определить... " в Receive действии и сделайте следующее:

    Setting message settings for the Receive activity

  8. Перетащите действие Sequence в разделе «Текст» TransactedReceiveScope. В действии Sequence перетащите два действия WriteLine и настройте свойства Text, как показано в следующей таблице.

    Действие (Activity) Значение
    Первое действие WriteLine "Служба: получение завершено"
    Второе действие WriteLine "Service: Received = " + requestMessage

    После этого рабочий процесс должен выглядеть так:

    Sequence after adding WriteLine activities

  9. Перетащите PrintTransactionInfo действие после второго WriteLine действия в тексте в действии TransactedReceiveScope .

    Sequence after adding PrintTransactionInfo

  10. Перетащите действие Assign, поместите его после действия PrintTransactionInfo и задайте свойства в соответствии со следующей таблицей.

    Свойство Значение
    по replyMessage
    Значение "Service: Sending reply."
  11. Перетащите действие WriteLine, поместите его после действия Assign и задайте для свойства Text значение "Service: Begin reply".

    После этого рабочий процесс должен выглядеть так:

    After adding Assign and WriteLine

  12. Щелкните правой Receive кнопкой мыши действие и выберите "Создать SendReply " и вставьте его после последнего WriteLine действия. Щелкните ссылку "Определить... " SendReplyToReceive в действии и внесите следующие параметры.

    Reply message settings

  13. WriteLine Перетащите действие после SendReplyToReceive действия и задайте для него Text значение "Service: Reply sent".

  14. Перетащите действие WriteLine в нижнюю область рабочего процесса и задайте для свойства Text значение "Service: Workflow ends, press ENTER to exit".

    Завершенный рабочий процесс службы должен выглядеть следующим образом:

    Complete Service Workflow

Реализуйте клиент рабочего процесса

  1. Добавьте новое приложение WCF Workflow с именем WorkflowClient к проекту Common. Для этого щелкните проект правой Common кнопкой мыши, выберите "Добавить", "Создать элемент", выберите "Рабочий процесс " в разделе "Установленные шаблоны " и выберите "Действие".

    Add an Activity project

  2. Перетащите действие Sequence в область конструктора.

  3. В действии Sequence перетащите действие WriteLine и задайте для его свойства Text значение "Client: Workflow starting". После этого рабочий процесс должен выглядеть так:

    Add a WriteLine activity

  4. Перетащите действие TransactionScope и поместите его после действия WriteLine. Выберите действие TransactionScope, нажмите кнопку «Переменные» и добавьте следующие переменные.

    Add variables to the TransactionScope

  5. Перетащите действие Sequence в текст действия TransactionScope.

  6. Перетащите действие PrintTransactionInfo в Sequence.

  7. WriteLine Перетащите действие после PrintTransactionInfo действия и задайте для свойства Text "Клиент: начало отправки". После этого рабочий процесс должен выглядеть так:

    Adding Client: Beginning Send activities

  8. Перетащите действие Send, поместите его после действия Assign и задайте следующие свойства:

    Свойство Значение
    EndpointConfigurationName workflowServiceEndpoint
    OperationName StartSample
    ServiceContractName ITransactionSample

    После этого рабочий процесс должен выглядеть так:

    Setting the Send activity properties

  9. Щелкните ссылку "Определить... " и сделайте следующее:

    Send activity message settings

  10. Щелкните правой Send кнопкой мыши действие и выберите Create ReceiveReply. Действие ReceiveReply будет автоматически помещено после действия Send.

  11. Щелкните ссылку «Определить...» в действии ReceiveReplyForSend и задайте следующие параметры:

    Setting the ReceiveForSend message settings

  12. Перетащите действие WriteLine, поместите его между действиями Send и ReceiveReply, а также задайте для свойства Text значение "Client: Send complete".

  13. Перетащите действие WriteLine, поместите его после действия ReceiveReply и задайте для свойства Text значение "Client side: Reply received = " + replyMessage.

  14. Перетащите действие PrintTransactionInfo и поместите его после действия WriteLine.

  15. WriteLine Перетащите действие в конце рабочего процесса и задайте для свойства Text значение "Клиентский рабочий процесс заканчивается". Завершенный рабочий процесс клиента должен выглядеть следующим образом.

    The completed client workflow

  16. Постройте решение.

Создание приложения службы

  1. Добавьте в решение новый проект консольного приложения с именем Service. Добавьте ссылки на следующие сборки:

    1. System.Activities.dll

    2. System.ServiceModel.dll

    3. System.ServiceModel.Activities.dll

  2. Откройте созданный файл Program.cs и следующий код:

          static void Main()  
          {  
              Console.WriteLine("Building the server.");  
              using (WorkflowServiceHost host = new WorkflowServiceHost(new DeclarativeServiceWorkflow(), new Uri("net.tcp://localhost:8000/TransactedReceiveService/Declarative")))  
              {
                  //Start the server  
                  host.Open();  
                  Console.WriteLine("Service started.");  
    
                  Console.WriteLine();  
                  Console.ReadLine();  
                  //Shutdown  
                  host.Close();  
              };
          }  
    
  3. Добавьте к проекту следующий файл app.config.

    <?xml version="1.0" encoding="utf-8" ?>  
    <!-- Copyright © Microsoft Corporation.  All rights reserved. -->  
    <configuration>  
        <system.serviceModel>  
            <bindings>  
                <netTcpBinding>  
                    <binding transactionFlow="true" />  
                </netTcpBinding>  
            </bindings>  
        </system.serviceModel>  
    </configuration>  
    

Создание клиентского приложения

  1. Добавьте в решение новый проект консольного приложения с именем Client. Добавьте ссылку на библиотеку System.Activities.dll.

  2. Откройте файл program.cs и добавьте следующий код:

    class Program  
    {  
    
        private static AutoResetEvent syncEvent = new AutoResetEvent(false);  
    
        static void Main(string[] args)  
        {  
            //Build client  
            Console.WriteLine("Building the client.");  
            WorkflowApplication client = new WorkflowApplication(new DeclarativeClientWorkflow());  
            client.Completed = Program.Completed;  
            client.Aborted = Program.Aborted;  
            client.OnUnhandledException = Program.OnUnhandledException;  
            //Wait for service to start  
            Console.WriteLine("Press ENTER once service is started.");  
            Console.ReadLine();  
    
            //Start the client
            Console.WriteLine("Starting the client.");  
            client.Run();  
            syncEvent.WaitOne();  
    
            //Sample complete  
            Console.WriteLine();  
            Console.WriteLine("Client complete. Press ENTER to exit.");  
            Console.ReadLine();  
        }  
    
        private static void Completed(WorkflowApplicationCompletedEventArgs e)  
        {  
            Program.syncEvent.Set();  
        }  
    
        private static void Aborted(WorkflowApplicationAbortedEventArgs e)  
        {  
            Console.WriteLine("Client Aborted: {0}", e.Reason);  
            Program.syncEvent.Set();  
        }  
    
        private static UnhandledExceptionAction OnUnhandledException(WorkflowApplicationUnhandledExceptionEventArgs e)  
        {  
            Console.WriteLine("Client had an unhandled exception: {0}", e.UnhandledException);  
            return UnhandledExceptionAction.Cancel;  
        }  
    }  
    

См. также