尝试将Autofac作为嵌入式程序集加载时出现FileNotFoundException错误

9
之前的Autofac版本可以使用,但是自从它转换成可移植类库后,它就无法加载。
我尝试了应用这里(KB2468871)列出的修复方法,但它告诉我不需要。
当我将Autofac.dll文件移到可执行文件相同的位置时,错误消失了。当它从外部DLL中加载时,它可以正常加载。
为什么它作为嵌入式DLL无法工作?
这里是异常信息:
        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.

        Stack trace: 
           at Autofac.Core.Registration.ComponentRegistration..ctor(Guid id, IInstanceActivator activator, IComponentLifetime lifetime, InstanceSharing sharing, InstanceOwnership ownership, IEnumerable`1 services, IDictionary`2 metadata)
           at Autofac.Core.Container..ctor()
           at Autofac.ContainerBuilder.Build(ContainerBuildOptions options)
           at MyApp.Configuration.Bootstrapper.Run(String[] args) in c:\Dev\MyApp\App\Configuration\Bootstrapper.cs:line 25
           at MyApp.Configuration.EntryPoint.Main(String[] args) in c:\Dev\MyApp\App\Configuration\EntryPoint.cs:line 22

如果需要帮助,这是.csproj文件中将DLL嵌入可执行文件的部分:
  <Target Name="AfterResolveReferences">
<ItemGroup>
  <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
    <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
  </EmbeddedResource>
</ItemGroup>

... 这里是 EntryPoint 类:

internal static class EntryPoint
{
    [STAThread]
    private static void Main(params string[] args)
    {
        AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => loadEmbeddedAssembly(e.Name);
        Bootstrapper.Run(args); // must call separate class when using embedded assemblies
    }

    private static Assembly loadEmbeddedAssembly(string name)
    {
        var container = Assembly.GetExecutingAssembly();
        var path = new AssemblyName(name).Name + ".dll";

        using (var stream = container.GetManifestResourceStream(path))
        {
            if (stream == null)
            {
                return null;
            }

            var bytes = new byte[stream.Length];
            stream.Read(bytes, 0, bytes.Length);
            return Assembly.Load(bytes);
        }
    }
}

看起来你的程序集解析处理程序没有运行。你能确认一下吗?此外,使用设计器将文件添加为资源要容易得多。您可以通过一个byte[]属性[root namespace].Properties.Resources.SomeEmbeddedFile访问它。将其放入MemoryStream中并完成它。GMRS是老派的。 - user1228
@Will 如果程序集解析处理程序没有运行,它将无法加载Autofac。Autofac正在被加载,只是试图加载旧版本的System.Core。似乎当它作为嵌入式资源加载时,程序集的可重定向属性不起作用。当我将Autofac dll复制到exe所在的同一文件夹中时,它确实可以工作。 - xofz
哦,明白了。可能与加载上下文有关。它们非常难以理解,并且根本没有很好的文件记录。你自己尝试加载了吗? - user1228
看起来类似于这个问题。在评论中,leppie建议使用LoadFrom,这似乎对提问者有用。 - default.kramer
2个回答

11
我可以轻松地通过对针对 Silverlight 并使用 System.Core 类型的 PCL 库使用 Assembly.Load(byte[]) 来重现您的问题。这个问题并不特定于 Autofac,这种加载程序集的方式非常恶劣,几乎和 Assembly.LoadFile() 一样糟糕。没有一个加载上下文会导致 DLL 地狱。
CLR 在处理这些 PCL 库引用时有相当困难的工作,需要将可重定向的程序集引用魔法般地映射到实际的引用上。换句话说,在您的情况下,2.0.5.0 引用需要映射到正确的运行时版本 4.0.0.0 上。如果 CLR 没有很好地了解实际运行时版本,那么它只能再次触发 AssemblyResolve 事件来尝试解决该问题。
解决方案在后来看来非常简单。只需拦截可重定向程序集引用的解决请求,并使用 Assembly.Load() 方法让 CLR 从 AppDomain 的加载上下文中进行排序。像这样修改 AssemblyResolve 事件处理程序:
private static Assembly loadEmbeddedAssembly(string name)
{
    if (name.EndsWith("Retargetable=Yes")) {
        return Assembly.Load(new AssemblyName(name));
    }
    // Rest of your code
    //...
}

这在我的测试应用程序中很有效,我相信它也可以解决您在Autofac方面的问题。


非常感谢你,汉斯!这样就解决了! - xofz
太好了,这帮助我很多,让我能够使用pdb文件支持来运行一个不错的AssemblyResolve处理程序,请参见https://dev59.com/g3I_5IYBdhLWcg3wHvQ5#21850462。 - nietras

0
也许问题出在您尝试加载EmbeddedAssembly的方式上。
我对如何实现它知之甚少,但是我知道来自code project的以下示例对我很有效,也许它能帮到您:-) 从嵌入资源中加载DLL (如果我误解了您的问题,请告诉我,我会删除我的回答)

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