Nota
O acesso a esta página requer autorização. Podes tentar iniciar sessão ou mudar de diretório.
O acesso a esta página requer autorização. Podes tentar mudar de diretório.
As atividades podem ser canceladas dentro de um fluxo de trabalho, por exemplo, por uma Parallel atividade cancelando ramificações incompletas quando o CompletionCondition é avaliado como true, ou por fora do fluxo de trabalho, se o host chamar Cancel. Para fornecer tratamento de cancelamento, os autores do fluxo de trabalho podem usar a CancellationScope atividade, a CompensableActivity atividade ou criar atividades personalizadas que forneçam lógica de cancelamento. Este tópico fornece uma visão geral do cancelamento em fluxos de trabalho.
Cancelamento, compensação e transações
As transações dão ao seu aplicativo a capacidade de anular (reverter) todas as alterações executadas dentro da transação se ocorrerem erros durante qualquer parte do processo de transação. No entanto, nem todo o trabalho que pode precisar ser cancelado ou desfeito é apropriado para transações, como trabalho de longa duração ou trabalho que não envolve recursos transacionais. A compensação fornece um modelo para desfazer trabalho não transacional concluído anteriormente se houver uma falha subsequente no fluxo de trabalho. O cancelamento fornece um modelo para os autores de fluxo de trabalho e atividade lidarem com o trabalho não transacional que não foi concluído. Se uma atividade não tiver concluído sua execução e for cancelada, sua lógica de cancelamento será invocada se estiver disponível.
Nota
Para obter mais informações sobre transações e compensação, consulte Transações e compensação.
Usando o CancellationScope
A CancellationScope atividade tem duas seções que podem conter atividades infantis: Body e CancellationHandler. O Body é onde as atividades que compõem a lógica da atividade são colocadas, e é onde as CancellationHandler atividades que fornecem lógica de cancelamento para a atividade são colocadas. Uma atividade só pode ser cancelada se não tiver sido concluída. No caso da atividade CancellationScope, a conclusão refere-se à conclusão das atividades no Body. Se um pedido de cancelamento for agendado e as atividades no Body não tiverem sido concluídas, então o CancellationScope será marcado como Canceled e as CancellationHandler atividades 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 WorkflowApplication que está hospedando o fluxo de trabalho. No exemplo a seguir, é criado um fluxo de trabalho que tem um CancellationScope. O fluxo de trabalho é invocado e, em seguida, o host faz uma chamada para Cancel. A execução principal do fluxo de trabalho é interrompida, o CancellationHandler do CancellationScope é invocado e, em seguida, o fluxo de trabalho é concluído 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 é invocado, a saída a seguir é exibida no console.
Iniciando o fluxo de trabalho.
CancellationHandler invocado.Processo de trabalho b30ebb30-df46-4d90-a211-e31c38d8db3c Cancelado.
Nota
Quando uma CancellationScope atividade é cancelada e invocada CancellationHandler , é responsabilidade do autor do fluxo de trabalho determinar o progresso que a atividade cancelada fez antes de ser cancelada, a fim de fornecer a lógica de cancelamento apropriada. O CancellationHandler não fornece qualquer informação sobre o progresso da atividade cancelada.
Um fluxo de trabalho também pode ser cancelado do host se uma exceção não tratada se propagar para além da raiz do fluxo de trabalho e um OnUnhandledException manipulador retornar Cancel. Neste exemplo, o fluxo de trabalho começa e, em seguida, lança um ApplicationException. Essa exceção não é tratada pelo fluxo de trabalho e, portanto, o OnUnhandledException manipulador é invocado. O manipulador instrui o ambiente de execução a cancelar o fluxo de trabalho, e a atividade CancellationHandler que está a ser executada no momento CancellationScope é invocada.
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 é invocado, a saída a seguir é exibida no console.
Iniciando o fluxo de trabalho.
OnUnhandledException no fluxo de trabalho 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9Uma ApplicationException foi lançada.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 pelo pai. Por exemplo, se uma Parallel atividade tiver várias ramificações em execução e se a sua avaliação CompletionCondition resultar em true, então as suas ramificações incompletas serão canceladas. Neste exemplo, é criada uma Parallel atividade que tem duas ramificações. Está CompletionCondition definido como true, para que o Parallel complete assim que qualquer um dos seus ramos for concluído. Neste exemplo, a ramificação 2 é concluída e, portanto, 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 é invocado, a saída a seguir é exibida no console.
Ramo 1 a iniciar.
Ramo 2 concluído.Ramo 1 cancelado.Fluxo de trabalho e0685e24-18ef-4a47-acf3-5c638732f3be Concluído. As atividades também são canceladas se uma exceção se propagar além da raiz da atividade, mas for gerida num nível superior no fluxo de trabalho. Neste exemplo, a lógica principal do fluxo de trabalho consiste em uma Sequence atividade. O Sequence é especificado como o Body de uma atividade CancellationScope que está contida numa atividade TryCatch. Uma exceção é lançada a partir do corpo de Sequence, é manipulada pela atividade pai TryCatch e Sequence é cancelada.
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 é invocado, a saída a seguir é exibida no console.
Início da sequência.
Sequência cancelada.Exceção apreendida.Workflow e3c18939-121e-4c43-af1c-ba1ce977ce55 Concluído.
Lançar exceções a partir de um CancellationHandler
Quaisquer exceções lançadas a partir de um CancellationHandler são fatais para o fluxo de trabalho. Se houver a possibilidade de exceções escaparem do CancellationHandler, use um TryCatch no CancellationHandler para capturar e tratar essas exceções.
Cancelamento usando CompensableActivity
À semelhança da CancellationScope atividade, o CompensableActivity tem um CancellationHandler. Se um CompensableActivity for cancelado, quaisquer atividades no seu CancellationHandler serão invocadas. Isso pode ser útil para reverter o trabalho parcialmente concluído e compensável. Para obter informações sobre como usar CompensableActivity para compensação e cancelamento, consulte Compensação.
Cancelamento usando atividades personalizadas
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 inserindo uma CancellationScope ou outra atividade personalizada que contenha lógica de cancelamento no corpo da atividade. As atividades derivadas de AsyncCodeActivity e NativeActivity podem substituir o seu respetivo método Cancel e fornecer lógica de cancelamento aí. CodeActivity As atividades derivadas não oferecem nenhuma provisão para cancelamento porque todo o seu trabalho é realizado em um único período de execução quando o método Execute é chamado pelo runtime. Se o método execute ainda não tiver sido chamado e uma atividade baseada em CodeActivity for cancelada, a atividade será fechada com um status de Canceled e o método Execute não será chamado.
Cancelamento usando NativeActivity
NativeActivity As atividades derivadas podem substituir o Cancel método para fornecer lógica de cancelamento personalizada. Se esse método não for substituído, a lógica de cancelamento do fluxo de trabalho padrão será aplicada. O cancelamento 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 tempo de execução sinaliza a atividade para cancelamento e lida automaticamente com a necessária limpeza. Se a atividade tiver apenas marcadores pendentes, os marcadores serão removidos e a atividade será marcada como Canceled. Quaisquer atividades infantis pendentes da atividade cancelada serão, por sua vez, canceladas. Qualquer tentativa de agendar atividades adicionais para crianças resultará na tentativa ser ignorada e a atividade será marcada como Canceled. Se alguma atividade infantil pendente for concluída no estado Canceled ou Faulted, então a atividade será marcada como Canceled. Tenha em atenção que um pedido de cancelamento pode ser ignorado. Se uma atividade não tiver nenhum marcador pendente ou executar atividades infantis e não agendar nenhum item de trabalho adicional depois de ser sinalizada para cancelamento, ela será concluída com êxito. Esse cancelamento padrão é suficiente para muitos cenários, mas se for necessária uma lógica de cancelamento adicional, as atividades de cancelamento internas ou as atividades personalizadas podem ser usadas.
No exemplo a seguir, é definida a substituição de uma atividade personalizada baseada em Cancel por uma NativeActivity. Quando a atividade é cancelada, essa sobrescrição lida com a lógica de cancelamento da atividade. 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();
}
}
NativeActivity As atividades derivadas podem determinar se o cancelamento foi solicitado inspecionando a IsCancellationRequested propriedade e considerar-se como canceladas chamando o MarkCanceled método. A chamada MarkCanceled não conclui imediatamente a atividade. Como de costume, o tempo de execução concluirá a atividade quando não tiver mais trabalho pendente, mas se MarkCanceled for chamado o estado final será Canceled em vez de Closed.
Cancelamento usando AsyncCodeActivity
AsyncCodeActivity As atividades baseadas podem também fornecer uma lógica de cancelamento personalizada ao substituir o método Cancel. Se esse método não for substituído, nenhum tratamento de cancelamento será executado se a atividade for cancelada. No exemplo a seguir, a sobrescrita de uma atividade personalizada baseada em Cancel é definida usando AsyncCodeActivity e ExecutePowerShell. Quando a atividade é cancelada, ela 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);
}
AsyncCodeActivity As atividades derivadas podem determinar se o cancelamento foi solicitado inspecionando a IsCancellationRequested propriedade e considerar-se como canceladas chamando o MarkCanceled método.