Azure Functions C# developer reference

This article is an introduction to developing Azure Functions by using C# in .NET class libraries.

Azure Functions supports C# and C# script programming languages. If you're looking for guidance on using C# in the Azure portal, see C# script (.csx) developer reference.

This article assumes that you've already read the following articles:

Functions class library project

In Visual Studio, the Azure Functions project template creates a C# class library project that contains the following files:

  • host.json - stores configuration settings that affect all functions in the project when running locally or in Azure.
  • local.settings.json - stores app settings and connection strings that are used when running locally.

Important

The build process creates a function.json file for each function. This function.json file is not meant to be edited directly. You can't change binding configuration or disable the function by editing this file. To disable a function, use the Disable attribute. For example, add a Boolean app setting MY_TIMER_DISABLED, and apply [Disable("MY_TIMER_DISABLED")] to your function. You can then enable and disable it by changing the app setting.

Methods recognized as functions

In a class library, a function is a static method with a FunctionName and a trigger attribute, as shown in the following example:

public static class SimpleExample
{
    [FunctionName("QueueTrigger")]
    public static void Run(
        [QueueTrigger("myqueue-items")] string myQueueItem, 
        TraceWriter log)
    {
        log.Info($"C# function processed: {myQueueItem}");
    }
} 

The FunctionName attribute marks the method as a function entry point. The name must be unique within a project, start with a letter and only contain letters, numbers, _ and -, up to 127 characters in length. Project templates often create a method named Run, but the method name can be any valid C# method name.

The trigger attribute specifies the trigger type and binds input data to a method parameter. The example function is triggered by a queue message, and the queue message is passed to the method in the myQueueItem parameter.

Method signature parameters

The method signature may contain parameters other than the one used with the trigger attribute. Here are some of the additional parameters that you can include:

The order of parameters in the function signature does not matter. For example, you can put trigger parameters before or after other bindings, and you can put the logger parameter before or after trigger or binding parameters.

Output binding example

The following example modifies the preceding one by adding an output queue binding. The function writes the queue message that triggers the function to a new queue message in a different queue.

public static class SimpleExampleWithOutput
{
    [FunctionName("CopyQueueMessage")]
    public static void Run(
        [QueueTrigger("myqueue-items-source")] string myQueueItem, 
        [Queue("myqueue-items-destination")] out string myQueueItemCopy,
        TraceWriter log)
    {
        log.Info($"CopyQueueMessage function processed: {myQueueItem}");
        myQueueItemCopy = myQueueItem;
    }
}

The binding reference articles (Storage queues, for example) explain which parameter types you can use with trigger, input, or output binding attributes.

Binding expressions example

The following code gets the name of the queue to monitor from an app setting, and it gets the queue message creation time in the insertionTime parameter.

public static class BindingExpressionsExample
{
    [FunctionName("LogQueueMessage")]
    public static void Run(
        [QueueTrigger("%queueappsetting%")] string myQueueItem,
        DateTimeOffset insertionTime,
        TraceWriter log)
    {
        log.Info($"Message content: {myQueueItem}");
        log.Info($"Created at: {insertionTime}");
    }
}

Autogenerated function.json

The build process creates a function.json file in a function folder in the build folder. As noted earlier, this file is not meant to be edited directly. You can't change binding configuration or disable the function by editing this file.

The purpose of this file is to provide information to the scale controller to use for scaling decisions on the consumption plan. For this reason, the file only has trigger info, not input or output bindings.

The generated function.json file includes a configurationSource property that tells the runtime to use .NET attributes for bindings, rather than function.json configuration. Here's an example:

{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.0.0",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "queueTrigger",
      "queueName": "%input-queue-name%",
      "name": "myQueueItem"
    }
  ],
  "disabled": false,
  "scriptFile": "..\\bin\\FunctionApp1.dll",
  "entryPoint": "FunctionApp1.QueueTrigger.Run"
}

Microsoft.NET.Sdk.Functions

The function.json file generation is performed by the NuGet package Microsoft.NET.Sdk.Functions.

The same package is used for both version 1.x and 2.x of the Functions runtime. The target framework is what differentiates a 1.x project from a 2.x project. Here are the relevant parts of .csproj files, showing different target frameworks and the same Sdk package:

Functions 1.x

<PropertyGroup>
  <TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
  <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.8" />
</ItemGroup>

Functions 2.x

<PropertyGroup>
  <TargetFramework>netstandard2.0</TargetFramework>
  <AzureFunctionsVersion>v2</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
  <PackageReference Include="Microsoft.NET.Sdk.Functions" Version="1.0.8" />
</ItemGroup>

Among the Sdk package dependencies are triggers and bindings. A 1.x project refers to 1.x triggers and bindings because those target the .NET Framework, while 2.x triggers and bindings target .NET Core.

