【愚公系列】2023年02月 WMS智能仓储系统-009.程序集动态注入

2023-03-16 16:21:20 浏览数 (2)

文章目录

  • 前言
    • 1.反射的概念
    • 2.程序集加载的方法
  • 一、程序集动态注入
  • 二、反射创建实例封装

前言

1.反射的概念

程序集动态注入可以避免手动注入程序集产生大量的代码,要实现程序集的动态注入其实就是需要用到反射。

反射技术其实就是动态获取程序集的元数据的功能,反射通过动态加载dll,然后对其进行解析,从而创建对象,调用成员。

Type是对类的描述,Type类是实现反射的一个重要的类,通过它我们可以获取类中的所有信息,包括方法、属性等。可以动态调用类的属性、方法。

反射的出现让创建对象的方式发生了改变,因为过去面完创建对象都是直接通过new。

type案例

代码语言:javascript复制
namespace My.Sqlserver.Dal
{
    public class SqlServerHelper
    {
        private int age = 16;
        public string Name { get; set; }
        public string Query()
        {
            return string.Empty;
        }
    }
   class SqlCmd
    {
    }
}
代码语言:javascript复制
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。

代码语言:javascript复制
<?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,可以使用语句:

代码语言:javascript复制
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

一、程序集动态注入

代码语言:javascript复制
#region 注册所有程序集
services.RegisterAssembly();
#endregion

RegisterAssembly扩展方法如下

代码语言:javascript复制
#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

二、反射创建实例封装

代码语言:javascript复制
/// <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 人点赞