首页 > 其他分享 >.Net Core中一种应用层各个项目工程自动注入(DI)的方式

.Net Core中一种应用层各个项目工程自动注入(DI)的方式

时间:2024-12-10 18:32:47浏览次数:10  
标签:Core Assembly DI services var return Net public asm

大家好,在DotNetCore开发中我们会用到DI也就是注入 一般在Program.cs文件中使用IServiceCollection去添加要注入的对象,但在实际开发中我们不可能去频繁的修改Program.cs文件并且把所有业务相关的注入对象都暴露在Program中也是存在开发风险的,因此有必要封装一个自动注入的服务,在Program中仅注册该定制服务就可实现上述需求是最好的方式。本文主要介绍一种符合该定制服务的功能实现。
大概分为以下几部分
1.创建一个自动DI的接口:IModuleInitializer
2.创建实现自动DI接口的实现类:ProjectModuleInitializer
3.给Microsoft.Extensions.DependencyInjection类添加扩展方法(Program中利用IServiceCollection注册相关注入对象用)
4.其他帮助类
整体思路:声明一个接口,在Microsoft.Extensions.DependencyInjection空间下追加扩展类和扩展方法,让该空间下的IServiceCollection能够引入到该扩展方法中,同时该接口在扩展方法中被订阅,这样每个项目中都可以自己写一些实现了该接口的类,在其中注册自己需要的服务,也就是相当于把自动注册这部分封装起来了
下面我们展示代码
1.自动DI注入接口:IModuleInitializer

 1 using Microsoft.Extensions.DependencyInjection;
 2 
 3 namespace WebApi.Core.ProjectDependencyInjection
 4 {
 5     public interface IModuleInitializer
 6     {
 7         /// <summary>
 8         /// 所有项目中的实现了IModuleInitializer接口都会被调用,请在Initialize中编写注册本模块需要的服务。
 9         /// 一个项目中可以放多个实现了IModuleInitializer的类。
10         /// 不过为了集中管理,还是建议一个项目中只放一个实现了IModuleInitializer的类
11         /// </summary>
12         public interface IModuleInitializer
13         {
14             public void Initialize(IServiceCollection services);
15         }
16     }
17 }

注意该接口的引用命名空间
using Microsoft.Extensions.DependencyInjection
此处很重要
2.给Microsoft.Extensions.DependencyInjection类添加扩展方法:
创建一个静态类ModuleInitializerExtensions和静态方法RunModuleInitializers

 1 using System.Reflection;
 2 using static WebApi.Core.ProjectDependencyInjection.IModuleInitializer;
 3 
 4 namespace Microsoft.Extensions.DependencyInjection
 5 {
 6     public static class ModuleInitializerExtensions
 7     {
 8         /// <summary>
 9         /// 每个项目中都可以自己写一些实现了IModuleInitializer接口的类,在其中注册自己需要的服务,这样避免所有内容到入口项目中注册
10         /// </summary>
11         /// <param name="services">IServiceCollection</param>
12         /// <param name="assemblies">获取工程下所有的项目文件</param>
13         public static IServiceCollection RunModuleInitializers(this IServiceCollection services,
14          IEnumerable<Assembly> assemblies)
15         {
16             //遍历项目文件
17             foreach (var asm in assemblies)
18             {
19                 //获取当前文件类型
20                 Type[] types = asm.GetTypes();
21                 //筛选并获取需要的类型(即:实现了IModuleInitializer接口的类)
22                 //筛选条件:非抽象类&&且实现了IModuleInitializer接口
23                 var moduleInitializerTypes = types.Where(t => !t.IsAbstract && typeof(IModuleInitializer).IsAssignableFrom(t));
24                 foreach (var implType in moduleInitializerTypes)
25                 {
26                     var initializer = (IModuleInitializer?)Activator.CreateInstance(implType);
27                     if (initializer == null)
28                     {
29                         throw new ApplicationException($"Cannot create ${implType}");
30                     }
31                     //通过接口实现需要批量注入的指定的业务对象
32                     initializer.Initialize(services);
33                 }
34             }
35             return services;
36         }
37     }
38 }

