在运行时手动加载程序集和依赖项(nuget 依赖项和 FileNotFoundException) [英] Manually loading of assemblies and dependencies at runtime (nuget dependencies & FileNotFoundException)
问题描述
我目前在使用 Assembly.LoadFrom(String)
在运行时加载程序集时遇到问题.虽然指定的程序集加载得很好,但当目标框架是 netcoreapp
或 netstandard
时,不会加载引用的第三方程序集(例如 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
通过 nuget
有一个对 Newtonsoft.Json
的包引用.根据 TargetFramework
(糟糕的条件 ItemGroups
)引用了附加程序集 ClassLibrary0
.
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>..
et20ClassLibrary0.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>..
etstandard1.0ClassLibrary0.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>..
etstandard1.0ClassLibrary0.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
的项目引用.
Has a project reference to 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="..ClassLibrary1ClassLibrary1.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
输出目录中,但在所有netcoreapp
和netstandard
中都没有>.- 所有
netcoreapp
和netstandard
输出目录都包含一个*.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 ofClassLibrary2
(=> project references are good too). - Copies of
Newtonsoft.Json
are only present innet
output directories but are missing in allnetcoreapp
andnetstandard
. - All
netcoreapp
andnetstandard
output directories contain a*.deps.json
file that correctly mentions theNewtonsoft.Json
package as a dependency.
调用 Assembly.LoadFrom(String)
但是不会将这些依赖项加载到 Newtonsoft.Json
在 netcoreapp
和 <代码>网络标准代码>.从指定的加载程序集运行代码后,这会在运行时导致 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%.nugetpackagespackage-nameversion
.但是,我并不是 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%.nugetpackagespackage-nameversion
.
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?
限制:
- 这需要是离线解决方案,不能即时下载软件包版本.
- 不能依赖于原始项目中的
.true - 不能依赖我对相关依赖项的引用.这样做的全部意义在于能够动态加载我在编译时不知道的程序集.
推荐答案
我通过编写自己的 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屋!