Durable Functions is an extension of Azure Functions that lets you write stateful functions in a serverless compute environment.
https://docs.microsoft.com
If you haven’t read the Microsoft Official documentation for durable function, I highly suggest it, the link is in the above citation.
Durable functions (1) provides “durable” or reliable execution of stateful functions. (2) It simplifies the “orchestration” of multiple functions that are interconnected to a feature.
Let’s look into the details of the default code when we add a new durable function in VisualStudio.
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace FunctionAppCore
{
public static class DurableFunctionSample
{
[FunctionName("DurableFunctionSample")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context)
{
var outputs = new List<string>();
// Replace "hello" with the name of your Durable Activity Function.
outputs.Add(await context.CallActivityAsync<string>("DurableFunctionSample_Hello", "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>("DurableFunctionSample_Hello", "Seattle"));
outputs.Add(await context.CallActivityAsync<string>("DurableFunctionSample_Hello", "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
[FunctionName("DurableFunctionSample_Hello")]
public static string SayHello([ActivityTrigger] string name, ILogger log)
{
log.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}
[FunctionName("DurableFunctionSample_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("DurableFunctionSample", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
}
}
Notice that there are three (3) functions – DurableFunctionSample, DurableFunctionSample_Hello, DurableFunctionSample_HttpStart.
Client Function
DurableFunctionSample_HttpStart is an Anonymous HttpTrigger function. It is using DurableOrchestrationClient class to run the Orchestrator function.
[FunctionName("DurableFunctionSample_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")]HttpRequestMessage req,
[OrchestrationClient]DurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
string instanceId = await starter.StartNewAsync("DurableFunctionSample", null);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
Notice that it is using the DurableOrchestrationClient.StartNewAsync that has two parameters (name and object) – name for the orchestrator name and an object parameter as an input for DurableFunctionSample orchestrator function.
Orchestrator Function
This is the main function which basically defines the workflow.
[FunctionName("DurableFunctionSample")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] DurableOrchestrationContext context)
{
var input = context.GetInput<SampleInputClass>();
var outputs = new List<string>();
// Replace "hello" with the name of your Durable Activity Function.
outputs.Add(await context.CallActivityAsync<string>("DurableFunctionSample_Hello", "Tokyo"));
outputs.Add(await context.CallActivityAsync<string>("DurableFunctionSample_Hello", "Seattle"));
outputs.Add(await context.CallActivityAsync<string>("DurableFunctionSample_Hello", "London"));
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
The DurableOrchestratinContext has only 1 input thus, GetInput<T>() function.
It can (1) call an activity function (which is basically just a function) or (2) call another orchestrator function.
It is using the DurableOrchestrationContext.CallActivityAsync(“ActivityName”, “ParameterInput”) to call and pass data to an activity.
There are different pattern – when calling an activity function inside the orchestrator – I highly suggest to read the Microsoft documentation of durable function which discusses the different patterns.
Activity Function
These functions are the granular task of the workflow (orchestor) that does the actual work.
[FunctionName("DurableFunctionSample_Hello")]
public static string SayHello([ActivityTrigger] string name, ILogger log)
{
log.LogInformation($"Saying hello to {name}.");
return $"Hello {name}!";
}
Notice that the ActivityTrigger type/decorator in the parameter. This defines that this function is an activity function.

Leave a comment