在运行时手动加载程序集和依赖项(nuget依赖项和FileNotFoundException) [英] Manually loading of assemblies and dependencies at runtime (nuget dependencies & FileNotFoundException)

查看:153
本文介绍了在运行时手动加载程序集和依赖项(nuget依赖项和FileNotFoundException)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前在使用Assembly.LoadFrom(String)在运行时加载程序集时遇到问题. 虽然可以很好地加载指定的程序集,但是当目标框架为netcoreappnetstandard时,不会加载引用的第三方程序集(例如nuget程序包).

I currently have an issue with loading assemblies at runtime using Assembly.LoadFrom(String). While the specified assembly is loaded just fine, referenced third-party assemblies (e.g. nuget packages) are not loaded when the targeted framework is either netcoreapp or netstandard.

为了解决这个问题,我创建了一个由三个项目组成的简单解决方案. 每个项目仅包含一个类. 我在这里使用Newtonsoft.Json作为nuget示例,但它可以是任何其他程序集.

To figure out the problem i have created a simple solution consisting of three projects. Each project contains exactly one class. I'm using Newtonsoft.Json as a nuget example here but it could be any other assembly.

ClassLibrary0.csproj

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>net20;netstandard1.0</TargetFrameworks>
  </PropertyGroup>

</Project>

namespace ClassLibrary0 {
    public class Class0 {

        public System.String SomeValue { get; set; }

    }
}

ClassLibrary1.csproj

具有通过nugetNewtonsoft.Json的程序包引用. 依赖于TargetFramework引用附加程序集ClassLibrary0(糟糕的条件ItemGroups).

Has a package reference to Newtonsoft.Json via nuget. Has a reference to additional assembly ClassLibrary0 depending on TargetFramework (shitty conditional ItemGroups).

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>net20;net35;net40;net45;net451;net452;net46;net461;net462;net47;net471;net472;netstandard1.0;netstandard1.1;netstandard1.2;netstandard1.3;netstandard1.4;netstandard1.5;netstandard1.6;netstandard2.0;netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;netcoreapp2.1</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='net20' OR '$(TargetFramework)'=='net35' OR '$(TargetFramework)'=='net40' OR '$(TargetFramework)'=='net45' OR '$(TargetFramework)'=='net451' OR '$(TargetFramework)'=='net452' OR '$(TargetFramework)'=='net46' OR '$(TargetFramework)'=='net461' OR '$(TargetFramework)'=='net462' OR '$(TargetFramework)'=='net47' OR '$(TargetFramework)'=='net471' OR '$(TargetFramework)'=='net472'">
    <Reference Include="ClassLibrary0">
      <HintPath>..\net20\ClassLibrary0.dll</HintPath>
    </Reference>
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='netstandard1.0' OR '$(TargetFramework)'=='netstandard1.1' OR '$(TargetFramework)'=='netstandard1.2' OR '$(TargetFramework)'=='netstandard1.3' OR '$(TargetFramework)'=='netstandard1.4' OR '$(TargetFramework)'=='netstandard1.5' OR '$(TargetFramework)'=='netstandard1.6' OR '$(TargetFramework)'=='netstandard2.0'">
    <Reference Include="ClassLibrary0">
      <HintPath>..\netstandard1.0\ClassLibrary0.dll</HintPath>
    </Reference>
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)'=='netcoreapp1.0' OR '$(TargetFramework)'=='netcoreapp1.1' OR '$(TargetFramework)'=='netcoreapp2.0' OR '$(TargetFramework)'=='netcoreapp2.1'">
    <Reference Include="ClassLibrary0">
      <HintPath>..\netstandard1.0\ClassLibrary0.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>

namespace ClassLibrary1 {
    public class Class1 {

        public System.String SomeValue { get; set; }

        public Class1() {
            var tmp = new ClassLibrary0.Class0();
            var tmp2 = new Newtonsoft.Json.DefaultJsonNameTable();
        }

    }
}

ClassLibrary2.csproj

具有对ClassLibrary1的项目引用.

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>net20;net35;net40;net45;net451;net452;net46;net461;net462;net47;net471;net472;netstandard1.0;netstandard1.1;netstandard1.2;netstandard1.3;netstandard1.4;netstandard1.5;netstandard1.6;netstandard2.0;netcoreapp1.0;netcoreapp1.1;netcoreapp2.0;netcoreapp2.1</TargetFrameworks>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" />
  </ItemGroup>

</Project>

namespace ClassLibrary2 {
    public class Class2 {

        public System.String SomeValue { get; set; }

        public Class2() {
            var tmp = new ClassLibrary1.Class1();
        }

    }
}

