依赖注入

本文详细介绍了ASP.NET Core中依赖注入的重要性,包括管理类依赖、对象生命周期(单例、作用域、瞬时)和注册方式(花式注册、尝试注册、泛型模板、Remove&Replace)。同时,讨论了实例获取的两种方式:通过FromServicesAttribute标注和构造函数,并强调了依赖注入的最佳实践,提醒开发者避免不恰当的容器使用和依赖管理。

一、为什么要实现依赖注入?

第一:可以轻松管理类之间的依赖,帮助在构建应用时遵循设计原则,确保代码的可维护性和可扩展性。
第二:ASP.NET Core 的整个架构中,依赖注入框架提供了对象创建和生命周期管理的
核心能力,各个组件相互协作,也是由依赖注入框架的能力来实现的。

二、生命周期

1.单例 Singleton

同一个类型就一个实例。
代码如下(示例):

 services.AddSingleton<IService>(new ClientService());//直接注入实例
 services.AddSingleton<IService, ClientServiceExt>();

2.作用域 Scoped

在同一个作用域内是同一个实例,典型的场景是EFDBContext对象。同一个作用域共用同一个DBContext对象。
代码如下(示例):

   services.AddScoped<IService>(serviceProvider =>
            {
                return new ClientServiceExt();
            });

3.瞬时(暂时) Transient

每一次获取都是不同的实例。
代码如下(示例):

 services.AddTransient<ITransientService, TransientService>();

使用场景总结:
对于方法都是线程安全的类型,一般使用单例。
对于对象依赖了处理上下文,或者使用了有限资源(如数据库连接),一般使用Scoped或者Transient。
Scoped、Transient的区别是你在同一个上下文中是否期望使用同一个实例,如果是,用Scoped,反之则使用Transient

三、注册方式

1.花式注册

代码如下(示例):

 services.AddSingleton<IService>(new ClientService());//直接注入实例
            services.AddSingleton<IService, ClientServiceExt>();
            //services.AddSingleton<IService, ClientService>();
            //services.AddSingleton<IService>(serviceProvider => {
            //   //serviceProvider.GetService();可以聚合复杂的实例
            //    return new ClientService();
            //});
            services.AddScoped<IService>(serviceProvider =>
            {
                return new ClientServiceExt();
            });

2.尝试注册

若已经注册过的类型实例则不再注册。
代码如下(示例):

//services.TryAddSingleton<IService, ClientService>();
            //services.TryAddSingleton<IService, ClientServiceExt>();
            //services.TryAddEnumerable(ServiceDescriptor.Singleton<IService, ClientServiceExt>());
            //services.TryAddEnumerable(ServiceDescriptor.Singleton<IService, ClientServiceExt>());
            //services.TryAddEnumerable(ServiceDescriptor.Singleton<IService>(new ClientServiceExt()));

            //services.TryAddEnumerable(ServiceDescriptor.Singleton<IService>(serviceProvider =>
            //{
            //    return new ClientServiceExt();
            //}));
            //services.TryAddEnumerable(ServiceDescriptor.Transient<IService>(serviceProvider =>
            //{
            //    return new ClientServiceExt();
            //}));
            //services.TryAddEnumerable(ServiceDescriptor.Scoped<IService>(serviceProvider =>
            //{
            //    return new ClientServiceExt();
            //}));

3.注册泛型模板

代码如下(示例):

services.AddSingleton(typeof(IGenericService<>), typeof(GenericService<>));

4.Remove&Replace

Replace:先删除匹配的第一个,然后再末尾追加注册新的实例。
Remove:移除某个服务类型的所有注册。
代码如下(示例):

 services.Replace(ServiceDescriptor.Singleton<IService, ClientServiceExt>());
 services.RemoveAll<IService>();

四、实例获取方式

1.通过标注FromServicesAttribute

FromServicesAttribute是一个mvc的模型绑定描述类,因此只在controller的action参数上生效。
代码如下(示例):

  [HttpGet]
        public int PrintService([FromServices]ISingletonService singletonService1, [FromServices]ISingletonService singletonService2
            , [FromServices]ITransientService transientService1, [FromServices]ITransientService transientService2
            , [FromServices]IScopedService scopedService1, [FromServices]IScopedService scopedService2
            )
        {
            Debug.WriteLine($"singletonService1's hashcode ={singletonService1.GetHashCode()}");
            Debug.WriteLine($"singletonService2's hashcode ={singletonService2.GetHashCode()}");
            Debug.WriteLine($"transientService1's hashcode ={transientService1.GetHashCode()}");
            Debug.WriteLine($"transientService2's hashcode ={transientService2.GetHashCode()}");
            Debug.WriteLine($"scopedService1's hashcode ={scopedService1.GetHashCode()}");
            Debug.WriteLine($"scopedService2's hashcode ={scopedService2.GetHashCode()}");
            return 0;
        }

2.通过构造函数

代码如下(示例):

  private readonly ILogger<WeatherForecastController> _logger;
        private readonly IGenericService<IService> _genericService;

        public WeatherForecastController(ILogger<WeatherForecastController> logger,IGenericService<IService> genericService)
        {
            _logger = logger;
            _genericService = genericService;
        }

这二种方式的区别:全局使用则通过构造函数来获取,单个服务使用则通过FromServices标注来获取。

五、源码下载

下载

总结

  1. 避免通过静态属性的方式访问容器对象
  2. 避免在服务内部使用 GetService 方式来获取实例
  3. 避免使用静态属性存储单例,应该使用容器管理单例对象
  4. 避免在服务中实例化依赖对象,应该使用依赖注入来获得依赖对象
  5. 避免向单例的类型注入范围的类型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值