使用反射从外部程序集实例化类

本文关键字:实例化 程序集 从外部 反射 | 更新日期: 2023-09-27 18:17:48

我目前正在尝试开发一种在外部项目中使用反射以编程方式运行测试类的方法。下面是一个简化的代码块,它应该可以展示我的问题。

string pathToDLL = @"C:'Path'To'Test'Project'UnitTests.dll";
IEnumerable<Type> testClasses = assembly.GetExportedTypes();
Type testClass = testClasses.First();
object testClassInstance = assembly.CreateInstance(testClass.FullName); 

这段代码抛出以下异常:

'assembly.CreateInstance(testClass.FullName)' threw an exception of type 'System.Reflection.TargetInvocationException'
    Data: {System.Collections.ListDictionaryInternal}
    HResult: -2146232828
    HelpLink: null
    InnerException: {System.IO.FileNotFoundException: Could not load file or assembly 'Project.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.
File name: 'Project.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
   at Project.UnitTests.TestClass..ctor()}
    Message: "Exception has been thrown by the target of an invocation."
    Source: "System.Private.CoreLib"
    StackTrace: "   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)'r'n   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)'r'n   at System.Activator.CreateInstance(Type type, Boolean nonPublic)'r'n   at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)'r'n   at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)'r'n   at System.Reflection.Assembly.CreateInstance(String typeName, Boolean ignoreCase, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)'r'n   at System.Reflection.Assembly.CreateInstance(String typeName)"

在堆栈跟踪中,它声明"无法加载文件或程序集'Project.Core…'"。

这个项目是目标DLL直接引用的项目(它测试的项目)。有人知道为什么它不能自动拾取这些dll吗?


我已经研究了解决这个问题的方法:

  1. 这可能是dll已经编译的方式-这可以改变,因为我控制这一点-这是目前通过在解决方案级别运行dotnet build */*/project.json。这成功地编译了所有内容,并且所有相关的dll似乎都填充在bin文件夹中。我也调查了是否改变为dotnet publishdotnet build */*/project.json --configuration Release,虽然似乎都没有帮助。

  2. 我还研究了使用不同的编译方法,如Activator.CreateInstance,再次没有骰子。

  3. 我似乎没有看到一种方法来加载多个dll到同一个Assembly类,以便我可以控制引用。由于AppDomains已经从。net Core中删除,这看起来不像是可能的,尽管我可能是错误的/在错误的区域寻找。


如果我正在做的事情似乎不可能,有没有人知道这种功能是否可以使用不同的方法来实现?例如Roslyn吗?

使用反射从外部程序集实例化类

我只是想我会用我设法找到的解决方案更新这个问题,以防别人和我一样有同样的问题。虽然我要感谢@Emrah Süngü为我指出了正确的方向。

Emrah让我注意到这样一个事实:我需要导入我想要加载的DLL的依赖项,以便调用存储在其中的类。一种方法是扩展你的app.config,以便导入这些依赖关系——但是我想在运行时这样做(我不知道我要在启动程序之前运行的项目),所以我需要寻找另一个解决方案。

如果你不使用。net Core,这是相对简单的,因为AppDomains可以用来加载所有的依赖并执行你的代码。然而,由于这已经从。net Core中删除,我需要找到另一个兼容的解决方案。

我考虑过运行一个单独的进程(或Powershell),并更改工作目录,以便进程运行在存储其所需的所有依赖项的目录中。然而,我找不到一种方法来允许我对运行这些方法的结果做出反应。

后来我研究了如何操作AssemblyLoadContext类,但是(在撰写本文时)几乎没有关于该类如何操作的文档。我确实找到了这个答案,它能显著地帮助…https://stackoverflow.com/a/37896162/6012159

为了使它工作,我必须做一个小小的改变,而不是每次都创建一个新的AssemblyLoader(这会导致在试图调用程序集中的方法时抛出异常),我每次都重用AssemblyLoader(这消除了这个问题)。

public class AssemblyLoader : AssemblyLoadContext
{
    private string folderPath;
    public AssemblyLoader(string folderPath)
    {
        this.folderPath = folderPath;
    }
    protected override Assembly Load(AssemblyName assemblyName)
    {
        var deps = DependencyContext.Default;
        var res = deps.CompileLibraries.Where(d => d.Name.Contains(assemblyName.Name)).ToList();
        if (res.Count > 0)
        {
            return Assembly.Load(new AssemblyName(res.First().Name));
        }
        else
        {
            var apiApplicationFileInfo = new FileInfo($"{folderPath}{Path.DirectorySeparatorChar}{assemblyName.Name}.dll");
            if (File.Exists(apiApplicationFileInfo.FullName))
            {
                return this.LoadFromAssemblyPath(apiApplicationFileInfo.FullName);
            }
        }
        return Assembly.Load(assemblyName);
    }
}

可用于加载程序集,如:

string directory = @"C:'Path'To'Project'bin'Debug'netcoreapp1.0'publish'";
string pathToDLL = @"C:'Path'To'Project'bin'Debug'netcoreapp1.0'publish'project.dll";
AssemblyLoader al = new AssemblyLoader(directory);
Assembly assembly = al.LoadFromAssemblyPath(pathToDLL);

我假设"UnitTests.dll"取决于(引用)其他dll(s)和您的程序不知道在哪里寻找那些引用的dll(s)。您应该(实际上必须)告诉它在哪里寻找这些dll(s)。默认情况下是与EXE相同的目录。你可以使用app.config来告诉你去哪里看。要使Load()方法成功,依赖的dll必须存储在应用程序的探测路径中。

这就是你得到错误的原因。

在这里你可以找到相关文章。https://msdn.microsoft.com/en-us/library/823z9h8w.aspx