【懒人有道】在asp.net core中实现程序集注入
前言
在asp.net core中,我巨硬引入了DI容器,我们可以在不使用第三方插件的情况下轻松实现依赖注入。如下代码:
View Code
1 // This method gets called by the runtime. Use this method to add services to the container. 2 public void ConfigureServices(IServiceCollection services) 3 { 4 //services.RegisterAssembly("IServices"); 5 services.AddSingleton<IUserService, UserService>(); 6 // Add framework services. 7 services.AddMvc(); 8 services.AddMvcCore() 9 .AddApiExplorer(); 10 services.AddSwaggerGen(options => 11 { 12 options.SwaggerDoc("v1", new Info() 13 { 14 Title = "Swagger测试", 15 Version = "v1", 16 Description = "Swagger测试RESTful API ", 17 TermsOfService = "None", 18 Contact = new Contact 19 { 20 Name = "来来吹牛逼", 21 Email = "xxoo123@outlook.com" 22 }, 23 }); 24 //设置xml注释文档,注意名称一定要与项目名称相同 25 var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "WebApi.xml"); 26 options.IncludeXmlComments(filePath); 27 }); 28 }
但是,随着公司业务的扩大,系统项目的功能模块急剧扩张,新增了不下百个或者千个Repository和Service(有点夸张了...),这时候如此单纯滴注入就有点操蛋了。
打懒主意
我可不可以通过反射技术来实现对程序集的注入呢??
试试就试试
首先,我私自先制定一些类名的约束。规则嘛,反正是自己定。比如:
- UserService --> IUserService
- UserRepository --> IUserRepository
- ......
- ClassName --> IClassName
1 /// <summary> 2 /// IServiceCollection扩展 3 /// </summary> 4 public static class ServiceExtension 5 { 6 /// <summary> 7 /// 用DI批量注入接口程序集中对应的实现类。 8 /// <para> 9 /// 需要注意的是,这里有如下约定: 10 /// IUserService --> UserService, IUserRepository --> UserRepository. 11 /// </para> 12 /// </summary> 13 /// <param name="service"></param> 14 /// <param name="interfaceAssemblyName">接口程序集的名称(不包含文件扩展名)</param> 15 /// <returns></returns> 16 public static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName) 17 { 18 if (service == null) 19 throw new ArgumentNullException(nameof(service)); 20 if (string.IsNullOrEmpty(interfaceAssemblyName)) 21 throw new ArgumentNullException(nameof(interfaceAssemblyName)); 22 23 var assembly = RuntimeHelper.GetAssembly(interfaceAssemblyName); 24 if (assembly == null) 25 { 26 throw new DllNotFoundException($"the dll \"{interfaceAssemblyName}\" not be found"); 27 } 28 29 //过滤掉非接口及泛型接口 30 var types = assembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface && !t.GetTypeInfo().IsGenericType); 31 32 foreach (var type in types) 33 { 34 var implementTypeName = type.Name.Substring(1); 35 var implementType = RuntimeHelper.GetImplementType(implementTypeName, type); 36 if (implementType != null) 37 service.AddSingleton(type, implementType); 38 } 39 return service; 40 } 41 42 /// <summary> 43 /// 用DI批量注入接口程序集中对应的实现类。 44 /// </summary> 45 /// <param name="service"></param> 46 /// <param name="interfaceAssemblyName">接口程序集的名称(不包含文件扩展名)</param> 47 /// <param name="implementAssemblyName">实现程序集的名称(不包含文件扩展名)</param> 48 /// <returns></returns> 49 public static IServiceCollection RegisterAssembly(this IServiceCollection service, string interfaceAssemblyName, string implementAssemblyName) 50 { 51 if (service == null) 52 throw new ArgumentNullException(nameof(service)); 53 if(string.IsNullOrEmpty(interfaceAssemblyName)) 54 throw new ArgumentNullException(nameof(interfaceAssemblyName)); 55 if (string.IsNullOrEmpty(implementAssemblyName)) 56 throw new ArgumentNullException(nameof(implementAssemblyName)); 57 58 var interfaceAssembly = RuntimeHelper.GetAssembly(interfaceAssemblyName); 59 if (interfaceAssembly == null) 60 { 61 throw new DllNotFoundException($"the dll \"{interfaceAssemblyName}\" not be found"); 62 } 63 64 var implementAssembly = RuntimeHelper.GetAssembly(implementAssemblyName); 65 if (implementAssembly == null) 66 { 67 throw new DllNotFoundException($"the dll \"{implementAssemblyName}\" not be found"); 68 } 69 70 //过滤掉非接口及泛型接口 71 var types = interfaceAssembly.GetTypes().Where(t => t.GetTypeInfo().IsInterface && !t.GetTypeInfo().IsGenericType); 72 73 foreach (var type in types) 74 { 75 //过滤掉抽象类、泛型类以及非class 76 var implementType = implementAssembly.DefinedTypes 77 .FirstOrDefault(t => t.IsClass && !t.IsAbstract && !t.IsGenericType && 78 t.GetInterfaces().Any(b => b.Name == type.Name)); 79 if (implementType != null) 80 { 81 service.AddSingleton(type, implementType.AsType()); 82 } 83 } 84 85 return service; 86 } 87 }
附上RuntimeHelper.cs的代码:
1 public class RuntimeHelper 2 { 3 /// <summary> 4 /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包 5 /// </summary> 6 /// <returns></returns> 7 public static IList<Assembly> GetAllAssemblies() 8 { 9 var list = new List<Assembly>(); 10 var deps = DependencyContext.Default; 11 var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包 12 foreach (var lib in libs) 13 { 14 try 15 { 16 var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name)); 17 list.Add(assembly); 18 } 19 catch (Exception) 20 { 21 // ignored 22 } 23 } 24 return list; 25 } 26 27 public static Assembly GetAssembly(string assemblyName) 28 { 29 return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName)); 30 } 31 32 public static IList<Type> GetAllTypes() 33 { 34 var list = new List<Type>(); 35 foreach (var assembly in GetAllAssemblies()) 36 { 37 var typeInfos = assembly.DefinedTypes; 38 foreach (var typeInfo in typeInfos) 39 { 40 list.Add(typeInfo.AsType()); 41 } 42 } 43 return list; 44 } 45 46 public static IList<Type> GetTypesByAssembly(string assemblyName) 47 { 48 var list = new List<Type>(); 49 var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(assemblyName)); 50 var typeInfos = assembly.DefinedTypes; 51 foreach (var typeInfo in typeInfos) 52 { 53 list.Add(typeInfo.AsType()); 54 } 55 return list; 56 } 57 58 public static Type GetImplementType(string typeName, Type baseInterfaceType) 59 { 60 return GetAllTypes().FirstOrDefault(t => 61 { 62 if (t.Name == typeName && 63 t.GetTypeInfo().GetInterfaces().Any(b => b.Name == baseInterfaceType.Name)) 64 { 65 var typeInfo = t.GetTypeInfo(); 66 return typeInfo.IsClass && !typeInfo.IsAbstract && !typeInfo.IsGenericType; 67 } 68 return false; 69 }); 70 } 71 }
好了,到此就基本完成了,记得在Startup.cs加上:
1 // This method gets called by the runtime. Use this method to add services to the container. 2 public IServiceProvider ConfigureServices(IServiceCollection services) 3 { 4 services.RegisterAssembly("IServices"); 5 6 // Add framework services. 7 services.AddMvc(); 8 }
总结
小记一下,好记性不如烂笔头。