Beteende för modellering av annullering i arbetsflöden
Aktiviteter kan avbrytas i ett arbetsflöde, till exempel genom att en Parallel aktivitet avbryter ofullständiga grenar när den CompletionCondition utvärderas till true
, eller utanför arbetsflödet, om värden anropar Cancel. För att tillhandahålla hantering av annullering kan arbetsflödesförfattare CancellationScope använda aktiviteten, CompensableActivity aktiviteten eller skapa anpassade aktiviteter som tillhandahåller avbokningslogik. Det här avsnittet innehåller en översikt över annullering i arbetsflöden.
Annullering, kompensation och transaktioner
Transaktioner ger ditt program möjlighet att avbryta (återställa) alla ändringar som körs i transaktionen om några fel inträffar under någon del av transaktionsprocessen. Allt arbete som kan behöva avbrytas eller ångras är dock inte lämpligt för transaktioner, till exempel långvarigt arbete eller arbete som inte omfattar transaktionsresurser. Kompensation ger en modell för att ångra tidigare slutfört icke-transaktionellt arbete om det uppstår ett efterföljande fel i arbetsflödet. Annullering tillhandahåller en modell för arbetsflödes- och aktivitetsförfattare som hanterar icke-transaktionellt arbete som inte har slutförts. Om en aktivitet inte har slutfört körningen och den avbryts anropas dess annulleringslogik om den är tillgänglig.
Kommentar
Mer information om transaktioner och kompensation finns i Transaktioner och kompensation.
Använda CancellationScope
Aktiviteten CancellationScope har två avsnitt som kan innehålla underordnade aktiviteter: Body och CancellationHandler. Det Body är där aktiviteterna som utgör aktivitetens logik placeras och CancellationHandler det är där aktiviteterna som tillhandahåller annulleringslogik för aktiviteten placeras. En aktivitet kan bara avbrytas om den inte har slutförts. När det gäller CancellationScope aktiviteten refererar slutförandet till slutförandet av aktiviteterna Bodyi . Om en annulleringsbegäran har schemalagts och aktiviteterna i Body inte har slutförts markeras CancellationScope de som Canceled och aktiviteterna CancellationHandler körs.
Avbryta ett arbetsflöde från värden
En värd kan avbryta ett arbetsflöde genom att anropa metoden för den Cancel WorkflowApplication instans som är värd för arbetsflödet. I följande exempel skapas ett arbetsflöde som har en CancellationScope. Arbetsflödet anropas och sedan anropar Cancelvärden . Huvudkörningen av arbetsflödet stoppas, CancellationHandler av CancellationScope anropas och sedan slutförs arbetsflödet med statusen 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 {0} Terminated.", e.InstanceId);
Console.WriteLine("Exception: {0}\n{1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
}
else
{
Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
}
};
// Run the workflow.
wfApp.Run();
Thread.Sleep(TimeSpan.FromSeconds(1));
wfApp.Cancel();
När det här arbetsflödet anropas visas följande utdata för konsolen.
Starta arbetsflödet.
CancellationHandler anropas.Arbetsflöde b30ebb30-df46-4d90-a211-e31c38d8db3c Avbröts.
Kommentar
När en CancellationScope aktivitet avbryts och anropas CancellationHandler är det arbetsflödesförfattarens ansvar att fastställa förloppet för den avbrutna aktiviteten som gjordes innan den avbröts för att tillhandahålla lämplig annulleringslogik. CancellationHandler Innehåller ingen information om förloppet för den avbrutna aktiviteten.
Ett arbetsflöde kan också avbrytas från värden om ett ohanterat undantag bubblar upp förbi arbetsflödets rot och OnUnhandledException hanteraren returnerar Cancel. I det här exemplet startar arbetsflödet och genererar sedan en ApplicationException. Det här undantaget hanteras inte av arbetsflödet och därför OnUnhandledException anropas hanteraren. Hanteraren instruerar körningen att avbryta arbetsflödet och att CancellationHandler aktiviteten som körs CancellationScope för närvarande anropas.
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 {0}\n{1}",
e.InstanceId, 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 {0} Terminated.", e.InstanceId);
Console.WriteLine("Exception: {0}\n{1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
}
else
{
Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
}
};
// Run the workflow.
wfApp.Run();
När det här arbetsflödet anropas visas följande utdata för konsolen.
Starta arbetsflödet.
OnUnhandledException i arbetsflödet 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9En ApplicationException kastades.CancellationHandler anropas.Arbetsflödet 6bb2d5d6-f49a-4c6d-a988-478afb86dbe9 avbröts.
Avbryta en aktivitet inifrån ett arbetsflöde
En aktivitet kan också avbrytas av dess överordnade. Om en Parallel aktivitet till exempel har flera grenar som körs och dess CompletionCondition utvärderas till true
avbryts dess ofullständiga grenar. I det här exemplet skapas en Parallel aktivitet som har två grenar. Dess CompletionCondition är inställd på så att true
slutförs Parallel så snart någon av dess grenar har slutförts. I det här exemplet slutförs gren 2, så gren 1 avbryts.
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 {0} Terminated.", e.InstanceId);
Console.WriteLine("Exception: {0}\n{1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
}
else
{
Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
}
};
// Run the workflow.
wfApp.Run();
När det här arbetsflödet anropas visas följande utdata för konsolen.
Gren 1 startar.
Gren 2 är klar.Gren 1 avbröts.Arbetsflödet e0685e24-18ef-4a47-acf3-5c638732f3be Slutfört. Aktiviteter avbryts också om ett undantag bubblar upp efter aktivitetens rot men hanteras på en högre nivå i arbetsflödet. I det här exemplet består huvudlogik för arbetsflödet av en Sequence aktivitet. Sequence anges som för Body en CancellationScope aktivitet som ingår i en TryCatch aktivitet. Ett undantag genereras från brödtexten i Sequence, hanteras av den överordnade TryCatch aktiviteten och Sequence avbryts.
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 {0} Terminated.", e.InstanceId);
Console.WriteLine("Exception: {0}\n{1}",
e.TerminationException.GetType().FullName,
e.TerminationException.Message);
}
else if (e.CompletionState == ActivityInstanceState.Canceled)
{
Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
}
else
{
Console.WriteLine("Workflow {0} Completed.", e.InstanceId);
}
};
// Run the workflow.
wfApp.Run();
När det här arbetsflödet anropas visas följande utdata för konsolen.
Sekvensen startar.
Sekvensen avbröts.Undantag har fångats.Arbetsflöde e3c18939-121e-4c43-af1c-ba1ce977ce55 Slutfört.
Utlöser undantag från en CancellationHandler
Alla undantag som genereras från CancellationHandler en CancellationScope är dödliga för arbetsflödet. Om det finns en möjlighet att undantag kommer från en CancellationHandleranvänder du en TryCatch i CancellationHandler för att fånga och hantera dessa undantag.
Annullering med CompensableActivity
CancellationScope Liksom aktiviteten CompensableActivity har har en CancellationHandler. Om en CompensableActivity avbryts anropas alla aktiviteter i den CancellationHandler . Detta kan vara användbart för att ångra delvis slutfört kompenserbart arbete. Information om hur du använder CompensableActivity för kompensation och annullering finns i Kompensation.
Annullering med anpassade aktiviteter
Författare av anpassad aktivitet kan implementera annulleringslogik i sina anpassade aktiviteter på flera olika sätt. Anpassade aktiviteter som härleds från Activity kan implementera annulleringslogik genom att placera en CancellationScope eller annan anpassad aktivitet som innehåller annulleringslogik i aktivitetens brödtext. AsyncCodeActivity och NativeActivity härledda aktiviteter kan åsidosätta respektive Cancel metod och tillhandahålla annulleringslogik där. CodeActivity härledda aktiviteter tillhandahåller ingen etablering för annullering eftersom allt deras arbete utförs i en enda körningskörning när körningen anropar Execute metoden. Om körningsmetoden ännu inte har anropats och en CodeActivity baserad aktivitet avbryts stängs aktiviteten med statusen Canceled Execute och metoden anropas inte.
Annullering med NativeActivity
NativeActivity härledda aktiviteter kan åsidosätta Cancel metoden för att tillhandahålla anpassad annulleringslogik. Om den här metoden inte åsidosätts tillämpas standardlogik för arbetsflödesavbokning. Standardavbokning är den process som inträffar för en NativeActivity som inte åsidosätter Cancel metoden eller vars Cancel metod anropar basmetoden NativeActivity Cancel . När en aktivitet avbryts flaggar körningen aktiviteten för annullering och hanterar automatiskt viss rensning. Om aktiviteten bara har utestående bokmärken tas bokmärkena bort och aktiviteten markeras som Canceled. Eventuella utestående underordnade aktiviteter för den avbrutna aktiviteten kommer i sin tur att avbrytas. Alla försök att schemalägga ytterligare underordnade aktiviteter resulterar i att försöket ignoreras och aktiviteten markeras som Canceled. Om någon utestående underordnad Canceled aktivitet slutförs i tillståndet eller Faulted markeras aktiviteten som Canceled. Observera att en begäran om annullering kan ignoreras. Om en aktivitet inte har några utestående bokmärken eller kör underordnade aktiviteter och inte schemalägger några ytterligare arbetsobjekt efter att ha flaggats för annullering slutförs den. Den här standardavbokningen räcker för många scenarier, men om ytterligare avbokningslogik behövs kan de inbyggda avbokningsaktiviteterna eller anpassade aktiviteterna användas.
I följande exempel definieras åsidosättningen Cancel av en NativeActivity baserad anpassad ParallelForEach
aktivitet. När aktiviteten avbryts hanterar den här åsidosättningen annulleringslogik för aktiviteten. Det här exemplet är en del av exemplet Non-Generic ParallelForEach .
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 härledda aktiviteter kan avgöra om annullering har begärts genom att IsCancellationRequested inspektera egenskapen och markera sig själva som avbrutna MarkCanceled genom att anropa metoden. Anropet MarkCanceled slutför inte aktiviteten omedelbart. Som vanligt slutför körningen aktiviteten när den inte har något mer enastående arbete, men om MarkCanceled den anropas blir Canceled det slutliga tillståndet i stället Closedför .
Annullering med AsyncCodeActivity
AsyncCodeActivity baserade aktiviteter kan också tillhandahålla anpassad annulleringslogik genom att Cancel åsidosätta metoden. Om den här metoden inte åsidosätts utförs ingen avbokningshantering om aktiviteten avbryts. I följande exempel definieras åsidosättningen Cancel av en AsyncCodeActivity baserad anpassad ExecutePowerShell
aktivitet. När aktiviteten avbryts utför den det önskade avbokningsbeteendet.
// 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 härledda aktiviteter kan avgöra om annullering har begärts genom att IsCancellationRequested inspektera egenskapen och markera sig själva som avbrutna MarkCanceled genom att anropa metoden.