注意命名空间和相关引用
3.创建获取解决方案下的所有项目文件获取方法
所需引用:
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics;
using System.Reflection.PortableExecutable;
using System.Reflection;
using System.Reflection.Metadata;

  1  public static class ReflectionHelper
  2  {
  3      /// <summary>
  4      /// 据产品名称获取程序集
  5      /// </summary>
  6      /// <param name="productName"></param>
  7      /// <returns></returns>
  8      public static IEnumerable<Assembly> GetAssembliesByProductName(string productName)
  9      {
 10          var asms = AppDomain.CurrentDomain.GetAssemblies();
 11          foreach (var asm in asms)
 12          {
 13              var asmCompanyAttr = asm.GetCustomAttribute<AssemblyProductAttribute>();
 14              if (asmCompanyAttr != null && asmCompanyAttr.Product == productName)
 15              {
 16                  yield return asm;
 17              }
 18          }
 19      }
 20      //是否是微软等的官方Assembly
 21      private static bool IsSystemAssembly(Assembly asm)
 22      {
 23          var asmCompanyAttr = asm.GetCustomAttribute<AssemblyCompanyAttribute>();
 24          if (asmCompanyAttr == null)
 25          {
 26              return false;
 27          }
 28          else
 29          {
 30              string companyName = asmCompanyAttr.Company;
 31              return companyName.Contains("Microsoft");
 32          }
 33      }
 34 
 35      private static bool IsSystemAssembly(string asmPath)
 36      {
 37          var moduleDef = AsmResolver.DotNet.ModuleDefinition.FromFile(asmPath);
 38          var assembly = moduleDef.Assembly;
 39          if (assembly == null)
 40          {
 41              return false;
 42          }
 43          var asmCompanyAttr = assembly.CustomAttributes.FirstOrDefault(c => c.Constructor?.DeclaringType?.FullName == typeof(AssemblyCompanyAttribute).FullName);
 44          if (asmCompanyAttr == null)
 45          {
 46              return false;
 47          }
 48          var companyName = ((AsmResolver.Utf8String?)asmCompanyAttr.Signature?.FixedArguments[0]?.Element)?.Value;
 49          if (companyName == null)
 50          {
 51              return false;
 52          }
 53          return companyName.Contains("Microsoft");
 54      }
 55 
 56      /// <summary>
 57      /// 判断file这个文件是否是程序集
 58      /// </summary>
 59      /// <param name="file"></param>
 60      /// <returns></returns>
 61      private static bool IsManagedAssembly(string file)
 62      {
 63          using var fs = File.OpenRead(file);
 64          using PEReader peReader = new PEReader(fs);
 65          return peReader.HasMetadata && peReader.GetMetadataReader().IsAssembly;
 66      }
 67 
 68      private static Assembly? TryLoadAssembly(string asmPath)
 69      {
 70          AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
 71          Assembly? asm = null;
 72          try
 73          {
 74              asm = Assembly.Load(asmName);
 75          }
 76          catch (BadImageFormatException ex)
 77          {
 78              Debug.WriteLine(ex);
 79          }
 80          catch (FileLoadException ex)
 81          {
 82              Debug.WriteLine(ex);
 83          }
 84 
 85          if (asm == null)
 86          {
 87              try
 88              {
 89                  asm = Assembly.LoadFile(asmPath);
 90              }
 91              catch (BadImageFormatException ex)
 92              {
 93                  Debug.WriteLine(ex);
 94              }
 95              catch (FileLoadException ex)
 96              {
 97                  Debug.WriteLine(ex);
 98              }
 99          }
100          return asm;
101      }
102 
103      /// <summary>
104      /// loop through all assemblies
105      /// </summary>
106      /// <returns></returns>
107      public static IEnumerable<Assembly> GetAllReferencedAssemblies(bool skipSystemAssemblies = true)
108      {
109          Assembly? rootAssembly = Assembly.GetEntryAssembly();
110          if (rootAssembly == null)
111          {
112              rootAssembly = Assembly.GetCallingAssembly();
113          }
114          var returnAssemblies = new HashSet<Assembly>(new AssemblyEquality());
115          var loadedAssemblies = new HashSet<string>();
116          var assembliesToCheck = new Queue<Assembly>();
117          assembliesToCheck.Enqueue(rootAssembly);
118          if (skipSystemAssemblies && IsSystemAssembly(rootAssembly) != false)
119          {
120              if (IsValid(rootAssembly))
121              {
122                  returnAssemblies.Add(rootAssembly);
123              }
124          }
125          while (assembliesToCheck.Any())
126          {
127              var assemblyToCheck = assembliesToCheck.Dequeue();
128              foreach (var reference in assemblyToCheck.GetReferencedAssemblies())
129              {
130                  if (!loadedAssemblies.Contains(reference.FullName))
131                  {
132                      var assembly = Assembly.Load(reference);
133                      if (skipSystemAssemblies && IsSystemAssembly(assembly))
134                      {
135                          continue;
136                      }
137                      assembliesToCheck.Enqueue(assembly);
138                      loadedAssemblies.Add(reference.FullName);
139                      if (IsValid(assembly))
140                      {
141                          returnAssemblies.Add(assembly);
142                      }
143                  }
144              }
145          }
146          var asmsInBaseDir = Directory.EnumerateFiles(AppContext.BaseDirectory,
147              "*.dll", new EnumerationOptions { RecurseSubdirectories = true });
148          foreach (var asmPath in asmsInBaseDir)
149          {
150              if (!IsManagedAssembly(asmPath))
151              {
152                  continue;
153              }
154              AssemblyName asmName = AssemblyName.GetAssemblyName(asmPath);
155              //如果程序集已经加载过了就不再加载
156              if (returnAssemblies.Any(x => AssemblyName.ReferenceMatchesDefinition(x.GetName(), asmName)))
157              {
158                  continue;
159              }
160              if (skipSystemAssemblies && IsSystemAssembly(asmPath))
161              {
162                  continue;
163              }
164              Assembly? asm = TryLoadAssembly(asmPath);
165              if (asm == null)
166              {
167                  continue;
168              }
169              //Assembly asm = Assembly.Load(asmName);
170              if (!IsValid(asm))
171              {
172                  continue;
173              }
174              if (skipSystemAssemblies && IsSystemAssembly(asm))
175              {
176                  continue;
177              }
178              returnAssemblies.Add(asm);
179          }
180          return returnAssemblies.ToArray();
181      }
182 
183      private static bool IsValid(Assembly asm)
184      {
185          try
186          {
187              asm.GetTypes();
188              asm.DefinedTypes.ToList();
189              return true;
190          }
191          catch (ReflectionTypeLoadException)
192          {
193              return false;
194          }
195      }
196 
197      class AssemblyEquality : EqualityComparer<Assembly>
198      {
199          public override bool Equals(Assembly? x, Assembly? y)
200          {
201              if (x == null && y == null) return true;
202              if (x == null || y == null) return false;
203              return AssemblyName.ReferenceMatchesDefinition(x.GetName(), y.GetName());
204          }
205 
206          public override int GetHashCode([DisallowNull] Assembly obj)
207          {
208              return obj.GetName().FullName.GetHashCode();
209          }
210      }
211  }

