Develop Azure Functions with C#

Last updated: January 15, 2024

Learn how to develop Azure Functions using C#. This guide covers setting up your development environment, creating function projects, defining triggers and bindings, and implementing best practices.

Set up your development environment

Before you start developing Azure Functions with C#, ensure you have the following tools installed:

  • .NET SDK: Install the latest .NET SDK. You can download it from the official .NET website.
  • Azure Functions Core Tools: This command-line toolset allows you to develop, test, and deploy Azure Functions locally. Install it using npm:
    npm install -g azure-functions-core-tools@4 --unsafe-perm true
  • Integrated Development Environment (IDE):
    • Visual Studio: Recommended for Windows users.
    • Visual Studio Code: A lightweight, cross-platform option with the Azure Functions extension.
    • JetBrains Rider: A powerful IDE for .NET development.
Note: Ensure your .NET SDK version is compatible with the Azure Functions runtime you intend to use. Refer to the official Azure Functions documentation for compatibility matrices.

Create a C# Function project

You can create a new C# Azure Functions project using the Azure Functions Core Tools or your IDE.

Using Azure Functions Core Tools:

Open your terminal or command prompt and navigate to the directory where you want to create your project. Then, run the following command:

func init MyFunctionProj --worker-runtime dotnet --docker

This command creates a new function project with the .NET runtime and includes Docker support.

Using Visual Studio:

  1. Open Visual Studio.
  2. Select "Create a new project".
  3. Search for "Azure Functions" and select the project template.
  4. Choose your desired Azure Functions version, runtime (e.g., .NET 6.0, .NET 7.0), and trigger type (e.g., HTTP trigger).
  5. Name your project and click "Create".

HTTP trigger

An HTTP trigger allows your function to be executed in response to an HTTP request. Here's a basic example of an HTTP-triggered C# function:

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;

namespace MyFunctionApp
{
    public static class HttpTriggerCSharp
    {
        [FunctionName("HttpTriggerCSharp")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
            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}. This HTTP triggered function executed successfully.";

            return new OkObjectResult(responseMessage);
        }
    }
}
Tip: You can specify different HTTP methods (GET, POST, PUT, DELETE, etc.) and access request details like headers, query parameters, and the request body.

Timer trigger

A timer trigger allows your function to run on a schedule, similar to cron jobs. You define the schedule using a CRON expression.

using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace MyFunctionApp
{
    public static class TimerTriggerCSharp
    {
        [FunctionName("TimerTriggerCSharp")]
        public static void Run(
            [TimerTrigger("0 */5 * * * *")] // Runs every 5 minutes
            TimerInfo myTimer,
            ILogger log)
        {
            log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
            // Your scheduled task logic here
        }
    }
}

The CRON expression "0 */5 * * * *" means "at second 0, every 5 minutes, every hour, every day of the month, every month, every day of the week".

Storage bindings

Azure Functions provides input and output bindings for various Azure storage services, such as Azure Blob Storage and Azure Table Storage. This simplifies data access by abstracting away the storage SDK code.

For example, to read a blob from Blob Storage:

using System.IO;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

namespace MyFunctionApp
{
    public static class BlobTriggerCSharp
    {
        [FunctionName("BlobTriggerCSharp")]
        public static void Run(
            [BlobTrigger("mycontainer/{name}.txt", Connection = "AzureWebJobsStorage")] Stream myBlob,
            string name,
            ILogger log)
        {
            log.LogInformation($"C# Blob trigger function processed blob\n Name:{name} \n Size: {myBlob.Length} Bytes");
            // Process the blob content from 'myBlob'
        }
    }
}

Dependency Injection

Azure Functions supports dependency injection, allowing you to manage and inject services into your functions. This promotes better code organization and testability.

You can use the built-in dependency injection features provided by ASP.NET Core.

Important: To use dependency injection, your function app needs to be configured to use the .NET generic host. This is the default for projects created with the .NET 6+ SDKs.

In your Program.cs (for generic host), you can register your services:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

var host = new HostBuilder()
    .ConfigureFunctionsWorkerDefaults()
    .ConfigureServices(services => {
        services.AddSingleton<IMyService, MyService>(); // Register your service
    })
    .Build();

host.Run();

Then, inject the service into your function:

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using System.Net;

public class MyHttpFunction
{
    private readonly IMyService _myService;

    public MyHttpFunction(IMyService myService)
    {
        _myService = myService;
    }

    [Function("MyHttpFunction")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req,
        FunctionContext context)
    {
        var logger = context.GetLogger<MyHttpFunction>();
        logger.LogInformation("C# Function processed a request.");

        var response = req.CreateResponse(HttpStatusCode.OK);
        response.WriteString($"Service result: {_myService.GetData()}");

        return response;
    }
}

Testing C# Functions

Writing tests for your Azure Functions is crucial for ensuring reliability. You can use standard .NET testing frameworks like MSTest, NUnit, or xUnit.

  • Unit Testing: Mock dependencies and test individual function logic in isolation.
  • Integration Testing: Test functions with real or simulated triggers and bindings, often using the Azure Functions Core Tools' local testing capabilities.

Microsoft provides excellent examples and guidance on testing Azure Functions in their official documentation.