【问题标题】:Loading an .NET Standard assembly which references a nuget dependency at runtime?加载在运行时引用 nuget 依赖项的 .NET Standard 程序集?
【发布时间】:2017-08-24 17:54:27
【问题描述】:

我目前正在使用AssemblyLoadContext.Default.LoadFromAssemblyPath(path/to/netstandard1.6lib.dll),并且很好奇如何处理该库可能具有的任何 nuget 依赖项?

例如: 库 A 动态加载库 B。 库 B 依赖于 NuGet 的 Redis。

库 B 加载正确,但在使用 redis 客户端时——我们收到一个令人讨厌的 FileNotFoundException ,抱怨找不到 redis 程序集。该场景确实是典型的模块加载器类型的事情。

Assembly assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(path);
if (assembly == null)
    throw new InvalidExtensionException(name, path);

TypeInfo type = assembly.DefinedTypes.FirstOrDefault(x => x.ImplementedInterfaces.Contains(typeof(IExtension)));
if (type == null)
    throw new InvalidExtensionException(name, path);

IExtension extension = Activator.CreateInstance(type.AsType(), name, _dependencyUtility) as IExtension;
if (extension == null)
    throw new InvalidExtensionException(name, path);

extensions.Add(extension);

当 Activator 创建实例时,扩展的构造函数会尝试创建一个新的 redis 客户端——结果一切都失败了。

关于如何在运行时处理来自 nuget 的 3 级依赖项的任何想法?

【问题讨论】:

  • 你可以在这里找到一些信息stackoverflow.com/questions/31859267/…
  • 这似乎更多是关于在当前(无论 .NET 核心/标准等效项是什么)AppDomain 中加载 nuget 包,而不是加载具有更多依赖项的依赖项。

标签: c# .net .net-core .net-standard


【解决方案1】:

DLL 必须在那里才能加载它们,AFAIK 你不应该在运行时下载 nugget 包,因为它会很慢,并且它可以在 nugget 源不可用的任何时候停止工作,或者更有可能,您没有互联网连接。

所以让你的项目依赖于那个 nugget 包,它会在构建之前被下载。

如果您对这种方法不感兴趣,那么我想您可以尝试从您的程序中执行 NuGet.exe 并使其首先下载所需的 DLL,但这会使您的程序在下载包时挂起文件。

【讨论】:

  • 嗯,nuget 存储库是在构建库 B 时下载的。因此,DLL 存在于系统中。但是当库 A 动态加载库 B 时(它甚至识别出库 B 在 Assembly.GetReferencedAssemblies() 中引用了 Redis),它找不到库 B 用来构建的 DLL 的位置。我不认为nuget是问题所在。我认为 AssemblyLoadContext.Default.LoadFromAssemblyPath 不知道如何加载子依赖项。
  • 程序集具有与引用的程序集名称相对应的元数据,但没有路径。要加载此引用,您需要一个解析器,它将解析程序集。由于依赖项 dll 与 LibraryB dll 不在同一路径中(未发布),因此您需要获取依赖项的路径。由于尚未发布,我认为您需要加载 LibraryB.runtimeconfig.dev.json 文件和 LibraryB.deps.json 并在那里获取路径。
【解决方案2】:

我最终需要做的是在我的项目的 csproj 文件中添加这个:<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>

然后调整我的模块加载器代码以遍历所有 DLL 并加载它们,然后尝试通过激活器从我的程序集中调用构造函数。

public void LoadExtensions()
{
    IConfigurationSection[] extensionConfigurations = _config.GetSections(EXTENSION_CONFIGURATION_KEY).ToArray();
    if (extensionConfigurations.Length == 0)
        return;

    HashSet<IExtension> extensions = new HashSet<IExtension>();
    foreach (IConfigurationSection extensionConfiguration in extensionConfigurations)
    {
        string name = extensionConfiguration.Key;
        string path = _config.Get($"{extensionConfiguration.Path}:path");

        _logger.Debug($"Loading extension: {name}");

        if (string.IsNullOrEmpty(path) || !File.Exists(path))
            throw new ConfigurationItemMissingException($"{extensionConfiguration.Path}:path");

        LoadAssembly(path, name);
    }

    foreach (var extensionType in _extensionTypes)
    {
        IExtension extension = Activator.CreateInstance(extensionType.Key.AsType(), extensionType.Value, _dependencyUtility) as IExtension;
        if (extension == null)
            throw new InvalidExtensionException(extensionType.Value, extensionType.Key.AssemblyQualifiedName);

        extensions.Add(extension);
    }

    Extensions = extensions;
}

private void LoadAssembly(string path, string name)
{
    FileInfo[] dlls = new DirectoryInfo(Path.GetDirectoryName(path)).GetFiles("*.dll");

    foreach (FileInfo dll in dlls)
    {
        Assembly asm = AssemblyLoadContext.Default.LoadFromAssemblyPath(dll.FullName);

        _logger.Info($"Loading assembly: {asm.FullName}");

        TypeInfo type = asm.DefinedTypes.FirstOrDefault(x => x.ImplementedInterfaces.Contains(typeof(IExtension)) && !x.IsAbstract);

        if (type == null)
            continue;

        _extensionTypes.Add(type, name);
    }
}

【讨论】:

    【解决方案3】:

    您不应手动解决程序集依赖项。

    只要确保当您动态加载您的库 B 时,所有相关的 dll-s 都可以被 .net 运行时访问。默认情况下,它将检查您的应用程序进程和 GAC 的工作目录。 如果您想自定义运行时的探测行为,您可以在配置文件中或在 C# 代码中使用 &lt;probing&gt; 设置。

    我建议您阅读这些文档,它们应该可以帮助您更详细地了解探测的工作原理:

    https://docs.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies

    https://docs.microsoft.com/en-us/dotnet/framework/configure-apps/specify-assembly-location

    您可以使用 fuslog 工具来解决依赖关系解析问题:

    https://docs.microsoft.com/en-us/dotnet/framework/tools/fuslogvw-exe-assembly-binding-log-viewer

    【讨论】:

    • 嗯。我认为这就是探测的工作方式——但是在库 A 的工作目录中拥有库 B 的依赖项并不起作用。我去看看 fuslog,谢谢!
    猜你喜欢
    • 2020-04-04
    • 2015-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多