Skip to content

Commit 9d6bbd1

Browse files
Merge pull request #87382 from craigshoemaker/crs-functions-di-updates
Functions DI updates based on customer issues
2 parents 4ed6471 + b39b3bc commit 9d6bbd1

File tree

1 file changed

+71
-19
lines changed

1 file changed

+71
-19
lines changed

articles/azure-functions/functions-dotnet-dependency-injection.md

Lines changed: 71 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ keywords: azure functions, functions, serverless architecture
1010
ms.service: azure-functions
1111
ms.devlang: dotnet
1212
ms.topic: reference
13-
ms.date: 05/28/2019
13+
ms.date: 09/05/2019
1414
ms.author: cshoe
1515
ms.reviewer: jehollan
1616
---
1717
# Use dependency injection in .NET Azure Functions
1818

1919
Azure Functions supports the dependency injection (DI) software design pattern, which is a technique to achieve [Inversion of Control (IoC)](https://docs.microsoft.com/dotnet/standard/modern-web-apps-azure-architecture/architectural-principles#dependency-inversion) between classes and their dependencies.
2020

21-
Azure Functions builds on top of the ASP.NET Core Dependency Injection features. Being aware of services, lifetimes, and design patterns of [ASP.NET Core dependency injection](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) before using DI features in an Azure Functions app is recommended.
21+
- Dependency injection in Azure Functions is built on the .NET Core Dependency Injection features. Familiarity with the [.NET Core dependency injection](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection) is recommended. There are differences, however, in how you override dependencies and how configuration values are read with Azure Functions on the Consumption plan.
2222

23-
Support for dependency injection begins with Azure Functions 2.x.
23+
- Support for dependency injection begins with Azure Functions 2.x.
2424

2525
## Prerequisites
2626

@@ -30,21 +30,18 @@ Before you can use dependency injection, you must install the following NuGet pa
3030

3131
- [Microsoft.NET.Sdk.Functions package](https://www.nuget.org/packages/Microsoft.NET.Sdk.Functions/) version 1.0.28 or later
3232

33-
- Optional: [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http/) Only required for registering HttpClient at startup
34-
3533
## Register services
3634

37-
To register services, you can 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.
35+
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.
3836

39-
To register the method, add the `FunctionsStartup` assembly attribute that specifies the type name used during startup. Also code is referencing a prerelease of [Microsoft.Azure.Cosmos](https://www.nuget.org/packages/Microsoft.Azure.Cosmos/) on Nuget.
37+
To register the method, add the `FunctionsStartup` assembly attribute that specifies the type name used during startup.
4038

4139
```csharp
4240
using System;
4341
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
4442
using Microsoft.Extensions.DependencyInjection;
4543
using Microsoft.Extensions.Http;
4644
using Microsoft.Extensions.Logging;
47-
using Microsoft.Azure.Cosmos;
4845

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

@@ -55,18 +52,30 @@ namespace MyNamespace
5552
public override void Configure(IFunctionsHostBuilder builder)
5653
{
5754
builder.Services.AddHttpClient();
55+
5856
builder.Services.AddSingleton((s) => {
59-
return new CosmosClient(Environment.GetEnvironmentVariable("COSMOSDB_CONNECTIONSTRING"));
57+
return new MyService();
6058
});
59+
6160
builder.Services.AddSingleton<ILoggerProvider, MyLoggerProvider>();
6261
}
6362
}
6463
}
6564
```
6665

66+
### Caveats
67+
68+
A series of registration steps run before and after the runtime processes the startup class. Therefore, the keep in mind the following items:
69+
70+
- *The startup class is meant for only setup and registration.* Avoid using services registered at startup during the startup process. For instance, don't try to log a message in a logger that is being registered during startup. This point of the registration process is too early for your services to be available for use. After the `Configure` method is run, the Functions runtime continues to register additional dependencies, which can affect how your services operate.
71+
72+
- *The dependency injection container only holds explicitly registered types*. The only services available as injectable types are what are setup in the `Configure` method. As a result, Functions-specific types like `BindingContext` and `ExecutionContext` aren't available during setup or as injectable types.
73+
6774
## Use injected dependencies
6875

69-
ASP.NET Core uses constructor injection to make your dependencies available to your function. The following sample demonstrates how the `IMyService` and `HttpClient` dependencies are injected into an HTTP-triggered function.
76+
Constructor injection is used to make your dependencies available in a function. The use of constructor injection requires that you do not use static classes.
77+
78+
The following sample demonstrates how the `IMyService` and `HttpClient` dependencies are injected into an HTTP-triggered function. This example uses the [Microsoft.Extensions.Http](https://www.nuget.org/packages/Microsoft.Extensions.Http/) package required to register an `HttpClient` at startup.
7079

7180
```csharp
7281
using System;
@@ -77,7 +86,6 @@ using Microsoft.Azure.WebJobs;
7786
using Microsoft.Azure.WebJobs.Extensions.Http;
7887
using Microsoft.AspNetCore.Http;
7988
using Microsoft.Extensions.Logging;
80-
using Newtonsoft.Json;
8189

8290
namespace MyNamespace
8391
{
@@ -107,24 +115,23 @@ namespace MyNamespace
107115
}
108116
```
109117

110-
The use of constructor injection means that you should not use static functions if you want to take advantage of dependency injection. For cosmos client refer [this](https://github.com/Azure/azure-cosmos-dotnet-v3/blob/master/Microsoft.Azure.Cosmos.Samples/CodeSamples/AzureFunctions/AzureFunctionsCosmosClient.cs).
111-
112118
## Service lifetimes
113119

114-
Azure Functions apps provide the same service lifetimes as [ASP.NET Dependency Injection](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection#service-lifetimes): transient, scoped, and singleton.
120+
Azure Functions apps provide the same service lifetimes as [ASP.NET Dependency Injection](https://docs.microsoft.com/aspnet/core/fundamentals/dependency-injection#service-lifetimes). For a Functions app, the different service lifetimes behave as follows:
115121

116-
In a functions app, a scoped service lifetime matches a function execution lifetime. Scoped services are created once per execution. Later requests for that service during the execution reuse the existing service instance. A singleton service lifetime matches the host lifetime and is reused across function executions on that instance.
117-
118-
Singleton lifetime services are recommended for connections and clients, for example `SqlConnection`, `CloudBlobClient`, or `HttpClient` instances.
122+
- **Transient**: Transient services are created upon each request of the service.
123+
- **Scoped**: The scoped service lifetime matches a function execution lifetime. Scoped services are created once per execution. Later requests for that service during the execution reuse the existing service instance.
124+
- **Singleton**: The singleton service lifetime matches the host lifetime and is reused across function executions on that instance. Singleton lifetime services are recommended for connections and clients, for example `SqlConnection` or `HttpClient` instances.
119125

120126
View or download a [sample of different service lifetimes](https://aka.ms/functions/di-sample) on GitHub.
121127

122128
## Logging services
123129

124-
If you need your own logging provider, the recommended way is to register an `ILoggerProvider` instance. Application Insights is added by Azure Functions automatically.
130+
If you need your own logging provider, register a custom type as an `ILoggerProvider` instance. Application Insights is added by Azure Functions automatically.
125131

126132
> [!WARNING]
127-
> Do not add `AddApplicationInsightsTelemetry()` to the services collection as it registers services that conflict with services provided by the environment.
133+
> - Do not add `AddApplicationInsightsTelemetry()` to the services collection as it registers services that conflict with services provided by the environment.
134+
> - Do not register your own `TelemetryConfiguration` or `TelemetryClient` if you are using the built-in Application Insights functionality.
128135
129136
## Function app provided services
130137

@@ -141,6 +148,51 @@ If there are other services you want to take a dependency on, [create an issue a
141148

142149
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](https://github.com/azure/azure-functions-host).
143150

151+
## Working with options and settings
152+
153+
Values defined in [app settings](./functions-how-to-use-azure-function-app-settings.md#settings) are available in an `IConfiguration` instance, which allows you to read app settings values in the startup class.
154+
155+
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. Consider the following class that includes a property named consistent with an app setting.
156+
157+
```csharp
158+
public class MyOptions
159+
{
160+
public string MyCustomSetting { get; set; }
161+
}
162+
```
163+
164+
From inside the `Startup.Configure` method, you can extract values from the `IConfiguration` instance into your custom type using the following code:
165+
166+
```csharp
167+
builder.Services.AddOptions<MyOptions>()
168+
.Configure<IConfiguration>((settings, configuration) =>
169+
{
170+
configuration.Bind(settings);
171+
});
172+
```
173+
174+
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.
175+
176+
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.
177+
178+
```csharp
179+
using System;
180+
using Microsoft.Extensions.Options;
181+
182+
public class HttpTrigger
183+
{
184+
private readonly MyOptions _settings;
185+
186+
public HttpTrigger(IOptions<MyOptions> options)
187+
{
188+
_service = service;
189+
_settings = options.Value;
190+
}
191+
}
192+
```
193+
194+
Refer to [Options pattern in ASP.NET Core](https://docs.microsoft.com/aspnet/core/fundamentals/configuration/options) for more details regarding working with options.
195+
144196
## Next steps
145197

146198
For more information, see the following resources:

0 commit comments

Comments
 (0)