4.在Progarm.cs中注册封装好的服务

 1     public class Program
 2     {
 3         public static void Main(string[] args)
 4         {
 5             var builder = WebApplication.CreateBuilder(args);
 6             
 7             //应用各个工程自动DI
 8             var asmr = ReflectionHelper.GetAllReferencedAssemblies();
 9             builder.Services.RunModuleInitializers(asmr);
10             
11             var app = builder.Build();
12             app.UseHttpsRedirection();
13             app.UseAuthentication();
14             app.UseAuthorization();
15             //其他服务注册
16                    ...
17             app.MapControllers();
18             app.Run();
19         }
20     }

5.接口实现并进行业务对象注入
注意引用:
using static WebApi.Core.ProjectDependencyInjection.IModuleInitializer;

 1    public class ProjectModuleInitializer : IModuleInitializer
 2    {
 3        public void Initialize(IServiceCollection services)
 4        {
 5            //DBContext
 6            services.AddSingleton(typeof(DBContext<>));
 7            //services.AddSingleton(typeof(DBContextFactory<>));
 8            //Base
 9            services.AddScoped(typeof(IBaseRepository<>), typeof(BaseRepositoryImpl<>));
10            //Book
11            services.AddScoped<IBookRepository, BookRepositoryImpl>();
12            services.AddScoped<IBookService, BookServiceImpl>();
13            //Person
14            services.AddScoped<IPersonRepository, PersonRepositoryImpl>();
15            services.AddScoped<IPersonService, PersonServiceImpl>();
16            //Student
17            services.AddScoped<IStudentRepository, StudentRepositoryImpl>();
18            services.AddScoped<IStudentService, StudentServiceImpl>();
19            //Image
20            services.AddScoped<IImageRepository, ImageRepositoryImpl>();
21            services.AddScoped<IImageService, ImageServiceImpl>();
22            //Product
23            //services.AddScoped<IProductRepository, ProductRepositoryImpl>();
24            services.AddScoped<IProductService, ProductServiceImpl>();
25        }
26    }

项目结构截图:

 

 

 

 

综上,即可在项目实际开发中进行自动DI注入 不必把所有业务注入对象都在Program.cs中注册