运行dotnet restore并重建解决方案后,可以在输出目录中观察到根本问题:

After running dotnet restore and rebuilding the solution the root problem can be observed in the output directories:

问题:

  • ClassLibrary0.dll的副本出现在所有输出目录中(=>引用第三方是好的).
  • ClassLibrary1.dll的副本出现在ClassLibrary2的所有输出目录中(=>项目引用也很好).
  • Newtonsoft.Json的副本仅出现在net输出目录中,但在所有netcoreappnetstandard中均不存在.
  • 所有netcoreappnetstandard输出目录都包含一个*.deps.json文件,该文件正确地将Newtonsoft.Json程序包称为依赖项.
  • Copies of ClassLibrary0.dll are present in all output directories (=> references to third-party are good).
  • Copies of ClassLibrary1.dll are present in all output directories of ClassLibrary2 (=> project references are good too).
  • Copies of Newtonsoft.Json are only present in net output directories but are missing in all netcoreapp and netstandard.
  • All netcoreapp and netstandard output directories contain a *.deps.json file that correctly mentions the Newtonsoft.Json package as a dependency.

Assembly.LoadFrom(String)的调用不会在netcoreappnetstandard的情况下将这些依赖项加载到Newtonsoft.Json. 从指定的已加载程序集运行代码后,这将在运行时导致FileNotFoundException.

A call to Assembly.LoadFrom(String) however won't load these dependencies to Newtonsoft.Json in case of netcoreapp and netstandard. This results in FileNotFoundException at runtime after running code from the specified loaded assemblies.

我尝试过的事情:

我正在尝试通过附加到AppDomain.AssemblyResolve事件来解决这些问题,但到目前为止我还很走运. 那些*.deps.json不包含依赖项的位置路径.

I am trying to resolve those by attaching to the AppDomain.AssemblyResolve event but so far i'm out of luck. Those *.deps.json don't contain a location path of the dependency.

我尝试在Path环境变量内的所有位置查找程序集,但是nuget软件包的位置似乎未在此处列出. 我所有机器上的位置似乎是%userprofile%\.nuget\packages\package-name\version\. 但是我不是100%肯定这将一直是所有可能执行我的代码的机器上nuget包的正确位置.

I've tried looking for the assembly in all the locations within the Path environment variable but the nuget package location doesn't seem to be listed there. The location on all my machines seems to be %userprofile%\.nuget\packages\package-name\version\. However i'm not 100% positive that this will always be the correct location for nuget packages on all machines that might execute my code.

实际问题:

在手动加载程序集时,是否有可靠的方法可以在运行时解决nuget依赖项?

Is there a solid way to resolve nuget dependencies at runtime when manually loading assemblies?

限制:

  • 这需要是一个脱机解决方案,不能即时下载软件包版本.
  • 不能依赖原始项目中的<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>.
  • 不能依靠我引用相关的依赖项. 这样做的全部目的是能够动态加载我在编译时不知道的程序集.
  • This needs to be an offline solution, no downloading of package versions on the fly.
  • Cannot rely on <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> in the original projects.
  • Cannot rely on me having references to the dependencies in question. The whole point of doing this is to be able to dynamically load assemblies that i can't know about at compile time.

推荐答案

我已经通过编写自己的NuGet程序包解析器解决了该问题,该程序在运行时会寻找合适的程序包.我还没有时间准备适当的文档,但现在已经可以使用了.在运行时解析需要使用以下内容附加到AppDomain.AssemblyResolve:

I have solved the problem by writing my own NuGet package resolver, which looks for the appropriate package at runtime. I haven't had the time for a proper documentation yet but it's already on my plate. Resolving at runtime requires to attach to AppDomain.AssemblyResolve with something like that:

private Assembly OnAssemblyResolve(Object sender, ResolveEventArgs args) {
    if(AssemblyResolver.Nuget.TryResolve(args, out IEnumerable<FileInfo> files)) {
        foreach(FileInfo file in files) {
            if(AssemblyHelper.TryLoadFrom(file, out Assembly assembly)) {
                return assembly;
            }
        }
    }

    return null;
}

这需要使用我的 NuGet包,其中包含解析器和一些帮手.还有一个文章进入解析器的详细信息和设计决策. 我意识到dotnet publish还将复制任何依赖关系,但这是一种特殊情况.

This requires the use of my NuGet package which contains the resolver and some helpers. There is also an article that goes into the details and design decisions of the resolver. I realize that dotnet publish will also copy any dependencies but this is a special edge case.

这篇关于在运行时手动加载程序集和依赖项(nuget依赖项和FileNotFoundException)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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