文章目录
概述
在 .NET Core 中,依赖注入(Dependency Injection, DI)是一种设计模式,用于将类的依赖关系从外部传入,而不是在类内部创建这些依赖。ASP.NET Core 内置了对 DI 的支持,并提供了几种不同的服务生命周期管理方式。本文档将详细介绍 AddTransient
、AddScoped
、AddSingleton
和 TryAddSingleton
四种方法,并通过示例展示它们的不同之处。
服务生命周期管理方法
1. AddTransient
- 生命周期:每次请求该服务时都会创建一个新的实例。
- 适用场景:当每次都需要一个新实例时使用。
- 示例:
builder.Services.AddTransient<ITransientService, TransientService>();
2. AddScoped
- 生命周期:在同一个作用域内共享同一个实例,例如,在处理一个 HTTP 请求期间,该作用域内的所有请求都将共享相同的实例。
- 适用场景:当需要在一次请求中保持数据的一致性时使用。
- 示例:
builder.Services.AddScoped<IScopedService, ScopedService>();
3. AddSingleton
- 生命周期:在整个应用程序生命周期中只有一个实例。
- 适用场景:当需要在整个应用程序中共享同一个实例时使用,如数据库连接池等。
- 示例:
builder.Services.AddSingleton<ISingletonService, SingletonService>();
4. TryAddSingleton
- 生命周期:与
AddSingleton
相同,但在注册服务时如果已经存在相同类型的服务,则不会再次添加。 - 适用场景:当你不确定是否已经有相同类型的服务被注册时使用,可以避免重复注册。
- 示例:
builder.Services.TryAddSingleton<ISingletonService, SingletonService>();
示例应用
假设我们有一个简单的 Web API 应用程序,其中包含四个服务:ITransientService
、IScopedService
和 ISingletonService
三个服务都有一个方法来获取其 ID,以便我们可以观察到它们的生命周期,另外还有一个ITestService
有三个方法分别请求了另外三个服务的获取ID的方法,用于模拟同一请求作用域内多次使用注入的情况。
服务接口和实现
Transient Service
public interface ITransientService
{
Guid GetId();
}
public class TransientService: ITransientService
{
private Guid _id = Guid.NewGuid();
public Guid GetId()
{
return _id;
}
}
Scoped Service
public interface IScopedService
{
Guid GetId();
}
public class ScopedService : IScopedService
{
private Guid _id = Guid.NewGuid();
public Guid GetId()
{
return _id;
}
}
Singleton Service
public interface ISingletonService
{
Guid GetId();
}
public class SingletonService : ISingletonService
{
private Guid _id = Guid.NewGuid();
public Guid GetId()
{
return _id;
}
}
Test Service
public interface ITestService
{
Guid GetScopeId();
Guid GetSingletonId();
Guid GetTransientId();
}
public class TestService: ITestService
{
private readonly ISingletonService _singletonService;
private readonly IScopedService _scopedService;
private readonly ITransientService _transientService;
public TestService(ISingletonService singletonService, IScopedService scopedService, ITransientService transientService)
{
_singletonService = singletonService;
_scopedService = scopedService;
_transientService = transientService;
}
public Guid GetScopeId()
{
return _scopedService.GetId();
}
public Guid GetSingletonId()
{
return _singletonService.GetId();
}
public Guid GetTransientId()
{
return _transientService.GetId();
}
}
注册服务
在 Program.cs
中注册这些服务:
builder.Services.AddTransient<ITransientService, TransientService>();
builder.Services.AddScoped<IScopedService, ScopedService>();
builder.Services.AddSingleton<ISingletonService, SingletonService>();
builder.Services.AddScoped<ITestService, TestService>();
测试控制器
为了验证这些服务的行为,可以在控制器中注入这些服务并调用 GetId
方法:
[Route("api/[controller]")]
[ApiController]
public class DITestController : ControllerBase
{
private readonly ISingletonService _singletonService;
private readonly IScopedService _scopedService;
private readonly ITransientService _transientService;
private readonly ITestService _testService;
public DITestController(ISingletonService singletonService, IScopedService scopedService, ITransientService transientService,ITestService testService)
{
_singletonService = singletonService;
_scopedService = scopedService;
_transientService = transientService;
_testService = testService;
}
[HttpGet("testSingleton")]
public IActionResult TestSingleton()
{
string str1 = _singletonService.GetId().ToString();
string str2 = _testService.GetSingletonId().ToString();
return Ok(new { str1, str2 });
}
[HttpGet("testScoped")]
public IActionResult TestScoped()
{
string str1 = _scopedService.GetId().ToString();
string str2 = _testService.GetScopeId().ToString();
return Ok(new { str1, str2 });
}
[HttpGet("testTransient")]
public IActionResult TestTransient()
{
string str1 = _transientService.GetId().ToString();
string str2 = _testService.GetTransientId().ToString();
return Ok(new { str1, str2 });
}
}
测试结果
- Transient:在同一请求作用域内返回不相同的 GUID(可以理解为每次给构造函数注入的都是不同的实例),每次请求都会返回不同的 GUID。
- Scoped:在同一请求作用域内返回相同的 GUID,每次请求都会返回不同的 GUID。
- Singleton:在整个应用程序生命周期内返回相同的 GUID。
总结
通过上述示例,我们可以清楚地看到不同生命周期的服务在实际应用中的行为。选择合适的服务生命周期可以提高应用程序的性能和可维护性。
- AddTransient:适用于每次都需要新实例的场景。
- AddScoped:适用于需要在一次请求中保持数据一致性的场景。
- AddSingleton:适用于需要在整个应用程序生命周期中共享同一个实例的场景。
- TryAddSingleton:适用于避免重复注册服务的场景。