Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
As atividades podem ser canceladas em um fluxo de trabalho, por exemplo por uma atividade de Parallel que cancela ramificações incompletos quando seu CompletionCondition avalia a true, ou fora de fluxo de trabalho, se o host chama Cancel. Para fornecer tratamento de cancelamento, os autores de fluxo de trabalho podem usar a atividade CancellationScope, a atividade CompensableActivity ou criar atividades personalizadas que forneçam lógica de cancelamento. Este tópico fornece uma visão geral de cancelamento em fluxos de trabalho.
Cancelar, compensação, e transações
As transações fornecem ao seu aplicativo a capacidade de abortar (reverter) todas as alterações executadas dentro da transação se ocorrer qualquer erro durante qualquer parte do processo de transação. No entanto, nem todo o trabalho que precise ser cancelado ou desfeito é apropriado para transações, como o trabalho longo ou o trabalho que não envolvem recursos transacionais. A compensação fornece um modelo para desfazer o trabalho não transacional anteriormente concluído se houver uma falha subsequente no fluxo de trabalho. O cancelamento oferece um modelo para que autores de fluxo de trabalho e atividades lidem com trabalhos não transacionais que não foram concluídos. Se uma atividade não concluiu sua execução e for cancelada, sua lógica de cancelamento será chamada, se estiver disponível.
Observação
Para obter mais informações sobre transações e compensação, consulte Transações e Compensação.
Usando CancellationScope
A atividade de CancellationScope tem duas seções que podem conter atividades filhos: Body e CancellationHandler. Body é onde as atividades que compõem a lógica da atividade são colocadas, e CancellationHandler é onde as atividades que fornecem lógica de cancelamento para a atividade são colocadas. Uma atividade pode ser cancelada somente se não tiver concluído. No caso da atividade de CancellationScope, a conclusão refere-se à conclusão das atividades em Body. Se uma solicitação de cancelamento for agendada e as atividades em Body não estiverem concluídas, então CancellationScope será marcado como Canceled e as atividades de CancellationHandler serão executadas.
Cancelando um fluxo de trabalho do host
Um host pode cancelar um fluxo de trabalho chamando o método Cancel da instância de WorkflowApplication que está hospedando o fluxo de trabalho. No exemplo a seguir um fluxo de trabalho é criado que tem CancellationScope. O fluxo de trabalho é chamado, e então o host faz uma chamada a Cancel. A execução do fluxo de trabalho é interrompida, CancellationHandler de CancellationScope é chamado, e então o fluxo de trabalho termina com um status de Canceled.
Activity wf = new CancellationScope
{
Body = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Starting the workflow."
},
new Delay
{
Duration = TimeSpan.FromSeconds(5)
},
new WriteLine
{
Text = "Ending the workflow."
}
}
},
CancellationHandler = new WriteLine
{
Text = "CancellationHandler invoked."
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Subscribe to any desired workflow lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
}
};
// Run the workflow.
wfApp.Run();
Thread.Sleep(TimeSpan.FromSeconds(1));
wfApp.Cancel();
Quando esse fluxo de trabalho é chamado, a seguinte saída é exibida no console.
Iniciando o fluxo de trabalho.
CancellationHandler invocado.Fluxo de trabalho b30ebb30-df46-4d90-a211-e31c38d8db3c cancelado.
Observação
Quando uma atividade de CancellationScope é cancelada e CancellationHandler é invocado, é responsabilidade do autor do fluxo de trabalho determinar o progresso que a atividade cancelada alcançou antes de ser cancelada, a fim de fornecer a lógica de cancelamento apropriada. CancellationHandler não fornece nenhuma informação sobre o andamento de atividade cancelada.
Um fluxo de trabalho também pode ser cancelado host se uma exceção não tratada borbulha anterior após a raiz de fluxo de trabalho e o manipulador de OnUnhandledException retorna Cancel. Nesse exemplo, o fluxo de trabalho é iniciado e, em seguida, gera um ApplicationException. Esta exceção não é tratada pelo fluxo de trabalho e então o manipulador de OnUnhandledException é chamado. O manipulador instrui o runtime para cancelar o fluxo de trabalho, e CancellationHandler atividade estão atualmente em execução de CancellationScope é chamado.
Activity wf = new CancellationScope
{
Body = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Starting the workflow."
},
new Throw
{
Exception = new InArgument<Exception>((env) =>
new ApplicationException("An ApplicationException was thrown."))
},
new WriteLine
{
Text = "Ending the workflow."
}
}
},
CancellationHandler = new WriteLine
{
Text = "CancellationHandler invoked."
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
// Subscribe to any desired workflow lifecycle events.
wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
// Display the unhandled exception.
Console.WriteLine($"OnUnhandledException in Workflow {e.InstanceId}\n{e.UnhandledException.Message}");
// Instruct the runtime to cancel the workflow.
return UnhandledExceptionAction.Cancel;
};
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
}
};
// Run the workflow.
wfApp.Run();
Quando esse fluxo de trabalho é chamado, a seguinte saída é exibida no console.
Iniciando o fluxo de trabalho.
OnUnhandledException no fluxo de trabalho 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9Um ApplicationException foi gerado.CancellationHandler invocado.Fluxo de trabalho 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9 cancelado.
Cancelando uma atividade de dentro de um fluxo de trabalho
Uma atividade também pode ser cancelada por seu pai. Por exemplo, se uma atividade de Parallel tem várias ramificações e que executa o CompletionCondition avalia a true suas ramificações incompletos serão canceladas em seguida. Nesse exemplo uma atividade de Parallel é criada que possui duas ramificações. O CompletionCondition é definido como true portanto Parallel concluir o que qualquer uma de suas ramificações terminar. Neste exemplo, a ramificação 2 é concluída, e assim a ramificação 1 é cancelada.
Activity wf = new Parallel
{
CompletionCondition = true,
Branches =
{
new CancellationScope
{
Body = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Branch 1 starting."
},
new Delay
{
Duration = TimeSpan.FromSeconds(2)
},
new WriteLine
{
Text = "Branch 1 complete."
}
}
},
CancellationHandler = new WriteLine
{
Text = "Branch 1 canceled."
}
},
new WriteLine
{
Text = "Branch 2 complete."
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
}
};
// Run the workflow.
wfApp.Run();
Quando esse fluxo de trabalho é chamado, a seguinte saída é exibida no console.
Ramificação 1 que inicia.
Ramificação 2 concluída.Ramificação 1 cancelada.Fluxo de trabalho e0685e24-18ef-4a47-acf3-5c638732f3be concluído. As atividades também são canceladas se surgir uma exceção para além da raiz da atividade, mas é tratada em um nível mais alto do fluxo de trabalho. Nesse exemplo, a principal lógica de fluxo de trabalho consiste em uma atividade de Sequence . Sequence é especificado como Body de uma atividade de CancellationScope que está contida por uma atividade de TryCatch . Uma exceção é lançada do corpo de Sequence, é tratada pela atividade pai de TryCatch , e Sequence é cancelado.
Activity wf = new TryCatch
{
Try = new CancellationScope
{
Body = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Sequence starting."
},
new Throw
{
Exception = new InArgument<Exception>((env) =>
new ApplicationException("An ApplicationException was thrown."))
},
new WriteLine
{
Text = "Sequence complete."
}
}
},
CancellationHandler = new WriteLine
{
Text = "Sequence canceled."
}
},
Catches =
{
new Catch<ApplicationException>
{
Action = new ActivityAction<ApplicationException>
{
Handler = new WriteLine
{
Text = "Exception caught."
}
}
}
}
};
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
if (e.CompletionState == ActivityInstanceState.Faulted)
{
Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
}
else
{
Console.WriteLine($"Workflow {e.InstanceId} Completed.");
}
};
// Run the workflow.
wfApp.Run();
Quando esse fluxo de trabalho é chamado, a seguinte saída é exibida no console.
Iniciar a sequência.
Sequência cancelada.Exceção capturada.Fluxo de trabalho e3c18939-121e-4c43-af1c-ba1ce977ce55 concluído.
Gerar exceções de um CancellationHandler
Todas as exceções geradas de CancellationHandler de CancellationScope são fatais ao fluxo de trabalho. Se houver uma chance de exceções que escapam de CancellationHandler, use TryCatch em CancellationHandler para capturar e tratar essas exceções.
Cancelamento usando CompensableActivity
Como a atividade de CancellationScope , CompensableActivity tem CancellationHandler. Se CompensableActivity é cancelado, todas as atividades em seu CancellationHandler são chamadas. Isso pode ser útil para desfazer o trabalho compensável parcialmente concluído. Para obter informações sobre como usar CompensableActivity para a compensação e cancelamento, consulte Compensação.
Cancelar usando atividades personalizados
Os autores de atividades personalizadas podem implementar a lógica de cancelamento em suas atividades personalizadas de várias maneiras diferentes. As atividades personalizadas que derivam de Activity podem implementar a lógica de cancelamento colocando CancellationScope ou outra atividade personalizada que contém a lógica de cancelamento no corpo da atividade. AsyncCodeActivity e as atividades derivadas NativeActivity podem substituir o método respectivo Cancel e fornecer a lógica de cancelamento lá. CodeActivity atividades derivadas não fornecem qualquer provisão para cancelamento, pois todo o trabalho é realizado em uma única execução quando o runtime chama o método Execute. Se o método executar não foi chamado ainda e uma atividade baseada em CodeActivity é cancelada, a atividade fecha com um status de Canceled e o método Execute não é chamado.
Cancelar usando NativeActivity
As atividades derivadas NativeActivity podem substituir o método Cancel para fornecer a lógica personalizada de cancelamento. Se este método não é substituído, então a lógica padrão de cancelamento de fluxo de trabalho é aplicada. Cancelar o padrão é o processo que ocorre para um NativeActivity que não substitui o método Cancel ou cujo método Cancel chama o método base NativeActivityCancel. Quando uma atividade é cancelada, o runtime sinaliza a atividade para cancelamento e automaticamente lida com algumas tarefas de limpeza. Se a atividade tiver apenas marcadores pendentes, os marcadores serão removidos e a atividade será marcada como Canceled. Todas as atividades filhos excelentes de atividade cancelada serão canceladas por sua vez. Qualquer tentativa de agendar atividades para crianças adicionais resultará na tentativa sendo ignorada, e a atividade será marcada como Canceled. Se alguma atividade pendente infantil for concluída nos estados Canceled ou Faulted, então a atividade será marcada como Canceled. Observe que uma solicitação de cancelamento pode ser ignorada. Se uma atividade não tem nenhum indicadores pendentes ou executar atividades filhos e não agenda os itens de trabalho adicionais após o embandeiramento para cancelamento, se concluirá com êxito. Este cancelamento padrão basta para muitos cenários, mas se lógica de cancelamento adicional for necessária, então as atividades internas de cancelamento ou atividades personalizadas podem ser usadas.
No exemplo a seguir, a substituição de Cancel de uma atividade personalizada com base NativeActivity de ParallelForEach é definida. Quando a atividade é cancelada, alças desta substituição lógica cancelar para atividades. Este exemplo faz parte do exemplo ParallelForEach não genérico.
protected override void Cancel(NativeActivityContext context)
{
// If we do not have a completion condition then we can just
// use default logic.
if (this.CompletionCondition == null)
{
base.Cancel(context);
}
else
{
context.CancelChildren();
}
}
As atividades derivadas NativeActivity podem determinar se o cancelamento foi solicitado inspecionando a propriedade IsCancellationRequested e marcar-se como canceladas chamando o método MarkCanceled. A chamada MarkCanceled não conclui imediatamente a atividade. Como de costume, o runtime concluirá a atividade quando não tiver mais trabalho pendente, mas, se MarkCanceled for chamado, o estado final será Canceled em vez de Closed.
Cancelar usando AsyncCodeActivity
As atividades baseadas em AsyncCodeActivity também podem fornecer a lógica personalizada de cancelamento substituindo o método Cancel. Se este método não for substituído, então nenhum tratamento de cancelamento é executado se a atividade for cancelada. No exemplo a seguir, a substituição de Cancel de uma atividade personalizada com base AsyncCodeActivity de ExecutePowerShell é definida. Quando a atividade é cancelada, executa o comportamento de cancelamento desejado.
// Called by the runtime to cancel the execution of this asynchronous activity.
protected override void Cancel(AsyncCodeActivityContext context)
{
Pipeline pipeline = context.UserState as Pipeline;
if (pipeline != null)
{
pipeline.Stop();
DisposePipeline(pipeline);
}
base.Cancel(context);
}
As atividades derivadas AsyncCodeActivity podem determinar se o cancelamento foi solicitado inspecionando a propriedade IsCancellationRequested e marcar-se como canceladas chamando o método MarkCanceled.