Durable Function-orkestreringar implementeras i kod och kan använda programmeringsspråkets inbyggda funktioner för felhantering. Det finns egentligen inga nya begrepp som du behöver lära dig att lägga till felhantering och kompensation i orkestreringarna. Det finns dock några beteenden som du bör känna till.
Kommentar
Version 4 av Node.js programmeringsmodellen för Azure Functions är allmänt tillgänglig. Den nya v4-modellen är utformad för att ha en mer flexibel och intuitiv upplevelse för JavaScript- och TypeScript-utvecklare. Läs mer om skillnaderna mellan v3 och v4 i migreringsguiden.
I följande kodfragment anger JavaScript (PM4) programmeringsmodellen V4, den nya upplevelsen.
Felaktigheter i aktivitetsfunktioner och underorkestreringar
I Durable Functions konverteras ohanterade undantag som genereras i aktivitetsfunktioner eller underorkestreringar tillbaka till orkestreringsfunktionen med hjälp av standardiserade undantagstyper.
Tänk dig till exempel följande orchestrator-funktion som utför en fondöverföring mellan två konton:
I Durable Functions C# in-process genereras ohanterade undantag som FunctionFailedException.
Undantagsmeddelandet identifierar vanligtvis vilka aktivitetsfunktioner eller underorkestreringar som orsakade felet. Om du vill komma åt mer detaljerad felinformation kontrollerar du egenskapen InnerException .
[FunctionName("TransferFunds")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var transferDetails = context.GetInput<TransferOperation>();
await context.CallActivityAsync("DebitAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
try
{
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.DestinationAccount,
Amount = transferDetails.Amount
});
}
catch (FunctionFailedException)
{
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
}
}
Kommentar
De tidigare C#-exemplen är för Durable Functions 2.x. För Durable Functions 1.x måste du använda DurableOrchestrationContext i stället för IDurableOrchestrationContext. Mer information om skillnaderna mellan versioner finns i artikeln Durable Functions-versioner .
I Durable Functions C# isolerad visas ohanterade undantag som TaskFailedException.
Undantagsmeddelandet identifierar vanligtvis vilka aktivitetsfunktioner eller underorkestreringar som orsakade felet. Om du vill komma åt mer detaljerad felinformation kontrollerar du egenskapen FailureDetails .
[FunctionName("TransferFunds")]
public static async Task Run(
[OrchestrationTrigger] TaskOrchestrationContext context, TransferOperation transferDetails)
{
await context.CallActivityAsync("DebitAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
try
{
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.DestinationAccount,
Amount = transferDetails.Amount
});
}
catch (TaskFailedException)
{
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
await context.CallActivityAsync("CreditAccount",
new
{
Account = transferDetails.SourceAccount,
Amount = transferDetails.Amount
});
}
}
Kommentar
- Undantagsmeddelandet identifierar vanligtvis vilka aktivitetsfunktioner eller underorkestreringar som orsakade felet. Om du vill komma åt mer detaljerad felinformation kontrollerar du egenskapen
FailureDetails .
- Som standard
FailureDetails innehåller feltypen, felmeddelandet, stackspårningen och eventuella kapslade inre undantag (var och en representeras som ett rekursivt FailureDetails objekt). Om du vill inkludera ytterligare undantagsegenskaper i felutdata kan du läsa Inkludera anpassade undantagsegenskaper för FailureDetails (.NET Isolerad).
const df = require("durable-functions");
module.exports = df.orchestrator(function* (context) {
const transferDetails = context.df.getInput();
yield context.df.callActivity("DebitAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
try {
yield context.df.callActivity("CreditAccount", {
account: transferDetails.destinationAccount,
amount: transferDetails.amount,
});
} catch (error) {
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
yield context.df.callActivity("CreditAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
}
})
const df = require("durable-functions");
df.app.orchestration("transferFunds", function* (context) {
const transferDetails = context.df.getInput();
yield context.df.callActivity("debitAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
try {
yield context.df.callActivity("creditAccount", {
account: transferDetails.destinationAccount,
amount: transferDetails.amount,
});
} catch (error) {
// Refund the source account.
// Another try/catch could be used here based on the needs of the application.
yield context.df.callActivity("creditAccount", {
account: transferDetails.sourceAccount,
amount: transferDetails.amount,
});
}
});
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
transfer_details = context.get_input()
yield context.call_activity('DebitAccount', {
'account': transfer_details['sourceAccount'],
'amount' : transfer_details['amount']
})
try:
yield context.call_activity('CreditAccount', {
'account': transfer_details['destinationAccount'],
'amount': transfer_details['amount'],
})
except:
yield context.call_activity('CreditAccount', {
'account': transfer_details['sourceAccount'],
'amount': transfer_details['amount']
})
main = df.Orchestrator.create(orchestrator_function)
Som standard skapar cmdletar i PowerShell inte undantag som kan fångas med hjälp av try/catch-block. Du har två alternativ för att ändra det här beteendet:
-
-ErrorAction Stop Använd flaggan när du anropar cmdletar, till exempel Invoke-DurableActivity.
- Ange inställningsvariabeln
$ErrorActionPreference till "Stop" i orchestrator-funktionen innan du anropar cmdletar.
param($Context)
$ErrorActionPreference = "Stop"
$transferDetails = $Context.Input
Invoke-DurableActivity -FunctionName 'DebitAccount' -Input @{ account = transferDetails.sourceAccount; amount = transferDetails.amount }
try {
Invoke-DurableActivity -FunctionName 'CreditAccount' -Input @{ account = transferDetails.destinationAccount; amount = transferDetails.amount }
} catch {
Invoke-DurableActivity -FunctionName 'CreditAccount' -Input @{ account = transferDetails.sourceAccount; amount = transferDetails.amount }
}
Mer information om felhantering i PowerShell finns i Try-Catch-Finally PowerShell-dokumentationen.
@FunctionName("TransferFunds")
public void transferFunds(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
TransferOperation transfer = ctx.getInput(TransferOperation.class);
ctx.callActivity(
"DebitAccount",
new OperationArgs(transfer.sourceAccount, transfer.amount)).await();
try {
ctx.callActivity(
"CreditAccount",
new OperationArgs(transfer.destinationAccount, transfer.amount)).await();
} catch (TaskFailedException ex) {
// Refund the source account on failure
ctx.callActivity(
"CreditAccount",
new OperationArgs(transfer.sourceAccount, transfer.amount)).await();
}
}
Om det första CreditAccount-funktionsanropet misslyckas kompenserar orkestreringsfunktionen genom att kreditera pengarna tillbaka till källkontot.
Fel i entitetsfunktioner
Undantagshanteringsbeteendet för entitetsfunktioner skiljer sig beroende på Durable Functions-värdmodellen.
I Durable Functions med C# i processen returneras de ursprungliga undantagstyper som genereras av entitetsfunktioner direkt till orkestratorn.
[FunctionName("Function1")]
public static async Task<string> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
try
{
var entityId = new EntityId(nameof(Counter), "myCounter");
await context.CallEntityAsync(entityId, "Add", 1);
}
catch (Exception ex)
{
// The exception type will be InvalidOperationException with the message "this is an entity exception".
}
return string.Empty;
}
[FunctionName("Counter")]
public static void Counter([EntityTrigger] IDurableEntityContext ctx)
{
switch (ctx.OperationName.ToLowerInvariant())
{
case "add":
throw new InvalidOperationException("this is an entity exception");
case "get":
ctx.Return(ctx.GetState<int>());
break;
}
}
I Durable Functions C# Isolerad visas undantag för orkestreraren som en EntityOperationFailedException. Om du vill komma åt den ursprungliga undantagsinformationen kontrollerar du dess FailureDetails egenskap.
[Function(nameof(MyOrchestrator))]
public static async Task<List<string>> MyOrchestrator(
[Microsoft.Azure.Functions.Worker.OrchestrationTrigger] TaskOrchestrationContext context)
{
var entityId = new Microsoft.DurableTask.Entities.EntityInstanceId(nameof(Counter), "myCounter");
try
{
await context.Entities.CallEntityAsync(entityId, "Add", 1);
}
catch (EntityOperationFailedException ex)
{
// Add your error handling
}
return new List<string>();
}
df.app.orchestration("counterOrchestration", function* (context) {
const entityId = new df.EntityId(counterEntityName, "myCounter");
try {
const currentValue = yield context.df.callEntity(entityId, "get");
if (currentValue < 10) {
yield context.df.callEntity(entityId, "add", 1);
}
} catch (err) {
context.log(`Entity call failed: ${err.message ?? err}`);
}
});
df.app.orchestration("counterOrchestration", function* (context) {
const entityId = new df.EntityId(counterEntityName, "myCounter");
try {
const currentValue = yield context.df.callEntity(entityId, "get");
if (currentValue < 10) {
yield context.df.callEntity(entityId, "add", 1);
}
} catch (err) {
context.log(`Entity call failed: ${err.message ?? err}`);
}
});
@myApp.orchestration_trigger(context_name="context")
def run_orchestrator(context):
try:
entityId = df.EntityId("Counter", "myCounter")
yield context.call_entity(entityId, "get")
return "finished"
except Exception as e:
# Add your error handling
Entitetsfunktioner stöds för närvarande inte i PowerShell.
Entitetsfunktioner stöds för närvarande inte i Java.
Automatiskt återförsök vid fel
När du anropar aktivitetsfunktioner eller underorkestreringsfunktioner kan du ange en princip för automatiskt återförsök. Följande exempel försöker anropa en funktion upp till tre gånger och väntar 5 sekunder mellan varje nytt försök:
[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
var retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: 3);
await context.CallActivityWithRetryAsync("FlakyFunction", retryOptions, null);
// ...
}
Kommentar
De tidigare C#-exemplen är för Durable Functions 2.x. För Durable Functions 1.x måste du använda DurableOrchestrationContext i stället för IDurableOrchestrationContext. Mer information om skillnaderna mellan versioner finns i artikeln Durable Functions-versioner .
[FunctionName("TimerOrchestratorWithRetry")]
public static async Task Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
var options = TaskOptions.FromRetryPolicy(new RetryPolicy(
maxNumberOfAttempts: 3,
firstRetryInterval: TimeSpan.FromSeconds(5)));
await context.CallActivityAsync("FlakyFunction", options: options);
// ...
}
const df = require("durable-functions");
module.exports = df.orchestrator(function*(context) {
const firstRetryIntervalInMilliseconds = 5000;
const maxNumberOfAttempts = 3;
const retryOptions =
new df.RetryOptions(firstRetryIntervalInMilliseconds, maxNumberOfAttempts);
yield context.df.callActivityWithRetry("FlakyFunction", retryOptions);
// ...
});
const df = require("durable-functions");
df.app.orchestration("callActivityWithRetry", function* (context) {
const firstRetryIntervalInMilliseconds = 5000;
const maxNumberOfAttempts = 3;
const retryOptions = new df.RetryOptions(firstRetryIntervalInMilliseconds, maxNumberOfAttempts);
yield context.df.callActivityWithRetry("flakyFunction", retryOptions);
// ...
});
import azure.functions as func
import azure.durable_functions as df
def orchestrator_function(context: df.DurableOrchestrationContext):
first_retry_interval_in_milliseconds = 5000
max_number_of_attempts = 3
retry_options = df.RetryOptions(first_retry_interval_in_milliseconds, max_number_of_attempts)
yield context.call_activity_with_retry('FlakyFunction', retry_options)
main = df.Orchestrator.create(orchestrator_function)
param($Context)
$retryOptions = New-DurableRetryOptions `
-FirstRetryInterval (New-TimeSpan -Seconds 5) `
-MaxNumberOfAttempts 3
Invoke-DurableActivity -FunctionName 'FlakyFunction' -RetryOptions $retryOptions
@FunctionName("TimerOrchestratorWithRetry")
public void timerOrchestratorWithRetry(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
final int maxAttempts = 3;
final Duration firstRetryInterval = Duration.ofSeconds(5);
RetryPolicy policy = new RetryPolicy(maxAttempts, firstRetryInterval);
TaskOptions options = new TaskOptions(policy);
ctx.callActivity("FlakeyFunction", options).await();
// ...
}
Aktivitetsfunktionsanropet i föregående exempel tar en parameter för att konfigurera en automatisk återförsöksprincip. Det finns flera alternativ för att anpassa principen för automatiskt återförsök:
-
Maximalt antal försök: Det maximala antalet försök. Om värdet är 1 görs inget nytt försök.
-
Första återförsöksintervallet: Hur lång tid det tar att vänta innan det första återförsöket.
-
Backoff-koefficient: Den koefficient som används för att fastställa ökningshastigheten för backoff. Standardvärdet är 1.
-
Maximalt återförsöksintervall: Den maximala väntetiden mellan återförsök.
-
Tidsgräns för återförsök: Den maximala tid som du behöver ägna åt att göra återförsök. Standardbeteendet är att försöka igen på obestämd tid.
Anpassade återförsökshanterare
När du använder .NET eller Java kan du också implementera återförsökshanterare i kod. Detta är användbart när deklarativa återförsöksprinciper inte är tillräckligt uttrycksfulla. För språk som inte stöder anpassade återförsökshanterare har du fortfarande möjlighet att implementera återförsöksprinciper med hjälp av loopar, undantagshantering och timers för att mata in fördröjningar mellan återförsök.
RetryOptions retryOptions = new RetryOptions(
firstRetryInterval: TimeSpan.FromSeconds(5),
maxNumberOfAttempts: int.MaxValue)
{
Handle = exception =>
{
// True to handle and try again, false to not handle and throw.
if (exception is TaskFailedException failure)
{
// Exceptions from TaskActivities are always this type. Inspect the
// inner Exception to get more details.
}
return false;
};
}
await ctx.CallActivityWithRetryAsync("FlakeyActivity", retryOptions, null);
TaskOptions retryOptions = TaskOptions.FromRetryHandler(retryContext =>
{
// Don't retry anything that derives from ApplicationException
if (retryContext.LastFailure.IsCausedBy<ApplicationException>())
{
return false;
}
// Quit after N attempts
return retryContext.LastAttemptNumber < 3;
});
try
{
await ctx.CallActivityAsync("FlakeyActivity", options: retryOptions);
}
catch (TaskFailedException)
{
// Case when the retry handler returns false...
}
JavaScript stöder för närvarande inte anpassade återförsökshanterare. Du har dock fortfarande möjlighet att implementera omprövningslogik direkt i orchestrator-funktionen med hjälp av loopar, undantagshantering och timers för att mata in fördröjningar mellan återförsök.
JavaScript stöder för närvarande inte anpassade återförsökshanterare. Du har dock fortfarande möjlighet att implementera omprövningslogik direkt i orchestrator-funktionen med hjälp av loopar, undantagshantering och timers för att mata in fördröjningar mellan återförsök.
Python stöder för närvarande inte anpassade återförsökshanterare. Du har dock fortfarande möjlighet att implementera omprövningslogik direkt i orchestrator-funktionen med hjälp av loopar, undantagshantering och timers för att mata in fördröjningar mellan återförsök.
PowerShell stöder för närvarande inte anpassade återförsökshanterare. Du har dock fortfarande möjlighet att implementera omprövningslogik direkt i orchestrator-funktionen med hjälp av loopar, undantagshantering och timers för att mata in fördröjningar mellan återförsök.
RetryHandler retryHandler = retryCtx -> {
// Don't retry anything that derives from RuntimeException
if (retryCtx.getLastFailure().isCausedBy(RuntimeException.class)) {
return false;
}
// Quit after N attempts
return retryCtx.getLastAttemptNumber() < 3;
};
TaskOptions options = new TaskOptions(retryHandler);
try {
ctx.callActivity("FlakeyActivity", options).await();
} catch (TaskFailedException ex) {
// Case when the retry handler returns false...
}
Funktionstimeouter
Du kanske vill avbryta ett funktionsanrop i en orkestreringsfunktion om det tar för lång tid att slutföra. Det rätta sättet att göra detta i dag är genom att skapa en varaktig timer med en "valfri" uppgiftsväljare, som i följande exempel:
[FunctionName("TimerOrchestrator")]
public static async Task<bool> Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
TimeSpan timeout = TimeSpan.FromSeconds(30);
DateTime deadline = context.CurrentUtcDateTime.Add(timeout);
using (var cts = new CancellationTokenSource())
{
Task activityTask = context.CallActivityAsync("FlakyFunction");
Task timeoutTask = context.CreateTimer(deadline, cts.Token);
Task winner = await Task.WhenAny(activityTask, timeoutTask);
if (winner == activityTask)
{
// success case
cts.Cancel();
return true;
}
else
{
// timeout case
return false;
}
}
}
Kommentar
De tidigare C#-exemplen är för Durable Functions 2.x. För Durable Functions 1.x måste du använda DurableOrchestrationContext i stället för IDurableOrchestrationContext. Mer information om skillnaderna mellan versioner finns i artikeln Durable Functions-versioner .
[Function("TimerOrchestrator")]
public static async Task<bool> Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
TimeSpan timeout = TimeSpan.FromSeconds(30);
DateTime deadline = context.CurrentUtcDateTime.Add(timeout);
using (var cts = new CancellationTokenSource())
{
Task activityTask = context.CallActivityAsync("FlakyFunction");
Task timeoutTask = context.CreateTimer(deadline, cts.Token);
Task winner = await Task.WhenAny(activityTask, timeoutTask);
if (winner == activityTask)
{
// success case
cts.Cancel();
return true;
}
else
{
// timeout case
return false;
}
}
}
const df = require("durable-functions");
const moment = require("moment");
module.exports = df.orchestrator(function*(context) {
const deadline = moment.utc(context.df.currentUtcDateTime).add(30, "s");
const activityTask = context.df.callActivity("FlakyFunction");
const timeoutTask = context.df.createTimer(deadline.toDate());
const winner = yield context.df.Task.any([activityTask, timeoutTask]);
if (winner === activityTask) {
// success case
timeoutTask.cancel();
return true;
} else {
// timeout case
return false;
}
});
const df = require("durable-functions");
const { DateTime } = require("luxon");
df.app.orchestration("timerOrchestrator", function* (context) {
const deadline = DateTime.fromJSDate(context.df.currentUtcDateTime).plus({ seconds: 30 });
const activityTask = context.df.callActivity("flakyFunction");
const timeoutTask = context.df.createTimer(deadline.toJSDate());
const winner = yield context.df.Task.any([activityTask, timeoutTask]);
if (winner === activityTask) {
// success case
timeoutTask.cancel();
return true;
} else {
// timeout case
return false;
}
});
import azure.functions as func
import azure.durable_functions as df
from datetime import datetime, timedelta
def orchestrator_function(context: df.DurableOrchestrationContext):
deadline = context.current_utc_datetime + timedelta(seconds = 30)
activity_task = context.call_activity('FlakyFunction')
timeout_task = context.create_timer(deadline)
winner = yield context.task_any(activity_task, timeout_task)
if winner == activity_task:
timeout_task.cancel()
return True
else:
return False
main = df.Orchestrator.create(orchestrator_function)
param($Context)
$expiryTime = New-TimeSpan -Seconds 30
$activityTask = Invoke-DurableActivity -FunctionName 'FlakyFunction'-NoWait
$timerTask = Start-DurableTimer -Duration $expiryTime -NoWait
$winner = Wait-DurableTask -Task @($activityTask, $timerTask) -NoWait
if ($winner -eq $activityTask) {
Stop-DurableTimerTask -Task $timerTask
return $True
}
else {
return $False
}
@FunctionName("TimerOrchestrator")
public boolean timerOrchestrator(
@DurableOrchestrationTrigger(name = "ctx") TaskOrchestrationContext ctx) {
Task<Void> activityTask = ctx.callActivity("SlowFunction");
Task<Void> timeoutTask = ctx.createTimer(Duration.ofMinutes(30));
Task<?> winner = ctx.anyOf(activityTask, timeoutTask).await();
if (winner == activityTask) {
// success case
return true;
} else {
// timeout case
return false;
}
}
Kommentar
Den här mekanismen avslutar inte aktivitetens funktionskörning som pågår. I stället tillåter den helt enkelt orkestreringsfunktionen att ignorera resultatet och gå vidare. Mer information finns i dokumentationen om Timers .
Ohanterade undantag
Om en orchestrator-funktion misslyckas med ett ohanterat undantag loggas information om undantaget och instansen slutförs med status Failed .
Inkludera anpassade undantagsegenskaper för FailureDetails (.NET Isolerad)
När du kör durable task-arbetsflöden i .NET Isolated-modellen serialiseras aktivitetsfel automatiskt till ett FailureDetails-objekt. Som standard innehåller det här objektet standardfält som:
- ErrorType – namnet på undantagstypen
- Meddelande – undantagsmeddelandet
- StackTrace – den serialiserade stackspårningen
- InnerFailure – ett kapslat FailureDetails-objekt för rekursiva inre undantag
Från och med Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 kan du utöka det här beteendet genom att implementera ett IExceptionPropertiesProvider (definierat i Microsoft.DurableTask.Worker från v1.16.1-paketet). Den här providern definierar vilka undantagstyper och vilka av deras egenskaper som ska ingå i ordlistan FailureDetails.Properties.
Kommentar
- Den här funktionen är endast tillgänglig i .NET Isolerad . Stöd för Java kommer att läggas till i en framtida version.
- Kontrollera att du använder Microsoft.Azure.Functions.Worker.Extensions.DurableTask v1.9.0 eller senare.
- Kontrollera att du använder Microsoft.DurableTask.Worker v1.16.1 eller senare.
Implementera en leverantör för undantagsegenskaper
Implementera en anpassad IExceptionPropertiesProvider för att extrahera och returnera valda egenskaper för de undantag som du bryr dig om. Den returnerade ordlistan serialiseras i fältet Egenskaper i FailureDetails när en matchande undantagstyp genereras.
using Microsoft.DurableTask.Worker;
public class CustomExceptionPropertiesProvider : IExceptionPropertiesProvider
{
public IDictionary<string, object?>? GetExceptionProperties(Exception exception)
{
return exception switch
{
ArgumentOutOfRangeException e => new Dictionary<string, object?>
{
["ParamName"] = e.ParamName,
["ActualValue"] = e.ActualValue
},
InvalidOperationException e => new Dictionary<string, object?>
{
["CustomHint"] = "Invalid operation occurred",
["TimestampUtc"] = DateTime.UtcNow
},
_ => null // Other exception types not handled
};
}
}
Registrera providern
Registrera din anpassade IExceptionPropertiesProvider i din .NET Isolated Worker-värd, vanligtvis i Program.cs:
using Microsoft.DurableTask.Worker;
using Microsoft.Extensions.DependencyInjection;
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults(builder =>
{
// Register custom exception properties provider
builder.Services.AddSingleton<IExceptionPropertiesProvider, CustomExceptionPropertiesProvider>();
})
.Build();
host.Run();
När ett undantag som matchar en av de hanterade typerna har registrerats inkluderas automatiskt de konfigurerade egenskaperna i dess FailureDetails.
Exempel på FailureDetails-utdata
När ett undantag inträffar som matchar providerns konfiguration får orkestreringen en serialiserad FailureDetails-struktur som den här:
{
"errorType": "TaskFailedException",
"message": "Activity failed with an exception.",
"stackTrace": "...",
"innerFailure": {
"errorType": "ArgumentOutOfRangeException",
"message": "Specified argument was out of range.",
"properties": {
"ParamName": "count",
"ActualValue": 42
}
}
}
Nästa steg