The Sdk package also depends on Newtonsoft.Json, and indirectly on WindowsAzure.Storage. These dependencies make sure that your project uses the versions of those packages that work with the Functions runtime version that the project targets. For example, Newtonsoft.Json has version 11 for .NET Framework 4.6.1, but the Functions runtime that targets .NET Framework 4.6.1 is only compatible with Newtonsoft.Json 9.0.1. So your function code in that project also has to use Newtonsoft.Json 9.0.1.

The source code for Microsoft.NET.Sdk.Functions is available in the GitHub repo azure-functions-vs-build-sdk.

Runtime version

Visual Studio uses the Azure Functions Core Tools to run Functions projects. The Core Tools is a command-line interface for the Functions runtime.

If you install the Core Tools by using npm, that doesn't affect the Core Tools version used by Visual Studio. For the Functions runtime version 1.x, Visual Studio stores Core Tools versions in %USERPROFILE%\AppData\Local\Azure.Functions.Cli and uses the latest version stored there. For Functions 2.x, the Core Tools are included in the Azure Functions and Web Jobs Tools extension. For both 1.x and 2.x, you can see what version is being used in the console output when you run a Functions project:

[3/1/2018 9:59:53 AM] Starting Host (HostId=contoso2-1518597420, Version=2.0.11353.0, ProcessId=22020, Debug=False, Attempt=0, FunctionsExtensionVersion=)

Supported types for bindings

Each binding has its own supported types; for instance, a blob trigger attribute can be applied to a string parameter, a POCO parameter, a CloudBlockBlob parameter, or any of several other supported types. The binding reference article for blob bindings lists all supported parameter types. For more information, see Triggers and bindings and the binding reference docs for each binding type.

Tip

If you plan to use the HTTP or WebHook bindings, plan to avoid port exhaustion that can be caused by improper instantiation of HttpClient. For more information, see How to manage connections in Azure Functions.

Binding to method return value

You can use a method return value for an output binding, by applying the attribute to the method return value. For examples, see Triggers and bindings.

Use the return value only if a successful function execution always results in a return value to pass to the output binding. Otherwise, use ICollector or IAsyncCollector, as shown in the following section.

Writing multiple output values

To write multiple values to an output binding, or if a successful function invocation might not result in anything to pass to the output binding, use the ICollector or IAsyncCollector types. These types are write-only collections that are written to the output binding when the method completes.

This example writes multiple queue messages into the same queue using ICollector:

public static class ICollectorExample
{
    [FunctionName("CopyQueueMessageICollector")]
    public static void Run(
        [QueueTrigger("myqueue-items-source-3")] string myQueueItem,
        [Queue("myqueue-items-destination")] ICollector<string> myDestinationQueue,
        TraceWriter log)
    {
        log.Info($"C# function processed: {myQueueItem}");
        myDestinationQueue.Add($"Copy 1: {myQueueItem}");
        myDestinationQueue.Add($"Copy 2: {myQueueItem}");
    }
}

Logging

To log output to your streaming logs in C#, include an argument of type TraceWriter. We recommend that you name it log. Avoid using Console.Write in Azure Functions.

TraceWriter is defined in the Azure WebJobs SDK. The log level for TraceWriter can be configured in host.json.

public static class SimpleExample
{
    [FunctionName("QueueTrigger")]
    public static void Run(
        [QueueTrigger("myqueue-items")] string myQueueItem, 
        TraceWriter log)
    {
        log.Info($"C# function processed: {myQueueItem}");
    }
} 

Note

For information about a newer logging framework that you can use instead of TraceWriter, see Write logs in C# functions in the Monitor Azure Functions article.

Async

To make a function asynchronous, use the async keyword and return a Task object.

public static class AsyncExample
{
    [FunctionName("BlobCopy")]
    public static async Task RunAsync(
        [BlobTrigger("sample-images/{blobName}")] Stream blobInput,
        [Blob("sample-images-copies/{blobName}", FileAccess.Write)] Stream blobOutput,
        CancellationToken token,
        TraceWriter log)
    {
        log.Info($"BlobCopy function processed.");
        await blobInput.CopyToAsync(blobOutput, 4096, token);
    }
}

Cancellation tokens

A function can accept a CancellationToken parameter, which enables the operating system to notify your code when the function is about to be terminated. You can use this notification to make sure the function doesn't terminate unexpectedly in a way that leaves data in an inconsistent state.

The following example shows how to check for impending function termination.

public static class CancellationTokenExample
{
    public static void Run(
        [QueueTrigger("inputqueue")] string inputText,
        TextWriter logger,
        CancellationToken token)
    {
        for (int i = 0; i < 100; i++)
        {
            if (token.IsCancellationRequested)
            {
                logger.WriteLine("Function was cancelled at iteration {0}", i);
                break;
            }
            Thread.Sleep(5000);
            logger.WriteLine("Normal processing for queue message={0}", inputText);
        }
    }
}

