【发布时间】:2012-05-07 11:15:32
【问题描述】:
请阅读整个问题。我有一个独特的情况,有几个我想解决的限制。
在我的代码中,我有一个表达式树,它被编译为Predicate<System.Type>。我的目标是加载一个程序集而不锁定它(它是项目的输出程序集,不断重建),将此谓词应用于其类型列表并返回结果类型 names:
// this is what I need:
return assembly.GetTypes().Where(t => predicate(t)).Select(t => t.FullName);
这个程序集应该加载到另一个应用程序域中,因为我想在获得所需信息后立即卸载它。
这就是棘手的地方。我面临几个问题:
如果我在另一个 appdomain 中加载程序集并简单地返回一个包含其所有类型的数组,以便我可以将谓词应用回我的主 appdomain,只要将类型编组回我的主 appdomain,我就会得到一个FileNotFoundException,表示找不到此程序集。这是有道理的,因为程序集只被加载到我创建的另一个 appdomain 中。将它也加载到主应用程序域中会破坏目的。
如果,或者,我尝试将谓词传递到另一个应用程序域,在那里应用它并返回一个字符串数组(完整类型名称),我得到一个SerializationException: "Cannot serialize delegates over unmanaged function pointers, dynamic methods or methods outside the delegate creator's assembly.",因为谓词是一个动态方法(从表达式树编译)。
在主应用程序域中加载它不会有这些问题,但是由于在不卸载整个应用程序域的情况下无法卸载已加载的程序集,因此一旦程序集发生更改(重建后),任何尝试加载程序集的尝试同名会导致异常。
背景:
我正在为 ReSharper 构建一个名为 Agent Mulder 的插件。该插件背后的想法是分析解决方案中的 DI/IoC 容器注册,并帮助 ReSharper 找出通过 DI 容器注册的类型的使用情况(您可以观看有关其工作原理的简短视频here)。
在大多数情况下,分析容器注册很简单——我只需要检测足够的信息就可以知道哪些具体类型受到影响。在这个温莎城堡的例子中:Component.For<IFoo>().ImplementedBy<Foo>() 得到的类型很明显,AllTypes.FromThisAssembly().BasedOn<IFoo>() 也很明显 - 给我足够的信息来推测受这条线影响的具体类型。但是,请考虑在 Castle Windsor 中进行此注册:
container.Register(Classes
.FromAssemblyInDirectory(new AssemblyFilter(".").FilterByName(an => an.Name.StartsWith("Ploeh.Samples.Booking")))
.Where(t => !(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dispatcher<>)))
.WithServiceAllInterfaces());
(source)
这里的信息取决于仅在运行时评估的谓词。
由于我所能做的就是静态分析它,我手中有来自Where 子句的 lambda 表达式的 ReSharper AST(在 ReSharper 中称为 PSI)表示。我可以将此 AST 转换为 LINQ 表达式树,然后将其编译为委托。
我的想法是通过反射加载输出程序集(由FromAssembly* 描述符确定),并将此委托应用于程序集的类型以获得类型名称(我只需要名称)。每次组件更改时都必须重新评估这一点(我现在不关心性能)。
总之,除非有人可以推荐一种更好的方法来确定受谓词影响的类型,否则我想知道如何通过反射来做到这一点(不幸的是,我没有考虑其他元数据阅读器,因为我有以某种方式将 lambda 表达式 AST 转换为不同数据类型的谓词,我不知道是否存在 1 对 1 的转换)。
感谢您的阅读。此问题可用时将有 500 分奖励。
【问题讨论】:
-
如果
Expression是可序列化的,您可以将Predicate委托创建为Expression,并将其传递到应用域边界,然后在“远程”应用域中编译它。但可悲的是,情况并非如此:( -
@leppie 是的,我想过序列化表达式并传递它,你是对的,它不像听起来那么简单......
标签: .net reflection delegates resharper appdomain