动态加载可移植类库时出现“无法加载文件或程序集'System.Core,Version=2.0.5.0,...”异常。

12
首先,我需要强调这个问题与此线程中的问题略有不同。另外,安装KB2468871也没有帮助。
我尝试尽可能简化这个问题。通常情况下,它涉及使用Assembly.LoadFile(...)在桌面应用程序中加载PCL程序集。
假设有一个.NET 4.0控制台应用程序(称为“C”)。它引用了.NET 4.0程序集(称为“N4”)和PCL程序集(称为“PCL”)。
其中N4看起来像这样:
using System.Linq;

namespace N4
{
    public class ClassInN4
    {
        public static string Greet()
        {
            return new string(
                "hello from N4"
                .ToCharArray()
                .Select(char.ToUpper)
                .ToArray()
            );
        }
    }
}

PCL看起来像这样:

using System.Linq;

namespace PCL
{
    public class ClassInPCL
    {
        public static string Greet()
        {
            return new string(
                "hello from pcl"
                .ToCharArray()
                .Select(char.ToUpper)
                .ToArray()
            );
        }
    }
}

而C看起来像这样:

using System;
using System.IO;
using System.Reflection;
using N4;
using PCL;

namespace C
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Test();
            Console.ReadLine();
        }

        private static void Test()
        {
            Test("N4", ClassInN4.Greet);
            Test("PCL", ClassInPCL.Greet);
        }

        private static void Test(
            string title, 
            Func<string> generator)
        {
            try
            {
                Console.WriteLine(
                    "{0}: {1}", title, generator());
            }
            catch (Exception e)
            {
                Console.WriteLine(
                    "{0}: {1} -> {2}", title, e.GetType(), e.Message);
            }
        }
    }
}

当您运行此应用程序时,您将获得完全正确的结果:
N4: HELLO FROM N4
PCL: HELLO FROM PCL

让我们在Program.Main中为CurrentDomain添加AssemblyResolve事件:

AppDomain.CurrentDomain.AssemblyResolve += (_, a) => {
    var fileName = Path.GetFullPath(
        new AssemblyName(a.Name).Name + ".data");
    Console.WriteLine("Probing '{0}'", fileName);
    return 
        File.Exists(fileName) 
        ? Assembly.LoadFile(fileName) 
        : null;
};

因此,如果无法找到汇编程序,则会尝试从“.data”文件中加载它。

让我们进入应用程序文件夹,将“N4.dll”重命名为“N4.data”,然后运行“C.exe”。

Probing 'C:\xxx\C\bin\Debug\N4.data'
N4: HELLO FROM N4
PCL: HELLO FROM PCL

所以它通过AssemblyResolve进行处理,最终加载了“N4.data”,并且与原始版本一样正常工作。

让我们将“N4.data”还原为“N4.dll”,并将“PCL.dll”重命名为“PCL.data”,然后……

Probing 'C:\xxx\C\bin\Debug\PCL.data'
N4: HELLO FROM N4
Probing 'C:\xxx\C\bin\Debug\System.Core.data'
Probing 'C:\xxx\C\bin\Debug\System.Core.data'
Probing 'C:\xxx\C\bin\Debug\System.Core.data'
PCL: System.IO.FileNotFoundException -> Could not load file or assembly 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes' or one of its dependencies. The system cannot find the file specified.

请注意,PCL程序集已经成功加载,问题是它无法再找到其依赖项(System.Core)了。
如果加载的程序集是可移植的,那么就像Assembly.LoadFile(fileName)不起作用一样。
有人遇到过这个问题吗?有人解决了这个问题吗?
你可以在这里找到所有文件。
编辑:感谢leppie强制我检查其他选项。我实际上想确定当我回答“是的,是的,我试过了”时我没有撒谎。显然这值得检查。
来自Suzanne Cook's .NET CLR Notes

注意 - 这两个不是同一件事情。

LoadFrom() 通过 Fusion 进行加载,如果在 LoadFrom 上下文中已经加载了具有相同标识的另一个程序集,则可以将其重定向到另一个路径上的程序集。 LoadFile() 根本不会通过 Fusion 绑定 - 加载程序就会直接加载调用者请求的内容。它不使用 Load 或 LoadFrom 上下文。


4
你尝试过使用Assembly.LoadFrom或将其作为字节数组加载吗? - leppie
2
我很震惊。LoadFrom(...)可以工作,而LoadFile(...)不行(顺便说一下,Load(byte[])也不行)。如果你把它作为答案,我可以给你打勾。谢谢。 - Milosz Krajewski
回答自己的问题并获取一些声望; p - leppie
3个回答

5
您可以在AssemblyResolve事件中返回您平台的System.Core程序集(例如.NET Framework 4.0的版本4.0.0.0),当请求2.0.5.0版本时。
我正在通过Load(byte[])加载所有存储为资源的引用程序集,这也无法解析2.0.5.0程序集,并且我从AppDomain.CurrentDomain.GetAssemblies()检索SystemSystem.Core

这有道理。虽然我没有考虑到这点,但我不确定它在我的情况下是否如预期般工作。AssemblyResolve在可重用库(针对4.0)中,但主应用程序可以是4.5。因此,如果其他库引用2.0.5.0,则我应加载4.0还是4.5。我怎么知道从库中主应用程序是4.5?另一个问题是“为什么要这样做?” System、System.Core、mscorlib、System.Xml、System.Data、System.???+1的意见,但没有“回答选中”,因为它没有解决所有问题。 - Milosz Krajewski
@MiloszKrajewski,AppDomain.CurrentDomain.GetAssemblies()会提供正确的程序集,您不必担心.NET 4或4.5。 - Lex Li

3
我认为你遇到这些问题是因为:

你遇到了异常,因为你没有安装最新的.NET更新。

http://www.paraesthesia.com/archive/2013/01/21/using-portable-class-libraries-update-net-framework.aspx

请注意WSUS部分 - 你可能认为自己已经安装了最新的更新,但实际上你的WSUS服务器已过期。

这个补丁可能有所帮助,但最好还是安装所有的.NET更新:

http://support.microsoft.com/kb/2468871

(来自上面的评论)

尝试使用LoadFrom(...)而不是LoadFile(...) / Load(byte[]),看看是否可以解决你的问题? :)


1
不行。我明确地说过:“此外,安装KB2468871也没有帮助。” 这与更新无关,只与使用LoadFile()有关。 - Milosz Krajewski
嗯...我在使用LoadFile()时也遇到了同样的问题,但通过使用Windows Update安装所有的.net补丁解决了这个问题...只是想确认一下,您是否已经安装了所有所需/可选的.net补丁? - Tod Thomson
1
这是我的问题,服务器只有W2k8 SP1和.NET 4.0 exe。应用SP2 +补丁修复了它。 - fiat

1
我遇到了同样的问题,最终采用了以下解决方案:在动态加载PCL程序集之前调用以下代码。
Assembly.Load("System, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes");
Assembly.Load("System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes");

如果在加载PCL程序集时缺少其他依赖项,您只需要在上面的代码中添加一行即可。 出于某种奇怪而难以理解的原因,它可以正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接