Transações de fluxo de entrada e saída de serviços de fluxo de trabalho
Serviços e clientes de fluxo de trabalho podem participar de transações. Para que uma operação de serviço se torne parte de uma transação ambiente, coloque uma atividade Receive dentro de uma atividade TransactedReceiveScope. Todas as chamadas feitas por uma atividade Send ou SendReply dentro do TransactedReceiveScope também serão feitas dentro da transação ambiente. Um aplicativo cliente de fluxo de trabalho pode criar uma transação ambiente usando a atividade TransactionScope e chamar operações de serviço usando a transação ambiente. Este tópico explica como criar um serviço de fluxo de trabalho e um cliente de fluxo de trabalho que participam de transações.
Aviso
Se uma instância de serviço de fluxo de trabalho for carregada em uma transação e o fluxo de trabalho contiver uma atividade Persist, a instância de fluxo de trabalho será bloqueada até que a transação atinja o tempo limite.
Importante
Sempre que você usa um TransactedReceiveScope, é recomendável colocar todos os Recebimentos no fluxo de trabalho dentro de atividades TransactedReceiveScope.
Importante
Ao usar TransactedReceiveScope, quando as mensagens chegam na ordem incorreta, o fluxo de trabalho é anulado ao tentar entregar a primeira mensagem fora de ordem. Verifique se o fluxo de trabalho sempre está em um ponto de parada consistente quando ele está ocioso. Isso permitirá reiniciar o fluxo de trabalho de um ponto de persistência anterior caso o fluxo de trabalho seja anulado.
Criar uma biblioteca compartilhada
Crie uma solução vazia do Visual Studio.
Adicione um projeto de biblioteca de classes chamado
Common
. Adicione referências aos assemblies a seguir:System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
System.Transactions.dll
Adicione uma nova classe chamada
PrintTransactionInfo
ao projetoCommon
. Essa classe é derivada de NativeActivity e sobrecarrega o método 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); } } } }
Essa é uma atividade nativa que exibe informações sobre a transação de ambiente e é usada nos fluxos de trabalho do serviço e do cliente usados neste tópico. Compile a solução para disponibilizar essa atividade na seção Comum da Caixa de Ferramentas.
Implementar o serviço de fluxo de trabalho
Adicione um novo Serviço de Fluxo de Trabalho do WCF, chamado
WorkflowService
, ao projetoCommon
. Para fazer isso, clique com o botão direito do mouse no projetoCommon
, selecione Adicionar, Novo Item..., selecione Fluxo de Trabalho em Modelos Instalados e selecione Serviço de Fluxo de Trabalho do WCF.Exclua as atividades
ReceiveRequest
eSendResponse
padrão.Arraste e solte uma atividade WriteLine na atividade
Sequential Service
. Defina a propriedade de texto como"Workflow Service starting ..."
, conforme mostrado no exemplo a seguir.![Adicionando uma atividade WriteLine à atividade de Serviço Sequencial(./media/flowing-transactions-into-and-out-of-workflow-services/add-writeline-sequential-service.jpg)
Arraste e solte um TransactedReceiveScope após a atividade WriteLine. A atividade TransactedReceiveScope pode ser encontrada na seção Mensagens da Caixa de Ferramentas. A atividade TransactedReceiveScope é composta por duas seções, Solicitação e Corpo. A seção Solicitação contém a atividade Receive. A seção Corpo contém as atividades a serem executadas em uma transação depois que uma mensagem é recebida.
Selecione a atividade TransactedReceiveScope e clique no botão Variáveis. Adicione as variáveis a seguir.
Observação
Você pode excluir a variável de dados que está presente por padrão. Você também pode usar a variável de identificador existente.
Arraste e solte uma atividade Receive na seção Solicitação da atividade TransactedReceiveScope. Defina as seguintes propriedades:
Propriedade Valor CanCreateInstance True (marque a caixa de seleção) OperationName StartSample ServiceContractName ITransactionSample O fluxo de trabalho deve se parecer com este:
Clique no link Definir... na atividade Receive e faça as seguintes configurações:
Arraste e solte uma atividade Sequence na seção Corpo do TransactedReceiveScope. Dentro da atividade Sequence, arraste e solte duas atividades WriteLine e defina as propriedades Text conforme mostrado na tabela a seguir.
Atividade Valor 1st WriteLine "Service: Receive Completed" 2nd WriteLine "Service: Received = " + requestMessage Agora, o fluxo de trabalho deve se parecer com este:
Arraste e solte a atividade
PrintTransactionInfo
após a segunda atividade WriteLine no Corpo na atividade TransactedReceiveScope.Arraste e solte uma atividade Assign após a atividade
PrintTransactionInfo
e defina suas propriedades de acordo com a tabela a seguir.Propriedade Valor Para replyMessage Valor "Service: Sending reply." Arraste e solte uma atividade WriteLine após a atividade Assign e defina sua propriedade Text como "Service: Begin reply".
Agora, o fluxo de trabalho deve se parecer com este:
Clique com o botão direito do mouse na atividade Receive, selecione Criar SendReply e cole após a última atividade WriteLine. Clique no link Definir... na atividade
SendReplyToReceive
e faça as configurações a seguir.Arraste e solte uma atividade WriteLine após a atividade
SendReplyToReceive
e defina sua propriedade Text como "Service: Reply sent".Arraste e solte uma atividade WriteLine na parte inferior do fluxo de trabalho e defina sua propriedade Text como "Service: Workflow ends, press ENTER to exit".
O fluxo de trabalho de serviço concluído deve ter esta aparência:
Implementar o cliente de fluxo de trabalho
Adicione um novo aplicativo de fluxo de trabalho do WCF, chamado
WorkflowClient
, ao projetoCommon
. Para fazer isso, clique com o botão direito do mouse no projetoCommon
, selecione Adicionar, Novo Item..., selecione Fluxo de Trabalho em Modelos Instalados e selecione Atividade.Arraste e solte uma atividade Sequence na superfície de design.
Dentro da atividade Sequence, arraste e solte uma atividade WriteLine e defina sua propriedade Text como
"Client: Workflow starting"
. Agora, o fluxo de trabalho deve se parecer com este:Arraste e solte uma atividade TransactionScope após a atividade WriteLine. Selecione a atividade TransactionScope, clique no botão Variáveis e adicione as variáveis a seguir.
Arraste e solte uma atividade Sequence no corpo da atividade TransactionScope.
Arrastar e soltar uma atividade
PrintTransactionInfo
dentro do SequenceArraste e solte uma atividade WriteLine após a atividade
PrintTransactionInfo
e defina sua propriedade Text como "Client: Beginning Send". Agora, o fluxo de trabalho deve se parecer com este:Arraste e solte uma atividade Send após a atividade Assign e defina as seguintes propriedades:
Propriedade Valor EndpointConfigurationName workflowServiceEndpoint OperationName StartSample ServiceContractName ITransactionSample Agora, o fluxo de trabalho deve se parecer com este:
Clique no link Definir... e defina as seguintes configurações:
Clique com o botão direito do mouse na atividade Send e selecione Criar ReceiveReply. A atividade ReceiveReply será colocada automaticamente após a atividade Send.
Clique no link Definir... na atividade ReceiveReplyForSend e defina as seguintes configurações:
Arraste e solte uma atividade WriteLine entre as atividades Send e ReceiveReply e defina sua propriedade Text como "Client: Send complete".
Arraste e solte uma atividade WriteLine após a atividade ReceiveReply e defina sua propriedade Text como "Client side: Reply received = " + replyMessage
Arraste e solte uma atividade
PrintTransactionInfo
após a atividade WriteLine.Arraste e solte uma atividade WriteLine no final do fluxo de trabalho e defina sua propriedade Text como "Client workflow ends". O fluxo de trabalho do cliente concluído deve ser semelhante ao diagrama a seguir.
Compile a solução.
Criar o aplicativo de serviço
Adicione um novo projeto de aplicativo de console chamado
Service
à solução. Adicione referências aos assemblies a seguir:System.Activities.dll
System.ServiceModel.dll
System.ServiceModel.Activities.dll
Abra o arquivo Program.cs gerado e o seguinte código:
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(); }; }
Adicione o arquivo app.config a seguir ao projeto.
<?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>
Criar o aplicativo cliente
Adicione um novo projeto de aplicativo de console chamado
Client
à solução. Adicione uma referência a System.Activities.dll.Abra o arquivo program.cs e adicione o código a seguir.
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; } }