.net 如何解决powershell模块中的程序集依赖项版本冲突
我有一个powershell脚本,如下所示:.net 如何解决powershell模块中的程序集依赖项版本冲突,.net,c#-4.0,powershell-3.0,.net,C# 4.0,Powershell 3.0,我有一个powershell脚本,如下所示: Import-Module module1.dll Import-Module module2.dll Get-SomethingFromModule1 | Get-SomethingFromModule2 我遇到的问题是module1.dll和module2.dll都引用了不同版本的SomeOtherLibrary.dll,而SomeOtherLibrary.dll的版本包含了我碰巧使用的破坏性更改 我能跑 Import-Module modu
Import-Module module1.dll
Import-Module module2.dll
Get-SomethingFromModule1 | Get-SomethingFromModule2
我遇到的问题是module1.dll
和module2.dll
都引用了不同版本的SomeOtherLibrary.dll
,而SomeOtherLibrary.dll
的版本包含了我碰巧使用的破坏性更改
我能跑
Import-Module module1.dll
Get-SomethingFromModule1
及
在单独的powershell会话中,每个会话的行为都正确
但是,我希望通过管道将数据从一个cmdlet传输到另一个cmdlet,并且Get-SomethingFromModule2
由于找不到方法而引发异常。我相信只加载/使用了SomeOtherLibrary.dll
的最新版本(或导入的第一个模块使用的版本)。有没有办法强制module1.dll
和module2.dll
加载/使用其特定版本的SomeOtherLibrary.dll
我试图避免更新引用并重新编译所有这些模块
谢谢我通过在.csproj文件中提供一个强名称密钥文件,对程序集
SomeOtherLibrary.dll
进行了强名称命名:
<PropertyGroup>
<AssemblyOriginatorKeyFile>StrongNameKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
StrongNameKey.snk
我现在可以导入这两个模块,每个模块都使用自己版本的程序集SomeOtherLibrary.dll
。这种方法仍然需要我更新引用并重新编译所有这些模块
但是,只要我对powershell模块引用的所有程序集进行了强命名,它就可以防止将来发生此问题。如果不选择强命名,您还可以在单独的AppDomain中运行cmdlet逻辑,从而在powershell加载和运行代码时隔离依赖项。通过一些简单的优化,如果考虑性能,单独的appdomain版本也可以相当快地运行 假设您有一个类似于以下内容的cmdlet:
using System.Management.Automation;
namespace CmdletOne
{
[Cmdlet(VerbsCommon.Show, "Path")]
public class ShowPathCmdlet : Cmdlet
{
protected override void ProcessRecord()
{
// ...
// Run some business logic & load assemblies
// ...
WriteObject("Value is foo");
}
}
}
要重构它,您需要做四件事。首先,您需要代码来管理appdomain的创建和远程处理。我使用了类似于这个helper类的东西:
using System;
using System.IO;
namespace Common
{
/// <summary>
/// General-purpose class that can put a remoting proxy around a given type and create a new appdomain for it to run in.
/// This effectively "sandboxes" the code being run and isolates its dependencies from other pieces of code.
/// </summary>
/// <typeparam name="T">The type of object that will be run in the sandbox. Must be compatible with Remoting.</typeparam>
public class ExecutionSandbox<t> : IDisposable
where T : MarshalByRefObject
{
/// <summary>
/// Local copy of the sandbox app domain
/// </summary>
private AppDomain _domain;
/// <summary>
/// Reference of the proxy wrapper for T
/// </summary>
public T ObjectProxy { get; private set; }
/// <summary>
/// Creates an instance of ExecutionSandbox
/// </summary>
/// <param name="assemblyPath" />The path where the assembly that contains type T may be found
public ExecutionSandbox(string assemblyPath)
{
Type sandboxedType = typeof (T);
AppDomainSetup domainInfo = new AppDomainSetup();
domainInfo.ApplicationBase = assemblyPath;
_domain = AppDomain.CreateDomain(string.Format("Sandbox.{0}", sandboxedType.Namespace), null, domainInfo);
string assemblyFileName = Path.Combine(assemblyPath, sandboxedType.Assembly.GetName().Name) + ".dll";
object instanceAndUnwrap = _domain.CreateInstanceFromAndUnwrap(assemblyFileName, sandboxedType.FullName);
ObjectProxy = (T)instanceAndUnwrap;
}
/// <summary>
/// Allows safe cleanup of the sandbox app domain.
/// </summary>
public void Dispose()
{
if (_domain != null)
{
AppDomain.Unload(_domain);
_domain = null;
}
ObjectProxy = null;
}
}
}
第三,您需要为代码创建一个包装器。此包装器的类型将用作远程代理,其代码将在单独的appdomain中运行。我的类似于此-请注意与原始cmdlet的相似性,还要注意继承链:
using System;
using Common;
namespace CmdletOne
{
public class Proxy : MarshalByRefObject, IProxy
{
public string DoWork()
{
// ...
// Run some business logic & load assemblies
// ...
return "foo";
}
}
}
最后,您需要重构原始cmdlet,以便它使用单独的appdomain执行代码。我的代码是这样的,其中包括一些额外的代码以优化性能:
using System;
using System.IO;
using System.Management.Automation;
using System.Reflection;
using Common;
namespace CmdletOne
{
[Cmdlet(VerbsCommon.Show, "Path")]
public class ShowPathCmdlet : Cmdlet
{
private static ExecutionSandbox _executionSandbox;
private readonly object _lockObject = new object();
protected override void ProcessRecord()
{
DateTime start = DateTime.Now;
lock (_lockObject)
{
if (_executionSandbox == null)
{
string cmdletExecutionPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
_executionSandbox = new ExecutionSandbox(cmdletExecutionPath);
}
}
Proxy proxy = _executionSandbox.Value;
string path = proxy.DoWork();
DateTime end = DateTime.Now;
WriteObject(string.Format("Value is {0}. Elapsed MS: {1}", path, (end - start).TotalMilliseconds));
}
}
}
可以找到有关此技术的更多详细信息,包括指向示例代码的链接
using System;
using Common;
namespace CmdletOne
{
public class Proxy : MarshalByRefObject, IProxy
{
public string DoWork()
{
// ...
// Run some business logic & load assemblies
// ...
return "foo";
}
}
}
using System;
using System.IO;
using System.Management.Automation;
using System.Reflection;
using Common;
namespace CmdletOne
{
[Cmdlet(VerbsCommon.Show, "Path")]
public class ShowPathCmdlet : Cmdlet
{
private static ExecutionSandbox _executionSandbox;
private readonly object _lockObject = new object();
protected override void ProcessRecord()
{
DateTime start = DateTime.Now;
lock (_lockObject)
{
if (_executionSandbox == null)
{
string cmdletExecutionPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
_executionSandbox = new ExecutionSandbox(cmdletExecutionPath);
}
}
Proxy proxy = _executionSandbox.Value;
string path = proxy.DoWork();
DateTime end = DateTime.Now;
WriteObject(string.Format("Value is {0}. Elapsed MS: {1}", path, (end - start).TotalMilliseconds));
}
}
}