尝试将 Autofac 作为嵌入式程序集加载时出现 FileNotFoundException [英] FileNotFoundException when trying to load Autofac as an embedded assembly

查看:19
本文介绍了尝试将 Autofac 作为嵌入式程序集加载时出现 FileNotFoundException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以前版本的 Autofac 可以工作,但由于他们转而将其设为可移植类库,因此无法加载.

Previous versions of Autofac worked, but since they switched to making it a Portable Class Library it won't load.

我尝试应用此处 (KB2468871) 列出的修复程序,但它告诉我不需要它.

I tried applying the fix listed here (KB2468871) but it told me that it was not needed.

当我将 Autofac.dll 文件移动到与可执行文件相同的位置时,错误就会消失.当它从外部 DLL 加载它时,它加载得很好.

The error goes away when I move the Autofac.dll file into the same location as the executable. When it loads it from the external DLL it loads fine.

为什么它不能作为嵌入式 DLL 工作?

Why won't it work as an embedded 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:DevMyAppAppConfigurationBootstrapper.cs:line 25
           at MyApp.Configuration.EntryPoint.Main(String[] args) in c:DevMyAppAppConfigurationEntryPoint.cs:line 22

如果有帮助,这里是 .csproj 文件中将 DLL 嵌入到可执行文件中的部分:

If it helps, here is the part of the .csproj file that embeds the DLLs into the executable:

  <Target Name="AfterResolveReferences">
<ItemGroup>
  <EmbeddedResource Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.Extension)' == '.dll'">
    <LogicalName>%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)</LogicalName>
  </EmbeddedResource>
</ItemGroup>

...这里是 EntryPoint 类:

... and here is the EntryPoint class:

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);
        }
    }
}

推荐答案

我可以通过在针对 Silverlight 并使用 System.Core 类型的 PCL 库上使用 Assembly.Load(byte[]) 轻松重现您的问题,这个问题不是特定于 Autofac.这是加载程序集的一种相当邪恶的方式,与 Assembly.LoadFile() 一样糟糕.没有加载上下文是 DLL 地狱的一个秘诀.

I could easily repro your problem by using Assembly.Load(byte[]) on a PCL library that targeted Silverlight and used a System.Core type, this problem isn't specific to Autofac. It is a rather evil way to load an assembly, about as bad as Assembly.LoadFile(). Not having a loading context is a recipe for DLL Hell.

CLR 在处理这些 PCL 库引用方面有一项令人讨厌的工作,它需要神奇地将可重定向程序集引用映射到真实的引用.换句话说,2.0.5.0 引用需要映射到运行时版本的正确引用,在您的情况下为 4.0.0.0.只有当它很清楚实际运行时版本是什么时,它才能做到这一点,没有加载上下文会使这变得困难.显然它不会尝试这样做,它会再次为 2.0.5.0 引用触发 AssemblyResolve 事件.

The CLR has an unenviable job to do with these PCL library references, it needs to magically map the retargetable assembly references to a real one. In other words, the 2.0.5.0 reference needs to be mapped to the correct one for the runtime version, 4.0.0.0 in your case. It can only do this if it has a good idea what the actual runtime version is, not having a loading context makes that difficult. Clearly it doesn't attempt to do so, it fires the AssemblyResolve event again for the 2.0.5.0 reference.

这是解决方案,事后看来非常简单.只需拦截可重定向程序集引用的解析请求,并使用 Assembly.Load() 让 CLR 将其从 AppDomain 的加载上下文中进行分类.像这样改变你的 AssemblyResolve 事件处理程序:

Which is the solution, remarkable simple in hind-sight. Just intercept the resolve request for the retargetable assembly references and use Assembly.Load() to let the CLR sort it out from the loading context of your AppDomain. Alter your AssemblyResolve event handler like this:

private static Assembly loadEmbeddedAssembly(string name)
{
    if (name.EndsWith("Retargetable=Yes")) {
        return Assembly.Load(new AssemblyName(name));
    }
    // Rest of your code
    //...
}

这在我的测试应用中运行良好,我相信它也能解决您使用 Autofac 的问题.

This worked well in my test app, I trust it will solve your problem with Autofac as well.

这篇关于尝试将 Autofac 作为嵌入式程序集加载时出现 FileNotFoundException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