# Friday, 23 November 2018

Azure Functions provide a simple way to deploy code in a scalable, cost-effective way.

By default, Azure Functions are stateless, which makes it difficult to create complex workflows with basic Azure functions - particularly long-running workflows, such as those that require human interaction.

A Durable Azure Function maintains state for a long time, without having to stay in memory, making it ideal for orchestrations. Stateful information is stored in an Azure Storage Account when the the process terminates. This saves you money, because the default pricing model for Azure functions only charges you while the function is running.

A Durable Function is not triggered in the same way as other Azure Functions (via HTTP, queue, database changes, timer, etc.) Rather, it is called from a "starter" function, which can be triggered in the usual way.

Rather than placing all logic within a single Durable Function, it usually makes more sense to split tasks into individual Activity Functions and have the Durable Function manage these. The most simple Durable Function would simply call multiple activities in sequence. A diagram of this is shown in Fig. 1.

DF01-DurableFunctionFlow
Fig. 1

You can create a Function App for an Azure Durable function in Visual Studio in the same way you create any function - by selecting File | New Project from the menu and selecting "Azure Functions" from the Project Templates dialog, as shown in Fig. 2.

DF02-NewFunctionProject
Fig. 2

Select "Azure Functions v2" from the top dropdown and HttpTrigger" from the list of templates, as shown in Fig. 3; then, click the [OK] button to create the solution and project.

DF03-FunctionTemplate
Fig. 3

The new project contains a function named "Function1". Right-click this function in the Solution Explorer and rename it to "StarterFunction", as shown in Fig. 4.

DF04-RenameFunction
Fig. 4

Open StarterFunction.cs and change the first line of the class from

[FunctionName("Function1")]

to

[FunctionName("StarterFunction")]

Now, you can add a Durable Function to the project. Right-click the project in the Solution Explorer and select Add | New Azure Function from the context menu, as shown in Fig. 5.

DF05-AddNewAzureFunction
Fig. 5

Name the new function "DurableFunction1", as shown in Fig. 6.

DF06-AddDurableFunction
Fig. 6

At the next dialog, select "Durable Function Orchestration" from the list of triggers and click the [OK] button to create the function, as shown in Fig. 7.

DF07-DurableFunctionsOrchestration
Fig. 7

This Durable Function will manage 3 functions, calling each one sequentially. To the project, add 3 new functions named "Function1", "Function2", and "Function3". It does not matter which trigger you choose, because we are going to overwrite the trigger. Paste the code below into each function:

    public static class Function1 
    { 
        [FunctionName("Function1")] 
        public static async Task<string> Run( 
            [ActivityTrigger] string msg, 
            ILogger log) 
        { 
            log.LogWarning("This is Function 1");

            await Task.Delay(10000); 
            msg += "Function1 done; "; 
            return msg; 
        } 
    }
  

Listing 1

    public static class Function2 
    { 
        [FunctionName("Function2")] 
        public static async Task<string> Run( 
             [ActivityTrigger] string msg, 
            ILogger log) 
        { 
            log.LogWarning("This is Function 2");

            await Task.Delay(10000); 
            msg += "Function2 done; "; 
            return msg; 
        } 
    }
  

Listing 2

    public static class Function3 
    { 
        [FunctionName("Function3")] 
        public static async Task<string> Run( 
            [ActivityTrigger] string msg, 
            ILogger log) 
        { 
            log.LogWarning("This is Function 3");

            await Task.Delay(10000); 
            msg += "Function3 done; "; 
            return msg; 
        } 
    }
  

Listing 3

As you can see, each function essentially does the same thing: log a brief message; wait 10 seconds; then, return a string consisting of the string passed in with a bit more appended to the end.

Notice also that the "msg" parameter in each function is decorated with the [ActivityTrigger] attribute, which is what makes each of these an Activity Function.

The Task.Delay() simulates a long-running activity. Imagine an activity that requires human input, such as a manager navigating to a web page and filling out a form. It might take days or weeks for this to happen. We certainly would not the application to continue running during this time: This would be an inefficient use of resources and it would be expensive. Durable functions handle this by storing state information in Azure storage; then retrieving that state when the function needs to resume.

Return to the DurableFunction1 class and replace the code with the following:

    public static class DurableFunction1 
    { 
        [FunctionName("DurableFunction1")] 
        public static async Task<IActionResult> Run( 
            [OrchestrationTrigger] DurableOrchestrationContext ctx, 
            ILogger log) 
        { 
            var msg = "Durable Function: "; 
             msg = await ctx.CallActivityAsync<string>("Function1", msg); 
            msg = await ctx.CallActivityAsync<string>("Function2", msg); 
            msg = await ctx.CallActivityAsync<string>("Function3", msg);

            // Use LogWarning, so it shows up in Yellow, making it easier to spot 
            log.LogWarning(msg);

            return new OkObjectResult(msg); 
        } 
    }
  

Listing 4

You will probably have to add the following to the top of the file in order for it to compile:

using Microsoft.AspNetCore.Mvc;

In Listing 4, we see that the Durable Function calls the 3 Activity functions in order. It passes to each Activity Function the output of the previous function. At then end of the orchestration, we expect to see a concatenation of messages from each of the 3 Activity Functions.

Notice also the parameter of type DurableOrchestrationContext, which is decorated with the [OrchestrationTrigger] attribute. This identifies this as a Durable Orchestration Function.

Finally, return to the StarterFunction class and replace the code with the following:

    public static class StarterFunction
    {
        [FunctionName("StarterFunction")]
        public static async Task<HttpResponseMessage> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
            HttpRequestMessage req,
            [OrchestrationClient] DurableOrchestrationClient starter,
            ILogger log)
        {
            log.LogInformation("About to start orchestration");

            var orchestrationId = await starter.StartNewAsync("DurableFunction1", log);
            return starter.CreateCheckStatusResponse(req, orchestrationId);
        }
    }
  

Listing 5

To see this in action, compile and run the project. A console will display similar to the one in Fig. 8.

DF08-RunFunction
Fig. 8.

You can trigger the StarterFunction by issuing an HTTP GET to the URL displayed in the console (in this case http://localhost:7071/api/StarterFunction). Open a browser, enter this URL into the address bar, and press [ENTER].

Watch the console. You should see the log statements in each of the functions display in turn. Finally, we will see the final value of the msg variable after being passed to all 3 Activity functions. The output should look something like fig. 9.

DF09-FunctionComplete
Fig. 9

This illustrates the concepts of a Durable Orchestration Function. You can view the source code in the SequentialDurableFunctionDemo project at my Azure-Function-Demos GitHub repository.