如何在C#中为运行时加载程序集及其所有依赖项以进行反射?

时间:2022-01-06 19:06:12

I'm writing a utility for myself, partly as an exercise in learning C# Reflection and partly because I actually want the resulting tool for my own use.

我正在为自己编写一个实用程序,部分原因是为了学习C#Reflection而且部分是因为我实际上想要自己使用的结果工具。

What I'm after is basically pointing the application at an assembly and choosing a given class from which to select properties that should be included in an exported HTML form as fields. That form will be then used in my ASP.NET MVC app as the beginning of a View.

我所追求的是基本上将应用程序指向程序集并选择一个给定的类,从中选择应作为字段包含在导出的HTML表单中的属性。然后,该表单将在我的ASP.NET MVC应用程序中用作View的开头。

As I'm using Subsonic objects for the applications where I want to use, this should be reasonable and I figured that, by wanting to include things like differing output HTML depending on data type, Reflection was the way to get this done.

因为我正在将Subsonic对象用于我想要使用的应用程序,所以这应该是合理的,我想通过想要根据数据类型包含不同输出HTML之类的东西,Reflection就是完成这项工作的方法。

What I'm looking for, however, seems to be elusive. I'm trying to take the DLL/EXE that's chosen through the OpenFileDialog as the starting point and load it:

然而,我正在寻找的东西似乎难以捉摸。我正在尝试将通过OpenFileDialog选择的DLL / EXE作为起点并加载它:

String FilePath = Path.GetDirectoryName(FileName);
System.Reflection.Assembly o = System.Reflection.Assembly.LoadFile(FileName);

That works fine, but because Subsonic-generated objects actually are full of object types that are defined in Subsonic.dll, etc., those dependent objects aren't loaded. Enter:

这工作正常,但由于Subsonic生成的对象实际上充满了在Subsonic.dll等中定义的对象类型,因此不会加载这些依赖对象。输入:

AssemblyName[] ReferencedAssemblies = o.GetReferencedAssemblies();

That, too, contains exactly what I would expect it to. However, what I'm trying to figure out is how to load those assemblies so that my digging into my objects will work properly. I understand that if those assemblies were in the GAC or in the directory of the running executable, I could just load them by their name, but that isn't likely to be the case for this use case and it's my primary use case.

那也包含了我所期望的。但是,我想弄清楚的是如何加载这些程序集,以便我挖掘到我的对象将正常工作。我理解,如果这些程序集在GAC或正在运行的可执行文件的目录中,我可以按名称加载它们,但对于这个用例而言可能不是这种情况,这是我的主要用例。

So, what it boils down to is how do I load a given assembly and all of its arbitrary assemblies starting with a filename and resulting in a completely Reflection-browsable tree of types, properties, methods, etc.

因此,它归结为如何加载给定的程序集及其所有任意程序集以文件名开头,并生成一个完全反射可浏览的类型,属性,方法等树。

I know that tools like Reflector do this, I just can't find the syntax for getting at it.

我知道像Reflector这样的工具可以做到这一点,我找不到获取它的语法。

2 个解决方案

#1


13  

Couple of options here:

几个选项在这里:

  1. Attach to AppDomain.AssemblyResolve and do another LoadFile based on the requested assembly.
  2. 附加到AppDomain.AssemblyResolve并根据请求的程序集执行另一个LoadFile。

  3. Spin up another AppDomain with the directory as its base and load the assemblies in that AppDomain.
  4. 以目录为基础启动另一个AppDomain,并在该AppDomain中加载程序集。

I'd highly recommend pursuing option 2, since that will likely be cleaner and allow you to unload all those assemblies after. Also, consider loading assemblies in the reflection-only context if you only need to reflect over them (see Assembly.ReflectionOnlyLoad).

我强烈建议继续使用选项2,因为这样可能会更清晰,并允许您在之后卸载所有这些组件。另外,如果只需要反映它们,请考虑在仅反射上下文中加载程序集(请参阅Assembly.ReflectionOnlyLoad)。

#2


3  

I worked out Kent Boogaart's second option. Essentially I had to:

我制定了Kent Boogaart的第二个选择。基本上我不得不:

1.) Implement the ResolveEventHandler in a separate class, inheriting from MarshalByRefObject and adding the Serializable attribute.

1.)在单独的类中实现ResolveEventHandler,继承MarshalByRefObject并添加Serializable属性。

2.) Add the current ApplicationBase, essentially where the event handler's dll is, to the AppDomain PrivateBinPath.

2.)将当前ApplicationBase(实际上是事件处理程序的dll所在的位置)添加到AppDomain PrivateBinPath。

You can find the code on github.

你可以在github上找到代码。

#1


13  

Couple of options here:

几个选项在这里:

  1. Attach to AppDomain.AssemblyResolve and do another LoadFile based on the requested assembly.
  2. 附加到AppDomain.AssemblyResolve并根据请求的程序集执行另一个LoadFile。

  3. Spin up another AppDomain with the directory as its base and load the assemblies in that AppDomain.
  4. 以目录为基础启动另一个AppDomain,并在该AppDomain中加载程序集。

I'd highly recommend pursuing option 2, since that will likely be cleaner and allow you to unload all those assemblies after. Also, consider loading assemblies in the reflection-only context if you only need to reflect over them (see Assembly.ReflectionOnlyLoad).

我强烈建议继续使用选项2,因为这样可能会更清晰,并允许您在之后卸载所有这些组件。另外,如果只需要反映它们,请考虑在仅反射上下文中加载程序集(请参阅Assembly.ReflectionOnlyLoad)。

#2


3  

I worked out Kent Boogaart's second option. Essentially I had to:

我制定了Kent Boogaart的第二个选择。基本上我不得不:

1.) Implement the ResolveEventHandler in a separate class, inheriting from MarshalByRefObject and adding the Serializable attribute.

1.)在单独的类中实现ResolveEventHandler,继承MarshalByRefObject并添加Serializable属性。

2.) Add the current ApplicationBase, essentially where the event handler's dll is, to the AppDomain PrivateBinPath.

2.)将当前ApplicationBase(实际上是事件处理程序的dll所在的位置)添加到AppDomain PrivateBinPath。

You can find the code on github.

你可以在github上找到代码。