翼度科技»论坛 编程开发 .net 查看内容

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

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
4. ASP.NET Core默认服务

之前讲了中间件,实际上一个中间件要正常进行工作,通常需要许多的服务配合进行,而中间件中的服务自然也是通过 Ioc 容器进行注册和注入的。前面也讲到,按照约定中间件的封装一般会提供一个 User{Middleware} 的扩展方法给用户使用,而服务注册中也有一个类似的约定,一般会有一个 Add{Services} 的扩展方法。
例如一个WebApi项目中,对于控制器路由终结点中间件的配置使用:
  1. builder.Services.AddControllers();
  2. var app = builder.Build();
  3. app.MapControllers();
复制代码
这也是我们在日常开发中可以学习的方式,随着业务增长,需要依赖注入的服务也越来越多,我们可以根据业务模块,通过扩展方法讲相应模块的服务注入注册进行封装,命名为 Add{Services},更加清晰明了地对我们的业务进行封装。
.NET Core 框架下默认提供250个以上的的服务,包括 ASP.NET Core MVC、EF Core 等等,当然这些服务很多不会默认就注入到容器中,我们在新建一个项目的时候,不同项目框架的模板会帮我们默认配置好一些最基本的必须的服务,其他的服务我们可以根据自己的需要进行使用。

5. 依赖注入配置变形

随着业务的增长,我们项目工作中的类型、服务越来越多,而每一个服务的依赖注入关系都需要在入口文件通过Service.Add{}方法去进行注册,这将是非常麻烦的,入口文件需要频繁改动,而且代码组织管理也会变得麻烦,非常不优雅。
在许多框架中会对这种通过 Service.Add{xxx} 的方式在代码中显式注册依赖注入关系的方式进行变形,有的可以通过配置文件进行注册,例如 Java Spring 框架就有这样大量的配置文件,有的可以通过接口进行默认注册,有的通过特性进行默认注册。
这里稍微简单介绍一下依赖注入默认注册的原理,其实也就是通过放射的一些手段,再加上一些约定好的规则而已。
首先需要三个生命周期接口,如下,这三个接口没有内容,仅仅只是作为标记而已。
  1. public interface ISingleton
  2. {
  3. }
  4. public interface IScoped
  5. {
  6. }
  7. public interface ITransient
  8. {
  9. }
复制代码
之后需要一个扩展方法,如下:
  1. namespace Microsoft.Extensions.DependencyInjection
  2. {
  3.     public static class ServiceCollectionDependencyExtensions
  4.     {
  5.         public static IServiceCollection AddAutoInject<T>(this IServiceCollection services)
  6.         {
  7.             var register = new ServiceRegister();
  8.             register.AddAssembly(services, typeof(T).Assembly);
  9.             return services;
  10.         }
  11.     }
  12. }
复制代码
这个扩展方法中调用了注册器,往容器中注入服务,实现如下:
  1. public class ServiceRegister
  2. {
  3.     public void AddAssembly(IServiceCollection services, Assembly assembly)
  4.     {
  5.         // 查找程序中的类型
  6.         var types = assembly.GetTypes().Where(t => t != null && t.IsClass && !t.IsAbstract && !t.IsGenericType);
  7.         // 遍历每一个类检查释放满足约定的规则
  8.         foreach (var type in types)
  9.         {
  10.             AddType(services, type);
  11.         }
  12.     }
  13.     /// <summary>
  14.     /// 添加当前类型的依赖注入关系
  15.     /// </summary>
  16.     /// <param name="services"></param>
  17.     /// <param name="type"></param>
  18.     public void AddType(IServiceCollection services, Type type)
  19.     {
  20.         var lifetime = GetLifetimeOrNull(type);
  21.         if (lifetime == null)
  22.         {
  23.             return;
  24.       
  25.         var exposeServices = ExposeService(type);
  26.         foreach (var serviceType in exposeServices)
  27.         {
  28.             var serviceDescriptor = new ServiceDescriptor(serviceType, type, lifetime.Value);
  29.             services.Add(serviceDescriptor);
  30.         }
  31.     }
  32.     /// <summary>
  33.     /// 根据标记接口确定生命周期,如果没有添加标记接口的,则不会被自动注册到容器
  34.     /// </summary>
  35.     /// <param name="type"></param>
  36.     /// <returns></returns>
  37.     public ServiceLifetime? GetLifetimeOrNull(Type type)
  38.     {
  39.         if (typeof(ISingleton).IsAssignableFrom(type))
  40.         {
  41.             return ServiceLifetime.Singleton;
  42.               }
  43.         if(typeof(IScoped).IsAssignableFrom(type))
  44.         {
  45.             return ServiceLifetime.Scoped;
  46.               }
  47.         if(typeof(ITransient).IsAssignableFrom(type))
  48.         {
  49.             return ServiceLifetime.Transient;
  50.               }
  51.         return null;
  52.     }
  53.     /// <summary>
  54.     /// 根据约定的规则查找当前类对于的服务类型
  55.     /// 通过接口实现的方式,查找当前类实现的接口,如果一个接口名称去除了 "I" 之后与当前类的后半段一样,
  56.     /// 则当前类应该被注册为这个接口的服务。
  57.     /// </summary>
  58.     /// <param name="type"></param>
  59.     /// <returns></returns>
  60.     public IList<Type> ExposeService(Type type)
  61.     {
  62.         var serviceTypes = new List<Type>();
  63.         var interfaces = type.GetInterfaces();
  64.         foreach (var interfacesType in interfaces)
  65.         {
  66.             var interfaceName = interfacesType.Name;
  67.             if (interfaceName.StartsWith("I"))
  68.             {
  69.                 interfaceName = interfaceName.Substring(1);
  70.                   }
  71.             if (type.Name.EndsWith(interfaceName))
  72.             {
  73.                 serviceTypes.Add(interfacesType);
  74.             }
  75.         }
  76.         return serviceTypes;
  77.     }
  78. }
复制代码
整体的逻辑就是查找遍历程序集中的所有类型,并通过判别类型是否实现之前定好的三个生命周期接口,从而确定类型是否需要自动注册到容器中,如果需要再根据约定好的规则获取需要注册的服务类型,并且构建服务描述器,再将其添加到容器中。
之后在入口文件中这样使用:
  1. builder.Services.AddAutoInject<Program>();
复制代码
而需要自动注入的服务只要多实现一个标记接口即可:
  1. public class Rabbit : IRabbit, ITransient
  2. {
  3. }
复制代码
以上主要介绍一下依赖注入自动化注册的思路和基本实现,代码只是一个基本的演示,比较简单,很多细节也没有在这里体现,但是核心的思路是和ABP框架中的自动注入的方式一样的,有兴趣详细了解一下ABP中的依赖注入的机制的童鞋,可以看一下我其他的文章:  ABP 依赖注入(1)


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


ASP.NET Core 系列:
目录:ASP.NET Core 系列总结
上一篇:ASP.NET Core - 依赖注入(三)

来源:https://www.cnblogs.com/wewant/archive/2023/03/01/17110720.html
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x

举报 回复 使用道具