繁体   English   中英

加载在运行时引用nuget依赖项的.NET Standard程序集?

[英]Loading an .NET Standard assembly which references a nuget dependency at runtime?

我目前正在使用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的第三级依赖项的任何想法?

必须加载DLL才能加载它们,AFAIK您不应该在运行时下载块包,因为它运行缓慢,并且在没有任何块源或很可能是在任何时候都可能停止工作没有互联网连接。

因此,使您的项目依赖于该块软件包,并且在构建之前将其下载。

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

我最终需要做的就是将其添加到项目的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);
    }
}

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

只要确保在动态加载库B时,.net运行时就可以访问所有依赖的dll。 默认情况下,它将检查您的应用程序进程和GAC的工作目录。 如果要自定义运行时的探测行为,可以使用配置文件中的<probing>设置或从C#代码中进行设置。

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

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

https://docs.microsoft.com/zh-cn/dotnet/framework/configure-apps/specify-assembly-location

要解决依赖关系解析,可以使用fuslog工具:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM