大家好,在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