From fad1516df91ad7da8e7392f99bd3f5cd10121849 Mon Sep 17 00:00:00 2001 From: Ryan Liu Date: Sun, 17 Aug 2025 15:30:31 -0700 Subject: [PATCH 1/4] Update dependency-injection-guidelines.md to suggest avoid singleton for stateless service Over years I have seen multiple companies use singleton as default IoC scope. So I want to update guide line to make clear what is the cost, suggest at least avoid singleton for stateless service --- .../extensions/dependency-injection-guidelines.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/core/extensions/dependency-injection-guidelines.md b/docs/core/extensions/dependency-injection-guidelines.md index 29ab417a6de92..1d6ec18196699 100644 --- a/docs/core/extensions/dependency-injection-guidelines.md +++ b/docs/core/extensions/dependency-injection-guidelines.md @@ -108,7 +108,7 @@ Register the instance with a scoped lifetime. Use instances with a transient lifetime. Use the factory pattern instead. +- Don't register instances with a transient lifetime. Use the factory pattern instead so the solved service can be manually disposed after it is done being used. - Don't resolve instances with a transient or scoped lifetime in the root scope. The only exception to this is if the app creates/recreates and disposes , but this isn't an ideal pattern. - Receiving an 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. @@ -153,6 +153,15 @@ The factory method of a singleton service, such as the second argument to [AddSi - 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). +- Only use singlton lifetime for the services with own state that is expensive to crete or globally shared. Avoid use singleton lifetime for the service which itself has no state. Most .NET IoC containers use "Transient" as 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. + - **Testing challenges**: Shared state and coupling can make unit testing more difficult. + - **Memory impact**: A singleton may keep a large object graph alive in memory for the lifetime of the application. + - **Fault tolerance**: If a singleton or any part of its dependency tree fails, it cannot easily recover. + - **Configuration reloading**: Singletons generally cannot support “hot reload” of configuration values. + - **Scope leakage**: A singleton can inadvertently capture scoped or transient dependencies, effectively promoting them to singletons and causing unintended side effects. + - **Initialization overhead**: When resolving a service (often starting from a scoped service in ASP.NET), the IoC container needs to look up for the singleton instance; If it does not already exist it need to create it in a thread-safe manner. While a stateless transient service is very cheap to create and destroy. Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Exceptions are rare, mostly special cases within the framework itself. From 617c93984b581d3b88203cc8a91f35914c7d17cd Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 13 Oct 2025 08:20:29 -0500 Subject: [PATCH 2/4] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/core/extensions/dependency-injection-guidelines.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/core/extensions/dependency-injection-guidelines.md b/docs/core/extensions/dependency-injection-guidelines.md index 1d6ec18196699..2816ba30d6d68 100644 --- a/docs/core/extensions/dependency-injection-guidelines.md +++ b/docs/core/extensions/dependency-injection-guidelines.md @@ -153,7 +153,7 @@ The factory method of a singleton service, such as the second argument to [AddSi - 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). -- Only use singlton lifetime for the services with own state that is expensive to crete or globally shared. Avoid use singleton lifetime for the service which itself has no state. Most .NET IoC containers use "Transient" as default scope. Considerations and drawbacks of singletons: +- Only use singleton lifetime for services with their own state that is expensive to create or globally shared. Avoid using singleton lifetime for services which themselves have no state. 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. - **Testing challenges**: Shared state and coupling can make unit testing more difficult. @@ -161,7 +161,7 @@ The factory method of a singleton service, such as the second argument to [AddSi - **Fault tolerance**: If a singleton or any part of its dependency tree fails, it cannot easily recover. - **Configuration reloading**: Singletons generally cannot support “hot reload” of configuration values. - **Scope leakage**: A singleton can inadvertently capture scoped or transient dependencies, effectively promoting them to singletons and causing unintended side effects. - - **Initialization overhead**: When resolving a service (often starting from a scoped service in ASP.NET), the IoC container needs to look up for the singleton instance; If it does not already exist it need to create it in a thread-safe manner. While a stateless transient service is very cheap to create and destroy. + - **Initialization overhead**: When resolving a service, the IoC container needs to look up the singleton instance. If it doesn't already exist, it needs to create it in a thread-safe manner. In contrast, a stateless transient service is very cheap to create and destroy. Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Exceptions are rare, mostly special cases within the framework itself. From 08e8d2de4cec068ee716ee7cea174f9c91e9ed3a Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 13 Oct 2025 08:22:42 -0500 Subject: [PATCH 3/4] Update docs/core/extensions/dependency-injection-guidelines.md --- docs/core/extensions/dependency-injection-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/core/extensions/dependency-injection-guidelines.md b/docs/core/extensions/dependency-injection-guidelines.md index 2816ba30d6d68..a7c52529aa7a4 100644 --- a/docs/core/extensions/dependency-injection-guidelines.md +++ b/docs/core/extensions/dependency-injection-guidelines.md @@ -159,7 +159,7 @@ The factory method of a singleton service, such as the second argument to [AddSi - **Testing challenges**: Shared state and coupling can make unit testing more difficult. - **Memory impact**: A singleton may keep a large object graph alive in memory for the lifetime of the application. - **Fault tolerance**: If a singleton or any part of its dependency tree fails, it cannot easily recover. - - **Configuration reloading**: Singletons generally cannot support “hot reload” of configuration values. + - **Configuration reloading**: Singletons generally cannot support "hot reload" of configuration values. - **Scope leakage**: A singleton can inadvertently capture scoped or transient dependencies, effectively promoting them to singletons and causing unintended side effects. - **Initialization overhead**: When resolving a service, the IoC container needs to look up the singleton instance. If it doesn't already exist, it needs to create it in a thread-safe manner. In contrast, a stateless transient service is very cheap to create and destroy. From ee186a8c2bad7ce7b01d85c56dbeff56fdf40ad8 Mon Sep 17 00:00:00 2001 From: David Pine Date: Mon, 13 Oct 2025 08:26:00 -0500 Subject: [PATCH 4/4] Update docs/core/extensions/dependency-injection-guidelines.md --- .../dependency-injection-guidelines.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/core/extensions/dependency-injection-guidelines.md b/docs/core/extensions/dependency-injection-guidelines.md index a7c52529aa7a4..91cae91385e2b 100644 --- a/docs/core/extensions/dependency-injection-guidelines.md +++ b/docs/core/extensions/dependency-injection-guidelines.md @@ -154,14 +154,14 @@ The factory method of a singleton service, such as the second argument to [AddSi - [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). - Only use singleton lifetime for services with their own state that is expensive to create or globally shared. Avoid using singleton lifetime for services which themselves have no state. 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. - - **Testing challenges**: Shared state and coupling can make unit testing more difficult. - - **Memory impact**: A singleton may keep a large object graph alive in memory for the lifetime of the application. - - **Fault tolerance**: If a singleton or any part of its dependency tree fails, it cannot easily recover. - - **Configuration reloading**: Singletons generally cannot support "hot reload" of configuration values. - - **Scope leakage**: A singleton can inadvertently capture scoped or transient dependencies, effectively promoting them to singletons and causing unintended side effects. - - **Initialization overhead**: When resolving a service, the IoC container needs to look up the singleton instance. If it doesn't already exist, it needs to create it in a thread-safe manner. In contrast, a stateless transient service is very cheap to create and destroy. + - **Thread safety**: A singleton must be implemented in a thread-safe way. + - **Coupling**: It can couple otherwise unrelated requests. + - **Testing challenges**: Shared state and coupling can make unit testing more difficult. + - **Memory impact**: A singleton may keep a large object graph alive in memory for the lifetime of the application. + - **Fault tolerance**: If a singleton or any part of its dependency tree fails, it cannot easily recover. + - **Configuration reloading**: Singletons generally cannot support "hot reload" of configuration values. + - **Scope leakage**: A singleton can inadvertently capture scoped or transient dependencies, effectively promoting them to singletons and causing unintended side effects. + - **Initialization overhead**: When resolving a service, the IoC container needs to look up the singleton instance. If it doesn't already exist, it needs to create it in a thread-safe manner. In contrast, a stateless transient service is very cheap to create and destroy. Like all sets of recommendations, you may encounter situations where ignoring a recommendation is required. Exceptions are rare, mostly special cases within the framework itself.