查看原文
其他

.NET Core 利用反射动态加载类库的方法

DotNet 2022-07-19

推荐关注↓

在.NET Framework时代,生成类库只需将类库项目编译好,然后拷贝到其他项目,即可引用或动态加载,相对来说,比较简单。但到了.NET Core时代,动态加载第三方类库,则稍微麻烦一些。

一、类库发布丢失Nuget依赖包

对于大部分类库来说,项目或多或少会引用第三方程序集,特别是Nuget程序包。通常编译类库项目生成的文件中,并不会包含引用的Nuget包相关类库,而是通过*.deps.json文件来描述类库所需的依赖。这样造成一种问题,如果该类库是通过动态加载的方式引用,则程序运行时,会提示“缺少某某dll”的问题。

解决上述问题,需要在该类库项目的.csproj文件中,在标签中加入true标志,该属性将告诉编译器,该项目是动态加载的组件。

相关链接:https://docs.microsoft.com/zh-cn/dotnet/core/project-sdk/msbuild-props#enabledynamicloading

二、类库编译生成多余的dll

对于类库项目来说,通常会引用解决方案中其他通用项目,而主体程序也会引用这些通用项目,所以对于类库来说,在编译生成的文件中,并不需要这些文件。这种情况下,也需要修改.csproj项目文件,如下图,生成的类库文件将不会包含pluginBase.csproj类库及其所有的依赖;

三、利用反射动态加载类库

如果按照文末参考文献中的教程,我尝试过仍会出现找不到“某某.dll”的问题,这边贴出我的代码,供参考:

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;

namespace LoadDLL
{
    /// <summary>
    /// 程序集加载器
    /// </summary>
    public class AssemblyLoader
    {
        private string _basePath;
        private AssemblyLoadContext context;


        public AssemblyLoader(string basePath)
        {
            _basePath = basePath;
        }

        public Type Load(string dllFileName, string typeName)
        {
                context = new AssemblyLoadContext(dllFileName);
                context.Resolving += Context_Resolving;
                //需要绝对路径
                string path = Path.Combine(_basePath, dllFileName);
                if (File.Exists(path))
                {
                    try
                    {
                        using (var stream = File.OpenRead(path))
                        {
                            Assembly assembly = context.LoadFromStream(stream);
                            Type type = assembly.GetType(typeName);
                            dicTypes.Add(typeName, type);
                            return type;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"加载节点{dllFileName}-{typeName}发生异常:{ex.Message},{ex.StackTrace}");
                    }

                }
                else
                {
                    Console.WriteLine($"节点动态库{dllFileName}不存在:{path}");
                }            
            return null;
        }

        /// <summary>
        /// 加载依赖文件
        /// </summary>
        /// <param name="context"></param>
        /// <param name="assemblyName"></param>
        /// <returns></returns>
        private Assembly Context_Resolving(AssemblyLoadContext context, AssemblyName assemblyName)
        {
            string expectedPath = Path.Combine(_basePath, assemblyName.Name + ".dll"); ;
            if (File.Exists(expectedPath))
            {
                try
                {
                    using (var stream = File.OpenRead(expectedPath))
                    {
                        return context.LoadFromStream(stream);
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"加载节点{expectedPath}发生异常:{ex.Message},{ex.StackTrace}");
                }
            }
            else
            {
                Console.WriteLine($"依赖文件不存在:{expectedPath}");
            }
            return null;
        }
    }
}

其中Context_Resolving(),是用于加载类库文件过程中,处理触发加载相关依赖文件的事件的方法,通过上述代码,可以保证将类库的完整地动态加载进程序,并且不会与其他动态加载类库项目发生程序集冲突的问题:比如A类库和B类库都有共同的依赖C,但两者的引用的C版本不同,通过AssemblyLoadContext可以保证A/B类库加载自己需要的版本。

四、结尾

在.NET Core动态加载类库还是挺多坑的,以上是我踩过来,与大家分享~

转自:iDream2016

链接:cnblogs.com/iDream2018/p/15349131.html


- EOF -

推荐阅读  点击标题可跳转
.NET 某上市工业智造 CPU+内存+挂死.NET Core 源码阅读 AspNetCoreRateLimitC# 防SQL注入最好实现方式是什么? 

看完本文有收获?请转发分享给更多人

推荐关注「DotNet」,提升.Net技能 

点赞和在看就是最大的支持❤️

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存