AddTransient、AddScoped和AddSingleton服务差异解析

AddTransient、AddScoped和AddSingleton服务差异解析

技术背景

在 .NET 的依赖注入(Dependency Injection,简称 DI)系统中,服务的生命周期管理是一个核心概念。合理地选择服务的生命周期,能够显著影响应用程序的性能、资源使用效率以及线程安全性。AddTransientAddScopedAddSingleton 是 .NET 中用于注册服务并指定其生命周期的方法,它们分别代表了三种不同的服务生命周期模式。

实现步骤

定义接口和实现类

首先,我们需要定义一组接口和对应的实现类,用于演示不同生命周期的服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;

namespace DependencyInjectionSample.Interfaces
{
public interface IOperation
{
Guid OperationId { get; }
}

public interface IOperationTransient : IOperation
{
}

public interface IOperationScoped : IOperation
{
}

public interface IOperationSingleton : IOperation
{
}

public interface IOperationSingletonInstance : IOperation
{
}
}

using System;
using DependencyInjectionSample.Interfaces;
namespace DependencyInjectionSample.Classes
{
public class Operation : IOperationTransient, IOperationScoped, IOperationSingleton, IOperationSingletonInstance
{
Guid _guid;
public Operation() : this(Guid.NewGuid())
{

}

public Operation(Guid guid)
{
_guid = guid;
}

public Guid OperationId => _guid;
}
}

注册服务

ConfigureServices 方法中,使用 AddTransientAddScopedAddSingleton 方法注册不同生命周期的服务。

1
2
3
4
5
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
services.AddTransient<OperationService, OperationService>();

创建服务类

创建一个 OperationService 类,该类依赖于不同生命周期的 Operation 类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using DependencyInjectionSample.Interfaces;

namespace DependencyInjectionSample.Services
{
public class OperationService
{
public IOperationTransient TransientOperation { get; }
public IOperationScoped ScopedOperation { get; }
public IOperationSingleton SingletonOperation { get; }
public IOperationSingletonInstance SingletonInstanceOperation { get; }

public OperationService(IOperationTransient transientOperation,
IOperationScoped scopedOperation,
IOperationSingleton singletonOperation,
IOperationSingletonInstance instanceOperation)
{
TransientOperation = transientOperation;
ScopedOperation = scopedOperation;
SingletonOperation = singletonOperation;
SingletonInstanceOperation = instanceOperation;
}
}
}

创建控制器

创建一个 OperationsController 控制器,用于演示不同生命周期的服务在请求中的表现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
using DependencyInjectionSample.Interfaces;
using DependencyInjectionSample.Services;
using Microsoft.AspNetCore.Mvc;

namespace DependencyInjectionSample.Controllers
{
public class OperationsController : Controller
{
private readonly OperationService _operationService;
private readonly IOperationTransient _transientOperation;
private readonly IOperationScoped _scopedOperation;
private readonly IOperationSingleton _singletonOperation;
private readonly IOperationSingletonInstance _singletonInstanceOperation;

public OperationsController(OperationService operationService,
IOperationTransient transientOperation,
IOperationScoped scopedOperation,
IOperationSingleton singletonOperation,
IOperationSingletonInstance singletonInstanceOperation)
{
_operationService = operationService;
_transientOperation = transientOperation;
_scopedOperation = scopedOperation;
_singletonOperation = singletonOperation;
_singletonInstanceOperation = singletonInstanceOperation;
}

public IActionResult Index()
{
// ViewBag contains controller-requested services
ViewBag.Transient = _transientOperation;
ViewBag.Scoped = _scopedOperation;
ViewBag.Singleton = _singletonOperation;
ViewBag.SingletonInstance = _singletonInstanceOperation;

// Operation service has its own requested services
ViewBag.Service = _operationService;
return View();
}
}
}

核心代码

AddTransient

1
services.AddTransient<IOperationTransient, Operation>();

每次请求 IOperationTransient 服务时,都会创建一个新的 Operation 实例。

AddScoped

1
services.AddScoped<IOperationScoped, Operation>();

在同一个请求范围内,每次请求 IOperationScoped 服务时,都会返回同一个 Operation 实例;不同请求范围则会创建新的实例。

AddSingleton

1
services.AddSingleton<IOperationSingleton, Operation>();

在整个应用程序生命周期内,只会创建一个 Operation 实例,所有请求都会使用该实例。

最佳实践

  • Transient:适用于轻量级、无状态的服务,因为每次请求都会创建新实例,可能会消耗更多的内存和资源。例如,简单的工具类服务。
  • Scoped:当需要在一个请求范围内保持状态时,使用 Scoped 生命周期是一个不错的选择。例如,在 ASP.NET Core 中,DbContext 默认使用 Scoped 生命周期,确保在同一个请求中使用同一个数据库上下文。
  • Singleton:对于需要在整个应用程序中共享状态的服务,如配置服务、日志服务、缓存服务等,使用 Singleton 生命周期可以提高性能和资源利用率。但需要注意线程安全问题。

常见问题

避免依赖倒置问题

一个服务不应该依赖于生命周期比自己短的服务,否则可能会导致“捕获依赖”(Captive Dependencies)问题。例如,Singleton 服务不应该依赖于 Transient 或 Scoped 服务,因为这会导致 Transient 或 Scoped 服务的实例被意外地保留在整个应用程序生命周期内,可能会引发线程安全问题和内存泄漏。

线程安全问题

Singleton 服务由于在整个应用程序生命周期内共享同一个实例,需要特别注意线程安全问题。如果服务包含可变状态,需要使用适当的锁机制来避免并发访问问题。

内存泄漏问题

Singleton 服务如果持有大量内存,并且在应用程序运行期间内存使用不断增长,可能会导致内存泄漏。因此,对于高内存需求但使用频率较低的服务,不建议使用 Singleton 生命周期。


AddTransient、AddScoped和AddSingleton服务差异解析
https://119291.xyz/posts/addtransient-addscoped-and-addsingleton-services-differences/
作者
ww
发布于
2025年5月21日
许可协议