diff --git a/.openpublishing.redirection.core.json b/.openpublishing.redirection.core.json index 55f81897ade37..cbaad5629525f 100644 --- a/.openpublishing.redirection.core.json +++ b/.openpublishing.redirection.core.json @@ -765,6 +765,22 @@ "source_path_from_root": "/docs/core/extensions/culture-insensitive-string-operations.md", "redirect_url": "/dotnet/core/extensions/performing-culture-insensitive-string-operations" }, + { + "source_path_from_root": "/docs/core/extensions/dependency-injection.md", + "redirect_url": "/dotnet/core/extensions/dependency-injection/overview" + }, + { + "source_path_from_root": "/docs/core/extensions/dependency-injection-basics.md", + "redirect_url": "/dotnet/core/extensions/dependency-injection/basics" + }, + { + "source_path_from_root": "/docs/core/extensions/dependency-injection-guidelines.md", + "redirect_url": "/dotnet/core/extensions/dependency-injection/guidelines" + }, + { + "source_path_from_root": "/docs/core/extensions/dependency-injection-usage.md", + "redirect_url": "/dotnet/core/extensions/dependency-injection/usage" + }, { "source_path_from_root": "/docs/core/getting-started.md", "redirect_url": "/dotnet/core/get-started", diff --git a/docs/ai/how-to/access-data-in-functions.md b/docs/ai/how-to/access-data-in-functions.md index 291b974d6a2f5..ffec2e2be098a 100644 --- a/docs/ai/how-to/access-data-in-functions.md +++ b/docs/ai/how-to/access-data-in-functions.md @@ -28,7 +28,7 @@ If you manually invoke an by calling < - A dictionary of named arguments. - : An arbitrary `IDictionary` for passing additional ambient data into the function. -- : An that lets the `AIFunction` resolve arbitrary state from a [dependency injection (DI)](../../core/extensions/dependency-injection.md) container. +- : An that lets the `AIFunction` resolve arbitrary state from a [dependency injection (DI)](../../core/extensions/dependency-injection/overview.md) container. If you want to access either the `AIFunctionArguments` or the `IServiceProvider` from within your delegate, create a parameter typed as `IServiceProvider` or `AIFunctionArguments`. That parameter will be bound to the relevant data from the `AIFunctionArguments` passed to `AIFunction.InvokeAsync()`. diff --git a/docs/ai/ichatclient.md b/docs/ai/ichatclient.md index f587670ec83f5..344a9f032b233 100644 --- a/docs/ai/ichatclient.md +++ b/docs/ai/ichatclient.md @@ -136,7 +136,7 @@ For scenarios where you need a different implementation for `GetResponseAsync` a ## Dependency injection - implementations are often provided to an application via [dependency injection (DI)](../core/extensions/dependency-injection.md). In the following example, an is added into the DI container, as is an `IChatClient`. The registration for the `IChatClient` uses a builder that creates a pipeline containing a caching client (which then uses an `IDistributedCache` retrieved from DI) and the sample client. The injected `IChatClient` can be retrieved and used elsewhere in the app. + implementations are often provided to an application via [dependency injection (DI)](../core/extensions/dependency-injection/overview.md). In the following example, an is added into the DI container, as is an `IChatClient`. The registration for the `IChatClient` uses a builder that creates a pipeline containing a caching client (which then uses an `IDistributedCache` retrieved from DI) and the sample client. The injected `IChatClient` can be retrieved and used elsewhere in the app. :::code language="csharp" source="snippets/microsoft-extensions-ai/ConsoleAI.DependencyInjection/Program.cs"::: diff --git a/docs/ai/microsoft-extensions-ai.md b/docs/ai/microsoft-extensions-ai.md index 3e1576eebbe6b..59a8c8e5ddc32 100644 --- a/docs/ai/microsoft-extensions-ai.md +++ b/docs/ai/microsoft-extensions-ai.md @@ -63,6 +63,6 @@ For more samples, see the [dotnet/ai-samples](https://aka.ms/meai-samples) GitHu - [Request a response with structured output](./quickstarts/structured-output.md) - [Build an AI chat app with .NET](./quickstarts/build-chat-app.md) -- [Dependency injection in .NET](../core/extensions/dependency-injection.md) +- [Dependency injection in .NET](../core/extensions/dependency-injection/overview.md) - [Caching in .NET](../core/extensions/caching.md) - [Rate limit an HTTP handler in .NET](../core/extensions/http-ratelimiter.md) diff --git a/docs/architecture/maui/dependency-injection.md b/docs/architecture/maui/dependency-injection.md index 2b93ca36b3775..a41164647b9d7 100644 --- a/docs/architecture/maui/dependency-injection.md +++ b/docs/architecture/maui/dependency-injection.md @@ -26,8 +26,8 @@ private readonly IAppEnvironmentService _appEnvironmentService; public ProfileViewModel( IAppEnvironmentService appEnvironmentService, - IDialogService dialogService, - INavigationService navigationService, + IDialogService dialogService, + INavigationService navigationService, ISettingsService settingsService) : base(dialogService, navigationService, settingsService) { @@ -51,7 +51,7 @@ There are several advantages to using a dependency injection container: In the context of a .NET MAUI app that uses MVVM, a dependency injection container will typically be used for registering and resolving views, registering and resolving view models, and for registering services and injecting them into view models. -There are many dependency injection containers available in .NET; the eShop multi-platform app uses `Microsoft.Extensions.DependencyInjection` to manage the instantiation of views, view models, and service classes in the app. `Microsoft.Extensions.DependencyInjection` facilitates building loosely coupled apps, and provides all of the features commonly found in dependency injection containers, including methods to register type mappings and object instances, resolve objects, manage object lifetimes, and inject dependent objects into constructors of objects that it resolves. For more information about `Microsoft.Extensions.DependencyInjection`, see [Dependency injection in .NET](../../core/extensions/dependency-injection.md). +There are many dependency injection containers available in .NET; the eShop multi-platform app uses `Microsoft.Extensions.DependencyInjection` to manage the instantiation of views, view models, and service classes in the app. `Microsoft.Extensions.DependencyInjection` facilitates building loosely coupled apps, and provides all of the features commonly found in dependency injection containers, including methods to register type mappings and object instances, resolve objects, manage object lifetimes, and inject dependent objects into constructors of objects that it resolves. For more information about `Microsoft.Extensions.DependencyInjection`, see [Dependency injection in .NET](../../core/extensions/dependency-injection/overview.md). In .NET MAUI, the `MauiProgram` class will call into the `CreateMauiApp` method to create a `MauiAppBuilder` object. The `MauiAppBuilder` object has a `Services` property of type `IServiceCollection`, which provides a place to register our components, such as views, view models, and services for dependency injection. Any components registered with the `Services` property will be provided to the dependency injection container when the `MauiAppBuilder.Build` method is called. @@ -82,7 +82,7 @@ public static class MauiProgram public static MauiApp CreateMauiApp() => MauiApp.CreateBuilder() .UseMauiApp() - // Omitted for brevity + // Omitted for brevity .RegisterAppServices() .RegisterViewModels() .RegisterViews() @@ -182,7 +182,7 @@ public partial class FiltersView : ContentPage ``` > [!TIP] -> The dependency injection container is great for creating view model instances. If a view model has dependencies, it will handle the creation and injection of any required services. Just make sure that you register your view models and any dependencies that they may have with the `CreateMauiApp` method in the `MauiProgram` class. +> The dependency injection container is great for creating view model instances. If a view model has dependencies, it will handle the creation and injection of any required services. Just make sure that you register your view models and any dependencies that they may have with the `CreateMauiApp` method in the `MauiProgram` class. ## Summary diff --git a/docs/azure/sdk/dependency-injection.md b/docs/azure/sdk/dependency-injection.md index 76220301d3a57..fb1f859f675d3 100644 --- a/docs/azure/sdk/dependency-injection.md +++ b/docs/azure/sdk/dependency-injection.md @@ -8,7 +8,7 @@ ms.date: 04/25/2025 # Dependency injection with the Azure SDK for .NET -This article demonstrates how to register Azure service clients from the [latest Azure client libraries for .NET](https://azure.github.io/azure-sdk/releases/latest/index.html#net) for [dependency injection in a .NET app](../../core/extensions/dependency-injection.md). Every modern .NET app starts up by using the instructions provided in a *Program.cs* file. +This article demonstrates how to register Azure service clients from the [latest Azure client libraries for .NET](https://azure.github.io/azure-sdk/releases/latest/index.html#net) for [dependency injection in a .NET app](../../core/extensions/dependency-injection/overview.md). Every modern .NET app starts up by using the instructions provided in a *Program.cs* file. ## Install packages @@ -66,7 +66,7 @@ In the preceding code: ## Use the registered clients -With the clients registered, as described in the [Register clients and subclients](#register-clients-and-subclients) section, you can now use them. In the following example, [constructor injection](../../core/extensions/dependency-injection.md#constructor-injection-behavior) is used to obtain the Blob Storage client and a factory for Service Bus sender subclients in an ASP.NET Core API controller: +With the clients registered, as described in the [Register clients and subclients](#register-clients-and-subclients) section, you can now use them. In the following example, [constructor injection](../../core/extensions/dependency-injection/overview.md#constructor-injection-behavior) is used to obtain the Blob Storage client and a factory for Service Bus sender subclients in an ASP.NET Core API controller: ```csharp [ApiController] @@ -75,7 +75,7 @@ public class MyApiController : ControllerBase { private readonly BlobServiceClient _blobServiceClient; private readonly ServiceBusSender _serviceBusSender; - + public MyApiController( BlobServiceClient blobServiceClient, IAzureClientFactory senderFactory) @@ -83,11 +83,11 @@ public class MyApiController : ControllerBase _blobServiceClient = blobServiceClient; _serviceBusSender = senderFactory.CreateClient("myQueueName"); } - + [HttpGet] public async Task> Get() { - BlobContainerClient containerClient = + BlobContainerClient containerClient = _blobServiceClient.GetBlobContainerClient("demo"); var results = new List(); @@ -270,7 +270,7 @@ At some point, you may want to change the default settings for a service client. ``` You can change the retry policy to suit your needs like so: - + ```csharp builder.Services.AddAzureClients(clientBuilder => { diff --git a/docs/azure/sdk/thread-safety.md b/docs/azure/sdk/thread-safety.md index 808337c510fc7..f82372e55ae96 100644 --- a/docs/azure/sdk/thread-safety.md +++ b/docs/azure/sdk/thread-safety.md @@ -117,4 +117,4 @@ Further guidance for properly managing and disposing of `HttpClient` instances c ## See also - [Dependency injection with the Azure SDK for .NET](./dependency-injection.md) -- [Dependency injection in .NET](../../core/extensions/dependency-injection.md) +- [Dependency injection in .NET](../../core/extensions/dependency-injection/overview.md) diff --git a/docs/azure/sdk/unit-testing-mocking.md b/docs/azure/sdk/unit-testing-mocking.md index d347fa527f539..23552038852cb 100644 --- a/docs/azure/sdk/unit-testing-mocking.md +++ b/docs/azure/sdk/unit-testing-mocking.md @@ -263,6 +263,6 @@ Here's how it works: ## See also -* [Dependency injection in .NET](../../core/extensions/dependency-injection.md) +* [Dependency injection in .NET](../../core/extensions/dependency-injection/overview.md) * [Unit testing best practices](../../core/testing/unit-testing-best-practices.md) * [Unit testing C# in .NET using dotnet test and xUnit](../../core/testing/unit-testing-csharp-with-xunit.md) diff --git a/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md index b59f61276b67e..16abacdc9d379 100644 --- a/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md +++ b/docs/core/compatibility/extensions/10.0/getkeyedservice-anykey.md @@ -21,7 +21,7 @@ Calling `GetKeyedServices()` with `KeyedService.AnyKey` returned all registratio ## New behavior -Starting in .NET 10, calling `GetKeyedService()` with `KeyedService.AnyKey` throws an . This ensures that `AnyKey` can't be used to resolve a single service, as it's [intended to represent a special case](../../../extensions/dependency-injection.md#keyedserviceanykey-property) rather than a specific key. +Starting in .NET 10, calling `GetKeyedService()` with `KeyedService.AnyKey` throws an . This ensures that `AnyKey` can't be used to resolve a single service, as it's [intended to represent a special case](../../../extensions/dependency-injection/overview.md#keyedserviceanykey-property) rather than a specific key. ```csharp var service = serviceProvider.GetKeyedService(typeof(IMyService), KeyedService.AnyKey); @@ -57,4 +57,4 @@ If you use `GetKeyedService()` or `GetKeyedServices()` with `KeyedService.AnyKey ## See also -- [Use KeyedService.AnyKey for fallbacks](../../../extensions/dependency-injection.md#keyedserviceanykey-property) +- [Use KeyedService.AnyKey for fallbacks](../../../extensions/dependency-injection/overview.md#keyedserviceanykey-property) diff --git a/docs/core/extensions/caching.md b/docs/core/extensions/caching.md index a3b6043c4ebcc..5be50b2aedaa1 100644 --- a/docs/core/extensions/caching.md +++ b/docs/core/extensions/caching.md @@ -177,7 +177,7 @@ Since the absolute expiration ( runs independent (or in the background) from the other application code. When an application starts running that hosts an implementation of the , the corresponding implementation (in this case the `BackgroundService` or "worker") start running in the same process. These hosted services are registered with DI as singletons, through the extension method. Other services can be registered with DI with any [service lifetime](dependency-injection.md#service-lifetimes). +One common strategy for caching data, is updating the cache independently from the consuming data services. The *Worker Service* template is a great example, as the runs independent (or in the background) from the other application code. When an application starts running that hosts an implementation of the , the corresponding implementation (in this case the `BackgroundService` or "worker") start running in the same process. These hosted services are registered with DI as singletons, through the extension method. Other services can be registered with DI with any [service lifetime](dependency-injection/service-lifetimes.md). > [!IMPORTANT] > The service lifetime's are very important to understand. When you call to register all of the in-memory caching services, the services are registered as singletons. @@ -321,7 +321,7 @@ To delete values in the distributed cache, call one of the remove APIs: ## See also -- [Dependency injection in .NET](dependency-injection.md) +- [Dependency injection in .NET](dependency-injection/overview.md) - [.NET Generic Host](generic-host.md) - [Worker Services in .NET](workers.md) - [Azure for .NET developers](../../azure/index.yml) diff --git a/docs/core/extensions/configuration.md b/docs/core/extensions/configuration.md index 36e5d6ae9b72a..e776960db98b6 100644 --- a/docs/core/extensions/configuration.md +++ b/docs/core/extensions/configuration.md @@ -62,7 +62,7 @@ Adding a configuration provider overrides previous configuration values. For exa ### Binding -One of the key advantages of using the .NET configuration abstractions is the ability to bind configuration values to instances of .NET objects. For example, the JSON configuration provider can be used to map *appsettings.json* files to .NET objects and is used with [dependency injection](dependency-injection.md). This enables the [options pattern](options.md), which uses classes to provide strongly typed access to groups of related settings. The default binder is reflection-based, but there's a [source generator alternative](configuration-generator.md) that's easy to enable. +One of the key advantages of using the .NET configuration abstractions is the ability to bind configuration values to instances of .NET objects. For example, the JSON configuration provider can be used to map *appsettings.json* files to .NET objects and is used with [dependency injection](dependency-injection/overview.md). This enables the [options pattern](options.md), which uses classes to provide strongly typed access to groups of related settings. The default binder is reflection-based, but there's a [source generator alternative](configuration-generator.md) that's easy to enable. .NET configuration provides various abstractions. Consider the following interfaces: diff --git a/docs/core/extensions/custom-configuration-provider.md b/docs/core/extensions/custom-configuration-provider.md index b12dffbcb5d21..8ec00a47b6d27 100644 --- a/docs/core/extensions/custom-configuration-provider.md +++ b/docs/core/extensions/custom-configuration-provider.md @@ -75,4 +75,4 @@ The preceding code configures the `WidgetOptions` object from the `"WidgetOption - [Configuration in .NET](configuration.md) - [Configuration providers in .NET](configuration-providers.md) - [Options pattern in .NET](options.md) -- [Dependency injection in .NET](dependency-injection.md) +- [Dependency injection in .NET](dependency-injection/overview.md) diff --git a/docs/core/extensions/custom-logging-provider.md b/docs/core/extensions/custom-logging-provider.md index b881d06a31487..88c1a9e25edd1 100644 --- a/docs/core/extensions/custom-logging-provider.md +++ b/docs/core/extensions/custom-logging-provider.md @@ -96,5 +96,5 @@ Running this simple application will render color output to the console window s - [Logging in .NET](logging.md) - [Logging providers in .NET](logging-providers.md) -- [Dependency injection in .NET](dependency-injection.md) +- [Dependency injection in .NET](dependency-injection/overview.md) - [High-performance logging in .NET](high-performance-logging.md) diff --git a/docs/core/extensions/dependency-injection.md b/docs/core/extensions/dependency-injection.md deleted file mode 100644 index 5e4e96efb0d62..0000000000000 --- a/docs/core/extensions/dependency-injection.md +++ /dev/null @@ -1,464 +0,0 @@ ---- -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#. -ms.date: 10/21/2025 -ms.topic: overview -ai-usage: ai-assisted ---- - -# .NET dependency injection - -.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: - -```csharp -public class MessageWriter -{ - public void Write(string message) - { - Console.WriteLine($"MessageWriter.Write(message: \"{message}\")"); - } -} -``` - -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 -{ - private readonly MessageWriter _messageWriter = new(); - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}"); - await Task.Delay(1_000, stoppingToken); - } - } -} -``` - -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: - -- 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 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. - -As an example, the `IMessageWriter` interface defines the `Write` method: - -:::code language="csharp" source="snippets/configuration/dependency-injection/IMessageWriter.cs"::: - -This interface is implemented by a concrete type, `MessageWriter`: - -:::code language="csharp" source="snippets/configuration/dependency-injection/MessageWriter.cs"::: - -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. - -:::code language="csharp" source="snippets/configuration/dependency-injection/Program.cs" highlight="5-8"::: - -In the preceding code, the sample app: - -- 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. - -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 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. - -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: - -```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. - -## 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: - -```csharp -public class ExampleService -{ - public ExampleService() - { - } - - public ExampleService(ILogger logger) - { - // omitted for brevity - } - - public ExampleService(FooService fooService, BarService barService) - { - // omitted for brevity - } -} -``` - -In the preceding code, assume that logging has been added and is resolvable from the service provider but the `FooService` and `BarService` types aren't. The constructor with the `ILogger` parameter resolves the `ExampleService` instance. Even though there's a constructor that defines more parameters, the `FooService` and `BarService` types aren't DI-resolvable. - -If there's ambiguity when discovering constructors, an exception is thrown. Consider the following C# example service: - -```csharp -public class ExampleService -{ - public ExampleService() - { - } - - public ExampleService(ILogger logger) - { - // omitted for brevity - } - - public ExampleService(IOptions options) - { - // omitted for brevity - } -} -``` - -> [!WARNING] -> The `ExampleService` code with ambiguous DI-resolvable type parameters throws an exception. **Don't** do this—it's intended to show what is meant by "ambiguous DI-resolvable types". - -In the preceding example, there are three constructors. The first constructor is parameterless and requires no services from the service provider. Assume that both logging and options have been added to the DI container and are DI-resolvable services. When the DI container attempts to resolve the `ExampleService` type, it throws an exception, as the two constructors are ambiguous. - -Avoid ambiguity by defining a constructor that accepts both DI-resolvable types instead: - -```csharp -public class ExampleService -{ - public ExampleService() - { - } - - public ExampleService( - ILogger logger, - IOptions options) - { - // omitted for brevity - } -} -``` - -## Register groups of services with extension methods - -.NET uses a convention for registering a group of related services. The convention is to use a single `Add{GROUP_NAME}` extension method to register all of the services required by a framework feature. For example, the extension method registers all of the services required for using options. - -## Framework-provided services - -When using any of the available host or app builder patterns, defaults are applied and services are registered by the framework. Consider some of the most popular host and app builder patterns: - -- -- -- -- -- -- - -After creating a builder from any of these APIs, the `IServiceCollection` has services defined by the framework, depending on [how you configured the host](generic-host.md#host-configuration). For apps based on the .NET templates, the framework could register hundreds of services. - -The following table lists a small sample of these framework-registered services: - -| Service type | Lifetime | -|------------------------------------------------------------------------------------|-----------| -| | Singleton | -| | Singleton | -| | Singleton | -| | Singleton | -| | Singleton | -| | Transient | -| | Singleton | -| | Singleton | -| | Singleton | - -## Service lifetimes - -Services can be registered with one of the following lifetimes: - -- [Transient](#transient) -- [Scoped](#scoped) -- [Singleton](#singleton) - -The following sections describe each of the preceding lifetimes. Choose an appropriate lifetime for each registered service. - -### Transient - -Transient lifetime services are created each time they're requested from the service container. To register a service as _transient_, call . - -In apps that process requests, transient services are disposed at the end of the request. This lifetime incurs per/request allocations, as services are resolved and constructed every time. For more information, see [Dependency Injection Guidelines: IDisposable guidance for transient and shared instances](dependency-injection-guidelines.md#idisposable-guidance-for-transient-and-shared-instances). - -### Scoped - -For web applications, a scoped lifetime indicates that services are created once per client request (connection). Register scoped services with . - -In apps that process requests, scoped services are disposed at the end of the request. - -> [!NOTE] -> When using Entity Framework Core, the extension method registers `DbContext` types with a scoped lifetime by default. - -A scoped service should always be used from within a scope—either an implicit scope (such as ASP.NET Core's per-request scope) or an explicit scope created with . - -Do ***not*** resolve a scoped service directly from a singleton using constructor injection or by requesting it from in the singleton. Doing so causes the scoped service to behave like a singleton, which can lead to incorrect state when processing subsequent requests. - -It's acceptable to resolve a scoped service within a singleton if you create and use an explicit scope with . - -It's also fine to: - -- Resolve a singleton service from a scoped or transient service. -- Resolve a scoped service from another scoped or transient service. - -By default, in the development environment, resolving a service from another service with a longer lifetime throws an exception. For more information, see [Scope validation](#scope-validation). - -### Singleton - -Singleton lifetime services are created either: - -- The first time they're requested. -- By the developer, when providing an implementation instance directly to the container. This approach is rarely needed. - -Every subsequent request of the service implementation from the dependency injection container uses the same instance. If the app requires singleton behavior, allow the service container to manage the service's lifetime. Don't implement the singleton design pattern and provide code to dispose of the singleton. Services should never be disposed by code that resolved the service from the container. If a type or factory is registered as a singleton, the container disposes the singleton automatically. - -Register singleton services with . Singleton services must be thread safe and are often used in stateless services. - -In apps that process requests, singleton services are disposed when the is disposed on application shutdown. Because memory isn't released until the app shuts down, consider memory use with a singleton service. - -## Service registration methods - -The framework provides service registration extension methods that are useful in specific scenarios: - -| Method | Automatic
object
disposal | Multiple
implementations | Pass args | -|--------|:-------------------------------:|:---------------------------:|:---------:| -| `Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()`

Example:

`services.AddSingleton();` | Yes | Yes | No | -| `Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})`

Examples:

`services.AddSingleton(sp => new MyDep());`
`services.AddSingleton(sp => new MyDep(99));` | Yes | Yes | Yes | -| `Add{LIFETIME}<{IMPLEMENTATION}>()`

Example:

`services.AddSingleton();` | Yes | No | No | -| `AddSingleton<{SERVICE}>(new {IMPLEMENTATION})`

Examples:

`services.AddSingleton(new MyDep());`
`services.AddSingleton(new MyDep(99));` | No | Yes | Yes | -| `AddSingleton(new {IMPLEMENTATION})`

Examples:

`services.AddSingleton(new MyDep());`
`services.AddSingleton(new MyDep(99));` | No | No | Yes | - -For more information on type disposal, see the [Disposal of services](dependency-injection-guidelines.md#disposal-of-services) section. - -Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. For example, consider the following code: - -```csharp -services.AddSingleton(); -``` - -This is equivalent to registering the service with both the service and implementation of the same types: - -```csharp -services.AddSingleton(); -``` - -This equivalency is why multiple implementations of a service can't be registered using the methods that don't take an explicit service type. These methods can register multiple *instances* of a service, but they all have the same *implementation* type. - -Any of the service registration methods can be used to register multiple service instances of the same service type. In the following example, `AddSingleton` is called twice with `IMessageWriter` as the service type. The second call to `AddSingleton` overrides the previous one when resolved as `IMessageWriter` and adds to the previous one when multiple services are resolved via `IEnumerable`. Services appear in the order they were registered when resolved via `IEnumerable<{SERVICE}>`. - -:::code language="csharp" source="snippets/configuration/console-di-ienumerable/Program.cs" highlight="11-16"::: - -The preceding sample source code registers two implementations of the `IMessageWriter`. - -:::code language="csharp" source="snippets/configuration/console-di-ienumerable/ExampleService.cs" highlight="7-16"::: - -The `ExampleService` defines two constructor parameters; a single `IMessageWriter`, and an `IEnumerable`. The single `IMessageWriter` is the last implementation to be registered, whereas the `IEnumerable` represents all registered implementations. - -The framework also provides `TryAdd{LIFETIME}` extension methods, which register the service only if there isn't already an implementation registered. - -In the following example, the call to `AddSingleton` registers `ConsoleMessageWriter` as an implementation for `IMessageWriter`. The call to `TryAddSingleton` has no effect because `IMessageWriter` already has a registered implementation: - -```csharp -services.AddSingleton(); -services.TryAddSingleton(); -``` - -The `TryAddSingleton` has no effect, as it was already added and the "try" fails. The `ExampleService` asserts the following: - -```csharp -public class ExampleService -{ - public ExampleService( - IMessageWriter messageWriter, - IEnumerable messageWriters) - { - Trace.Assert(messageWriter is ConsoleMessageWriter); - Trace.Assert(messageWriters.Single() is ConsoleMessageWriter); - } -} -``` - -For more information, see: - -- -- -- -- - -The [TryAddEnumerable(ServiceDescriptor)](xref:Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable%2A) methods register the service only if there isn't already an implementation *of the same type*. Multiple services are resolved via `IEnumerable<{SERVICE}>`. When registering services, add an instance if one of the same types wasn't already added. Library authors use `TryAddEnumerable` to avoid registering multiple copies of an implementation in the container. - -In the following example, the first call to `TryAddEnumerable` registers `MessageWriter` as an implementation for `IMessageWriter1`. The second call registers `MessageWriter` for `IMessageWriter2`. The third call has no effect because `IMessageWriter1` already has a registered implementation of `MessageWriter`: - -```csharp -public interface IMessageWriter1 { } -public interface IMessageWriter2 { } - -public class MessageWriter : IMessageWriter1, IMessageWriter2 -{ -} - -services.TryAddEnumerable(ServiceDescriptor.Singleton()); -services.TryAddEnumerable(ServiceDescriptor.Singleton()); -services.TryAddEnumerable(ServiceDescriptor.Singleton()); -``` - -Service registration is order-independent except when registering multiple implementations of the same type. - -`IServiceCollection` is a collection of objects. The following example shows how to register a service by creating and adding a `ServiceDescriptor`: - -```csharp -string secretKey = Configuration["SecretKey"]; -var descriptor = new ServiceDescriptor( - typeof(IMessageWriter), - _ => new DefaultMessageWriter(secretKey), - ServiceLifetime.Transient); - -services.Add(descriptor); -``` - -The built-in `Add{LIFETIME}` methods use the same approach. For example, see the [AddScoped source code](https://github.com/dotnet/extensions/blob/v3.1.8/src/DependencyInjection/DI.Abstractions/src/ServiceCollectionServiceExtensions.cs#L216-L237). - -### Constructor injection behavior - -Services can be resolved using or . `ActivatorUtilities` creates objects that aren't registered in the container and is used with some framework features. - -Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values. - -When `IServiceProvider` or `ActivatorUtilities` resolve services, constructor injection requires a *public* constructor. - -When `ActivatorUtilities` resolves services, constructor injection requires that only one applicable constructor exists. Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection. - -## Scope validation - -When the app runs in the `Development` environment and calls [CreateApplicationBuilder](generic-host.md#host-builder-settings) to build the host, the default service provider performs checks to verify that: - -- Scoped services aren't resolved from the root service provider. -- Scoped services aren't injected into singletons. - -The root service provider is created when is called. The root service provider's lifetime corresponds to the app's lifetime when the provider starts with the app and is disposed when the app shuts down. - -Scoped services are disposed by the container that created them. If a scoped service is created in the root container, the service's lifetime is effectively promoted to singleton because it's only disposed by the root container when the app shuts down. Validating service scopes catches these situations when `BuildServiceProvider` is called. - -## Scope scenarios - -The is always registered as a singleton, but the can vary based on the lifetime of the containing class. For example, if you resolve services from a scope, and any of those services take an , it's a scoped instance. - -To achieve scoping services within implementations of , such as the , *don't* inject the service dependencies via constructor injection. Instead, inject , create a scope, then resolve dependencies from the scope to use the appropriate service lifetime. - -:::code language="csharp" source="snippets/configuration/worker-scope/Worker.cs"::: - -In the preceding code, while the app is running, the background service: - -- Depends on the . -- Creates an for resolving other services. -- Resolves scoped services for consumption. -- Works on processing objects and then relaying them, and finally marks them as processed. - -From the sample source code, you can see how implementations of can benefit from scoped service lifetimes. - -## Keyed services - -You can register services and perform lookups based on a key. In other words, it's possible to register multiple services with different keys and use this key for the lookup. - -For example, consider the case where you have different implementations of the interface `IMessageWriter`: `MemoryMessageWriter` and `QueueMessageWriter`. - -You can register these services using the overload of the service registration methods (seen earlier) that supports a key as a parameter: - -```csharp -services.AddKeyedSingleton("memory"); -services.AddKeyedSingleton("queue"); -``` - -The `key` isn't limited to `string`. The `key` can be any `object` you want, as long as the type correctly implements `Equals`. - -In the constructor of the class that uses `IMessageWriter`, you add the to specify the key of the service to resolve: - -```csharp -public class ExampleService -{ - public ExampleService( - [FromKeyedServices("queue")] IMessageWriter writer) - { - // Omitted for brevity... - } -} -``` - -### KeyedService.AnyKey property - -The property provides a special key for working with keyed services. You can register a service using `KeyedService.AnyKey` as a fallback that matches any key. This is useful when you want to provide a default implementation for any key that doesn't have an explicit registration. - -:::code language="csharp" source="snippets/di/anykey/csharp/AnyKeyExamples/Program.cs" id="FallbackRegistration"::: - -In the preceding example: - -- Requesting `ICache` with key `"premium"` returns the `PremiumCache` instance. -- Requesting `ICache` with any other key (like `"basic"` or `"standard"`) creates a new `DefaultCache` using the `AnyKey` fallback. - -> [!IMPORTANT] -> Starting in .NET 10, calling `GetKeyedService()` with `KeyedService.AnyKey` throws an because `AnyKey` is intended as a registration fallback, not as a query key. For more information, see [Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey](../compatibility/extensions/10.0/getkeyedservice-anykey.md). - -## See also - -- [Quickstart: Dependency injection basics](dependency-injection-basics.md) -- [Tutorial: Use dependency injection in .NET](dependency-injection-usage.md) -- [Dependency injection guidelines](dependency-injection-guidelines.md) -- [Dependency injection in ASP.NET Core](/aspnet/core/fundamentals/dependency-injection) -- [NDC Conference Patterns for DI app development](https://www.youtube.com/watch?v=x-C-CNBVTaY) -- [Explicit dependencies principle](../../architecture/modern-web-apps-azure/architectural-principles.md#explicit-dependencies) -- [Inversion of control containers and the dependency injection pattern (Martin Fowler)](https://www.martinfowler.com/articles/injection.html) diff --git a/docs/core/extensions/dependency-injection-basics.md b/docs/core/extensions/dependency-injection/basics.md similarity index 78% rename from docs/core/extensions/dependency-injection-basics.md rename to docs/core/extensions/dependency-injection/basics.md index 4b68752f25da1..208be8ac339de 100644 --- a/docs/core/extensions/dependency-injection-basics.md +++ b/docs/core/extensions/dependency-injection/basics.md @@ -1,16 +1,17 @@ --- -title: Dependency injection basics +title: Dependency injection basics quickstart description: Learn how to use dependency injection (DI) in your .NET apps with this simple example. Follow along with this pragmatic guide to understand DI basics in C#. ms.date: 01/22/2025 no-loc: [Transient, Scoped, Singleton, Example] +ms.topic: tutorial --- -# Understand dependency injection basics in .NET +# Quickstart: Dependency injection basics in .NET -In this article, you create a .NET console app that manually creates a and corresponding . You learn how to register services and resolve them using dependency injection (DI). This article uses the [Microsoft.Extensions.DependencyInjection](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection) NuGet package to demonstrate the basics of DI in .NET. +In this quickstart, you create a .NET console app that manually creates a and corresponding . You learn how to register services and resolve them using dependency injection (DI). This article uses the [Microsoft.Extensions.DependencyInjection](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection) NuGet package to demonstrate the basics of DI in .NET. > [!NOTE] -> This article doesn't take advantage of the [Generic Host](generic-host.md) features. For a more comprehensive guide, see [Use dependency injection in .NET](dependency-injection-usage.md). +> This article doesn't take advantage of the [Generic host](../generic-host.md) features. For a more comprehensive guide, see [Use dependency injection in .NET](usage.md). ## Get started @@ -18,15 +19,15 @@ To get started, create a new .NET console application named **DI.Basics**. Some - [Visual Studio: **File > New > Project**](/visualstudio/get-started/csharp/tutorial-console) menu. - [Visual Studio Code](https://code.visualstudio.com/) and the [C# Dev Kit extension's](https://code.visualstudio.com/docs/csharp/project-management): **Solution Explorer** menu option. -- [.NET CLI: `dotnet new console`](../tools/dotnet-new-sdk-templates.md#console) command in the terminal. +- [.NET CLI: `dotnet new console`](../../tools/dotnet-new-sdk-templates.md#console) command in the terminal. You need to add the package reference to the [Microsoft.Extensions.DependencyInjection](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection) in the project file. Regardless of the approach, ensure the project resembles the following XML of the _DI.Basics.csproj_ file: -:::code language="XML" source="snippets/di/di-basics/di-basics.csproj"::: +:::code language="XML" source="snippets/basics/di-basics.csproj"::: ## Dependency injection basics -Dependency injection is a design pattern that allows you to remove hard-coded dependencies and make your application more maintainable and testable. DI 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 is a design pattern that allows you to remove hard-coded dependencies and make your application more maintainable and testable. DI is a technique for achieving [Inversion of Control (IoC)](../../../architecture/modern-web-apps-azure/architectural-principles.md#dependency-inversion) between classes and their dependencies. The abstractions for DI in .NET are defined in the [Microsoft.Extensions.DependencyInjection.Abstractions](https://www.nuget.org/packages/Microsoft.Extensions.DependencyInjection.Abstractions) NuGet package: @@ -38,17 +39,17 @@ In .NET, DI is managed by adding services and configuring them in an `IServiceCo ## Create example services -Not all services are created equally. Some services require a new instance each time that the service container gets them (_transient_), while others should be shared across requests (_scoped_) or for the entire lifetime of the app (_singleton_). For more information on service lifetimes, see [Service lifetimes](dependency-injection.md#service-lifetimes). +Not all services are created equally. Some services require a new instance each time that the service container gets them (_transient_), while others should be shared across requests (_scoped_) or for the entire lifetime of the app (_singleton_). For more information on service lifetimes, see [Service lifetimes](service-lifetimes.md). Likewise, some services only expose a concrete type, while others are expressed as a contract between an interface and an implementation type. You create several variations of services to help demonstrate these concepts. Create a new C# file named _IConsole.cs_ and add the following code: -:::code source="snippets/di/di-basics/IConsole.cs"::: +:::code source="snippets/basics/IConsole.cs"::: This file defines an `IConsole` interface that exposes a single method, `WriteLine`. Next, create a new C# file named _DefaultConsole.cs_ and add the following code: -:::code source="snippets/di/di-basics/DefaultConsole.cs"::: +:::code source="snippets/basics/DefaultConsole.cs"::: The preceding code represents the default implementation of the `IConsole` interface. The `WriteLine` method conditionally writes to the console based on the `IsEnabled` property. @@ -57,11 +58,11 @@ The preceding code represents the default implementation of the `IConsole` inter Next, create an _IGreetingService.cs_ file and add the following C# code: -:::code source="snippets/di/di-basics/IGreetingService.cs"::: +:::code source="snippets/basics/IGreetingService.cs"::: Then add a new C# file named _DefaultGreetingService.cs_ and add the following code: -:::code source="snippets/di/di-basics/DefaultGreetingService.cs"::: +:::code source="snippets/basics/DefaultGreetingService.cs"::: The preceding code represents the default implementation of the `IGreetingService` interface. The service implementation requires an `IConsole` as a primary constructor parameter. The `Greet` method: @@ -71,7 +72,7 @@ The preceding code represents the default implementation of the `IGreetingServic The last service to create is the _FarewellService.cs_ file, add the following C# code before continuing: -:::code source="snippets/di/di-basics/FarewellService.cs"::: +:::code source="snippets/basics/FarewellService.cs"::: The `FarewellService` represents a concrete type, not an interface. It should be declared as `public` to make it accessible to consumers. Unlike other service implementation types that were declared as `internal` and `sealed`, this code demonstrates that not all services need to be interfaces. It also shows that service implementations can be `sealed` to prevent inheritance and `internal` to restrict access to the assembly. @@ -79,7 +80,7 @@ The `FarewellService` represents a concrete type, not an interface. It should be Open the _Program.cs_ file and replace the existing code with the following C# code: -:::code source="snippets/di/di-basics/Program.cs"::: +:::code source="snippets/basics/Program.cs"::: The preceding updated code demonstrates the how-to: @@ -101,7 +102,7 @@ All of these services are registered as singletons, although for this sample, it ## Run the sample app -To run the sample app, either press F5 in Visual Studio, Visual Studio Code, or run the `dotnet run` command in the terminal. When the app completes, you should see the following output: +To run the sample app, either press F5 in Visual Studio or Visual Studio Code, or run the `dotnet run` command in the terminal. When the app completes, you should see the following output: ```console Hello, David! @@ -120,20 +121,20 @@ These methods are convenience methods that create a dependency via DI doesn't require that the receiver implement itself. The receiver of the dependency shouldn't call on that dependency. - Use scopes to control the lifetimes of services. Scopes aren't hierarchical, and there's no special connection among scopes. -For more information on resource cleanup, see [Implement a `Dispose` method](../../standard/garbage-collection/implementing-dispose.md) or [Implement a `DisposeAsync` method](../../standard/garbage-collection/implementing-disposeasync.md). Additionally, consider the [Disposable transient services captured by container](#disposable-transient-services-captured-by-container) scenario as it relates to resource cleanup. +For more information on resource cleanup, see [Implement a `Dispose` method](../../../standard/garbage-collection/implementing-dispose.md) or [Implement a `DisposeAsync` method](../../../standard/garbage-collection/implementing-disposeasync.md). Additionally, consider the [Disposable transient services captured by container](#disposable-transient-services-captured-by-container) scenario as it relates to resource cleanup. ## Default service container replacement @@ -138,24 +138,24 @@ The following third-party containers can be used with ASP.NET Core apps: Create thread-safe singleton services. If a singleton service has a dependency on a transient service, the transient service might also require thread safety depending on how it's used by the singleton. The factory method of a singleton service, such as the second argument to [AddSingleton\(IServiceCollection, Func\)](xref:Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddSingleton%2A), doesn't need to be thread-safe. Like a type (`static`) constructor, it's guaranteed to be called only once by a single thread. -Additionally, the process of resolving services from the built-in .NET dependency injection container is thread-safe. +Additionally, the process of resolving services from the built-in .NET dependency injection container is thread-safe. Once an `IServiceProvider` or `IServiceScope` has been built, it's safe to resolve services concurrently from multiple threads. > [!NOTE] -> Thread safety of the DI container itself only guarantees that constructing and resolving services is safe. It doesn't make the resolved service instances themselves thread-safe. +> Thread safety of the DI container itself only guarantees that constructing and resolving services is safe. It doesn't make the resolved service instances themselves thread-safe. > Any service (especially singletons) that holds shared mutable state must implement its own synchronization logic if accessed concurrently. ## Recommendations - `async/await` and `Task` based service resolution isn't supported. Because C# doesn't support asynchronous constructors, use asynchronous methods after synchronously resolving the service. - Avoid storing data and configuration directly in the service container. For example, a user's shopping cart shouldn't typically be added to the service container. Configuration should use the options pattern. Similarly, avoid "data holder" objects that only exist to allow access to another object. It's better to request the actual item via DI. -- Avoid static access to services. For example, avoid capturing [IApplicationBuilder.ApplicationServices](xref:Microsoft.AspNetCore.Builder.IApplicationBuilder.ApplicationServices) as a static field or property for use elsewhere. +- Avoid static access to services. For example, avoid capturing as a static field or property for use elsewhere. - Keep [DI factories](#async-di-factories-can-cause-deadlocks) fast and synchronous. - Avoid using the [*service locator pattern*](#scoped-service-as-singleton). For example, don't invoke to obtain a service instance when you can use DI instead. -- Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Both of these practices mix [Inversion of Control](/dotnet/standard/modern-web-apps-azure-architecture/architectural-principles#dependency-inversion) strategies. +- Another service locator variation to avoid is injecting a factory that resolves dependencies at runtime. Both of these practices mix [Inversion of Control](../../../architecture/modern-web-apps-azure/architectural-principles.md#dependency-inversion) strategies. - Avoid calls to when configuring services. Calling `BuildServiceProvider` typically happens when the developer wants to resolve a service when registering another service. Instead, use an overload that includes the `IServiceProvider` for this reason. - [Disposable transient services are captured](#disposable-transient-services-captured-by-container) by the container for disposal. This can turn into a memory leak if resolved from the top-level container. -- Enable scope validation to make sure the app doesn't have singletons that capture scoped services. For more information, see [Scope validation](dependency-injection.md#scope-validation). +- Enable scope validation to make sure the app doesn't have singletons that capture scoped services. For more information, see [Scope validation](overview.md#scope-validation). - Only use singleton lifetime for services with their own state that is expensive to create or globally shared. Avoid using singleton lifetime for services that have no state themself. Most .NET IoC containers use "Transient" as the default scope. Considerations and drawbacks of singletons: - **Thread safety**: A singleton must be implemented in a thread-safe way. - **Coupling**: It can couple otherwise unrelated requests. @@ -185,7 +185,7 @@ When you register *transient* services that implement , In the preceding anti-pattern, 1,000 `ExampleDisposable` objects are instantiated and rooted. They won't be disposed of until the `serviceProvider` instance is disposed. -For more information on debugging memory leaks, see [Debug a memory leak in .NET](../diagnostics/debug-memory-leak.md). +For more information on debugging memory leaks, see [Debug a memory leak in .NET](../../diagnostics/debug-memory-leak.md). ### Async DI factories can cause deadlocks @@ -197,7 +197,7 @@ In the preceding code, the `implementationFactory` is given a lambda expression :::image type="content" source="media/deadlock-with-async-factory-01.png" lightbox="media/deadlock-with-async-factory-01.png" alt-text="Anti-pattern: Deadlock with async factory inner issue. Do not copy!"::: -For more information on asynchronous guidance, see [Asynchronous programming: Important info and advice](../../csharp/asynchronous-programming/async-scenarios.md#review-considerations-for-asynchronous-programming). For more information debugging deadlocks, see [Debug a deadlock in .NET](../diagnostics/debug-deadlock.md). +For more information on asynchronous guidance, see [Asynchronous programming: Important info and advice](../../../csharp/asynchronous-programming/async-scenarios.md#review-considerations-for-asynchronous-programming). For more information debugging deadlocks, see [Debug a deadlock in .NET](../../diagnostics/debug-deadlock.md). When you're running this anti-pattern and the deadlock occurs, you can view the two threads waiting from Visual Studio's Parallel Stacks window. For more information, see [View threads and tasks in the Parallel Stacks window](/visualstudio/debugger/using-the-parallel-stacks-window). @@ -209,11 +209,11 @@ The term ["captive dependency"](https://blog.ploeh.dk/2014/06/02/captive-depende In the preceding code, `Foo` is registered as a singleton and `Bar` is scoped - which on the surface seems valid. However, consider the implementation of `Foo`. -:::code language="csharp" source="snippets/configuration/di-anti-patterns/Foo.cs"::: +:::code language="csharp" source="snippets/anti-patterns/Foo.cs"::: The `Foo` object requires a `Bar` object, and since `Foo` is a singleton, and `Bar` is scoped, this is a misconfiguration. As is, `Foo` is only instantiated once, and it holds onto `Bar` for its lifetime, which is longer than the intended scoped lifetime of `Bar`. Consider validating scopes by passing `validateScopes: true` to the . When you validate the scopes, you get an with a message similar to "Cannot consume scoped service 'Bar' from singleton 'Foo'.". -For more information, see [Scope validation](dependency-injection.md#scope-validation). +For more information, see [Scope validation](overview.md#scope-validation). ### Scoped service as singleton @@ -225,6 +225,6 @@ In the preceding code, `Bar` is retrieved within an . 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. + +> [!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. + +:::code language="csharp" source="snippets/overview/Program.cs" highlight="3-6"::: + +In the preceding code example, the highlighted lines: + +- 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. + +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. + +Now, imagine you want to switch out `MessageWriter` with a type that uses the [framework-provided logging service](service-registration.md#framework-provided-services). Create a class `LoggingMessageWriter` that depends on by requesting it in the constructor. + +:::code language="csharp" source="snippets/overview/LoggingMessageWriter.cs"::: + +To switch from `MessageWriter` to `LoggingMessageWriter`, simply update the call to `AddSingleton` to register this new `IMessageWriter` implementation: + +```csharp +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). + +## Constructor injection behavior + +Services can be resolved using (the built-in service container) or . `ActivatorUtilities` creates objects that aren't registered in the container and is used with some framework features. + +Constructors can accept arguments that aren't provided by dependency injection, but the arguments must assign default values. + +When `IServiceProvider` or `ActivatorUtilities` resolve services, constructor injection requires a *public* constructor. + +When `ActivatorUtilities` resolves services, constructor injection requires that only one applicable constructor exists. Constructor overloads are supported, but only one overload can exist whose arguments can all be fulfilled by dependency injection. + +## Constructor selection 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 example service: + +```csharp +public class ExampleService +{ + public ExampleService() + { + } + + public ExampleService(ILogger logger) + { + // ... + } + + public ExampleService(ServiceA serviceA, ServiceB serviceB) + { + // ... + } +} +``` + +In the preceding code, assume that logging has been added and is resolvable from the service provider but the `ServiceA` and `ServiceB` types aren't. The constructor with the `ILogger` parameter resolves the `ExampleService` instance. Even though there's a constructor that defines more parameters, the `ServiceA` and `ServiceB` types aren't DI-resolvable. + +If there's ambiguity when discovering constructors, an exception is thrown. Consider the following C# example service: + +> [!WARNING] +> This `ExampleService` code with ambiguous DI-resolvable type parameters throws an exception. **Don't** do this—it's intended to show what is meant by "ambiguous DI-resolvable types". + +```csharp +public class ExampleService +{ + public ExampleService() + { + } + + public ExampleService(ILogger logger) + { + // ... + } + + public ExampleService(IOptions options) + { + // ... + } +} +``` + +In the preceding example, there are three constructors. The first constructor is parameterless and requires no services from the service provider. Assume that both logging and options have been added to the DI container and are DI-resolvable services. When the DI container attempts to resolve the `ExampleService` type, it throws an exception, as the two constructors are ambiguous. + +Avoid ambiguity by defining a constructor that accepts both DI-resolvable types instead: + +```csharp +public class ExampleService +{ + public ExampleService() + { + } + + public ExampleService( + ILogger logger, + IOptions options) + { + // ... + } +} +``` + +## Scope validation + +[Scoped services](service-lifetimes.md#scoped) are disposed by the container that created them. If a scoped service is created in the root container, the service's lifetime is effectively promoted to [singleton](service-lifetimes.md#singleton) because it's only disposed by the root container when the app shuts down. Validating service scopes catches these situations when `BuildServiceProvider` is called. + +When an app runs in the development environment and calls [CreateApplicationBuilder](../generic-host.md#host-builder-settings) to build the host, the default service provider performs checks to verify that: + +- Scoped services aren't resolved from the root service provider. +- Scoped services aren't injected into singletons. + +## Scope scenarios + +The is always registered as a singleton, but the can vary based on the lifetime of the containing class. For example, if you resolve services from a scope, and any of those services take an , it's a scoped instance. + +To achieve scoping services within implementations of , such as the , *don't* inject the service dependencies via constructor injection. Instead, inject , create a scope, then resolve dependencies from the scope to use the appropriate service lifetime. + +:::code language="csharp" source="../snippets/configuration/worker-scope/Worker.cs"::: + +In the preceding code, while the app is running, the background service: + +- Depends on the . +- Creates an for resolving other services. +- Resolves scoped services for consumption. +- Works on processing objects and then relaying them, and finally marks them as processed. + +From the sample source code, you can see how implementations of can benefit from scoped service lifetimes. + +## Keyed services + +You can register services and perform lookups based on a key. In other words, it's possible to register multiple services with different keys and use this key for the lookup. + +For example, consider the case where you have different implementations of the interface `IMessageWriter`: `MemoryMessageWriter` and `QueueMessageWriter`. + +You can register these services using the overload of the service registration methods (seen earlier) that supports a key as a parameter: + +```csharp +services.AddKeyedSingleton("memory"); +services.AddKeyedSingleton("queue"); +``` + +The `key` isn't limited to `string`. The `key` can be any `object` you want, as long as the type correctly implements `Equals`. + +In the constructor of the class that uses `IMessageWriter`, you add the to specify the key of the service to resolve: + +```csharp +public class ExampleService +{ + public ExampleService( + [FromKeyedServices("queue")] IMessageWriter writer) + { + // Omitted for brevity... + } +} +``` + +### KeyedService.AnyKey property + +The property provides a special key for working with keyed services. You can register a service using `KeyedService.AnyKey` as a fallback that matches any key. This is useful when you want to provide a default implementation for any key that doesn't have an explicit registration. + +:::code language="csharp" source="snippets/anykey/Program.cs" id="FallbackRegistration"::: + +In the preceding example: + +- Requesting `ICache` with key `"premium"` returns the `PremiumCache` instance. +- Requesting `ICache` with any other key (like `"basic"` or `"standard"`) creates a new `DefaultCache` using the `AnyKey` fallback. + +> [!IMPORTANT] +> Starting in .NET 10, calling `GetKeyedService()` with `KeyedService.AnyKey` throws an because `AnyKey` is intended as a registration fallback, not as a query key. For more information, see [Fix issues in GetKeyedService() and GetKeyedServices() with AnyKey](../../compatibility/extensions/10.0/getkeyedservice-anykey.md). + +## See also + +- [Quickstart: Dependency injection basics](basics.md) +- [Tutorial: Use dependency injection in .NET](usage.md) +- [Dependency injection guidelines](guidelines.md) +- [Dependency injection in ASP.NET Core](/aspnet/core/fundamentals/dependency-injection) +- [NDC Conference Patterns for DI app development](https://www.youtube.com/watch?v=x-C-CNBVTaY) +- [Explicit dependencies principle](../../../architecture/modern-web-apps-azure/architectural-principles.md#explicit-dependencies) +- [Inversion of control containers and the dependency injection pattern (Martin Fowler)](https://www.martinfowler.com/articles/injection.html) diff --git a/docs/core/extensions/dependency-injection/service-lifetimes.md b/docs/core/extensions/dependency-injection/service-lifetimes.md new file mode 100644 index 0000000000000..44981cda5eebf --- /dev/null +++ b/docs/core/extensions/dependency-injection/service-lifetimes.md @@ -0,0 +1,48 @@ +--- +title: Service lifetimes (dependency injection) +description: "Learn about the three different services lifetimes in dependency injection in .NET: transient, scoped, and singleton." +ms.date: 10/21/2025 +--- + +# Service lifetimes + +Services can be registered with a [transient](#transient), [scoped](#scoped), or [singleton](#singleton) lifetime. Choose an appropriate lifetime for each registered service. + +## Transient + +A service with a *transient* lifetime is created each time it's requested from the service container. To register a service as transient, call . + +In apps that process requests, transient services are disposed at the end of the request. This lifetime incurs per-request allocations, as services are resolved and constructed every time. For more information, see [IDisposable guidance for transient and shared instances](guidelines.md#idisposable-guidance-for-transient-and-shared-instances). + +## Scoped + +For web applications, a *scoped* lifetime indicates that services are created once per client request (connection). In apps that process requests, scoped services are disposed at the end of the request. Register scoped services by calling . + +> [!NOTE] +> When using Entity Framework Core, the extension method registers `DbContext` types with a scoped lifetime by default. + +A scoped service should always be used from within a scope–either an implicit scope (such as ASP.NET Core's per-request scope) or an explicit scope created with . + +Do **_not_** resolve a scoped service directly from a singleton using constructor injection or by requesting it from in the singleton. Doing so causes the scoped service to behave like a singleton, which can lead to incorrect state when processing subsequent requests. + +It's acceptable to resolve a scoped service within a singleton if you create and use an explicit scope with . + +It's also fine to: + +- Resolve a singleton service from a scoped or transient service. +- Resolve a scoped service from another scoped or transient service. + +By default, in the development environment, resolving a service from another service with a longer lifetime throws an exception. For more information, see [Scope validation](overview.md#scope-validation). + +## Singleton + +Singleton lifetime services are created either: + +- The first time they're requested. +- By the developer, when providing an implementation instance directly to the container. This approach is rarely needed. + +Every subsequent request of the service implementation from the dependency injection container uses the same instance. If the app requires singleton behavior, allow the service container to manage the service's lifetime. Don't implement the singleton design pattern and provide code to dispose of the singleton. Services should never be disposed by code that resolved the service from the container. If a type or factory is registered as a singleton, the container disposes the singleton automatically. + +Register singleton services with . Singleton services must be thread safe and are often used in stateless services. + +In apps that process requests, singleton services are disposed when the is disposed on application shutdown. Because memory isn't released until the app shuts down, consider memory use with a singleton service. diff --git a/docs/core/extensions/dependency-injection/service-registration.md b/docs/core/extensions/dependency-injection/service-registration.md new file mode 100644 index 0000000000000..7d4f0d7711311 --- /dev/null +++ b/docs/core/extensions/dependency-injection/service-registration.md @@ -0,0 +1,142 @@ +--- +title: Service registration (dependency injection) +description: Learn how to register services for dependency injection using different methods for different scenarios. +ms.date: 10/21/2025 +--- + +# Service registration + +This article discusses registering groups of services and framework-provided services. It also provides details about the service-registration extension methods that .NET provides. + +## Register groups of services with extension methods + +.NET uses a convention for registering a group of related services. The convention is to use a single `Add{GROUP_NAME}` extension method to register all of the services required by a framework feature. For example, the extension method registers all of the services required for using options. + +## Framework-provided services + +When using any of the available host or app-builder patterns, defaults are applied and services are registered by the framework. Consider some of the most popular host and app-builder patterns: + +- +- +- +- +- +- + +After creating a builder from any of these APIs, the `IServiceCollection` has services defined by the framework, depending on [how you configured the host](../generic-host.md#host-configuration). For apps based on the .NET templates, the framework could register hundreds of services. + +The following table lists a small sample of these framework-registered services: + +| Service type | [Lifetime](service-lifetimes.md) | +|------------------------------------------------------------------------------------|-----------| +| | Singleton | +| | Singleton | +| | Singleton | +| | Singleton | +| | Singleton | +| | Transient | +| | Singleton | +| | Singleton | +| | Singleton | + +## Registration methods + +The framework provides service-registration extension methods that are useful in specific scenarios: + +| Method | Automatic object disposal | Multiple implementations | Pass args | +|--------|:-------------------------:|:------------------------:|:---------:| +| `Add{LIFETIME}<{SERVICE}, {IMPLEMENTATION}>()`

Example:

`services.AddSingleton();` | Yes | Yes | No | +| `Add{LIFETIME}<{SERVICE}>(sp => new {IMPLEMENTATION})`

Examples:

`services.AddSingleton(sp => new MyDep());`
`services.AddSingleton(sp => new MyDep(99));` | Yes | Yes | Yes | +| `Add{LIFETIME}<{IMPLEMENTATION}>()`

Example:

`services.AddSingleton();` | Yes | No | No | +| `AddSingleton<{SERVICE}>(new {IMPLEMENTATION})`

Examples:

`services.AddSingleton(new MyDep());`
`services.AddSingleton(new MyDep(99));` | No | Yes | Yes | +| `AddSingleton(new {IMPLEMENTATION})`

Examples:

`services.AddSingleton(new MyDep());`
`services.AddSingleton(new MyDep(99));` | No | No | Yes | + +For more information on type disposal, see [Disposal of services](guidelines.md#disposal-of-services). + +Registering a service with only an implementation type is equivalent to registering that service with the same implementation and service type. For example, consider the following code: + +```csharp +services.AddSingleton(); +``` + +This is equivalent to registering the service with both the service and implementation of the same types: + +```csharp +services.AddSingleton(); +``` + +This equivalency is why multiple implementations of a service can't be registered using the methods that don't take an explicit service type. These methods can register multiple *instances* of a service, but they all have the same *implementation* type. + +Any of the service registration methods can be used to register multiple service instances of the same service type. In the following example, `AddSingleton` is called twice with `IMessageWriter` as the service type. The second call to `AddSingleton` overrides the previous one when resolved as `IMessageWriter` and adds to the previous one when multiple services are resolved via `IEnumerable`. Services appear in the order they were registered when resolved via `IEnumerable<{SERVICE}>`. + +:::code language="csharp" source="snippets/console-ienumerable/Program.cs" highlight="11-16"::: + +The preceding sample source code registers two implementations of the `IMessageWriter`. + +:::code language="csharp" source="snippets/console-ienumerable/ExampleService.cs" highlight="7-16"::: + +The `ExampleService` defines two constructor parameters; a single `IMessageWriter`, and an `IEnumerable`. The single `IMessageWriter` is the last implementation to be registered, whereas the `IEnumerable` represents all registered implementations. + +The framework also provides `TryAdd{LIFETIME}` extension methods, which register the service only if there isn't already an implementation registered. + +In the following example, the call to `AddSingleton` registers `ConsoleMessageWriter` as an implementation for `IMessageWriter`. The call to `TryAddSingleton` has no effect because `IMessageWriter` already has a registered implementation: + +```csharp +services.AddSingleton(); +services.TryAddSingleton(); +``` + +The `TryAddSingleton` has no effect, as it was already added and the "try" fails. The `ExampleService` asserts the following: + +```csharp +public class ExampleService +{ + public ExampleService( + IMessageWriter messageWriter, + IEnumerable messageWriters) + { + Trace.Assert(messageWriter is ConsoleMessageWriter); + Trace.Assert(messageWriters.Single() is ConsoleMessageWriter); + } +} +``` + +For more information, see: + +- +- +- +- + +The [TryAddEnumerable(ServiceDescriptor)](xref:Microsoft.Extensions.DependencyInjection.Extensions.ServiceCollectionDescriptorExtensions.TryAddEnumerable%2A) methods register the service only if there isn't already an implementation *of the same type*. Multiple services are resolved via `IEnumerable<{SERVICE}>`. When registering services, add an instance if one of the same types wasn't already added. Library authors use `TryAddEnumerable` to avoid registering multiple copies of an implementation in the container. + +In the following example, the first call to `TryAddEnumerable` registers `MessageWriter` as an implementation for `IMessageWriter1`. The second call registers `MessageWriter` for `IMessageWriter2`. The third call has no effect because `IMessageWriter1` already has a registered implementation of `MessageWriter`: + +```csharp +public interface IMessageWriter1 { } +public interface IMessageWriter2 { } + +public class MessageWriter : IMessageWriter1, IMessageWriter2 +{ +} + +services.TryAddEnumerable(ServiceDescriptor.Singleton()); +services.TryAddEnumerable(ServiceDescriptor.Singleton()); +services.TryAddEnumerable(ServiceDescriptor.Singleton()); +``` + +Service registration is order-independent except when registering multiple implementations of the same type. + +`IServiceCollection` is a collection of objects. The following example shows how to register a service by creating and adding a `ServiceDescriptor`: + +```csharp +string secretKey = Configuration["SecretKey"]; +var descriptor = new ServiceDescriptor( + typeof(IMessageWriter), + _ => new DefaultMessageWriter(secretKey), + ServiceLifetime.Transient); + +services.Add(descriptor); +``` + +The built-in `Add{LIFETIME}` methods use the same approach. For example, see the [AddScoped source code](https://github.com/dotnet/extensions/blob/v3.1.8/src/DependencyInjection/DI.Abstractions/src/ServiceCollectionServiceExtensions.cs#L216-L237). diff --git a/docs/core/extensions/snippets/configuration/di-anti-patterns/Bar.cs b/docs/core/extensions/dependency-injection/snippets/anti-patterns/Bar.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/di-anti-patterns/Bar.cs rename to docs/core/extensions/dependency-injection/snippets/anti-patterns/Bar.cs diff --git a/docs/core/extensions/snippets/configuration/di-anti-patterns/ExampleDisposable.cs b/docs/core/extensions/dependency-injection/snippets/anti-patterns/ExampleDisposable.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/di-anti-patterns/ExampleDisposable.cs rename to docs/core/extensions/dependency-injection/snippets/anti-patterns/ExampleDisposable.cs diff --git a/docs/core/extensions/snippets/configuration/di-anti-patterns/Foo.cs b/docs/core/extensions/dependency-injection/snippets/anti-patterns/Foo.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/di-anti-patterns/Foo.cs rename to docs/core/extensions/dependency-injection/snippets/anti-patterns/Foo.cs diff --git a/docs/core/extensions/snippets/configuration/di-anti-patterns/Program.cs b/docs/core/extensions/dependency-injection/snippets/anti-patterns/Program.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/di-anti-patterns/Program.cs rename to docs/core/extensions/dependency-injection/snippets/anti-patterns/Program.cs diff --git a/docs/core/extensions/snippets/configuration/di-anti-patterns/di-anti-patterns.csproj b/docs/core/extensions/dependency-injection/snippets/anti-patterns/di-anti-patterns.csproj similarity index 100% rename from docs/core/extensions/snippets/configuration/di-anti-patterns/di-anti-patterns.csproj rename to docs/core/extensions/dependency-injection/snippets/anti-patterns/di-anti-patterns.csproj diff --git a/docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/AnyKeyExamples.csproj b/docs/core/extensions/dependency-injection/snippets/anykey/AnyKeyExamples.csproj similarity index 100% rename from docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/AnyKeyExamples.csproj rename to docs/core/extensions/dependency-injection/snippets/anykey/AnyKeyExamples.csproj diff --git a/docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/DefaultCache.cs b/docs/core/extensions/dependency-injection/snippets/anykey/DefaultCache.cs similarity index 100% rename from docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/DefaultCache.cs rename to docs/core/extensions/dependency-injection/snippets/anykey/DefaultCache.cs diff --git a/docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/ICache.cs b/docs/core/extensions/dependency-injection/snippets/anykey/ICache.cs similarity index 100% rename from docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/ICache.cs rename to docs/core/extensions/dependency-injection/snippets/anykey/ICache.cs diff --git a/docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/PremiumCache.cs b/docs/core/extensions/dependency-injection/snippets/anykey/PremiumCache.cs similarity index 100% rename from docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/PremiumCache.cs rename to docs/core/extensions/dependency-injection/snippets/anykey/PremiumCache.cs diff --git a/docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/Program.cs b/docs/core/extensions/dependency-injection/snippets/anykey/Program.cs similarity index 100% rename from docs/core/extensions/snippets/di/anykey/csharp/AnyKeyExamples/Program.cs rename to docs/core/extensions/dependency-injection/snippets/anykey/Program.cs diff --git a/docs/core/extensions/snippets/di/di-basics/DefaultConsole.cs b/docs/core/extensions/dependency-injection/snippets/basics/DefaultConsole.cs similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/DefaultConsole.cs rename to docs/core/extensions/dependency-injection/snippets/basics/DefaultConsole.cs diff --git a/docs/core/extensions/snippets/di/di-basics/DefaultGreetingService.cs b/docs/core/extensions/dependency-injection/snippets/basics/DefaultGreetingService.cs similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/DefaultGreetingService.cs rename to docs/core/extensions/dependency-injection/snippets/basics/DefaultGreetingService.cs diff --git a/docs/core/extensions/snippets/di/di-basics/FarewellService.cs b/docs/core/extensions/dependency-injection/snippets/basics/FarewellService.cs similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/FarewellService.cs rename to docs/core/extensions/dependency-injection/snippets/basics/FarewellService.cs diff --git a/docs/core/extensions/snippets/di/di-basics/IConsole.cs b/docs/core/extensions/dependency-injection/snippets/basics/IConsole.cs similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/IConsole.cs rename to docs/core/extensions/dependency-injection/snippets/basics/IConsole.cs diff --git a/docs/core/extensions/snippets/di/di-basics/IGreetingService.cs b/docs/core/extensions/dependency-injection/snippets/basics/IGreetingService.cs similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/IGreetingService.cs rename to docs/core/extensions/dependency-injection/snippets/basics/IGreetingService.cs diff --git a/docs/core/extensions/snippets/di/di-basics/Program.ServiceDescriptors.cs b/docs/core/extensions/dependency-injection/snippets/basics/Program.ServiceDescriptors.cs similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/Program.ServiceDescriptors.cs rename to docs/core/extensions/dependency-injection/snippets/basics/Program.ServiceDescriptors.cs diff --git a/docs/core/extensions/snippets/di/di-basics/Program.cs b/docs/core/extensions/dependency-injection/snippets/basics/Program.cs similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/Program.cs rename to docs/core/extensions/dependency-injection/snippets/basics/Program.cs diff --git a/docs/core/extensions/snippets/di/di-basics/di-basics.csproj b/docs/core/extensions/dependency-injection/snippets/basics/di-basics.csproj similarity index 100% rename from docs/core/extensions/snippets/di/di-basics/di-basics.csproj rename to docs/core/extensions/dependency-injection/snippets/basics/di-basics.csproj diff --git a/docs/core/extensions/snippets/configuration/console-di-disposable/Program.cs b/docs/core/extensions/dependency-injection/snippets/console-disposable/Program.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-disposable/Program.cs rename to docs/core/extensions/dependency-injection/snippets/console-disposable/Program.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-disposable/ScopedDisposable.cs b/docs/core/extensions/dependency-injection/snippets/console-disposable/ScopedDisposable.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-disposable/ScopedDisposable.cs rename to docs/core/extensions/dependency-injection/snippets/console-disposable/ScopedDisposable.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-disposable/SingletonDisposable.cs b/docs/core/extensions/dependency-injection/snippets/console-disposable/SingletonDisposable.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-disposable/SingletonDisposable.cs rename to docs/core/extensions/dependency-injection/snippets/console-disposable/SingletonDisposable.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-disposable/TransientDisposable.cs b/docs/core/extensions/dependency-injection/snippets/console-disposable/TransientDisposable.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-disposable/TransientDisposable.cs rename to docs/core/extensions/dependency-injection/snippets/console-disposable/TransientDisposable.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-disposable/console-di-disposable.csproj b/docs/core/extensions/dependency-injection/snippets/console-disposable/console-di-disposable.csproj similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-disposable/console-di-disposable.csproj rename to docs/core/extensions/dependency-injection/snippets/console-disposable/console-di-disposable.csproj diff --git a/docs/core/extensions/snippets/configuration/console-di-ienumerable/ConsoleMessageWriter.cs b/docs/core/extensions/dependency-injection/snippets/console-ienumerable/ConsoleMessageWriter.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-ienumerable/ConsoleMessageWriter.cs rename to docs/core/extensions/dependency-injection/snippets/console-ienumerable/ConsoleMessageWriter.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-ienumerable/ExampleService.cs b/docs/core/extensions/dependency-injection/snippets/console-ienumerable/ExampleService.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-ienumerable/ExampleService.cs rename to docs/core/extensions/dependency-injection/snippets/console-ienumerable/ExampleService.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-ienumerable/IMessageWriter.cs b/docs/core/extensions/dependency-injection/snippets/console-ienumerable/IMessageWriter.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-ienumerable/IMessageWriter.cs rename to docs/core/extensions/dependency-injection/snippets/console-ienumerable/IMessageWriter.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-ienumerable/LoggingMessageWriter.cs b/docs/core/extensions/dependency-injection/snippets/console-ienumerable/LoggingMessageWriter.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-ienumerable/LoggingMessageWriter.cs rename to docs/core/extensions/dependency-injection/snippets/console-ienumerable/LoggingMessageWriter.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-ienumerable/Program.cs b/docs/core/extensions/dependency-injection/snippets/console-ienumerable/Program.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-ienumerable/Program.cs rename to docs/core/extensions/dependency-injection/snippets/console-ienumerable/Program.cs diff --git a/docs/core/extensions/snippets/configuration/console-di-ienumerable/console-di-ienumerable.csproj b/docs/core/extensions/dependency-injection/snippets/console-ienumerable/console-di-ienumerable.csproj similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di-ienumerable/console-di-ienumerable.csproj rename to docs/core/extensions/dependency-injection/snippets/console-ienumerable/console-di-ienumerable.csproj diff --git a/docs/core/extensions/snippets/configuration/console-di/ExampleScopedService.cs b/docs/core/extensions/dependency-injection/snippets/console/ExampleScopedService.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/ExampleScopedService.cs rename to docs/core/extensions/dependency-injection/snippets/console/ExampleScopedService.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/ExampleSingletonService.cs b/docs/core/extensions/dependency-injection/snippets/console/ExampleSingletonService.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/ExampleSingletonService.cs rename to docs/core/extensions/dependency-injection/snippets/console/ExampleSingletonService.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/ExampleTransientService.cs b/docs/core/extensions/dependency-injection/snippets/console/ExampleTransientService.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/ExampleTransientService.cs rename to docs/core/extensions/dependency-injection/snippets/console/ExampleTransientService.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/IExampleScopedService.cs b/docs/core/extensions/dependency-injection/snippets/console/IExampleScopedService.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/IExampleScopedService.cs rename to docs/core/extensions/dependency-injection/snippets/console/IExampleScopedService.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/IExampleSingletonService.cs b/docs/core/extensions/dependency-injection/snippets/console/IExampleSingletonService.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/IExampleSingletonService.cs rename to docs/core/extensions/dependency-injection/snippets/console/IExampleSingletonService.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/IExampleTransientService.cs b/docs/core/extensions/dependency-injection/snippets/console/IExampleTransientService.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/IExampleTransientService.cs rename to docs/core/extensions/dependency-injection/snippets/console/IExampleTransientService.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/IReportServiceLifetime.cs b/docs/core/extensions/dependency-injection/snippets/console/IReportServiceLifetime.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/IReportServiceLifetime.cs rename to docs/core/extensions/dependency-injection/snippets/console/IReportServiceLifetime.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/Program.cs b/docs/core/extensions/dependency-injection/snippets/console/Program.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/Program.cs rename to docs/core/extensions/dependency-injection/snippets/console/Program.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/ServiceLifetimeReporter.cs b/docs/core/extensions/dependency-injection/snippets/console/ServiceLifetimeReporter.cs similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/ServiceLifetimeReporter.cs rename to docs/core/extensions/dependency-injection/snippets/console/ServiceLifetimeReporter.cs diff --git a/docs/core/extensions/snippets/configuration/console-di/console-di.csproj b/docs/core/extensions/dependency-injection/snippets/console/console-di.csproj similarity index 100% rename from docs/core/extensions/snippets/configuration/console-di/console-di.csproj rename to docs/core/extensions/dependency-injection/snippets/console/console-di.csproj diff --git a/docs/core/extensions/snippets/configuration/dependency-injection/LoggingMessageWriter.cs b/docs/core/extensions/dependency-injection/snippets/overview/LoggingMessageWriter.cs similarity index 67% rename from docs/core/extensions/snippets/configuration/dependency-injection/LoggingMessageWriter.cs rename to docs/core/extensions/dependency-injection/snippets/overview/LoggingMessageWriter.cs index c77548b6cc000..15731e37d45c5 100644 --- a/docs/core/extensions/snippets/configuration/dependency-injection/LoggingMessageWriter.cs +++ b/docs/core/extensions/dependency-injection/snippets/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/dependency-injection/snippets/overview/Program.cs b/docs/core/extensions/dependency-injection/snippets/overview/Program.cs new file mode 100644 index 0000000000000..2708ce60a7cc0 --- /dev/null +++ b/docs/core/extensions/dependency-injection/snippets/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/dependency-injection/snippets/overview/dependency-injection.csproj similarity index 86% rename from docs/core/extensions/snippets/configuration/dependency-injection/dependency-injection.csproj rename to docs/core/extensions/dependency-injection/snippets/overview/dependency-injection.csproj index 03469f00496f6..8f86351148293 100644 --- a/docs/core/extensions/snippets/configuration/dependency-injection/dependency-injection.csproj +++ b/docs/core/extensions/dependency-injection/snippets/overview/dependency-injection.csproj @@ -4,7 +4,6 @@ net10.0 enable true - DependencyInjection.Example diff --git a/docs/core/extensions/dependency-injection-usage.md b/docs/core/extensions/dependency-injection/usage.md similarity index 72% rename from docs/core/extensions/dependency-injection-usage.md rename to docs/core/extensions/dependency-injection/usage.md index 06691a26c1b67..e1821a1381b6b 100644 --- a/docs/core/extensions/dependency-injection-usage.md +++ b/docs/core/extensions/dependency-injection/usage.md @@ -9,29 +9,29 @@ ai-usage: ai-assisted # Tutorial: Use dependency injection in .NET -This tutorial shows how to use [dependency injection (DI) in .NET](dependency-injection.md). With *Microsoft Extensions*, DI is managed by adding services and configuring them in an . The interface exposes the instance, which acts as a container of all the registered services. +This tutorial shows how to use [dependency injection (DI) in .NET](overview.md). DI is managed by adding services and configuring them in an . The interface exposes the instance, which acts as a container of all the registered services. In this tutorial, you learn how to: > [!div class="checklist"] > -> - Create a .NET console app that uses dependency injection -> - Build and configure a [Generic Host](generic-host.md) -> - Write several interfaces and corresponding implementations -> - Use service lifetime and scoping for DI +> - Create a .NET console app that uses dependency injection. +> - Build and configure a [Generic host](../generic-host.md). +> - Write several interfaces and corresponding implementations. +> - Use service lifetime and scoping for DI. ## Prerequisites -- [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet) or later. +- [.NET Core 8.0 SDK](https://dotnet.microsoft.com/download/dotnet) or later. - Familiarity with creating new .NET applications and installing NuGet packages. ## Create a new console application -Using either the [dotnet new](../tools/dotnet-new.md) command or an IDE new project wizard, create a new .NET console application named **ConsoleDI.Example**. Add the [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting) NuGet package to the project. +Using either the [dotnet new](../../tools/dotnet-new.md) command or an IDE new project wizard, create a new .NET console application named **ConsoleDI.Example**. Add the [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting) NuGet package to the project. Your new console app project file should resemble the following: -:::code language="xml" source="snippets/configuration/console-di/console-di.csproj"::: +:::code language="xml" source="snippets/console/console-di.csproj"::: > [!IMPORTANT] > In this example, the [Microsoft.Extensions.Hosting](https://www.nuget.org/packages/Microsoft.Extensions.Hosting) NuGet package is required to build and run the app. Some metapackages might contain the `Microsoft.Extensions.Hosting` package, in which case an explicit package reference isn't required. @@ -42,7 +42,7 @@ In this sample app, you learn how dependency injection handles service lifetime. *IReportServiceLifetime.cs* -:::code source="snippets/configuration/console-di/IReportServiceLifetime.cs"::: +:::code source="snippets/console/IReportServiceLifetime.cs"::: The `IReportServiceLifetime` interface defines: @@ -51,15 +51,15 @@ The `IReportServiceLifetime` interface defines: *IExampleTransientService.cs* -:::code source="snippets/configuration/console-di/IExampleTransientService.cs"::: +:::code source="snippets/console/IExampleTransientService.cs"::: *IExampleScopedService.cs* -:::code source="snippets/configuration/console-di/IExampleScopedService.cs"::: +:::code source="snippets/console/IExampleScopedService.cs"::: *IExampleSingletonService.cs* -:::code source="snippets/configuration/console-di/IExampleSingletonService.cs"::: +:::code source="snippets/console/IExampleSingletonService.cs"::: All of the subinterfaces of `IReportServiceLifetime` explicitly implement the `IReportServiceLifetime.Lifetime` with a default. For example, `IExampleTransientService` explicitly implements `IReportServiceLifetime.Lifetime` with the `ServiceLifetime.Transient` value. @@ -69,15 +69,15 @@ The example implementations all initialize their `Id` property with the result o *ExampleTransientService.cs* -:::code source="snippets/configuration/console-di/ExampleTransientService.cs"::: +:::code source="snippets/console/ExampleTransientService.cs"::: *ExampleScopedService.cs* -:::code source="snippets/configuration/console-di/ExampleScopedService.cs"::: +:::code source="snippets/console/ExampleScopedService.cs"::: *ExampleSingletonService.cs* -:::code source="snippets/configuration/console-di/ExampleSingletonService.cs"::: +:::code source="snippets/console/ExampleSingletonService.cs"::: Each implementation is defined as `internal sealed` and implements its corresponding interface. They're not required to be `internal` or `sealed`, however, it's common to treat implementations as `internal` to avoid leaking implementation types to external consumers. Furthermore, since each type isn't extended, it's marked as `sealed`. For example, `ExampleSingletonService` implements `IExampleSingletonService`. @@ -87,7 +87,7 @@ Add the following service lifetime reporter class, which acts as a service to th *ServiceLifetimeReporter.cs* -:::code source="snippets/configuration/console-di/ServiceLifetimeReporter.cs"::: +:::code source="snippets/console/ServiceLifetimeReporter.cs"::: The `ServiceLifetimeReporter` defines a constructor that requires each of the aforementioned service interfaces, that is, `IExampleTransientService`, `IExampleScopedService`, and `IExampleSingletonService`. The object exposes a single method that allows the consumer to report on the service with a given `lifetimeDetails` parameter. When invoked, the `ReportServiceLifetimeDetails` method logs each service's unique identifier with the service lifetime message. The log messages help to visualize the service lifetime. @@ -95,7 +95,7 @@ The `ServiceLifetimeReporter` defines a constructor that requires each of the af Update *Program.cs* with the following code: -:::code source="snippets/configuration/console-di/Program.cs" id="Program" highlight="8-11"::: +:::code source="snippets/console/Program.cs" id="Program" highlight="8-11"::: Each `services.Add{LIFETIME}<{SERVICE}>` extension method adds (and potentially configures) services. We recommend that apps follow this convention. Don't place extension methods in the namespace unless you're authoring an official Microsoft package. Extension methods that are defined within the `Microsoft.Extensions.DependencyInjection` namespace: @@ -104,7 +104,7 @@ Each `services.Add{LIFETIME}<{SERVICE}>` extension method adds (and potentially The app: -- Creates an instance with [host builder settings](generic-host.md#host-builder-settings). +- Creates an instance with [host builder settings](../generic-host.md#host-builder-settings). - Configures services and adds them with their corresponding service lifetime. - Calls and assigns an instance of . - Calls `ExemplifyServiceLifetime`, passing in the . @@ -115,7 +115,7 @@ In this sample app, you created several interfaces and corresponding implementat When you run the app, it displays output similar to the following: -:::code source="snippets/configuration/console-di/Program.cs" id="Output"::: +:::code source="snippets/console/Program.cs" id="Output"::: From the app output, you can see that: @@ -125,6 +125,6 @@ From the app output, you can see that: ## See also -- [Dependency injection guidelines](dependency-injection-guidelines.md) -- [Understand dependency injection basics in .NET](dependency-injection-basics.md) +- [Dependency injection guidelines](guidelines.md) +- [Quickstart: Dependency injection basics](basics.md) - [Dependency injection in ASP.NET Core](/aspnet/core/fundamentals/dependency-injection) diff --git a/docs/core/extensions/generic-host.md b/docs/core/extensions/generic-host.md index 74265e398c5a7..4137dc979fcbb 100644 --- a/docs/core/extensions/generic-host.md +++ b/docs/core/extensions/generic-host.md @@ -320,7 +320,7 @@ To ensure a smooth transition of clients to a new destination when working with ## See also -- [Dependency injection in .NET](dependency-injection.md) +- [Dependency injection in .NET](dependency-injection/overview.md) - [Logging in .NET](logging.md) - [Configuration in .NET](configuration.md) - [Worker Services in .NET](workers.md) diff --git a/docs/core/extensions/httpclient-factory-keyed-di.md b/docs/core/extensions/httpclient-factory-keyed-di.md index 84793b989fa67..b3ea537ef469a 100644 --- a/docs/core/extensions/httpclient-factory-keyed-di.md +++ b/docs/core/extensions/httpclient-factory-keyed-di.md @@ -10,7 +10,7 @@ ms.date: 01/27/2025 In this article, you learn how to integrate `IHttpClientFactory` with Keyed Services. -[_Keyed Services_](dependency-injection.md#keyed-services) (also called _Keyed DI_) is a dependency injection (DI) feature that allows you to conveniently operate with multiple implementations of a single service. Upon registration, you can associate different _service keys_ with the specific implementations. At runtime, this key is used in lookup in combination with a service type, which means you can retrieve a specific implementation by passing the matching key. For more information on Keyed Services, and DI in general, see [.NET dependency injection][di]. +[_Keyed Services_](dependency-injection/overview.md#keyed-services) (also called _Keyed DI_) is a dependency injection (DI) feature that allows you to conveniently operate with multiple implementations of a single service. Upon registration, you can associate different _service keys_ with the specific implementations. At runtime, this key is used in lookup in combination with a service type, which means you can retrieve a specific implementation by passing the matching key. For more information on Keyed Services, and DI in general, see [.NET dependency injection][di]. For an overview on how to use `IHttpClientFactory` in your .NET application, see [IHttpClientFactory with .NET][hcf]. @@ -237,12 +237,12 @@ public class MyController( > `KeyedService.AnyKey` registrations define a mapping from _any_ key value to some service instance. However, as a result, the Container validation doesn't apply, and an _erroneous_ key value _silently_ leads to a _wrong instance_ being injected. > [!IMPORTANT] -> For Keyed `HttpClient`s, a mistake in the client name can result in erroneously injecting an "unknown" client—meaning, a client whose name was never registered. +> For Keyed `HttpClient` clients, a mistake in the client name can result in erroneously injecting an "unknown" client—meaning, a client whose name was never registered. The same is true for the plain Named clients: `IHttpClientFactory` doesn't require the client name to be explicitly registered (aligning with the way the [Options pattern](options.md) works). The factory gives you an unconfigured—or, more precisely, default-configured—`HttpClient` for any unknown name. > [!NOTE] -> Therefore, it's important to keep in mind: the "Keyed by default" approach covers not only all _registered_ `HttpClient`s, but all the clients that `IHttpClientFactory` is _able to create_. +> Therefore, it's important to keep in mind: the "Keyed by default" approach covers not only all _registered_ `HttpClient` clients, but all the clients that `IHttpClientFactory` is _able to create_. ```csharp services.ConfigureHttpClientDefaults(b => b.AddAsKeyed()); @@ -298,5 +298,5 @@ If called together or any of them more than once, `AddAsKeyed()` and `RemoveAsKe - [Common `IHttpClientFactory` usage issues][hcf-troubleshooting] [hcf]: httpclient-factory.md -[di]: dependency-injection.md +[di]: dependency-injection/overview.md [hcf-troubleshooting]: httpclient-factory-troubleshooting.md diff --git a/docs/core/extensions/httpclient-factory.md b/docs/core/extensions/httpclient-factory.md index 67a016b81ade5..c520b733f38e1 100644 --- a/docs/core/extensions/httpclient-factory.md +++ b/docs/core/extensions/httpclient-factory.md @@ -376,7 +376,7 @@ builder.ConfigurePrimaryHttpMessageHandler((handler, provider) => - [Implement HTTP retry with exponential backoff][http-retry] [hcf-issues]: httpclient-factory-troubleshooting.md -[di]: dependency-injection.md +[di]: dependency-injection/overview.md [logging]: logging.md [config]: configuration.md [httpclient]: ../../fundamentals/networking/http/httpclient.md diff --git a/docs/core/extensions/localization.md b/docs/core/extensions/localization.md index 1931b814819fc..8c484a14237dc 100644 --- a/docs/core/extensions/localization.md +++ b/docs/core/extensions/localization.md @@ -242,6 +242,6 @@ The sample application does not provide resource files for `"fr-CA"`, but when c - [Globalizing and localizing .NET applications](globalization-and-localization.md) - [Package and deploy resources in .NET apps](package-and-deploy-resources.md) - [`Microsoft.Extensions.Localization`](https://www.nuget.org/packages/microsoft.extensions.localization) -- [Dependency injection in .NET](dependency-injection.md) +- [Dependency injection in .NET](dependency-injection/overview.md) - [Logging in .NET](logging.md) - [ASP.NET Core localization](/aspnet/core/fundamentals/localization) diff --git a/docs/core/extensions/logging.md b/docs/core/extensions/logging.md index 0b092407522ec..c5bc10ebb86a6 100644 --- a/docs/core/extensions/logging.md +++ b/docs/core/extensions/logging.md @@ -36,7 +36,7 @@ This project file for this example includes two NuGet packages: Consider making these changes to the previous example when logging in a less trivial scenario: -- If your application uses [Dependency Injection (DI)](dependency-injection.md) or a host such as ASP.NET's [WebApplication](/aspnet/core/fundamentals/minimal-apis/webapplication) or [Generic Host](generic-host.md), use `ILoggerFactory` and `ILogger` objects from their respective DI containers rather than creating them directly. For more information, see [Integration with DI and Hosts](#integration-with-hosts-and-dependency-injection). +- If your application uses [Dependency Injection (DI)](dependency-injection/overview.md) or a host such as ASP.NET's [WebApplication](/aspnet/core/fundamentals/minimal-apis/webapplication) or [Generic Host](generic-host.md), use `ILoggerFactory` and `ILogger` objects from their respective DI containers rather than creating them directly. For more information, see [Integration with DI and Hosts](#integration-with-hosts-and-dependency-injection). - Logging [compile-time source generation](logger-message-generator.md) is usually a better alternative to `ILogger` extension methods like `LogInformation`. Logging source generation offers better performance, stronger typing, and avoids spreading `string` constants throughout your methods. The tradeoff is that using this technique requires a bit more code. @@ -52,7 +52,7 @@ Consider making these changes to the previous example when logging in a less tri ## Integration with hosts and dependency injection -If your application uses [Dependency Injection (DI)](dependency-injection.md) or a host such as ASP.NET's [WebApplication](/aspnet/core/fundamentals/minimal-apis/webapplication) or [Generic Host](generic-host.md), use `ILoggerFactory` and `ILogger` objects from the DI container rather than creating them directly. +If your application uses [Dependency Injection (DI)](dependency-injection/overview.md) or a host such as ASP.NET's [WebApplication](/aspnet/core/fundamentals/minimal-apis/webapplication) or [Generic Host](generic-host.md), use `ILoggerFactory` and `ILogger` objects from the DI container rather than creating them directly. ### Get an ILogger from DI diff --git a/docs/core/extensions/options-library-authors.md b/docs/core/extensions/options-library-authors.md index 1503e23762f5b..dae7fddff3fc6 100644 --- a/docs/core/extensions/options-library-authors.md +++ b/docs/core/extensions/options-library-authors.md @@ -153,5 +153,5 @@ Consumers in this pattern provide a lambda expression (or a delegate that satisf ## See also - [Options pattern in .NET](options.md) -- [Dependency injection in .NET](dependency-injection.md) -- [Dependency injection guidelines](dependency-injection-guidelines.md) +- [Dependency injection in .NET](dependency-injection/overview.md) +- [Dependency injection guidelines](dependency-injection/guidelines.md) diff --git a/docs/core/extensions/options.md b/docs/core/extensions/options.md index a4e22a4713d13..1509ef7128be1 100644 --- a/docs/core/extensions/options.md +++ b/docs/core/extensions/options.md @@ -16,7 +16,7 @@ Options also provide a mechanism to validate configuration data. For more inform ## Bind hierarchical configuration -The preferred way to read related configuration values is using the options pattern. The options pattern is possible through the interface, where the generic type parameter `TOptions` is constrained to a `class`. The `IOptions` can later be provided through dependency injection. For more information, see [Dependency injection in .NET](dependency-injection.md). +The preferred way to read related configuration values is using the options pattern. The options pattern is possible through the interface, where the generic type parameter `TOptions` is constrained to a `class`. The `IOptions` can later be provided through dependency injection. For more information, see [Dependency injection in .NET](dependency-injection/overview.md). For example, to read the highlighted configuration values from an _appsettings.json_ file: @@ -57,7 +57,7 @@ In the preceding code, the `ConfigurationBinder.Get` is used to acquire an in > [!IMPORTANT] > The class exposes several APIs, such as `.Bind(object instance)` and `.Get()` that are ***not*** constrained to `class`. When using any of the [Options interfaces](#options-interfaces), you must adhere to aforementioned [options class constraints](#options-class). -An alternative approach when using the options pattern is to bind the `"TransientFaultHandlingOptions"` section and add it to the [dependency injection service container](dependency-injection.md). In the following code, `TransientFaultHandlingOptions` is added to the service container with and bound to configuration: +An alternative approach when using the options pattern is to bind the `"TransientFaultHandlingOptions"` section and add it to the [dependency injection service container](dependency-injection/overview.md). In the following code, `TransientFaultHandlingOptions` is added to the service container with and bound to configuration: ```csharp HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); @@ -85,18 +85,18 @@ In the preceding code, changes to the JSON configuration file after the app has - Does ***not*** support: - Reading of configuration data after the app has started. - [Named options](#named-options-support-using-iconfigurenamedoptions) -- Is registered as a [Singleton](dependency-injection.md#singleton) and can be injected into any [service lifetime](dependency-injection.md#service-lifetimes). +- Is registered as a [Singleton](dependency-injection/service-lifetimes.md#singleton) and can be injected into any [service lifetime](dependency-injection/service-lifetimes.md). : -- Is useful in scenarios where options should be recomputed on every injection resolution, in [scoped or transient lifetimes](dependency-injection.md#service-lifetimes). For more information, see [Use IOptionsSnapshot to read updated data](#use-ioptionssnapshot-to-read-updated-data). -- Is registered as [Scoped](dependency-injection.md#scoped) and therefore can't be injected into a Singleton service. +- Is useful in scenarios where options should be recomputed on every injection resolution, in [scoped or transient lifetimes](dependency-injection/service-lifetimes.md). For more information, see [Use IOptionsSnapshot to read updated data](#use-ioptionssnapshot-to-read-updated-data). +- Is registered as [Scoped](dependency-injection/service-lifetimes.md#scoped) and therefore can't be injected into a Singleton service. - Supports [named options](#named-options-support-using-iconfigurenamedoptions). : - Is used to retrieve options and manage options notifications for `TOptions` instances. -- Is registered as a [Singleton](dependency-injection.md#singleton) and can be injected into any [service lifetime](dependency-injection.md#service-lifetimes). +- Is registered as a [Singleton](dependency-injection/service-lifetimes.md#singleton) and can be injected into any [service lifetime](dependency-injection/service-lifetimes.md). - Supports: - Change notifications - [Named options](#named-options-support-using-iconfigurenamedoptions) @@ -123,8 +123,8 @@ When you use , options are The difference between `IOptionsMonitor` and `IOptionsSnapshot` is that: -- `IOptionsMonitor` is a [singleton service](dependency-injection.md#singleton) that retrieves current option values at any time, which is especially useful in singleton dependencies. -- `IOptionsSnapshot` is a [scoped service](dependency-injection.md#scoped) and provides a snapshot of the options at the time the `IOptionsSnapshot` object is constructed. Options snapshots are designed for use with transient and scoped dependencies. +- `IOptionsMonitor` is a [singleton service](dependency-injection/service-lifetimes.md#singleton) that retrieves current option values at any time, which is especially useful in singleton dependencies. +- `IOptionsSnapshot` is a [scoped service](dependency-injection/service-lifetimes.md#scoped) and provides a snapshot of the options at the time the `IOptionsSnapshot` object is constructed. Options snapshots are designed for use with transient and scoped dependencies. The following code uses . @@ -251,7 +251,7 @@ All options are named instances. ](xref:Microsoft.Extensions.Options.OptionsBuilder`1). `OptionsBuilder` provides overloads of [Configure](xref:Microsoft.Extensions.Options.OptionsBuilder`1.Configure%2A) that allow use of up to five services to configure options: diff --git a/docs/core/extensions/queue-service.md b/docs/core/extensions/queue-service.md index 5f0cff17c503e..2767ea2006b7f 100644 --- a/docs/core/extensions/queue-service.md +++ b/docs/core/extensions/queue-service.md @@ -5,7 +5,7 @@ ms.date: 10/20/2025 ms.topic: tutorial --- -# Create a Queue Service +# Create a queue service A queue service is a great example of a long-running service, where work items can be queued and worked on sequentially as previous work items are completed. Relying on the Worker Service template, you build out new functionality on top of the . @@ -75,7 +75,7 @@ The services are registered in (*Program.cs*). The hosted service is registered :::code source="snippets/workers/queue-service/Program.cs" range="18-19"::: -For more information on registering services, see [Dependency injection in .NET](dependency-injection.md). +For more information on registering services, see [Dependency injection in .NET](dependency-injection/overview.md). ## Verify service functionality diff --git a/docs/core/extensions/scoped-service.md b/docs/core/extensions/scoped-service.md index 748a138620088..d9acecab073e7 100644 --- a/docs/core/extensions/scoped-service.md +++ b/docs/core/extensions/scoped-service.md @@ -7,7 +7,7 @@ ms.topic: tutorial # Use scoped services within a `BackgroundService` -When you register implementations of using any of the extension methods—the service is registered as a singleton. There might be scenarios where you'd like to rely on a scoped service. For more information, see [Dependency injection in .NET: Service lifetimes](dependency-injection.md#service-lifetimes). +When you register implementations of using any of the extension methods, the service is registered as a singleton. There might be scenarios where you'd like to rely on a scoped service. For more information, see [Service lifetimes](dependency-injection/service-lifetimes.md). In this tutorial, you learn how to: @@ -22,15 +22,14 @@ In this tutorial, you learn how to: ## Prerequisites - The [.NET 8.0 SDK or later](https://dotnet.microsoft.com/download/dotnet/8.0) -- A .NET integrated development environment (IDE) - - Feel free to use [Visual Studio](https://visualstudio.microsoft.com) +- A .NET integrated development environment (IDE), for example, [Visual Studio](https://visualstudio.microsoft.com) [!INCLUDE [file-new-worker](includes/file-new-worker.md)] ## Create scoped services -To use [scoped services](dependency-injection.md#scoped) within a `BackgroundService`, create a scope with the API. No scope is created for a hosted service by default. The scoped background service contains the background task's logic. +To use [scoped services](dependency-injection/service-lifetimes.md#scoped) within a `BackgroundService`, create a scope with the API. No scope is created for a hosted service by default. The scoped background service contains the background task's logic. :::code source="snippets/workers/scoped-service/IScopedProcessingService.cs"::: @@ -56,7 +55,7 @@ Replace the template *Program.cs* file contents with the following C# code: The services are registered in (*Program.cs*). The hosted service is registered with the extension method. -For more information on registering services, see [Dependency injection in .NET](dependency-injection.md). +For more information on registering services, see [Dependency injection in .NET](dependency-injection/overview.md). ## Verify service functionality diff --git a/docs/core/extensions/snippets/configuration/configuration.sln b/docs/core/extensions/snippets/configuration/configuration.sln index 8f7374a168a11..fa57585fa6f10 100644 --- a/docs/core/extensions/snippets/configuration/configuration.sln +++ b/docs/core/extensions/snippets/configuration/configuration.sln @@ -13,12 +13,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-basic", "console-ba EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-custom-logging", "console-custom-logging\console-custom-logging.csproj", "{43322C7A-36CD-4EBE-880F-EAAB26BEA8F1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-di", "console-di\console-di.csproj", "{0D667FE4-D288-44CC-954E-8E24F249C534}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-di-disposable", "console-di-disposable\console-di-disposable.csproj", "{1B517FD0-A269-4087-B58B-7DCC40C47866}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-di-ienumerable", "console-di-ienumerable\console-di-ienumerable.csproj", "{37083C11-8B5C-44AD-BE9C-6EE4DF4C232B}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-env", "console-env\console-env.csproj", "{1CEEE236-B214-436E-9740-F90171777332}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "console-host", "console-host\console-host.csproj", "{8F38E687-904B-4862-BC76-720B55833715}" @@ -45,10 +39,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}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "options-configparam", "options-configparam\options-configparam.csproj", "{2EE94D26-59BA-4496-92DD-7D8AFAD1ED86}" 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/timer-service.md b/docs/core/extensions/timer-service.md index 4ccc5c74cfbfa..1c8f4ca82657b 100644 --- a/docs/core/extensions/timer-service.md +++ b/docs/core/extensions/timer-service.md @@ -52,7 +52,7 @@ Replace the existing `Program` contents with the following C# code: The service is registered in (*Program.cs*) with the `AddHostedService` extension method. This is the same extension method you use when registering subclasses, as they both implement the interface. -For more information on registering services, see [Dependency injection in .NET](dependency-injection.md). +For more information on registering services, see [Dependency injection in .NET](dependency-injection/overview.md). ## Verify service functionality diff --git a/docs/core/extensions/windows-service.md b/docs/core/extensions/windows-service.md index 31d3498bf42c2..a0754d8e81895 100644 --- a/docs/core/extensions/windows-service.md +++ b/docs/core/extensions/windows-service.md @@ -106,7 +106,7 @@ Replace the template *Program.cs* file contents with the following C# code: The `AddWindowsService` extension method configures the app to work as a Windows Service. The service name is set to `".NET Joke Service"`. The hosted service is registered for dependency injection. -For more information on registering services, see [Dependency injection in .NET](dependency-injection.md). +For more information on registering services, see [Dependency injection in .NET](dependency-injection/overview.md). ## Publish the app diff --git a/docs/core/extensions/workers.md b/docs/core/extensions/workers.md index 5b9568221b3fd..a3dcccc8196f7 100644 --- a/docs/core/extensions/workers.md +++ b/docs/core/extensions/workers.md @@ -15,7 +15,7 @@ There are numerous reasons for creating long-running services such as: Background service processing usually doesn't involve a user interface (UI), but UIs can be built around them. In the early days with .NET Framework, Windows developers could create Windows Services for these purposes. Now with .NET, you can use the , which is an implementation of , or implement your own. -With .NET, you're no longer restricted to Windows. You can develop cross-platform background services. Hosted services are logging, configuration, and dependency injection (DI) ready. They're a part of the extensions suite of libraries, meaning they're fundamental to all .NET workloads that work with the [generic host](generic-host.md). +With .NET, you're no longer restricted to Windows. You can develop cross-platform background services. Hosted services are logging, configuration, and dependency-injection (DI) ready. They're a part of the extensions suite of libraries, meaning they're fundamental to all .NET workloads that work with the [generic host](generic-host.md). [!INCLUDE [worker-template-workloads](includes/worker-template-workloads.md)] @@ -52,10 +52,10 @@ The Worker template doesn't enable server garbage collection (GC) by default, as ``` -_**Tradeoffs and considerations**_ +#### Tradeoffs and considerations | Enabled | Disabled | -|--|--| +|---------|----------| | Efficient memory management: Automatically reclaims unused memory to prevent memory leaks and optimize resource usage. | Improved real-time performance: Avoids potential pauses or interruptions caused by garbage collection in latency-sensitive applications. | | Long-term stability: Helps maintain stable performance in long-running services by managing memory over extended periods. | Resource efficiency: May conserve CPU and memory resources in resource-constrained environments. | | Reduced maintenance: Minimizes the need for manual memory management, simplifying maintenance. | Manual memory control: Provides fine-grained control over memory for specialized applications. | @@ -99,7 +99,7 @@ The preceding *Dockerfile* steps include: - Changing the working directory to */src*. - Copying the contents and publishing the .NET app: - The app is published using the [`dotnet publish`](../tools/dotnet-publish.md) command. -- Relayering the .NET SDK image from `mcr.microsoft.com/dotnet/runtime:8.0` (the `base` alias). +- Re-layering the .NET SDK image from `mcr.microsoft.com/dotnet/runtime:8.0` (the `base` alias). - Copying the published build output from the */publish*. - Defining the entry point, which delegates to [`dotnet App.BackgroundService.dll`](../tools/dotnet.md). diff --git a/docs/core/resilience/http-resilience.md b/docs/core/resilience/http-resilience.md index afabd20437c69..6f84fdc153a9d 100644 --- a/docs/core/resilience/http-resilience.md +++ b/docs/core/resilience/http-resilience.md @@ -232,7 +232,7 @@ For more information, see [Options pattern in .NET](../extensions/options.md). ## Example usage -Your app relies on [dependency injection](../extensions/dependency-injection.md) to resolve the `ExampleClient` and its corresponding . The code builds the and resolves the `ExampleClient` from it. +Your app relies on [dependency injection](../extensions/dependency-injection/overview.md) to resolve the `ExampleClient` and its corresponding . The code builds the and resolves the `ExampleClient` from it. :::code language="csharp" source="snippets/http-resilience/Program.cs" id="usage"::: diff --git a/docs/core/testing/unit-testing-best-practices.md b/docs/core/testing/unit-testing-best-practices.md index 353a8e74b06d1..c31a77c166fbd 100644 --- a/docs/core/testing/unit-testing-best-practices.md +++ b/docs/core/testing/unit-testing-best-practices.md @@ -109,7 +109,7 @@ There are several important best practices to follow when writing unit tests. Th ### Avoid infrastructure dependencies -Try not to introduce dependencies on infrastructure when writing unit tests. The dependencies make the tests slow and brittle and should be reserved for integration tests. You can avoid these dependencies in your application by following the [Explicit Dependencies Principle](https://deviq.com/explicit-dependencies-principle) and by using [.NET dependency injection](../extensions/dependency-injection.md). You can also keep your unit tests in a separate project from your integration tests. This approach ensures your unit test project doesn't have references to or dependencies on infrastructure packages. +Try not to introduce dependencies on infrastructure when writing unit tests. The dependencies make the tests slow and brittle and should be reserved for integration tests. You can avoid these dependencies in your application by following the [Explicit Dependencies Principle](https://deviq.com/explicit-dependencies-principle) and by using [.NET dependency injection](../extensions/dependency-injection/overview.md). You can also keep your unit tests in a separate project from your integration tests. This approach ensures your unit test project doesn't have references to or dependencies on infrastructure packages. ### Follow test naming standards diff --git a/docs/devops/create-dotnet-github-action.md b/docs/devops/create-dotnet-github-action.md index 8aa67c4ca90f3..e4c09dc316fd2 100644 --- a/docs/devops/create-dotnet-github-action.md +++ b/docs/devops/create-dotnet-github-action.md @@ -60,7 +60,7 @@ The `Program` file is simplified for brevity, to explore the full sample source, - [Top-level statements](../csharp/tutorials/top-level-statements.md) - [Generic Host](../core/extensions/generic-host.md) -- [Dependency injection](../core/extensions/dependency-injection.md) +- [Dependency injection](../core/extensions/dependency-injection/overview.md) External project or package references can be used, and registered with dependency injection. The `Get` is a static local function, which requires the `IHost` instance, and is used to resolve required services. With the `CommandLine.Parser.Default` singleton, the app gets a `parser` instance from the `args`. When the arguments are unable to be parsed, the app exits with a non-zero exit code. For more information, see [Setting exit codes for actions](https://docs.github.com/actions/creating-actions/setting-exit-codes-for-actions). @@ -215,7 +215,7 @@ For more information, see [GitHub Docs: Working with the Container registry](htt ## See also - [.NET Generic Host](../core/extensions/generic-host.md) -- [Dependency injection in .NET](../core/extensions/dependency-injection.md) +- [Dependency injection in .NET](../core/extensions/dependency-injection/overview.md) - [Code metrics values](/visualstudio/code-quality/code-metrics-values) - [Open-source GitHub Action build in .NET](https://github.com/svrooij/dotnet-feeder/) with a [workflow](https://github.com/svrooij/dotnet-feeder/blob/main/.github/workflows/build.yml) for building and pushing the docker image automatically. diff --git a/docs/fundamentals/code-analysis/quality-rules/ca1812.md b/docs/fundamentals/code-analysis/quality-rules/ca1812.md index c3e5c8087e3aa..71809158b87ac 100644 --- a/docs/fundamentals/code-analysis/quality-rules/ca1812.md +++ b/docs/fundamentals/code-analysis/quality-rules/ca1812.md @@ -50,7 +50,7 @@ It is safe to suppress a warning from this rule. We recommend that you suppress - The class is created through late-bound reflection methods such as . -- The class is registered in an inversion of control (IoC) container as part of the [dependency injection](../../../core/extensions/dependency-injection.md) pattern. +- The class is registered in an inversion of control (IoC) container as part of the [dependency injection](../../../core/extensions/dependency-injection/overview.md) pattern. - The class is created automatically by the runtime or ASP.NET. Some examples of automatically created classes are those that implement or . diff --git a/docs/fundamentals/index.yml b/docs/fundamentals/index.yml index fbb1df078346c..82fe8517df101 100644 --- a/docs/fundamentals/index.yml +++ b/docs/fundamentals/index.yml @@ -136,7 +136,7 @@ landingContent: - linkListType: concept links: - text: Dependency injection in .NET - url: ../core/extensions/dependency-injection.md + url: ../core/extensions/dependency-injection/overview.md - text: Configuration in .NET url: ../core/extensions/configuration.md - text: Logging in .NET diff --git a/docs/fundamentals/toc.yml b/docs/fundamentals/toc.yml index 0114c6ac17aba..73202d2e14101 100644 --- a/docs/fundamentals/toc.yml +++ b/docs/fundamentals/toc.yml @@ -1041,22 +1041,23 @@ items: - name: The System.Random class href: runtime-libraries/system-random.md - name: Artificial intelligence (AI) - displayName: microsoft.extensions.ai,ollama,ai,openai,azure inference,ichatclient + displayName: ai,microsoft.extensions.ai,ollama,openai,azure inference,ichatclient href: ../ai/microsoft-extensions-ai.md?toc=/dotnet/fundamentals/toc.json&bc=/dotnet/breadcrumb/toc.json - name: Dependency injection items: - name: Overview - href: ../core/extensions/dependency-injection.md + href: ../core/extensions/dependency-injection/overview.md displayName: dependency injection,di,ioc,ioc container,dependency container,inversion of control - - name: Dependency injection basics - href: ../core/extensions/dependency-injection-basics.md - displayName: dependency injection basics,di basics,ioc basics,ioc container basics,dependency container basics,inversion of control basics - - name: Use dependency injection - href: ../core/extensions/dependency-injection-usage.md - displayName: use dependency injection,di,di examples,ioc,ioc container,dependency container,inversion of control - - name: Dependency injection guidelines - href: ../core/extensions/dependency-injection-guidelines.md - displayName: dependency injection best practices,guidelines,di,ioc,ioc container,dependency container,inversion of control + - name: Service registration + href: ../core/extensions/dependency-injection/service-registration.md + - name: Service lifetimes + href: ../core/extensions/dependency-injection/service-lifetimes.md + - name: "Quickstart: Dependency injection basics" + href: ../core/extensions/dependency-injection/basics.md + - name: "Tutorial: Use dependency injection" + href: ../core/extensions/dependency-injection/usage.md + - name: Guidelines + href: ../core/extensions/dependency-injection/guidelines.md - name: Configuration items: - name: Overview @@ -1335,12 +1336,12 @@ items: href: runtime-libraries/system-resources-resourcereader.md - name: SatelliteContractVersionAttribute class href: runtime-libraries/system-resources-satellitecontractversionattribute.md - - name: Worker Services + - name: Worker services items: - name: Overview displayName: workers,worker service,BackgroundService,IHostedService,queue service,timer service href: ../core/extensions/workers.md - - name: Create a Queue Service + - name: Create a queue service href: ../core/extensions/queue-service.md - name: Use scoped services with a BackgroundService href: ../core/extensions/scoped-service.md @@ -1351,7 +1352,7 @@ items: displayName: msi,windows service installer,setup.exe - name: Implement the IHostedService interface href: ../core/extensions/timer-service.md - - name: Deploy a Worker Service to Azure + - name: Deploy a worker service to Azure href: ../core/extensions/cloud-service.md - name: Caching href: ../core/extensions/caching.md diff --git a/docs/orleans/host/client.md b/docs/orleans/host/client.md index 5724fa0064485..66da52c604fc4 100644 --- a/docs/orleans/host/client.md +++ b/docs/orleans/host/client.md @@ -26,7 +26,7 @@ Despite these drawbacks, co-hosting client code with grain code is a popular opt ### Obtain a client from a host -If you host using the [.NET Generic Host](../../core/extensions/generic-host.md), the client is automatically available in the host's [dependency injection](../../core/extensions/dependency-injection.md) container. You can inject it into services such as [ASP.NET controllers](/aspnet/core/mvc/controllers/actions) or implementations. +If you host using the [.NET Generic Host](../../core/extensions/generic-host.md), the client is automatically available in the host's [dependency injection](../../core/extensions/dependency-injection/overview.md) container. You can inject it into services such as [ASP.NET controllers](/aspnet/core/mvc/controllers/actions) or implementations. :::zone target="docs" pivot="orleans-7-0" diff --git a/docs/orleans/host/monitoring/index.md b/docs/orleans/host/monitoring/index.md index dba903d718bb8..2bd315fe70cb1 100644 --- a/docs/orleans/host/monitoring/index.md +++ b/docs/orleans/host/monitoring/index.md @@ -13,7 +13,7 @@ Observability is one of the most important aspects of a distributed system. It's ## Logging -Orleans uses [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging) for all silo and client logs. You can use any logging provider compatible with `Microsoft.Extensions.Logging`. Your app code relies on [dependency injection](../../../core/extensions/dependency-injection.md) to get an instance of and uses it to log messages. For more information, see [Logging in .NET](../../../core/extensions/logging.md). +Orleans uses [Microsoft.Extensions.Logging](https://www.nuget.org/packages/Microsoft.Extensions.Logging) for all silo and client logs. You can use any logging provider compatible with `Microsoft.Extensions.Logging`. Your app code relies on [dependency injection](../../../core/extensions/dependency-injection/overview.md) to get an instance of and uses it to log messages. For more information, see [Logging in .NET](../../../core/extensions/logging.md). :::zone target="docs" pivot="orleans-7-0" diff --git a/docs/standard/garbage-collection/implementing-dispose.md b/docs/standard/garbage-collection/implementing-dispose.md index 844620c904934..de6677bde0066 100644 --- a/docs/standard/garbage-collection/implementing-dispose.md +++ b/docs/standard/garbage-collection/implementing-dispose.md @@ -170,7 +170,7 @@ The following derived classes in the namespac ## See also -- [Disposal of services](../../core/extensions/dependency-injection-guidelines.md#disposal-of-services) +- [Disposal of services](../../core/extensions/dependency-injection/guidelines.md#disposal-of-services) - - - diff --git a/docs/standard/garbage-collection/implementing-disposeasync.md b/docs/standard/garbage-collection/implementing-disposeasync.md index 3af8e8524b161..c0d4d788edf40 100644 --- a/docs/standard/garbage-collection/implementing-disposeasync.md +++ b/docs/standard/garbage-collection/implementing-disposeasync.md @@ -172,7 +172,7 @@ The highlighted lines in the following code show what it means to have "stacked For a dual implementation example of `IDisposable` and `IAsyncDisposable`, see the source code [on GitHub](https://github.com/dotnet/runtime/blob/035b729d829368c2790d825bd02db14f0c0fd2ea/src/libraries/System.Text.Json/src/System/Text/Json/Writer/Utf8JsonWriter.cs#L297-L345). -- [Disposal of services](../../core/extensions/dependency-injection-guidelines.md#disposal-of-services) +- [Disposal of services](../../core/extensions/dependency-injection/guidelines.md#disposal-of-services) - - - diff --git a/docs/standard/garbage-collection/includes/disposables-and-dependency-injection.md b/docs/standard/garbage-collection/includes/disposables-and-dependency-injection.md index f8f026325da40..a2db9ba4adbb0 100644 --- a/docs/standard/garbage-collection/includes/disposables-and-dependency-injection.md +++ b/docs/standard/garbage-collection/includes/disposables-and-dependency-injection.md @@ -5,6 +5,6 @@ ms.custom: include --- > [!TIP] -> With regard to dependency injection, when registering services in an , the [service lifetime](../../../core/extensions/dependency-injection.md#service-lifetimes) is managed implicitly on your behalf. The and corresponding orchestrate resource cleanup. Specifically, implementations of and are properly disposed at the end of their specified lifetime. +> With regard to dependency injection, when registering services in an , the [service lifetime](../../../core/extensions/dependency-injection/service-lifetimes.md) is managed implicitly on your behalf. The and corresponding orchestrate resource cleanup. Specifically, implementations of and are properly disposed at the end of their specified lifetime. > -> For more information, see [Dependency injection in .NET](../../../core/extensions/dependency-injection.md). +> For more information, see [Dependency injection in .NET](../../../core/extensions/dependency-injection/overview.md). diff --git a/docs/standard/runtime-libraries-overview.md b/docs/standard/runtime-libraries-overview.md index 42b69d05b4c2b..e820db6f0d3c3 100644 --- a/docs/standard/runtime-libraries-overview.md +++ b/docs/standard/runtime-libraries-overview.md @@ -23,7 +23,7 @@ The following table shows some examples of package-provided libraries. |---------------------------------|----------------------------------------| | [`Microsoft.Extensions.AI`][ai] | [AI](../ai/microsoft-extensions-ai.md) | | [`Microsoft.Extensions.Configuration`][configuration] | [Configuration](../core/extensions/configuration.md) | -| [`Microsoft.Extensions.DependencyInjection`][di] | [Dependency injection](../core/extensions/dependency-injection.md) | +| [`Microsoft.Extensions.DependencyInjection`][di] | [Dependency injection](../core/extensions/dependency-injection/overview.md) | | [`Microsoft.Extensions.FileSystemGlobbing`][fsg] | [File globbing](../core/extensions/file-globbing.md) | | [`Microsoft.Extensions.Hosting`][host] | [Generic Host](../core/extensions/generic-host.md) | | [`Microsoft.Extensions.Http`][http] | [HTTP](../core/extensions/httpclient-factory.md) |