以上 谢谢!

标签:Core,Assembly,DI,services,var,return,Net,public,asm
From: https://www.cnblogs.com/bsx133/p/18597830

相关文章

  • 效率暴涨!5 款设计师必备的 Stable Diffusion WebUI 模型
    大家好!今天继续为大家推荐5款优质的设计类StableDiffusionWebUI模型,包含春节插画、3D电商场景、中式花草元素、商业扁平插画等。一、CJillustration春节插画这是一个专门为春节炼制的SDXL大模型,是近几年国内比较流行的商业插画的风格。内容主要为家庭聚会和宴......
  • Stable Diffusion【二次元模型】:超级出色的动漫大模型
    今天给大家介绍的是一个动漫风格的大模型AnimagineXL3.1。AnimagineXL3.1基于StableDiffusionXL构建,旨在通过生成准确而详细的动漫角色,成为动漫迷、艺术家和内容创作者的宝贵资源。AnimagineXL3.1是AnimagineXLV3系列的升级更新,增强了之前的AnimagineXL......
  • Stable Diffusion【真人模型】:专注于亚洲女性写实的超高人气大模型
    在人工智能领域,StableDiffusion真人模型以其专注于亚洲女性写实的特点,成为了一款超高人气的大模型。它不仅展现了人工智能在图像生成领域的强大能力,更让人们感受到了真实的美丽。让我们一起走进StableDiffusion真人模型,领略它的魅力所在。在SD1.5时代,有一款非常受欢迎的......
  • 采集opc ua转profinet IO项目案例
    目录1 案例说明 12 VFBOX网关工作原理 13 准备工作 24 配置VFBOX网关采集OPCUA的数据 25 用PROFINETIO协议转发数据 46 案例总结 61 案例说明设置网关采集OPCUA设备数据把采集的数据转成profinetIO协议转发给其他系统。2 VFBOX网关工作原理VFBOX网关是协议转换网......
  • EDI系统与业务系统集成:选择中间数据库还是REST API方案?
    EDI项目中,对外企业可以借助专业的EDI系统,基于AS2、OFTP等国际通用的EDI传输协议搭建传输通道,并基于这些传输通道实现安全、可靠地数据传输。对内企业如何实现业务系统和EDI系统之间的数据同步呢?企业可以通过中间数据库、RESTAPI、WebService、共享文件夹等方式实现EDI系统与企业......
  • 细数 Profinet 转 CAN 广泛应用的典型案例
    介绍一款Profinet转CAN的网关,使得CAN设备能够轻松接入PROFINET网络,实现数据交换。无论是汽车制造、大型设备、工业控制,还是智能家庭、机器人网络互联,这款网关都能发挥关键作用。捷米特JM-PN-CAN网关作为PROFINET的从站功能通讯网关,连接到PROFINET总线中作为从站使用,连接......
  • 超强AI绘画工具StableDiffusion,SD整合包V4.9 来了 版本win加mac安装包以及搭载PS安装
    众所周知,StableDiffusion是非常强大的AI绘图工具,今天为大家带来的是StableDiffusionSD整合包v4.9版本安装说明。这里带来的安装版本是9月最新整合包sd-webui-aki-v4.9版本。WIN加MAC有需要stablediffusion整合包以及模型插件,可以扫描下方,免费获取一、整合包......
  • conftest.c:1:10: fatal error: jni.h: No such file or directory
     001、rocky9中编译安装R报错:conftest.c:1:10:fatalerror:jni.h:Nosuchfileordirectory这个错误表明编译器在尝试编译一个C语言源文件时,无法找到头文件jni.h。jni.h是JavaNativeInterface(JNI)的头文件,它允许Java代码调用本地C/C++代码。 002、编译时缺乏jni.h的......
  • [模板] 单源最短路 Dijksra
    单源最短路:Dijksra单源最短路,时间复杂度\(O(nlog(n+m))\)不适用于有负权边的图#include<bits/stdc++.h>usingnamespacestd;usingll=longlong;structedge{intto,w;};vector<vector<edge>>adj;vector<int>dis;vector<bool>vis;intn,m,......
  • macOS 完全卸载 Android Studio 教程
    在macOS上,直接删除AndroidStudio应用后,许多配置文件和缓存可能仍会保留在系统中。为了彻底清除AndroidStudio,以下是完整的卸载步骤。1.删除AndroidStudio应用本体打开Finder,进入/Applications目录。找到AndroidStudio.app,然后将其拖动到废纸篓。清空废纸篓......