如何将 StackExchange.Redis.dll 加载到 powershell 中?

How to load StackExchange.Redis.dll into powershell?

我正在尝试创建一个 powershell 脚本来清除 redis 缓存,它在 Azure 中,但我认为这不相关。我已经看到 2 个示例,我试图复制人们将 StackExchange.Redis.dll 加载到他们的脚本中的位置:https://www.powershellgallery.com/packages/Saritasa.Redis/1.2.0/Content/Saritasa.Redis.psm1 and .

我已经从 nuget.org 下载了当前的 StackExchange.Redis.dll。我试图将它加载到 2 台服务器上,一台安装了 .Net 4.61,另一台安装了 .Net 4.8。我在两者上都遇到了同样的问题。

如果我尝试使用 [System.Reflection.Assembly]::LoadFrom,我会得到如下结果:

PS E:\redis\stackexchange.redis.2.2.88\lib\net461> dir


    Directory: E:\redis\stackexchange.redis.2.2.88\lib\net461


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        05/11/2021     00:42     637440 StackExchange.Redis.dll
-a---        05/11/2021     00:42     705989 StackExchange.Redis.xml


PS E:\redis\stackexchange.redis.2.2.88\lib\net461> [System.Reflection.Assembly]::LoadFrom("E:\redis\stackexchange.redis.
2.2.88\lib\net461\StackExchange.Redis.dll")

GAC    Version        Location
---    -------        --------
False  v4.0.30319     E:\redis\stackexchange.redis.2.2.88\lib\net461\StackExchange.Redis.dll


PS E:\redis\stackexchange.redis.2.2.88\lib\net461> [StackExchange.Redis.ConnectionMultiplexer]::Connect($myConnectionStr
ing)
Unable to find type [StackExchange.Redis.ConnectionMultiplexer]: make sure that the assembly containing this type is
loaded.
At line:1 char:1
+ [StackExchange.Redis.ConnectionMultiplexer]::Connect($myConnectionString)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (StackExchange.R...tionMultiplexer:TypeName) [], RuntimeException
    + FullyQualifiedErrorId : TypeNotFound

PS E:\redis\stackexchange.redis.2.2.88\lib\net461>

如果我尝试使用 Add-Type 我得到:

PS E:\redis\stackexchange.redis.2.2.88\lib\net461> Add-Type -AssemblyName .\StackExchange.Redis.dll
Add-Type : Could not load file or assembly '.\StackExchange.Redis.dll' or one of its dependencies. The given assembly
name or codebase was invalid. (Exception from HRESULT: 0x80131047)
At line:1 char:1
+ Add-Type -AssemblyName .\StackExchange.Redis.dll
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Add-Type], FileLoadException
    + FullyQualifiedErrorId : System.IO.FileLoadException,Microsoft.PowerShell.Commands.AddTypeCommand

PS E:\redis\stackexchange.redis.2.2.88\lib\net461>

我查看了 nuget.org 中的依赖项,我看到一个名为 Pipelines.Sockets.Unofficial 的非 Microsoft 依赖项,我也下载了它并得到了同样的东西。有一个完整的其他依赖项的层次结构,我认为它们都是 .Net 的一部分,如果服务器上安装了 .Net,我肯定不必下载它们吗?感谢您的帮助,我已经试了一整天了!

我会把我学到的一些东西放在我的代码之上,以防像我这样没有经验的人阅读这篇文章。这些点没有任何顺序:

  • 要下载 nuget 包供 powershell 使用,最简单的方法是使用 Install-Package cmdlet,例如: Install-Package -Name System.IO.Pipelines -ProviderName NuGet -SkipDependencies -Destination C:\Stackexchange.Redis-packages -RequiredVersion 5.0.1 请注意 -SkipDependencies 是必需的,因为没有它 Install-Package 会给我一条关于循环依赖的错误消息,如 https://endjin.com/blog/2020/12/how-to-consume-a-nuget-package-in-powershell 中所述。你必须自己下载依赖项! 另一种方法是:在 nuget.org 点击下载 link 下载 .nupkg 文件,将其重命名为 .zip,解压文件,然后在文件资源管理器 right-click dll 文件中,点击属性, 解锁。

  • 我认为这个应用程序非常适合显示 DLL 的 DLL 依赖关系,它显示依赖关系的整个层次结构以及是否找到它们https://github.com/lucasg/Dependencies

  • 在powershell中获取dll文件的汇编版本: Get-ChildItem -Filter *.dll -Recurse | Select-Object Name,@{n='FileVersion';e={$_.VersionInfo.FileVersion}},@{n='AssemblyVersion';e={[Reflection.AssemblyName]::GetAssemblyName($_.FullName).Version}} 来自 Get file version and assembly version of DLL files in the current directory and all sub directories

  • 加载 DLL 时,Add-Type 的行为与 [System.Reflection.Assembly]::LoadFrom() 不同。 Add-Type 似乎加载了有用的依赖项。如果 Add-Type -Literalpath <dllFileName> 失败并显示错误消息“Add-Type:无法加载一种或多种请求的类型。检索 LoaderExceptions 属性 以获得更多信息 information”你可以用$error[0].Exception.GetBaseException().LoaderExceptionshttps://www.reddit.com/r/PowerShell/comments/7a4vw6/addtype_how_do_i_retrieve_the_loaderexceptions/

    得到它要找的DLL
  • 为了解决层次结构中的 DLL 依赖于同一 DLL 的不同版本(这似乎很常见)的情况,这个人的解决方案简直太棒了。我找不到任何替代品,它似乎工作得很好 https://www.azurefromthetrenches.com/powershell-binding-redirects-and-visual-studio-team-services/

