[英]Manually loading of assemblies and dependencies at runtime (nuget dependencies & FileNotFoundException)
I currently have an issue with loading assemblies at runtime using Assembly.LoadFrom(String)
.我目前在使用
Assembly.LoadFrom(String)
在运行时加载程序集时遇到问题。 While the specified assembly is loaded just fine, referenced third-party assemblies (eg nuget packages) are not loaded when the targeted framework is either netcoreapp
or netstandard
.虽然指定的程序集加载得很好,但当目标框架是
netcoreapp
或netstandard
时,不会加载引用的第三方程序集(例如 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.我在这里使用
Newtonsoft.Json
作为 nuget 示例,但它可以是任何其他程序集。
ClassLibrary0.csproj 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 ClassLibrary1.csproj
Has a package reference to Newtonsoft.Json
via nuget
.通过
nuget
有一个对Newtonsoft.Json
的包引用。 Has a reference to additional assembly ClassLibrary0
depending on TargetFramework
(shitty conditional ItemGroups
).根据
TargetFramework
(糟糕的条件ItemGroups
)引用了其他程序集ClassLibrary0
。
<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 ClassLibrary2.csproj
Has a project reference to ClassLibrary1
.有一个对
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();
}
}
}
After running dotnet restore
and rebuilding the solution the root problem can be observed in the output directories:运行
dotnet restore
并重建解决方案后,可以在输出目录中观察到根本问题:
The Problem:问题:
ClassLibrary0.dll
are present in all output directories (=> references to third-party are good).ClassLibrary0.dll
副本(=> 对第三方的引用很好)。ClassLibrary1.dll
are present in all output directories of ClassLibrary2
(=> project references are good too).ClassLibrary1.dll
存在于所有的输出目录ClassLibrary2
(=>项目引用也很好)。Newtonsoft.Json
are only present in net
output directories but are missing in all netcoreapp
and netstandard
. Newtonsoft.Json
副本仅出现在net
输出目录中,但在所有netcoreapp
和netstandard
中都netstandard
。netcoreapp
and netstandard
output directories contain a *.deps.json
file that correctly mentions the Newtonsoft.Json
package as a dependency.netcoreapp
和netstandard
输出目录都包含一个*.deps.json
文件,该文件正确地将Newtonsoft.Json
包作为依赖项提及。 A call to Assembly.LoadFrom(String)
however won't load these dependencies to Newtonsoft.Json
in case of netcoreapp
and netstandard
.但是,在
netcoreapp
和netstandard
情况下,调用Assembly.LoadFrom(String)
不会将这些依赖项加载到Newtonsoft.Json
。 This results in FileNotFoundException
at runtime after running code from the specified loaded assemblies.从指定的加载程序集运行代码后,这会在运行时导致
FileNotFoundException
。
What i've tried:我试过的:
I am trying to resolve those by attaching to the AppDomain.AssemblyResolve
event but so far i'm out of luck.我试图通过附加到
AppDomain.AssemblyResolve
事件来解决这些问题,但到目前为止我不走运。 Those *.deps.json
don't contain a location path of the dependency.那些
*.deps.json
不包含依赖项的位置路径。
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.我尝试在
Path
环境变量中的所有位置查找程序集,但 nuget 包位置似乎没有列在那里。 The location on all my machines seems to be %userprofile%\\.nuget\\packages\\package-name\\version\\
.我所有机器上的位置似乎都是
%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.但是,我并不是 100% 肯定这将始终是所有可能执行我的代码的机器上 nuget 包的正确位置。
The actual question:实际问题:
Is there a solid way to resolve nuget dependencies at runtime when manually loading assemblies?手动加载程序集时,是否有可靠的方法在运行时解决 nuget 依赖项?
Restrictions:限制:
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
in the original projects.<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
。I have solved the problem by writing my own NuGet package resolver, which looks for the appropriate package at runtime.我通过编写自己的 NuGet 包解析器解决了这个问题,它在运行时查找合适的包。 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:
在运行时解析需要使用以下内容附加到 AppDomain.AssemblyResolve:
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;
}
This requires the use of my NuGet package which contains the resolver and some helpers.这需要使用我的NuGet 包,其中包含解析器和一些帮助器。 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.我意识到
dotnet publish
也会复制任何依赖项,但这是一个特殊的边缘情况。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.