C# developer reference for Azure Functions
This article provides a C# developer reference for Azure Functions. Azure Functions can be written in several languages, including C#. This reference covers key concepts, language-specific details, and best practices for developing C# functions.
- In-process model: Functions run within the same process as the Functions host. This is the default for .NET 6+ functions and offers the best performance.
- Isolated worker model: Functions run in a separate process from the Functions host. This model provides greater flexibility, allowing you to use different .NET versions and have more control over the execution environment.
Core Concepts for C# Functions
Function Triggers and Bindings
Triggers and bindings are fundamental to Azure Functions, allowing your functions to respond to events and interact with other Azure services and external systems without writing explicit integration code. In C#, these are typically defined using attributes.
Example: HTTP Trigger with Input Binding
The following example shows a C# HTTP-triggered function that also uses an input binding to read a message from Azure Queue Storage.
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
public static class HttpTriggerQueueInput
{
    [FunctionName("HttpTriggerQueueInput")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
        [Queue("myqueue-items")] string queueMessage, // Input binding to read a queue message
        ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");
        string name = req.Query["name"];
        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        dynamic data = JsonConvert.DeserializeObject(requestBody);
        name = name ?? data?.name;
        string responseMessage = string.IsNullOrEmpty(name)
            ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
            : $"Hello, {name}! The message from the queue is: '{queueMessage}'.";
        return new OkObjectResult(responseMessage);
    }
}
            Function Host and Runtime
The Functions host manages the execution of your functions. For C# functions, this involves loading your code, managing dependencies, and handling trigger invocations.
Dependency Injection
Azure Functions (especially in the isolated worker model) extensively uses dependency injection. You can inject services like ILogger, HttpClient, and custom services into your function constructors or methods.
Microsoft.Extensions.DependencyInjection package for advanced DI scenarios.
            Configuration
You can manage configuration settings for your C# functions using application settings. These are accessible via environment variables or the IConfiguration service.
public static class ConfigExample
{
    [FunctionName("ConfigExample")]
    public static void Run(
        [TimerTrigger("0 */5 * * * *")] TimerInfo myTimer,
        ILogger log,
        ExecutionContext context)
    {
        log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
        // Accessing configuration settings (e.g., from local.settings.json or app settings)
        var config = new ConfigurationBuilder()
            .SetBasePath(context.FunctionDirectory)
            .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .Build();
        string mySetting = config["MyCustomSetting"];
        log.LogInformation($"MyCustomSetting value: {mySetting}");
    }
}
            Common C# Function Scenarios
Working with HTTP Requests
HTTP-triggered functions are the most common type. They can handle GET, POST, PUT, DELETE, and other HTTP methods. The HttpRequest object provides access to request headers, body, query parameters, and route parameters.
Timer-Triggered Functions
Timer triggers allow you to schedule function executions at regular intervals. The schedule is defined using a CRON expression.
Storage Triggers and Bindings
Functions can interact with Azure Storage services like Blob Storage, Queue Storage, and Table Storage. Input bindings can read data, and output bindings can write data.
Cosmos DB Integration
Azure Functions provides built-in bindings for Azure Cosmos DB, enabling you to read documents or trigger functions based on changes in a Cosmos DB container.
Best Practices for C# Functions
- Statelessness: Design your functions to be stateless. Any state should be persisted externally (e.g., in a database or storage service).
- Idempotency: Ensure your functions can be executed multiple times with the same input without causing unintended side effects. This is crucial for reliability.
- Logging: Use the ILoggerinterface to log information, warnings, and errors. This is essential for debugging and monitoring.
- Error Handling: Implement robust error handling and exception management.
- Dependency Injection: Leverage DI for managing dependencies and improving testability.
- Performance Optimization: Be mindful of cold starts and consider strategies like pre-warming or optimizing startup code.
- Configuration Management: Store sensitive information and configuration settings securely, preferably in Azure Key Vault or application settings.