Use dependency injection in .NET Azure Functions (original) (raw)

Azure Functions supports the dependency injection (DI) software design pattern, which is a technique to achieve Inversion of Control (IoC) between classes and their dependencies.

Important

The guidance in this article applies only to C# class library functions, which run in-process with the runtime. This custom dependency injection model doesn't apply to .NET isolated functions, which lets you run .NET functions out-of-process. The .NET isolated worker process model relies on regular ASP.NET Core dependency injection patterns. To learn more, see Dependency injection in the .NET isolated worker process guide.

Prerequisites

Before you can use dependency injection, you must install the following NuGet packages:

Register services

To register services, create a method to configure and add components to an IFunctionsHostBuilder instance. The Azure Functions host creates an instance of IFunctionsHostBuilder and passes it directly into your method.

Warning

For function apps running in the Consumption or Premium plans, modifications to configuration values used in triggers can cause scaling errors. Any changes to these properties by the FunctionsStartup class results in a function app startup error.

Injection of IConfiguration can lead to unexpected behavior. To learn more about adding configuration sources, see Customizing configuration sources.

To register the method, add the FunctionsStartup assembly attribute that specifies the type name used during startup.

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]

namespace MyNamespace;

public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        builder.Services.AddHttpClient();

        builder.Services.AddSingleton<IMyService>((s) => {
            return new MyService();
        });

        builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
    }
}

This example uses the Microsoft.Extensions.Http package required to register an HttpClient at startup.

Caveats

A series of registration steps run before and after the runtime processes the startup class. Therefore, keep in mind the following items:

Use injected dependencies

Constructor injection is used to make your dependencies available in a function. The use of constructor injection requires that you don't use static classes for injected services or for your function classes.

The following sample demonstrates how the IMyService and HttpClient dependencies are injected into an HTTP-triggered function.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Threading.Tasks;

namespace MyNamespace;

public class MyHttpTrigger
{
    private readonly HttpClient _client;
    private readonly IMyService _service;

    public MyHttpTrigger(IHttpClientFactory httpClientFactory, IMyService service)
    {
        this._client = httpClientFactory.CreateClient();
        this._service = service;
    }

    [FunctionName("MyHttpTrigger")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
        ILogger log)
    {
        var response = await _client.GetAsync("https://microsoft.com");
        var message = _service.GetMessage();

        return new OkObjectResult("Response from function with injected dependencies.");
    }
}

This example uses the Microsoft.Extensions.Http package required to register an HttpClient at startup.

Service lifetimes

Azure Functions apps provide the same service lifetimes as ASP.NET Dependency Injection. For a Functions app, the different service lifetimes behave as follows:

View or download a sample of different service lifetimes on GitHub.

Logging services

If you need your own logging provider, register a custom type as an instance of ILoggerProvider, which is available through the Microsoft.Extensions.Logging.Abstractions NuGet package.

Application Insights is added by Azure Functions automatically.

Warning

ILogger and ILoggerFactory

The host injects ILogger<T> and ILoggerFactory services into constructors. However, by default these new logging filters are filtered out of the function logs. You need to modify the host.json file to opt in to extra filters and categories.

The following example demonstrates how to add an ILogger<HttpTrigger> with logs that are exposed to the host.

namespace MyNamespace;

public class HttpTrigger
{
    private readonly ILogger<HttpTrigger> _log;

    public HttpTrigger(ILogger<HttpTrigger> log)
    {
        _log = log;
    }

    [FunctionName("HttpTrigger")]
    public async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req)
    {
        _log.LogInformation("C# HTTP trigger function processed a request.");

        // ...
}

The following example host.json file adds the log filter.

{
    "version": "2.0",
    "logging": {
        "applicationInsights": {
            "samplingSettings": {
                "isEnabled": true,
                "excludedTypes": "Request"
            }
        },
        "logLevel": {
            "MyNamespace.HttpTrigger": "Information"
        }
    }
}

For more information about log levels, see Configure log levels.

Function app provided services

The function host registers many services. The following services are safe to take as a dependency in your application:

