ASP.NET Core - 依赖注入(四)

ops/2025/1/15 5:18:06/

ASP.NET Core - 依赖注入(四)

  • 4. ASP.NET Core默认服务
  • 5. 依赖注入配置变形

4. ASP.NET Core默认服务

之前讲了中间件,实际上一个中间件要正常进行工作,通常需要许多的服务配合进行,而中间件中的服务自然也是通过 Ioc 容器进行注册和注入的。前面也讲到,按照约定中间件的封装一般会提供一个 User{Middleware} 的扩展方法给用户使用,而服务注册中也有一个类似的约定,一般会有一个 Add{Services} 的扩展方法。

例如一个WebApi项目中,对于控制器路由终结点中间件的配置使用:

builder.Services.AddControllers();var app = builder.Build();
app.MapControllers();

这也是我们在日常开发中可以学习的方式,随着业务增长,需要依赖注入的服务也越来越多,我们可以根据业务模块,通过扩展方法将相应模块的服务注入注册进行封装,命名为 Add{Services},更加清晰明了地对我们的业务进行封装。

.NET Core 框架下默认提供250个以上的的服务,包括 ASP.NET Core MVC、EF Core 等等,当然这些服务很多不会默认就注入到容器中,我们在新建一个项目的时候,不同项目框架的模板会帮我们默认配置好一些最基本的必须的服务,其他的服务我们可以根据自己的需要进行使用。

在这里插入图片描述

5. 依赖注入配置变形

随着业务的增长,我们项目工作中的类型、服务越来越多,而每一个服务的依赖注入关系都需要在入口文件通过Service.Add{xxx}方法去进行注册,这将是非常麻烦的,入口文件需要频繁改动,而且代码组织管理也会变得麻烦,非常不优雅。

在许多框架中会对这种通过 Service.Add{xxx} 在代码中显式注册依赖注入关系的方式进行变形,有的可以通过配置文件进行注册,例如 Java Spring 框架就有这样大量的配置文件,有的可以通过接口进行默认注册,有的通过特性进行默认注册。

这里稍微简单介绍一下依赖注入默认注册的原理,其实也就是通过反射的一些手段,再加上一些约定好的规则而已。

首先需要三个生命周期接口,如下,这三个接口没有内容,仅仅只是作为标记而已。

public interface ISingleton
{
}
public interface IScoped
{
}
public interface ITransient
{
}

之后需要一个扩展方法,如下:

namespace Microsoft.Extensions.DependencyInjection
{public static class ServiceCollectionDependencyExtensions{public static IServiceCollection AddAutoInject<T>(this IServiceCollection services){var register = new ServiceRegister();register.AddAssembly(services, typeof(T).Assembly);return services;}}
}

这个扩展方法中调用了注册器,往容器中注入服务,实现如下:

public class ServiceRegister
{public void AddAssembly(IServiceCollection services, Assembly assembly){// 查找程序中的类型var types = assembly.GetTypes().Where(t => t != null && t.IsClass && !t.IsAbstract && !t.IsGenericType);// 遍历每一个类检查释放满足约定的规则foreach (var type in types){AddType(services, type);}}/// <summary>/// 添加当前类型的依赖注入关系/// </summary>/// <param name="services"></param>/// <param name="type"></param>public void AddType(IServiceCollection services, Type type){var lifetime = GetLifetimeOrNull(type);if (lifetime == null){return;}var exposeServices = ExposeService(type);foreach (var serviceType in exposeServices){var serviceDescriptor = new ServiceDescriptor(serviceType, type, lifetime.Value);services.Add(serviceDescriptor);}}/// <summary>/// 根据标记接口确定生命周期,如果没有添加标记接口的,则不会被自动注册到容器/// </summary>/// <param name="type"></param>/// <returns></returns>public ServiceLifetime? GetLifetimeOrNull(Type type){if (typeof(ISingleton).IsAssignableFrom(type)){return ServiceLifetime.Singleton;}if(typeof(IScoped).IsAssignableFrom(type)){return ServiceLifetime.Scoped;}if(typeof(ITransient).IsAssignableFrom(type)){return ServiceLifetime.Transient;}return null;}/// <summary>/// 根据约定的规则查找当前类对于的服务类型/// 通过接口实现的方式,查找当前类实现的接口,如果一个接口名称去除了 "I" 之后与当前类的后半段一样,/// 则当前类应该被注册为这个接口的服务。/// </summary>/// <param name="type"></param>/// <returns></returns>public IList<Type> ExposeService(Type type){var serviceTypes = new List<Type>();var interfaces = type.GetInterfaces();foreach (var interfacesType in interfaces){var interfaceName = interfacesType.Name;if (interfaceName.StartsWith("I")){interfaceName = interfaceName.Substring(1);}if (type.Name.EndsWith(interfaceName)){serviceTypes.Add(interfacesType);}}return serviceTypes;}
}

整体的逻辑就是查找遍历程序集中的所有类型,并通过判别类型是否实现之前定好的三个生命周期接口,从而确定类型是否需要自动注册到容器中,如果需要再根据约定好的规则获取需要注册的服务类型,并且构建服务描述器,再将其添加到容器中。

之后在入口文件中这样使用:

builder.Services.AddAutoInject<Program>();

而需要自动注入的服务只要多实现一个标记接口即可:

public class Rabbit : IRabbit, ITransient
{
}

以上主要介绍一下依赖注入自动化注册的思路和基本实现,代码只是一个基本的演示,比较简单,很多细节也没有在这里体现,但是核心的思路是和ABP框架中的自动注入的方式一样的,有兴趣详细了解一下ABP中的依赖注入的机制的童鞋,可以看一下我其他的文章: ABP 依赖注入(1)



参考文章:
ASP.NET Core 依赖注入 | Microsoft Learn
理解ASP.NET Core - 依赖注入(Dependency Injection)



ASP.NET Core 系列:

目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core — 依赖注入(三)
下一篇:ASP.NET Core — 配置系统之配置读取)


