Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/csharp-4.0/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
.net 如何解决powershell模块中的程序集依赖项版本冲突_.net_C# 4.0_Powershell 3.0 - Fatal编程技术网

.net 如何解决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

我有一个powershell脚本,如下所示:

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));
        }
    }
}