Service Type Lifetime Description
Microsoft.Extensions.Configuration.IConfiguration Singleton Runtime configuration
Microsoft.Azure.WebJobs.Host.Executors.IHostIdProvider Singleton Responsible for providing the ID of the host instance

If there are other services you want to take a dependency on, create an issue and propose them on GitHub.

Overriding host services

Overriding services provided by the host is currently not supported. If there are services you want to override, create an issue and propose them on GitHub.

Working with options and settings

Values defined in app settings are available in an IConfiguration instance, which allows you to read app settings values in the startup class.

You can extract values from the IConfiguration instance into a custom type. Copying the app settings values to a custom type makes it easy test your services by making these values injectable. Settings read into the configuration instance must be simple key/value pairs. For functions running in an Elastic Premium plan, application setting names can only contain letters, numbers (0-9), periods (.), colons (:) and underscores (_). For more information, see App setting considerations.

Consider the following class that includes a property named consistent with an app setting:

public class MyOptions
{
    public string MyCustomSetting { get; set; }
}

And a local.settings.json file that might structure the custom setting as follows:

{
  "IsEncrypted": false,
  "Values": {
    "MyOptions:MyCustomSetting": "Foobar"
  }
}

From inside the Startup.Configure method, you can extract values from the IConfiguration instance into your custom type using the following code:

builder.Services.AddOptions<MyOptions>()
    .Configure<IConfiguration>((settings, configuration) =>
    {
        configuration.GetSection("MyOptions").Bind(settings);
    });

Calling Bind copies values that have matching property names from the configuration into the custom instance. The options instance is now available in the IoC container to inject into a function.

The options object is injected into the function as an instance of the generic IOptions interface. Use the Value property to access the values found in your configuration.

using System;
using Microsoft.Extensions.Options;

public class HttpTrigger
{
    private readonly MyOptions _settings;

    public HttpTrigger(IOptions<MyOptions> options)
    {
        _settings = options.Value;
    }
}

For more information, see Options pattern in ASP.NET Core.

Using ASP.NET Core user secrets

When you develop your app locally, ASP.NET Core provides a Secret Manager tool that allows you to store secret information outside the project root. It makes it less likely that secrets are accidentally committed to source control. Azure Functions Core Tools (version 3.0.3233 or later) automatically reads secrets created by the ASP.NET Core Secret Manager.

To configure a .NET Azure Functions project to use user secrets, run the following command in the project root.

dotnet user-secrets init

Then use the dotnet user-secrets set command to create or update secrets.

dotnet user-secrets set MySecret "my secret value"

To access user secrets values in your function app code, use IConfiguration or IOptions.

Customizing configuration sources

To specify other configuration sources, override the ConfigureAppConfiguration method in your function app's StartUp class.

The following sample adds configuration values from both base and optional environment-specific app settings files.

using System.IO;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

[assembly: FunctionsStartup(typeof(MyNamespace.Startup))]

namespace MyNamespace;

public class Startup : FunctionsStartup
{
    public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
    {
        FunctionsHostBuilderContext context = builder.GetContext();

        builder.ConfigurationBuilder
            .AddJsonFile(Path.Combine(context.ApplicationRootPath, "appsettings.json"), optional: true, reloadOnChange: false)
            .AddJsonFile(Path.Combine(context.ApplicationRootPath, $"appsettings.{context.EnvironmentName}.json"), optional: true, reloadOnChange: false)
            .AddEnvironmentVariables();
    }
    
    public override void Configure(IFunctionsHostBuilder builder)
    {
    }
}

Add configuration providers to the ConfigurationBuilder property of IFunctionsConfigurationBuilder. For more information on using configuration providers, see Configuration in ASP.NET Core.

A FunctionsHostBuilderContext is obtained from IFunctionsConfigurationBuilder.GetContext(). Use this context to retrieve the current environment name and resolve the location of configuration files in your function app folder.

By default, configuration files such as appsettings.json aren't automatically copied to the function app's output folder. Update your .csproj file to match the following sample to ensure the files are copied.

<None Update="appsettings.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>      
</None>
<None Update="appsettings.Development.json">
    <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>

Next steps

For more information, see the following resources: