【愚公系列】2023年02月 WMS智能仓储系统-009.程序集动态注入
前言
1.反射的概念
程序集动态注入可以避免手动注入程序集产生大量的代码,要实现程序集的动态注入其实就是需要用到反射。
反射技术其实就是动态获取程序集的元数据的功能,反射通过动态加载dll,然后对其进行解析,从而创建对象,调用成员。
Type是对类的描述,Type类是实现反射的一个重要的类,通过它我们可以获取类中的所有信息,包括方法、属性等。可以动态调用类的属性、方法。
反射的出现让创建对象的方式发生了改变,因为过去面完创建对象都是直接通过new。
type案例
namespace My.Sqlserver.Dal
{
public class SqlServerHelper
{
private int age = 16;
public string Name { get; set; }
public string Query()
{
return string.Empty;
}
}
class SqlCmd
{
}
}
using System;
using System.Reflection;
namespace ReflectedDemo
{
class Program
{
static void Main(string[] args)
{
//加载程序集文件,在bin目录中查找
Assembly assembly = Assembly.Load("My.Sqlserver.Dal");
Console.WriteLine("----------------Modules----------------------");
var modules = assembly.GetModules();
foreach(var module in modules)
{
Console.WriteLine(module.Name);
}
Console.WriteLine("----------------Types----------------------");
var types = assembly.GetTypes(); //获取程序集中所有的类型,包括公开的和不公开的
foreach(var type in types)
{
Console.WriteLine(type.Name);
Console.WriteLine(type.FullName);
var members= type.GetMembers(); //获取Type中所有的公共成员
Console.WriteLine("----------------members----------------------");
foreach(var m in members)
{
Console.WriteLine(m.Name);
}
}
Console.WriteLine("----------------GetExportedTypes----------------------");
var exportedTypes = assembly.GetExportedTypes(); //获取程序集中所有的公共类型
foreach(var t in exportedTypes)
{
Console.WriteLine(t.Name);
}
Console.WriteLine("----------------GetType----------------------");
var typeName= assembly.GetType("SqlServerHelper");//获取程序集中指定名称的类型对象
Console.WriteLine(typeName.Name);
}
}
}
2.程序集加载的方法
.NET 加载程序集主要有Load, LoadFrom, LoadFile三个函数如下:
1.Load(AssemblyName)、Load(string)
通过接受一个程序集标识来加载程序集。如果是强命名程序集,则标识包括程序集名称、版本、语言文化、以及公有密钥标记,Load方法将导致CLR按照隐式加载的策略寻找并加载程序集。弱命名程序集则只是一个不带文件扩展名的程序集的名称,CLR不会到GAC中查找,如果没有指定私有目录,则在工作目录查找,如Assembly.Load(“Math”)。其中私有目录的定义可以在配置文件中指定。如应用程序MyApp.exe的配置文件可以定义为MyApp.exe.config。
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="App;App1;" />
</assemblyBinding>
</runtime>
</configuration>
2.LoadFrom(string)
参数是包括程序集清单的文件的名称或路径,包括文件扩展名。如需要加载D:/App/math.dll,可以使用语句:
Assembly a = Assembly.LoadFrom(@"D:/App/math.dll");
3.LoadFile(string)
用来加载指定路径上的程序集文件的内容。使用 LoadFile 方法来加载和检查具有相同标识但位于不同路径中的程序集。与 LoadFrom 不同,LoadFile 不会将文件加载到 LoadFrom 上下文中,也不会使用加载路径解析依赖项。LoadFile 在这个受限制的方案中很有用,因为 LoadFrom 不能用于加载标识相同但路径不同的程序集;它只加载第一个这样的程序集。
具体案例请看我另一篇文章:https://blog.csdn.net/aa2528877987/article/details/107897070
一、程序集动态注入
#region 注册所有程序集
services.RegisterAssembly();
#endregion
RegisterAssembly扩展方法如下
#region dynamic injection
/// <summary>
/// judge the dll to be injected by IDependency
/// </summary>
/// <param name="services">services</param>
private static IServiceCollection RegisterAssembly(this IServiceCollection services)
{
var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
var referencedAssemblies = System.IO.Directory.GetFiles(path, "ModernWMS*.dll").Select(Assembly.LoadFrom).ToArray();
var types = referencedAssemblies
.SelectMany(a => a.DefinedTypes)
.Select(type => type.AsType())
.Where(x => x != typeof(IDependency) && typeof(IDependency).IsAssignableFrom(x)).ToArray();
var implementTypes = types.Where(x => x.IsClass).ToArray();
var interfaceTypes = types.Where(x => x.IsInterface).ToArray();
foreach (var implementType in implementTypes)
{
var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
if (interfaceType != null)
services.AddScoped(interfaceType, implementType);
}
services.AddScoped<Services.IAccountService, Services.AccountService>();
return services;
}
#endregion
二、反射创建实例封装
/// <summary>
/// 反射帮助类
/// </summary>
public static class ReflectionHelper
{
/// <summary>
/// 创建对象实例
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="fullName">命名空间.类型名
/// <param name="assemblyName">程序集
/// <returns></returns>
public static T CreateInstance<t>(string fullName, string assemblyName)
{
string path = fullName + "," + assemblyName;//命名空间.类型名,程序集
Type o = Type.GetType(path);//加载类型
object obj = Activator.CreateInstance(o, true);//根据类型创建实例
return (T)obj;//类型转换并返回
}
/// <summary>
/// 创建对象实例
/// </summary>
/// <typeparam name="T">要创建对象的类型</typeparam>
/// <param name="assemblyName">类型所在程序集名称
/// <param name="nameSpace">类型所在命名空间
/// <param name="className">类型名
/// <returns></returns>
public static T CreateInstance<t>(string assemblyName, string nameSpace, string className)
{
try
{
string fullName = nameSpace + "." + className;//命名空间.类型名
//此为第一种写法
object ect = Assembly.Load(assemblyName).CreateInstance(fullName);//加载程序集,创建程序集里面的 命名空间.类型名 实例
return (T)ect;//类型转换并返回
//下面是第二种写法
//string path = fullName + "," + assemblyName;//命名空间.类型名,程序集
//Type o = Type.GetType(path);//加载类型
//object obj = Activator.CreateInstance(o, true);//根据类型创建实例
//return (T)obj;//类型转换并返回
}
catch
{
//发生异常,返回类型的默认值
return default(T);
}
}
}
- 点赞
- 收藏
- 关注作者
评论(0)