Environment variables

To get an environment variable or an app setting value, use System.Environment.GetEnvironmentVariable, as shown in the following code example:

public static class EnvironmentVariablesExample
{
    [FunctionName("GetEnvironmentVariables")]
    public static void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, TraceWriter log)
    {
        log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
        log.Info(GetEnvironmentVariable("AzureWebJobsStorage"));
        log.Info(GetEnvironmentVariable("WEBSITE_SITE_NAME"));
    }

    public static string GetEnvironmentVariable(string name)
    {
        return name + ": " +
            System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
    }
}

App settings can be read from environment variables both when developing locally and when running in Azure. When developing locally, app settings come from the Values collection in the local.settings.json file. In both environments, local and Azure, GetEnvironmentVariable("<app setting name>") retrieves the value of the named app setting. For instance, when you're running locally, "My Site Name" would be returned if your local.settings.json file contains { "Values": { "WEBSITE_SITE_NAME": "My Site Name" } }.

The System.Configuration.ConfigurationManager.AppSettings property is an alternative API for getting app setting values, but we recommend that you use GetEnvironmentVariable as shown here.

Binding at runtime

In C# and other .NET languages, you can use an imperative binding pattern, as opposed to the declarative bindings in attributes. Imperative binding is useful when binding parameters need to be computed at runtime rather than design time. With this pattern, you can bind to supported input and output bindings on-the-fly in your function code.

Define an imperative binding as follows:

  • Do not include an attribute in the function signature for your desired imperative bindings.
  • Pass in an input parameter Binder binder or IBinder binder.
  • Use the following C# pattern to perform the data binding.

    using (var output = await binder.BindAsync<T>(new BindingTypeAttribute(...)))
    {
        ...
    }
    

    BindingTypeAttribute is the .NET attribute that defines your binding, and T is an input or output type that's supported by that binding type. T cannot be an out parameter type (such as out JObject). For example, the Mobile Apps table output binding supports six output types, but you can only use ICollector or IAsyncCollector with imperative binding.

Single attribute example

The following example code creates a Storage blob output binding with blob path that's defined at run time, then writes a string to the blob.

public static class IBinderExample
{
    [FunctionName("CreateBlobUsingBinder")]
    public static void Run(
        [QueueTrigger("myqueue-items-source-4")] string myQueueItem,
        IBinder binder,
        TraceWriter log)
    {
        log.Info($"CreateBlobUsingBinder function processed: {myQueueItem}");
        using (var writer = binder.Bind<TextWriter>(new BlobAttribute(
                    $"samples-output/{myQueueItem}", FileAccess.Write)))
        {
            writer.Write("Hello World!");
        };
    }
}

BlobAttribute defines the Storage blob input or output binding, and TextWriter is a supported output binding type.

Multiple attribute example

The preceding example gets the app setting for the function app's main Storage account connection string (which is AzureWebJobsStorage). You can specify a custom app setting to use for the Storage account by adding the StorageAccountAttribute and passing the attribute array into BindAsync<T>(). Use a Binder parameter, not IBinder. For example:

public static class IBinderExampleMultipleAttributes
{
    [FunctionName("CreateBlobInDifferentStorageAccount")]
    public async static Task RunAsync(
            [QueueTrigger("myqueue-items-source-binder2")] string myQueueItem,
            Binder binder,
            TraceWriter log)
    {
        log.Info($"CreateBlobInDifferentStorageAccount function processed: {myQueueItem}");
        var attributes = new Attribute[]
        {
        new BlobAttribute($"samples-output/{myQueueItem}", FileAccess.Write),
        new StorageAccountAttribute("MyStorageAccount")
        };
        using (var writer = await binder.BindAsync<TextWriter>(attributes))
        {
            await writer.WriteAsync("Hello World!!");
        }
    }
}

Triggers and bindings

The following table shows the bindings that are supported in the two major versions of the Azure Functions runtime.

Type 1.x 2.x Trigger Input Output
Blob Storage 1
Cosmos DB
Event Grid
Event Hubs
External File2
External Table2
HTTP 1
Microsoft Graph
Excel tables
Microsoft Graph
OneDrive files
Microsoft Graph
Outlook email
Microsoft Graph
Events
Microsoft Graph
Auth tokens
Mobile Apps
Notification Hubs
Queue storage 1
SendGrid
Service Bus
Table storage 1
Timer
Twilio
Webhooks

1 In 2.x, all bindings except HTTP, Timer, and Azure Storage must be registered. See Register binding extensions.

2 Experimental — not supported and might be abandoned in the future.

Next steps