http://www.ppmy.cn/ops/150210.html

相关文章

Java学习,集合遍历

Java遍历集合&#xff08;如List, Set, Map等&#xff09;通常有多种方法。遍历集合的方式&#xff0c;包括传统for循环、增强的for循环&#xff08;也称"for-each"循环&#xff09;、迭代器&#xff08;Iterator&#xff09;以及流&#xff08;Stream&#xff09;AP…

Mysql--运维篇--备份和恢复(逻辑备份,mysqldump,物理备份,热备份,温备份,冷备份,二进制文件备份和恢复等)

MySQL 提供了多种备份方式&#xff0c;每种方式适用于不同的场景和需求。根据备份的粒度、速度、恢复时间和对数据库的影响&#xff0c;可以选择合适的备份策略。主要备份方式有三大类&#xff1a;逻辑备份&#xff08;mysqldump&#xff09;&#xff0c;物理备份和二进制文件备…

2025年三个月自学手册 网络安全(黑客技术)

&#x1f91f; 基于入门网络安全/黑客打造的&#xff1a;&#x1f449;黑客&网络安全入门&进阶学习资源包 前言 什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“…

MySQL 学习指南与资料分享

MySQL 学习资料 MySQL 学习资料 MySQL 学习资料 在如今数据驱动发展的大趋势下&#xff0c;MySQL 作为开源关系型数据库管理系统的佼佼者&#xff0c;广泛应用于各个领域&#xff0c;从个人博客搭建到大型电商平台的数据管理&#xff0c;它都发挥着关键作用。如果你立志掌握这…

Ubuntu 部署Docker + Dify,遇到的坑, 最新亲测镜像

这里写自定义目录标题 Ubuntu 部署Docker Dify virtual box双向粘贴 VBoxClient --clipboard -d Ubuntu docker更新软件包安装docker依赖添加Docker官方GPG密钥添加Docker软件源安装docker配置用户组&#xff08;可选&#xff09;运行docker systemctl start docker验证是否成…

网管平台(进阶篇):路由器的管理实践

在当今数字化时代&#xff0c;路由器作为网络连接的核心设备&#xff0c;其管理对于确保网络的稳定、高效和安全至关重要。本文旨在深入探讨路由器管理的重要性、基本设置步骤、高级功能配置以及日常维护&#xff0c;帮助读者构建一个高效且安全的网络环境。 一、路由器管理的…

mysql的mvcc理解

人阅读 一、说到mvcc就少不了事务隔离级别&#xff08;大白话解释&#xff09; 序列化&#xff08;SERIALIZABLE&#xff09;&#xff1a;事务之间完全隔离&#xff0c;当成一个序列&#xff0c;一个一个执行。 1 可重复读&#xff08;REPEATABLE READ&#xff09;&#xff…

C#里使用libxl设置EXCEL里公式计算的例子

在EXCEL的使用里,经常使用的是公式功能, 为什么会这样说呢? 因为公式是一种自动化计算工具,并且可以固化人类的智慧,相当于把复杂的计算功能嵌入到固定的数据处理了。比如一个经验丰富的财务人员,可以编制一个复杂公式计算的表格,只要一个不懂财务的人员,输入每个人的…