【发布时间】:2011-03-02 14:03:54
【问题描述】:
更新:我现在有了一个让我更满意的解决方案,虽然没有解决我提出的所有问题,但它确实为这样做留下了明确的道路。我已经更新了自己的答案以反映这一点。
原始问题
给定一个应用程序域,Fusion(.Net 程序集加载器)将为给定程序集探测许多不同的位置。显然,我们认为这个功能是理所当然的,因为探测似乎嵌入在 .Net 运行时(Assembly._nLoad 内部方法似乎是反射加载时的入口点 - 我假设隐式加载可能被覆盖相同的底层算法),作为开发人员,我们似乎无法访问这些搜索路径。
我的问题是我有一个组件,它执行大量动态类型解析,并且需要能够确保给定 AppDomain 的所有用户部署程序集在它开始工作之前被预加载。是的,它会减慢启动速度——但我们从这个组件中获得的好处完全超过了这一点。
我已经写过的基本加载算法如下。它深度扫描一组文件夹中的任何 .dll(.exe 正在被排除此刻),如果在程序集中找不到它的 AssemblyName,则使用 Assembly.LoadFrom 加载 dll已经加载到 AppDomain 中(这实现效率低,但可以稍后优化):
void PreLoad(IEnumerable<string> paths)
{
foreach(path p in paths)
{
PreLoad(p);
}
}
void PreLoad(string p)
{
//all try/catch blocks are elided for brevity
string[] files = null;
files = Directory.GetFiles(p, "*.dll", SearchOption.AllDirectories);
AssemblyName a = null;
foreach (var s in files)
{
a = AssemblyName.GetAssemblyName(s);
if (!AppDomain.CurrentDomain.GetAssemblies().Any(
assembly => AssemblyName.ReferenceMatchesDefinition(
assembly.GetName(), a)))
Assembly.LoadFrom(s);
}
}
使用 LoadFrom 是因为我发现使用 Load() 可能会导致 Fusion 加载重复的程序集,如果它在探测它时找不到从它期望找到它的位置加载的程序集。
因此,有了这个,我现在要做的就是按照优先顺序(从高到低)获取 Fusion 在搜索程序集时将使用的搜索路径列表。然后我可以简单地遍历它们。
GAC 与此无关,我对 Fusion 可能使用的任何环境驱动的固定路径不感兴趣 - 只有那些可以从 AppDomain 收集到的路径,其中包含明确为应用部署的程序集。
我的第一次迭代只是使用了 AppDomain.BaseDirectory。这适用于服务、表单应用和控制台应用。
但是,它不适用于 Asp.Net 网站,因为至少有两个主要位置 - AppDomain.DynamicDirectory(Asp.Net 放置动态生成的页面类和 Asp 页面代码引用的任何程序集) ),然后是站点的 Bin 文件夹 - 可以从 AppDomain.SetupInformation.PrivateBinPath 属性中找到。
所以我现在有了最基本的应用程序类型的工作代码(Sql Server 托管的 AppDomain 是另一个故事,因为文件系统是虚拟化的) - 但几天前我遇到了一个有趣的问题,该代码根本没有'不起作用:nUnit 测试运行器。
这使用了影子复制(因此我的算法需要从影子复制放置文件夹中发现并加载它们,而不是从 bin 文件夹中),并将 PrivateBinPath 设置为相对于基本目录。
当然还有很多我可能没有考虑过的其他托管场景;但它必须是有效的,否则 Fusion 会在加载程序集时阻塞。
我想停止四处摸索,并在 hack 上引入 hack 以适应这些新场景的出现 - 我想要的是,在给定 AppDomain 及其设置信息的情况下,能够生成我应该扫描的文件夹列表为了获取所有将要加载的 DLL;不管 AppDomain 是如何设置的。如果 Fusion 可以将它们视为完全相同,那么我的代码也应该如此。
当然,如果 .Net 更改其内部结构,我可能不得不更改算法 - 这只是我必须承受的一个十字架。同样,我很高兴将 SQL Server 和任何其他类似环境视为目前仍不受支持的边缘情况。
有什么想法吗!?
【问题讨论】:
-
不是一个真正的答案,但对我很有帮助的是这篇 MSDN 文章:msdn.microsoft.com/en-us/library/yx7xezcf.aspx)。并且提到的 Fuslogvw.exe 应该有助于获得“为什么找不到那个 dll”。
-
ralf.w:我确实想知道我是否可以通过获取一个不存在的程序集的融合日志并对其进行解析以查看它正在寻找的所有位置来作弊。虽然它可能会起作用,但我觉得我必须在目标框上打开 Fusion Logging(= 慢);加上我会按异常编码的事实,这完全是错误的!感谢文章的链接——也许更多的 MSDN 挖掘可能会给我答案......
-
我正好有这个问题,你愿意分享你得到的代码吗?谢谢:)
-
@Andrew Bullock:愿意;但我还没有完成编写和测试它呢!一旦我有了,我肯定会在这里发布代码。确实,如果您事先得到一个好的解决方案,请随时在此处添加答案,我会将您标记为答案:)
标签: c# .net reflection assemblies