使用反射从外部程序集实例化 class
Using reflection to instantiate a class from an external assembly
我目前正在尝试使用反射以编程方式在外部项目中开发一种 运行 测试 classes 的方法。这是一段简化的代码,应该可以展示我的问题。
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?
我研究了解决这个问题的方法:
可能是 dll 的编译方式 - 这可以更改,因为我可以控制它 - 目前由 运行 dotnet build */*/project.json
解决等级。这成功编译了所有内容,并且所有相关的 DLL 似乎都填充在 bin 文件夹中。我还调查了是否更改为 dotnet publish
或 dotnet build */*/project.json --configuration Release
,但似乎都没有帮助。
我也研究过使用不同的编译方法,比如 Activator.CreateInstance
再次没有骰子。
我似乎没有找到将多个 DLL 加载到同一个程序集中的方法 class 以便我可以控制引用。由于 AppDomains 已从 .NET Core 中删除,这看起来不太可能,尽管我可能 mistaken/looking 在错误的区域。
如果我正在做的事情看起来不太可能,有谁知道是否可以使用不同的方法实现这种功能? IE。罗斯林?
我假设 "UnitTests.dll" 依赖于(引用)其他 dll,而您的程序不知道在哪里寻找那些引用的 dll。您也应该(实际上必须)告诉它在哪里寻找那些 dll。默认情况下是与您的 EXE 相同的目录。您可以使用 app.config 来告诉要查找的其他位置。要使 Load() 成功,依赖的 dll 必须存储在应用程序的探测路径中。
这就是您收到错误的原因。
在这里您可以找到相关文章。
https://msdn.microsoft.com/en-us/library/823z9h8w.aspx
我只是想用我设法找到的解决方案来更新这个问题,以防其他人遇到与我相同的问题。虽然我要感谢@Emrah Süngü 为我指明了正确的方向。
Emrah 让我注意到我需要导入我想要加载的 DLL 的依赖项,以便调用存储在其中的 类。一种方法是扩展您的 app.config 以导入这些依赖项 - 但是我想在 运行 时间执行此操作(对于我不知道我要去的项目 运行 在启动程序之前)所以我需要寻找另一个解决方案。
如果您不使用 .NET Core,这相对简单,因为 AppDomains can be used to load all of the dependencies and execute your code. However, since this has been removed from .NET Core 我需要找到另一个兼容的解决方案。
我考虑过 运行 一个单独的进程(或 Powershell)的想法,并更改工作目录,以便该进程 运行 在存储所有依赖项的目录中它需要。但是,我找不到一种方法可以让我对 运行 方法的结果做出反应。
后来我研究了如何操纵 AssemblyLoadContext class, but (at the time of writing) there is little to no documentation on how this class. I did find this answer which was able to helped significantly...
为了让它工作,我确实必须做一点小改动,而不是每次都创建一个新的 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);
我目前正在尝试使用反射以编程方式在外部项目中开发一种 运行 测试 classes 的方法。这是一段简化的代码,应该可以展示我的问题。
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?
我研究了解决这个问题的方法:
可能是 dll 的编译方式 - 这可以更改,因为我可以控制它 - 目前由 运行
dotnet build */*/project.json
解决等级。这成功编译了所有内容,并且所有相关的 DLL 似乎都填充在 bin 文件夹中。我还调查了是否更改为dotnet publish
或dotnet build */*/project.json --configuration Release
,但似乎都没有帮助。我也研究过使用不同的编译方法,比如
Activator.CreateInstance
再次没有骰子。我似乎没有找到将多个 DLL 加载到同一个程序集中的方法 class 以便我可以控制引用。由于 AppDomains 已从 .NET Core 中删除,这看起来不太可能,尽管我可能 mistaken/looking 在错误的区域。
如果我正在做的事情看起来不太可能,有谁知道是否可以使用不同的方法实现这种功能? IE。罗斯林?
我假设 "UnitTests.dll" 依赖于(引用)其他 dll,而您的程序不知道在哪里寻找那些引用的 dll。您也应该(实际上必须)告诉它在哪里寻找那些 dll。默认情况下是与您的 EXE 相同的目录。您可以使用 app.config 来告诉要查找的其他位置。要使 Load() 成功,依赖的 dll 必须存储在应用程序的探测路径中。
这就是您收到错误的原因。
在这里您可以找到相关文章。 https://msdn.microsoft.com/en-us/library/823z9h8w.aspx
我只是想用我设法找到的解决方案来更新这个问题,以防其他人遇到与我相同的问题。虽然我要感谢@Emrah Süngü 为我指明了正确的方向。
Emrah 让我注意到我需要导入我想要加载的 DLL 的依赖项,以便调用存储在其中的 类。一种方法是扩展您的 app.config 以导入这些依赖项 - 但是我想在 运行 时间执行此操作(对于我不知道我要去的项目 运行 在启动程序之前)所以我需要寻找另一个解决方案。
如果您不使用 .NET Core,这相对简单,因为 AppDomains can be used to load all of the dependencies and execute your code. However, since this has been removed from .NET Core 我需要找到另一个兼容的解决方案。
我考虑过 运行 一个单独的进程(或 Powershell)的想法,并更改工作目录,以便该进程 运行 在存储所有依赖项的目录中它需要。但是,我找不到一种方法可以让我对 运行 方法的结果做出反应。
后来我研究了如何操纵 AssemblyLoadContext class, but (at the time of writing) there is little to no documentation on how this class. I did find this answer which was able to helped significantly...
为了让它工作,我确实必须做一点小改动,而不是每次都创建一个新的 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);