【懒人有道】在asp.net core中实现程序集注入

前言

在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<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             services.Configure<MemoryCacheEntryOptions>(
 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         }

 

总结

小记一下,好记性不如烂笔头。
posted @ 2017-05-17 10:18  杨浪  阅读(3653)  评论(5编辑  收藏  举报