From 80fd9638b0a1a9871dcb406d5bd366b3c4d594dd Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:52:48 -0800 Subject: [PATCH 1/5] make example compilable --- docs/core/extensions/dependency-injection.md | 91 ++++++------------- .../dependency-injection/IMessageWriter.cs | 6 -- .../dependency-injection/MessageWriter.cs | 9 -- .../dependency-injection/Program.cs | 10 -- .../dependency-injection/Worker.cs | 13 --- .../LoggingMessageWriter.cs | 4 +- .../configuration/overview/Program.cs | 40 ++++++++ .../dependency-injection.csproj | 1 - 8 files changed, 67 insertions(+), 107 deletions(-) delete mode 100644 docs/core/extensions/snippets/configuration/dependency-injection/IMessageWriter.cs delete mode 100644 docs/core/extensions/snippets/configuration/dependency-injection/MessageWriter.cs delete mode 100644 docs/core/extensions/snippets/configuration/dependency-injection/Program.cs delete mode 100644 docs/core/extensions/snippets/configuration/dependency-injection/Worker.cs rename docs/core/extensions/snippets/configuration/{dependency-injection => overview}/LoggingMessageWriter.cs (67%) create mode 100644 docs/core/extensions/snippets/configuration/overview/Program.cs rename docs/core/extensions/snippets/configuration/{dependency-injection => overview}/dependency-injection.csproj (86%) diff --git a/docs/core/extensions/dependency-injection.md b/docs/core/extensions/dependency-injection.md index 5e4e96efb0d62..4ad85fb2ce36f 100644 --- a/docs/core/extensions/dependency-injection.md +++ b/docs/core/extensions/dependency-injection.md @@ -1,6 +1,6 @@ --- title: Dependency injection -description: Learn how to use dependency injection within your .NET apps. Discover how to registration services, define service lifetimes, and express dependencies in C#. +description: Learn how to use dependency injection within your .NET apps. Discover how to define service lifetimes and express dependencies in C#. ms.date: 10/21/2025 ms.topic: overview ai-usage: ai-assisted @@ -10,19 +10,11 @@ ai-usage: ai-assisted .NET supports the *dependency injection* (DI) software design pattern, which is a technique for achieving [Inversion of Control (IoC)](../../architecture/modern-web-apps-azure/architectural-principles.md#dependency-inversion) between classes and their dependencies. Dependency injection in .NET is a built-in part of the framework, along with configuration, logging, and the options pattern. -A *dependency* is an object that another object depends on. Examine the following `MessageWriter` class with a `Write` method that other classes depend on: +A *dependency* is an object that another object depends on. The following `MessageWriter` class has a `Write` method that other classes might depend on: -```csharp -public class MessageWriter -{ - public void Write(string message) - { - Console.WriteLine($"MessageWriter.Write(message: \"{message}\")"); - } -} -``` +:::code language="csharp" source="snippets/overview/Program.cs" id="SnippetMW"::: -A class can create an instance of the `MessageWriter` class to use its `Write` method. In the following example, the `MessageWriter` class is a dependency of the `Worker` class: +A class can create an instance of the `MessageWriter` class to use its `Write` method. In the following example, the `MessageWriter` class is a *dependency* of the `Worker` class: ```csharp public class Worker : BackgroundService @@ -40,85 +32,54 @@ public class Worker : BackgroundService } ``` -The class creates and directly depends on the `MessageWriter` class. Hard-coded dependencies, such as in the previous example, are problematic and should be avoided for the following reasons: +In this case, the `Worker` class creates and directly depends on the `MessageWriter` class. Hard-coded dependencies like this are problematic and should be avoided for the following reasons: - To replace `MessageWriter` with a different implementation, you must modify the `Worker` class. - If `MessageWriter` has dependencies, the `Worker` class must also configure them. In a large project with multiple classes depending on `MessageWriter`, the configuration code becomes scattered across the app. - This implementation is difficult to unit test. The app should use a mock or stub `MessageWriter` class, which isn't possible with this approach. -Dependency injection addresses these problems through: +## The concept + +Dependency injection addresses hard-coded dependency problems through: - The use of an interface or base class to abstract the dependency implementation. -- Registration of the dependency in a service container. .NET provides a built-in service container, . Services are typically registered at the app's start-up and appended to an . Once all services are added, use to create the service container. -- *Injection* of the service into the constructor of the class where it's used. The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed. +- Registration of the dependency in a *service container*. -As an example, the `IMessageWriter` interface defines the `Write` method: + .NET provides a built-in service container, . Services are typically registered at the app's start-up and appended to an . Once all services are added, use to create the service container. -:::code language="csharp" source="snippets/configuration/dependency-injection/IMessageWriter.cs"::: +- Injection of the service into the constructor of the class where it's used. -This interface is implemented by a concrete type, `MessageWriter`: + The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it's no longer needed. -:::code language="csharp" source="snippets/configuration/dependency-injection/MessageWriter.cs"::: +> [!TIP] +> In dependency injection terminology, a *service* is typically an object that provides a service to other objects, such as the `IMessageWriter` service. The service isn't related to a web service, although it might use a web service. -The sample code registers the `IMessageWriter` service with the concrete type `MessageWriter`. The method registers the service with a singleton lifetime, the lifetime of the app. [Service lifetimes](#service-lifetimes) are described later in this article. +As an example, assume the `IMessageWriter` interface defines the `Write` method. This interface is implemented by a concrete type, `MessageWriter`, shown previously. The following sample code registers the `IMessageWriter` service with the concrete type `MessageWriter`. The method registers the service with a [*singleton* lifetime](service-lifetimes.md#singleton), which means it isn't disposed until the app shuts down. -:::code language="csharp" source="snippets/configuration/dependency-injection/Program.cs" highlight="5-8"::: +:::code language="csharp" source="snippets/overview/Program.cs" highlight="3-6"::: -In the preceding code, the sample app: +In the preceding code example, the highlighted lines: -- Creates a host app builder instance. -- Configures the services by registering: - - The `Worker` as a hosted service. For more information, see [Worker Services in .NET](workers.md). - - The `IMessageWriter` interface as a singleton service with a corresponding implementation of the `MessageWriter` class. -- Builds the host and runs it. +- Create a host app builder instance. +- Configure the services by registering the `Worker` as a [hosted service](workers.md) and the `IMessageWriter` interface as a singleton service with a corresponding implementation of the `MessageWriter` class. +- Build the host and run it. The host contains the dependency injection service provider. It also contains all the other relevant services required to automatically instantiate the `Worker` and provide the corresponding `IMessageWriter` implementation as an argument. -:::code language="csharp" source="snippets/configuration/dependency-injection/Worker.cs"::: +By using the DI pattern, the worker service doesn't use the concrete type `MessageWriter`, only the `IMessageWriter` interface that it implements. This design makes it easy to change the implementation that the worker service uses without modifying the worker service. The worker service also doesn't *create an instance* of `MessageWriter`. The DI container creates the instance. -By using the DI pattern, the worker service: +Now, imagine you want to switch out `MessageWriter` with a type that uses the [framework-provided logging service](#framework-provided-services). Create a class `LoggingMessageWriter` that depends on by requesting it in the constructor. -- Doesn't use the concrete type `MessageWriter`, only the `IMessageWriter` interface that it implements. This makes it easy to change the implementation that the worker service uses without modifying the worker service. -- Doesn't create an instance of `MessageWriter`. The DI container creates the instance. +:::code language="csharp" source="snippets/overview/LoggingMessageWriter.cs"::: -The implementation of the `IMessageWriter` interface can be improved using the built-in logging API: - -:::code language="csharp" source="snippets/configuration/dependency-injection/LoggingMessageWriter.cs"::: - -The updated call to `AddSingleton` registers the new `IMessageWriter` implementation: +To switch from `MessageWriter` to `LoggingMessageWriter`, simply update the call to `AddSingleton` to register this new `IMessageWriter` implementation: ```csharp builder.Services.AddSingleton(); ``` -The (`builder`) type is part of the `Microsoft.Extensions.Hosting` NuGet package. - -`LoggingMessageWriter` depends on , which it requests in the constructor. `ILogger` is a [framework-provided service](#framework-provided-services). - -It's not unusual to use dependency injection in a chained fashion. Each requested dependency in turn requests its own dependencies. The container resolves the dependencies in the graph and returns the fully resolved service. The collective set of dependencies that must be resolved is typically called a *dependency tree*, *dependency graph*, or *object graph*. - -The container resolves `ILogger` by taking advantage of [(generic) open types](/dotnet/csharp/language-reference/language-specification/types#843-open-and-closed-types), which eliminates the need to register every [(generic) constructed type](/dotnet/csharp/language-reference/language-specification/types#84-constructed-types). - -In dependency injection terminology, a *service* is typically an object that provides a service to other objects, such as the `IMessageWriter` service. The service isn't related to a web service, although it might use a web service. - -The framework provides a robust logging system. The `IMessageWriter` implementations shown in the preceding examples demonstrate basic DI, not logging. Most apps shouldn't need to write loggers. The following code demonstrates using the default logging, which only requires the `Worker` to be registered as a hosted service : - -```csharp -public sealed class Worker(ILogger logger) : BackgroundService -{ - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); - - await Task.Delay(1_000, stoppingToken); - } - } -} -``` - -Using the preceding code, there's no need to update _Program.cs_, because the framework provides logging. +> [!TIP] +> The container resolves `ILogger` by taking advantage of [(generic) open types](/dotnet/csharp/language-reference/language-specification/types#843-open-and-closed-types), which eliminates the need to register every [(generic) constructed type](/dotnet/csharp/language-reference/language-specification/types#84-constructed-types). ## Multiple constructor discovery rules diff --git a/docs/core/extensions/snippets/configuration/dependency-injection/IMessageWriter.cs b/docs/core/extensions/snippets/configuration/dependency-injection/IMessageWriter.cs deleted file mode 100644 index dc85aeaf428dd..0000000000000 --- a/docs/core/extensions/snippets/configuration/dependency-injection/IMessageWriter.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace DependencyInjection.Example; - -public interface IMessageWriter -{ - void Write(string message); -} diff --git a/docs/core/extensions/snippets/configuration/dependency-injection/MessageWriter.cs b/docs/core/extensions/snippets/configuration/dependency-injection/MessageWriter.cs deleted file mode 100644 index 118702d2734a8..0000000000000 --- a/docs/core/extensions/snippets/configuration/dependency-injection/MessageWriter.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace DependencyInjection.Example; - -public class MessageWriter : IMessageWriter -{ - public void Write(string message) - { - Console.WriteLine($"MessageWriter.Write(message: \"{message}\")"); - } -} diff --git a/docs/core/extensions/snippets/configuration/dependency-injection/Program.cs b/docs/core/extensions/snippets/configuration/dependency-injection/Program.cs deleted file mode 100644 index ef79801a87fa5..0000000000000 --- a/docs/core/extensions/snippets/configuration/dependency-injection/Program.cs +++ /dev/null @@ -1,10 +0,0 @@ -using DependencyInjection.Example; - -HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); - -builder.Services.AddHostedService(); -builder.Services.AddSingleton(); - -using IHost host = builder.Build(); - -host.Run(); diff --git a/docs/core/extensions/snippets/configuration/dependency-injection/Worker.cs b/docs/core/extensions/snippets/configuration/dependency-injection/Worker.cs deleted file mode 100644 index c40efdf98efae..0000000000000 --- a/docs/core/extensions/snippets/configuration/dependency-injection/Worker.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace DependencyInjection.Example; - -public sealed class Worker(IMessageWriter messageWriter) : BackgroundService -{ - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - messageWriter.Write($"Worker running at: {DateTimeOffset.Now}"); - await Task.Delay(1_000, stoppingToken); - } - } -} diff --git a/docs/core/extensions/snippets/configuration/dependency-injection/LoggingMessageWriter.cs b/docs/core/extensions/snippets/configuration/overview/LoggingMessageWriter.cs similarity index 67% rename from docs/core/extensions/snippets/configuration/dependency-injection/LoggingMessageWriter.cs rename to docs/core/extensions/snippets/configuration/overview/LoggingMessageWriter.cs index c77548b6cc000..15731e37d45c5 100644 --- a/docs/core/extensions/snippets/configuration/dependency-injection/LoggingMessageWriter.cs +++ b/docs/core/extensions/snippets/configuration/overview/LoggingMessageWriter.cs @@ -1,6 +1,4 @@ -namespace DependencyInjection.Example; - -public class LoggingMessageWriter( +public class LoggingMessageWriter( ILogger logger) : IMessageWriter { public void Write(string message) => diff --git a/docs/core/extensions/snippets/configuration/overview/Program.cs b/docs/core/extensions/snippets/configuration/overview/Program.cs new file mode 100644 index 0000000000000..2708ce60a7cc0 --- /dev/null +++ b/docs/core/extensions/snippets/configuration/overview/Program.cs @@ -0,0 +1,40 @@ +HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); + +builder.Services.AddHostedService(); +builder.Services.AddSingleton(); + +using IHost host = builder.Build(); + +host.Run(); + +// +public class MessageWriter : IMessageWriter +{ + public void Write(string message) + { + Console.WriteLine($"MessageWriter.Write(message: \"{message}\")"); + } +} +// + +// +public interface IMessageWriter +{ + void Write(string message); +} +// + +// +public sealed class Worker(IMessageWriter messageWriter) : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + messageWriter.Write($"Worker running at: {DateTimeOffset.Now}"); + await Task.Delay(1_000, stoppingToken); + } + } +} + +// diff --git a/docs/core/extensions/snippets/configuration/dependency-injection/dependency-injection.csproj b/docs/core/extensions/snippets/configuration/overview/dependency-injection.csproj similarity index 86% rename from docs/core/extensions/snippets/configuration/dependency-injection/dependency-injection.csproj rename to docs/core/extensions/snippets/configuration/overview/dependency-injection.csproj index 03469f00496f6..8f86351148293 100644 --- a/docs/core/extensions/snippets/configuration/dependency-injection/dependency-injection.csproj +++ b/docs/core/extensions/snippets/configuration/overview/dependency-injection.csproj @@ -4,7 +4,6 @@ net10.0 enable true - DependencyInjection.Example From 7043da8ace077c85460addd0f7d73a86d6503081 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:56:35 -0800 Subject: [PATCH 2/5] add chaining section --- docs/core/extensions/dependency-injection.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/core/extensions/dependency-injection.md b/docs/core/extensions/dependency-injection.md index 4ad85fb2ce36f..4a8485cbd93a4 100644 --- a/docs/core/extensions/dependency-injection.md +++ b/docs/core/extensions/dependency-injection.md @@ -81,6 +81,10 @@ builder.Services.AddSingleton(); > [!TIP] > The container resolves `ILogger` by taking advantage of [(generic) open types](/dotnet/csharp/language-reference/language-specification/types#843-open-and-closed-types), which eliminates the need to register every [(generic) constructed type](/dotnet/csharp/language-reference/language-specification/types#84-constructed-types). +## Chaining + +It's not unusual to use dependency injection in a chained fashion. Each requested dependency in turn requests its own dependencies. The container resolves the dependencies in the graph and returns the fully resolved service. The collective set of dependencies that must be resolved is typically called a *dependency tree*, *dependency graph*, or *object graph*. + ## Multiple constructor discovery rules When a type defines more than one constructor, the service provider has logic for determining which constructor to use. The constructor with the most parameters where the types are DI-resolvable is selected. Consider the following C# example service: From 9376691b981c7fa745d513bca640730c63d88517 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:02:51 -0800 Subject: [PATCH 3/5] fix errors --- docs/core/extensions/dependency-injection.md | 2 +- docs/core/extensions/snippets/configuration/configuration.sln | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/core/extensions/dependency-injection.md b/docs/core/extensions/dependency-injection.md index 4a8485cbd93a4..a0e0cc0f9b5cb 100644 --- a/docs/core/extensions/dependency-injection.md +++ b/docs/core/extensions/dependency-injection.md @@ -54,7 +54,7 @@ Dependency injection addresses hard-coded dependency problems through: > [!TIP] > In dependency injection terminology, a *service* is typically an object that provides a service to other objects, such as the `IMessageWriter` service. The service isn't related to a web service, although it might use a web service. -As an example, assume the `IMessageWriter` interface defines the `Write` method. This interface is implemented by a concrete type, `MessageWriter`, shown previously. The following sample code registers the `IMessageWriter` service with the concrete type `MessageWriter`. The method registers the service with a [*singleton* lifetime](service-lifetimes.md#singleton), which means it isn't disposed until the app shuts down. +As an example, assume the `IMessageWriter` interface defines the `Write` method. This interface is implemented by a concrete type, `MessageWriter`, shown previously. The following sample code registers the `IMessageWriter` service with the concrete type `MessageWriter`. The method registers the service with a [*singleton* lifetime](#singleton), which means it isn't disposed until the app shuts down. :::code language="csharp" source="snippets/overview/Program.cs" highlight="3-6"::: diff --git a/docs/core/extensions/snippets/configuration/configuration.sln b/docs/core/extensions/snippets/configuration/configuration.sln index 8f7374a168a11..9de9b6cf7419c 100644 --- a/docs/core/extensions/snippets/configuration/configuration.sln +++ b/docs/core/extensions/snippets/configuration/configuration.sln @@ -45,8 +45,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-xml", "console-xml\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "custom-provider", "custom-provider\custom-provider.csproj", "{B366FEA5-FB69-4CE2-87B9-2470FA3C69F1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "dependency-injection", "dependency-injection\dependency-injection.csproj", "{E7F32B1F-46E6-4DAD-8929-F4C301202A70}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "di-anti-patterns", "di-anti-patterns\di-anti-patterns.csproj", "{BEF829E5-EED9-4B9D-8ECE-2DFC3C240946}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "options-action", "options-action\options-action.csproj", "{BBCE4882-E6C2-4017-B5F9-3AF4ED8CADF3}" From dd2a613cf1c82f494c92dbf2a031b19602aca1b4 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:05:39 -0800 Subject: [PATCH 4/5] move snippets folder --- .../snippets/{configuration => }/overview/LoggingMessageWriter.cs | 0 .../extensions/snippets/{configuration => }/overview/Program.cs | 0 .../{configuration => }/overview/dependency-injection.csproj | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename docs/core/extensions/snippets/{configuration => }/overview/LoggingMessageWriter.cs (100%) rename docs/core/extensions/snippets/{configuration => }/overview/Program.cs (100%) rename docs/core/extensions/snippets/{configuration => }/overview/dependency-injection.csproj (100%) diff --git a/docs/core/extensions/snippets/configuration/overview/LoggingMessageWriter.cs b/docs/core/extensions/snippets/overview/LoggingMessageWriter.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/overview/LoggingMessageWriter.cs rename to docs/core/extensions/snippets/overview/LoggingMessageWriter.cs diff --git a/docs/core/extensions/snippets/configuration/overview/Program.cs b/docs/core/extensions/snippets/overview/Program.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/overview/Program.cs rename to docs/core/extensions/snippets/overview/Program.cs diff --git a/docs/core/extensions/snippets/configuration/overview/dependency-injection.csproj b/docs/core/extensions/snippets/overview/dependency-injection.csproj similarity index 100% rename from docs/core/extensions/snippets/configuration/overview/dependency-injection.csproj rename to docs/core/extensions/snippets/overview/dependency-injection.csproj From c23507ece8596fc6a6aea20524bde88de1ccfe10 Mon Sep 17 00:00:00 2001 From: Genevieve Warren <24882762+gewarren@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:14:19 -0800 Subject: [PATCH 5/5] remove unnecessary snippet tags --- docs/core/extensions/snippets/overview/Program.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/core/extensions/snippets/overview/Program.cs b/docs/core/extensions/snippets/overview/Program.cs index 2708ce60a7cc0..09f41bcefd4d3 100644 --- a/docs/core/extensions/snippets/overview/Program.cs +++ b/docs/core/extensions/snippets/overview/Program.cs @@ -17,14 +17,11 @@ public void Write(string message) } // -// public interface IMessageWriter { void Write(string message); } -// -// public sealed class Worker(IMessageWriter messageWriter) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) @@ -36,5 +33,3 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) } } } - -//