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

C# 当前项目自动服务DI类

8

主题

8

帖子

24

积分

新手上路

Rank: 1

积分
24
思路

我发现 .NET Core WebAPi项目有一个与Springboot的不同之处,就是Springboot项目有自动装配机制,他可以将在src下面与启动类在同一级包目录下的类进行扫描注册
而之前我了解到Springboot的自动装配机制本质上也就是通过扫描对应包,然后进行通过它自身进行服务注册,我虽然写不出那么牛掰的东西,但是我也打算大致仿照一下
步骤如下:

  • 准备几个装饰类Server、Config等
  • 扫描当前程序集以及引用程序集被改装饰(注解)描述了的类和接口
  • 这里要注意注册的顺序,注册的顺序如果出错,就会爆出异常(本人已经经历过)
  • 同时还需要注意,我们读取配置注册配置类必须是在最前的,因为配置加载最好是在程序启动前全部加载完(不过想想也有懒加载的情况,这里我没有难么多考虑)

代码实现

我这里加载文件配置的代码和注册类的代码写到了一起,有点耦合,大家看可用适当的做拆分,OK希望我的这个小工具类可用帮助到大家,如果大家有更好的想法,希望写出来
  1. using gobangBack.Annotation;
  2. using System;
  3. using System.Reflection;
  4. namespace gobangBack.Utils
  5. {
  6.     // 这个扩展方法仅仅对于本项目
  7.     /// <summary>
  8.     /// 步骤:
  9.     /// 1.对于当前项目的Service目录下的所有带有Service结尾的类型进行父子关系判断
  10.     /// 2.进行服务类型和实现类型的一次性注入
  11.     /// </summary>
  12.     public static class ServiceAutoDI
  13.     {
  14.         public static readonly  Queue<Type> RegisterQueue = new Queue<Type>();
  15.         public static void AutoAllDI(this IServiceCollection service) {
  16.             AutoServerDIFromService(service);
  17.         }
  18.         // 1.自动注册服务类
  19.         public static void AutoServerDIFromService(this IServiceCollection service)
  20.         {
  21.             
  22.              // 1.获取当前程序集中有被Server修饰过的所有的类(并且不能是抽象类)
  23.              var classTypes = AppDomain.CurrentDomain.GetAssemblies()
  24.                         .SelectMany(assembly => assembly.GetTypes())
  25.                         .Where(type => type.IsClass && !type.IsAbstract)
  26.                         .Where(type => Attribute.IsDefined(type, typeof(Server)))
  27.                         .ToList();
  28.             // 2.获得当前程序集中被Server修饰过的所有接口
  29.             var interfaceTypes = AppDomain.CurrentDomain.GetAssemblies()
  30.                         .SelectMany(assembly => assembly.GetTypes())
  31.                         .Where(type => type.IsInterface)
  32.                         .Where(type => Attribute.IsDefined(type, typeof(Server)))
  33.                         .ToList();
  34.             // 3.获取当前程序集中被Config修饰过的配置类
  35.             var configClassTypes = AppDomain.CurrentDomain.GetAssemblies()
  36.                         .SelectMany(assembly => assembly.GetTypes())
  37.                         .Where(type => type.IsClass && !type.IsAbstract)
  38.                         .Where(type => Attribute.IsDefined(type, typeof(Config)))
  39.                         .ToList();
  40.             // 3.判断类型的构造方法中是否有被Server修饰的接口或者是被Config修饰的类
  41.             // 3.2获取到所有Server修饰类的构造函数中的所有参数
  42.             var quoteInterface = classTypes
  43.                 .SelectMany(classType => classType.GetConstructors())
  44.                 .SelectMany(constructor => constructor.GetParameters())
  45.             // 4.2 判断参数中是否有interfaceTypes的值,如果有,就将其找出放入注入队列中
  46.                 .Where(param => interfaceTypes.Contains(param.ParameterType))
  47.                 .Select(param => param.ParameterType).ToList();
  48.             foreach (var interfaceType in quoteInterface)
  49.             {
  50.                 RegisterQueue.Enqueue(interfaceType);
  51.                 interfaceTypes.Remove(interfaceType);
  52.             }
  53.             // 5.进行配置读取
  54.             // 5.1.先加载好配置文件
  55.             var configuration = new ConfigurationBuilder()
  56.                 .AddJsonFile("appsettings.json")
  57.                 .Build();
  58.             // 5.2.进行配置文件的读取,配置的名称必须和类名一致
  59.          
  60.             configClassTypes.ForEach(config =>
  61.             {
  62.                 // 1.获取到配置类结尾的部分
  63.                 string configName = config.FullName.Substring(config.FullName.LastIndexOf(".")+1);
  64.                 // 2.将配置类的配置添加到容器中
  65.                 // 2.1.获取到OptionsConfigurationServiceCollectionExtensions类中名称为Configure参数为IServiceCollection,IConfiguration的方法
  66.                 // 2.2.讲它的泛型类型设置config类型
  67.                 MethodInfo configureMethod = typeof(OptionsConfigurationServiceCollectionExtensions)
  68.                     .GetMethod("Configure", new Type[] { typeof(IServiceCollection), typeof(IConfiguration) })
  69.                     .MakeGenericMethod(config);
  70.                 // 3.读取配置文件中的名称为对应名称的部分,进行加载
  71.                 configureMethod.Invoke(null, new object[] { service, configuration.GetSection(configName) });
  72.                 // 4.注册配置类
  73.                 service.AddScoped(config);
  74.             });
  75.             // 6.优先注册在队列中的接口
  76.             var prioityInterface = RegisterQueue.ToArray();
  77.             foreach(var interfaceType in prioityInterface)
  78.             {
  79.                var implenmentTypes = classTypes.Where(classType => classType.IsAssignableTo(interfaceType))
  80.                     .ToList();
  81.                 implenmentTypes.ForEach(type =>
  82.                 {
  83.                     service.AddScoped(interfaceType, type);
  84.                 });
  85.             }
  86.             // 7.注册其它的接口类型
  87.             foreach(var interfaceType in interfaceTypes)
  88.             {
  89.                 var implenmentTypes = classTypes.Where(classType => classType.IsAssignableTo(interfaceType))
  90.                     .ToList();
  91.                 implenmentTypes.ForEach(type =>
  92.                 {
  93.                     service.AddScoped(interfaceType, type);
  94.                 });
  95.             }
  96.         }
  97.     }
  98. }
复制代码

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

举报 回复 使用道具