Направление транзакций в службы рабочего процесса и из них
Службы и клиенты рабочих процессов могут использоваться в транзакциях. Чтобы сделать операцию службы частью внешней транзакции, поместите действие Receive в действие TransactedReceiveScope. Все вызовы, выполненные действием Send или SendReply в области TransactedReceiveScope, также будут выполнены во внешней транзакции. Клиентское приложение рабочего процесса может создавать внешнюю транзакцию с помощью действия TransactionScope и вызывать операции службы с помощью внешних транзакций. В данном разделе описывается создание службы рабочего процесса и клиента рабочего процесса, которые участвуют в транзакции.
Предупреждение
Если экземпляр службы рабочего процесса загружается в транзакцию, а рабочий процесс содержит Persist действие, экземпляр рабочего процесса блокируется до истечения времени ожидания транзакции.
Внимание
При использовании области TransactedReceiveScope рекомендуется размещать все операции получения в рабочем процессе в действиях, связанных с областью TransactedReceiveScope.
Внимание
Если используется область TransactedReceiveScope, а сообщения поступают в неверном порядке, рабочий процесс будет прерван при попытке доставки первого сообщения, пришедшего в неправильном порядке. Убедитесь, что рабочий процесс всегда находится в согласованном состоянии при освобождении рабочего процесса. Это позволит перезапустить рабочий процесс с прежней сохраненной точки в случае, если он был прерван.
Создание общей библиотеки
Создайте новое пустое решение Visual Studio.
Добавьте новый проект библиотеки классов с именем
Common
. Добавьте ссылки на следующие сборки:System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
System.Transactions.dll
Добавьте новый класс с именем
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); } } } }
Это собственное действие, при выполнении которого отображаются данные о внешней транзакции, и оно используется в службе и клиенте рабочих процессов, описываемых в этом разделе. Создайте решение, чтобы сделать это действие доступным в разделе "Общие" панели элементов.
Реализация службы рабочего процесса
Добавьте в проект новую службу
WorkflowService
Common
рабочих процессов WCF. Для этого щелкните проект правойCommon
кнопкой мыши, выберите "Добавить", "Создать элемент", выберите "Рабочий процесс" в разделе "Установленные шаблоны" и выберите "Служба рабочих процессов WCF".Удалите заданные по умолчанию действия
ReceiveRequest
иSendResponse
.Перетащите действие с именем WriteLine в
Sequential Service
. Задайте значение для свойства текста"Workflow Service starting ..."
, как показано в следующем примере.! [Добавление действия WriteLine в действие последовательной службы(./media/flowing-transactions-in-and-out-workflow-services/add-writeline-sequential-service.jpg)
Перетащите действие TransactedReceiveScope и поместите его после действия WriteLine. Действие TransactedReceiveScope можно найти в разделе "Обмен сообщениями" панели элементов. Действие TransactedReceiveScope состоит из двух разделов "Запрос " и "Текст". Раздел "Запрос" содержит Receive действие. Раздел " Текст" содержит действия, выполняемые в рамках транзакции после получения сообщения.
TransactedReceiveScope Выберите действие и нажмите кнопку "Переменные". Добавьте следующие переменные.
Примечание.
Можно удалить переменную данных, заданную по умолчанию. Также можно использовать существующую переменную обработки.
Перетащите Receive действие в разделе TransactedReceiveScope "Запрос" действия. Задайте следующие свойства:
Свойство Значение CanCreateInstance True (установите флажок) OperationName StartSample ServiceContractName ITransactionSample Рабочий процесс должен выглядеть так:
Щелкните ссылку "Определить... " в Receive действии и сделайте следующее:
Перетащите действие Sequence в разделе «Текст» TransactedReceiveScope. В действии Sequence перетащите два действия WriteLine и настройте свойства Text, как показано в следующей таблице.
Действие (Activity) Значение Первое действие WriteLine "Служба: получение завершено" Второе действие WriteLine "Service: Received = " + requestMessage После этого рабочий процесс должен выглядеть так:
Перетащите
PrintTransactionInfo
действие после второго WriteLine действия в тексте в действии TransactedReceiveScope .Перетащите действие Assign, поместите его после действия
PrintTransactionInfo
и задайте свойства в соответствии со следующей таблицей.Свойство Значение по replyMessage Значение "Service: Sending reply." Перетащите действие WriteLine, поместите его после действия Assign и задайте для свойства Text значение "Service: Begin reply".
После этого рабочий процесс должен выглядеть так:
Щелкните правой Receive кнопкой мыши действие и выберите "Создать SendReply " и вставьте его после последнего WriteLine действия. Щелкните ссылку "Определить... "
SendReplyToReceive
в действии и внесите следующие параметры.WriteLine Перетащите действие после
SendReplyToReceive
действия и задайте для него Text значение "Service: Reply sent".Перетащите действие WriteLine в нижнюю область рабочего процесса и задайте для свойства Text значение "Service: Workflow ends, press ENTER to exit".
Завершенный рабочий процесс службы должен выглядеть следующим образом:
Реализуйте клиент рабочего процесса
Добавьте новое приложение WCF Workflow с именем
WorkflowClient
к проектуCommon
. Для этого щелкните проект правойCommon
кнопкой мыши, выберите "Добавить", "Создать элемент", выберите "Рабочий процесс " в разделе "Установленные шаблоны " и выберите "Действие".Перетащите действие Sequence в область конструктора.
В действии Sequence перетащите действие WriteLine и задайте для его свойства Text значение
"Client: Workflow starting"
. После этого рабочий процесс должен выглядеть так:Перетащите действие TransactionScope и поместите его после действия WriteLine. Выберите действие TransactionScope, нажмите кнопку «Переменные» и добавьте следующие переменные.
Перетащите действие Sequence в текст действия TransactionScope.
Перетащите действие
PrintTransactionInfo
в Sequence.WriteLine Перетащите действие после
PrintTransactionInfo
действия и задайте для свойства Text "Клиент: начало отправки". После этого рабочий процесс должен выглядеть так:Перетащите действие Send, поместите его после действия Assign и задайте следующие свойства:
Свойство Значение EndpointConfigurationName workflowServiceEndpoint OperationName StartSample ServiceContractName ITransactionSample После этого рабочий процесс должен выглядеть так:
Щелкните ссылку "Определить... " и сделайте следующее:
Щелкните правой Send кнопкой мыши действие и выберите Create ReceiveReply. Действие ReceiveReply будет автоматически помещено после действия Send.
Щелкните ссылку «Определить...» в действии ReceiveReplyForSend и задайте следующие параметры:
Перетащите действие WriteLine, поместите его между действиями Send и ReceiveReply, а также задайте для свойства Text значение "Client: Send complete".
Перетащите действие WriteLine, поместите его после действия ReceiveReply и задайте для свойства Text значение "Client side: Reply received = " + replyMessage.
Перетащите действие
PrintTransactionInfo
и поместите его после действия WriteLine.WriteLine Перетащите действие в конце рабочего процесса и задайте для свойства Text значение "Клиентский рабочий процесс заканчивается". Завершенный рабочий процесс клиента должен выглядеть следующим образом.
Постройте решение.
Создание приложения службы
Добавьте в решение новый проект консольного приложения с именем
Service
. Добавьте ссылки на следующие сборки:System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
Откройте созданный файл 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(); }; }
Добавьте к проекту следующий файл 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>
Создание клиентского приложения
Добавьте в решение новый проект консольного приложения с именем
Client
. Добавьте ссылку на библиотеку System.Activities.dll.Откройте файл 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; } }