我用过的DLL 我最终将这些 DLL 文件放在一个文件夹中:

Name                                       FileVersion   AssemblyVersion
----                                       -----------   ---------------
Microsoft.Bcl.AsyncInterfaces.dll          6.0.21.52210  6.0.0.0
Pipelines.Sockets.Unofficial.dll           2.2.0.45337   1.0.0.0
StackExchange.Redis.dll                    2.2.88.56325  2.0.0.0
System.Buffers.dll                         4.6.28619.01  4.0.3.0
System.IO.Pipelines.dll                    5.0.120.57516 5.0.0.1
System.Memory.dll                          4.6.28619.01  4.0.1.1
System.Numerics.Vectors.dll                4.6.26515.06  4.1.4.0
System.Runtime.CompilerServices.Unsafe.dll 6.0.21.52210  6.0.0.0
System.Threading.Channels.dll              6.0.21.52210  6.0.0.0
System.Threading.Tasks.Extensions.dll      4.6.28619.01  4.2.0.1

代码还没有完成,但是显示了Stackexhange.Redis DLL的加载和使用。

# Code to load the DLLs needed for Stackexchange.Redis.dll and clear the cache
# Basically copied from https://www.azurefromthetrenches.com/powershell-binding-redirects-and-visual-studio-team-services/

$DllPath = 'C:\Stackexchange.Redis-packages\combined DLLS'
$redisHostName = '<my cache name here>.redis.cache.windows.net'
$redisConnectionString = '<my cache name here>.redis.cache.windows.net:6380,password=<my cache password here>,ssl=True,abortConnect=False'

# Load DLL assemblies into memory, required by the event handler below
$SystemBuffersDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\System.Buffers.dll")
$SystemRuntimeCompilerServicesUnsafeDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\System.Runtime.CompilerServices.Unsafe.dll")
$SystemMemoryDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\System.Memory.dll")
$SystemSystemThreadingTasksExtensionsDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\System.Threading.Tasks.Extensions.dll")
$SystemIoPipelinesDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\System.IO.Pipelines.dll")
$MicrosoftBclAsyncInterfacesDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\Microsoft.Bcl.AsyncInterfaces.dll")
$PipelinesSocketsUnofficialDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\Pipelines.Sockets.Unofficial.dll")
$SystemThreadingChannelsDll = [System.Reflection.Assembly]::LoadFrom("$DllPath\System.Threading.Channels.dll")

# Event handler to be run when the AssemblyResolve event occurs
$onAssemblyResolveEventHandler = [System.ResolveEventHandler] {
    param($sender, $e)

    Write-Verbose "Assembly resolve event for $($e.Name)"

    $dllName = $e.Name.Split(',')[0]
  
    switch ($dllName) {
        'System.Buffers' {return $SystemBuffersDll}
        'System.Runtime.CompilerServices.Unsafe' {return $SystemRuntimeCompilerServicesUnsafeDll}
        'System.Memory' {return $SystemMemoryDll}
        'System.Threading.Tasks.Extensions' {return $SystemSystemThreadingTasksExtensionsDll}
        'Microsoft.Bcl.AsyncInterfaces' {return $MicrosoftBclAsyncInterfacesDll}
        'Pipelines.Sockets.Unofficial' {return $PipelinesSocketsUnofficialDll}
        'System.Threading.Channels' {return $SystemThreadingChannelsDll}
        'System.Numerics.Vectors' {return $SystemNumericsVectorsDll}
        'System.IO.Pipelines' {return $SystemIoPipelinesDll}
    }

    foreach($assembly in [System.AppDomain]::CurrentDomain.GetAssemblies()) {
        if ($assembly.FullName -eq $e.Name) {
        return $assembly
        }
    }
      return $null
}

# Set up the handler above to be triggered when the AssemblyResolve event occurs
[System.AppDomain]::CurrentDomain.add_AssemblyResolve($onAssemblyResolveEventHandler)


# Load StackExchange.Redis.dll, prefer Add-Type because it seems to include dependencies, LoadFrom doesn't so might get an error later
Add-Type -LiteralPath  "$DllPath\StackExchange.Redis.dll"    

$redis = [StackExchange.Redis.ConnectionMultiplexer]::Connect("$redisConnectionString, allowAdmin=true")
$redisServer = $redis.GetServer($redisHostName, 6380)
# $rs.FlushAllDatabases(async=true)
$redisServer.FlushAllDatabases()

# Detach the event handler (not detaching can lead to stack overflow issues when closing PS)
[System.AppDomain]::CurrentDomain.remove_AssemblyResolve($onAssemblyResolveEventHandler)