前言
在asp.net core中,我巨硬引入了DI容器,我们可以在不使用第三方插件的情况下轻松实现依赖注入。如下代码:
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(); 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 Contact19 {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 ///2 /// IServiceCollection扩展 3 /// 4 public static class ServiceExtension 5 { 6 ///7 /// 用DI批量注入接口程序集中对应的实现类。 8 /// 13 /// 14 /// 接口程序集的名称(不包含文件扩展名)15 ///9 /// 需要注意的是,这里有如下约定:10 /// IUserService --> UserService, IUserRepository --> UserRepository.11 /// 12 ///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 /// 43 /// 用DI批量注入接口程序集中对应的实现类。44 /// 45 /// 46 /// 接口程序集的名称(不包含文件扩展名)47 /// 实现程序集的名称(不包含文件扩展名)48 ///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 //过滤掉抽象类、泛型类以及非class76 var implementType = implementAssembly.DefinedTypes77 .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 ///4 /// 获取项目程序集,排除所有的系统程序集(Microsoft.***、System.***等)、Nuget下载包 5 /// 6 ///7 public static IList GetAllAssemblies() 8 { 9 var list = new List ();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 try15 {16 var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));17 list.Add(assembly);18 }19 catch (Exception)20 {21 // ignored22 }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 GetAllTypes()33 {34 var list = new List ();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 GetTypesByAssembly(string assemblyName)47 {48 var list = new List ();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 services.Configure( 6 options => options.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5));//设置缓存有效时间为5分钟。 7 8 // Add framework services. 9 services.AddMvc();10 11 return services.BuilderInterceptableServiceProvider(builder => builder.SetDynamicProxyFactory());12 }
总结
小记一下,好记性不如烂笔头。