.NET Core Web API 依赖注入(DI)服务生命周期

概述

在 .NET Core 中,依赖注入(Dependency Injection, DI)是一种设计模式,用于将类的依赖关系从外部传入,而不是在类内部创建这些依赖。ASP.NET Core 内置了对 DI 的支持,并提供了几种不同的服务生命周期管理方式。本文档将详细介绍 AddTransientAddScopedAddSingletonTryAddSingleton 四种方法,并通过示例展示它们的不同之处。

服务生命周期管理方法

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 应用程序,其中包含四个服务:ITransientServiceIScopedServiceISingletonService三个服务都有一个方法来获取其 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:适用于避免重复注册服务的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值