简体   繁体   English

当前 PowerShell 实例的 state 对 C# 中的此操作无效

[英]The state of the current PowerShell instance is not valid for this operation in C#

I am having below method which is being called for different PS scripts and I would like the PowerShell object to be created only once and for that I made the Powershell object as static(see below code). I am having below method which is being called for different PS scripts and I would like the PowerShell object to be created only once and for that I made the Powershell object as static(see below code). but then it gives me error但后来它给了我错误

The state of the current PowerShell instance is not valid for this operation.当前 PowerShell 实例的 state 对该操作无效。

How should I handle this?我该如何处理? what is the best way to optimize my below code?优化以下代码的最佳方法是什么? NB: Below code works fine if I remove static.注意:如果我删除 static,下面的代码可以正常工作。

class DataRulesPSScripts
    {
        static PowerShell ps = PowerShell.Create();
        public IEnumerable<object> RunScriptBlock( ScriptBlock scriptBlock, Dictionary<string, object> scriptParameters )
        {
            var vars = scriptParameters.Select( p => new PSVariable( p.Key, p.Value ) ).ToList();
            return scriptBlock.InvokeWithContext( null, vars );
        }

        public async Task<ScriptBlock> CreateScriptBlock( string pSScript )
        {            
            ps.AddScript( pSScript );
            var scriptBlock = (await ps.InvokeAsync())[0].BaseObject as ScriptBlock;
            return scriptBlock;
        }
    }
}

this is called from here:这是从这里调用的:

internal async Task<string> GeneratePartitionKey( Dictionary<string, EntityProperty> arg)
        {       
            var result =await GenerateKeys(arg);
            return result[0].ToString();
        }        

        internal async Task<string> GenerateRowKey( Dictionary<string, EntityProperty> arg )
        {
            var result = await GenerateKeys( arg );
            return result[1].ToString();
        }

        private async Task<List<object>> GenerateKeys( Dictionary<string, EntityProperty> arg )
        {
            var pars = new Dictionary<string, object>();
            pars.Add( "_", arg );
            DataRulesPSScripts ds = new DataRulesPSScripts();
            var scriptBlock = await ds.CreateScriptBlock( PSScript );
            var results = ds.RunScriptBlock( scriptBlock, pars ).ToList();
            return results;
        }

There is no reason to create and interact with ScriptBlock instances directly in your C# code - they are used internally by the PowerShell SDK : [1] They are internally created and stored when you pass a piece of PowerShell code as a string to the PowerShell.AddScript() method, and are invoked via the PowerShell instance's , .Invoke() method. There is no reason to create and interact with ScriptBlock instances directly in your C# code - they are used internally by the PowerShell SDK : [1] They are internally created and stored when you pass a piece of PowerShell code as a string to the PowerShell.AddScript()方法,并通过PowerShell实例的.Invoke()方法调用。

While your indirect way of obtaining a script block for direct execution in C# code by letting a PowerShell instance create and output it for you via an .AddScript() call ( ps.AddScript( pSScript ); var scriptBlock = (await ps.InvokeAsync())[0].BaseObject as ScriptBlock; ) does give you a script block that you can call directly from C# code via its .Invoke() method (if you created the script block directly in C# code, you wouldn't be able to invoke it all, due to not being connected to a PowerShell runspace ), such calls only provide success output - output from all other PowerShell streams would be lost - that is, the originating PowerShell instance's .Streams property wouldn't reflect such Z78E6221F6393D1356681DB While your indirect way of obtaining a script block for direct execution in C# code by letting a PowerShell instance create and output it for you via an .AddScript() call ( ps.AddScript( pSScript ); var scriptBlock = (await ps.InvokeAsync())[0].BaseObject as ScriptBlock; )确实为您提供了一个脚本块,您可以通过其.Invoke()方法直接从 C# 代码调用(如果您直接在 C# 代码中创建脚本块,您将无法to invoke it all, due to not being connected to a PowerShell runspace ), such calls only provide success output - output from all other PowerShell streams would be lost - that is, the originating PowerShell instance's .Streams property wouldn't reflect such Z78E6221F6393D1356681DB 398F14CE6DZ, which notably makes non-terminating errors that occurred inaccessible, and, similarly, the .HadErrors property would not reflect whether non-terminating errors occurred. 398F14CE6DZ,这尤其使发生的非终止错误无法访问,同样, .HadErrors属性不会反映是否发生了非终止错误。 Therefore, this approach should be avoided .因此,应该避免这种方法。 [2] [2]

Here's an example that creates a script bock implicitly, behind the scenes , via PowerShell.AddScript() , passes an argument to it and invokes it:这是一个通过PowerShell.AddScript()在幕后隐式创建脚本块的示例,将参数传递给它并调用它:

// Define the script-block text.
// It expects a single argument that is an object with .Name and .Code
// properties, whose values are echoed.
// NOTE: Do NOT include { ... }
var scriptBlockText = "$obj = $args[0]; $obj.Name; $obj.Code";

// Define an object to pass to the script block.
var obj = new { Name = "Abc", Code = 42 };

using (var ps = PowerShell.Create()) {
  
  // Add the script block and an argument to pass to it.
  ps
    .AddScript(scriptBlockText)
    .AddArgument(obj);

  // Invoke and echo the results.  
  foreach (var o in ps.Invoke()) {
    Console.WriteLine(o);
  }

}

However, the above isn't reusable , because once you've added arguments or parameters with .AddParameter(s) or .AddArgument() , you cannot remove them and specify different ones to perform another call - as far as I know.但是,上述内容不可重用,因为一旦添加了 arguments 或带有.AddParameter(s).AddArgument()的参数,就无法删除它们并指定不同的参数来执行另一个调用 - 据我所知。

The workaround is to use PowerShell pipeline input (as provided via the optional input parameter you can pass to PowerShell.Invoke() , as that enables repeated invocations with different input. However, your script block must then be constructed accordingly:解决方法是使用 PowerShell管道输入(通过您可以传递给PowerShell.Invoke()的可选input参数提供,因为这样可以使用不同的输入进行重复调用。但是,您的脚本块必须相应地构造:

// Define the script-block text.
// This time, expect the input to come via the *pipeline*, which
// can be accessed via the $input enumerator.
// NOTE: Do NOT include { ... }
var scriptBlockText = "$obj = $($input); $obj.Name; $obj.Code";

// Define two objects to pass to the script block, one each in 
// two separate invocations:
object[] objects = {
  new { Name = "Abc", Code = 42 },
  new { Name = "Def", Code = 43 }
};

using (var ps = PowerShell.Create()) {
  
  // Add the script block.
  ps.AddScript(scriptBlockText);

  // Perform two separate invocations.
  foreach (var obj in objects) {

    // For housekeeping, clean the previous non-success streams.
    ps.Streams.ClearStreams();

    // Invoke the script block with the object at hand and echo the results.
    // Note: The input argument must be an enumerable, so we wrap the object
    //       in an aux. array.
    foreach (var o in ps.Invoke(new[] { obj })) {
      Console.WriteLine(o);
    }

  }

}

Alternatively, if feasible, consider making do without script blocks , as they require parsing (albeit as one-time overhead in this case) and - on Windows - are subject to the effective execution policy , which could prevent their execution (though you can bypass such a restriction on a per-process basis, see this answer ).或者,如果可行,请考虑不使用脚本块,因为它们需要解析(尽管在这种情况下是一次性开销)并且 - 在 Windows 上 - 受有效执行策略的约束,这可能会阻止它们的执行(尽管您可以绕过对每个进程的这种限制,请参阅此答案)。

Without script blocks, you'd have to invoke one or more commands individually, using PowerShell.AddCommand() calls, separating multiple independent commands with PowerShell.AddStatement() .如果没有脚本块,您必须单独调用一个或多个命令,使用PowerShell.AddCommand()调用,使用 PowerShell.AddStatement( PowerShell.AddStatement()分隔多个独立命令。

  • If a single command or a pipeline of command accepts all input via the pipeline , you can use the same approach as above.如果单个命令或命令管道通过管道接受所有输入,则可以使用与上述相同的方法。

  • Otherwise - if .AddParameter(s) / .AddArgument() are needed - you'd have to call ps.Commands.Clear() and re-add the commands before every (repeat) invocation ;否则 - 如果需要.AddParameter(s) / .AddArgument() - 您必须调用ps.Commands.Clear()在每次(重复)调用之前重新添加命令 however, compared to calling .AddScript() , this should introduce little overhead.但是,与调用.AddScript()相比,这应该会带来很少的开销。


Adaptation of the reusable technique to your code :使可重用技术适应您的代码

Class DataRulesPSScripts , which uses a static PowerShell instance and adds the script block once, in its static constructor. Class DataRulesPSScripts ,它使用 static PowerShell实例并在其 ZA8125ZDF1D456E95D29E62 构造函数中添加一次脚本块

  • Note: Consider using an instance property instead and making the class implement IDisposable to allow users of the class control over the PowerShell instance's lifecycle.注意:考虑改用实例属性并使 class 实现IDisposable以允许 class 的用户控制 PowerShell 实例的生命周期。
class DataRulesPSScripts
{
  static PowerShell ps = PowerShell.Create();
  // The script-block text:
  // Note that $ParamA and $ParamB must correspond to the keys of the
  // dictionary passed to the script block on invocation via .InvokeAsync()
  static string PSScript = @"$argDict = $($input); & { param($ParamA, $ParamB) [pscustomobject] @{ Partition = $ParamA; Key = 1 }, [pscustomobject] @{ Row = $ParamB; Key = 2 } } @argDict";

  static DataRulesPSScripts() {
    // Add the script-block text only once, which therefore incurs the
    // overhead of parsing the text into a script block only once,
    // and allows repeated later invocations via .Invoke() with pipeline input.
    ps.AddScript(PSScript);
  }
  public async Task<IEnumerable<object>> RunScriptBlock(Dictionary<string, EntityProperty> scriptParameters)
  {
    // Pass the parameter dictionary as pipeline input.
    // Note: Since dictionaries are enumerable themselves, an aux. array
    //       is needed to pass the dictionary as a single object.
    return await ps.InvokeAsync<object>(new [] { scriptParameters });
  }

}

The code that uses the class, which passes the parameters via the pipeline :使用 class 的代码通过管道传递参数:

internal async Task<string> GeneratePartitionKey(Dictionary<string, EntityProperty> arg)
{
  var result = await GenerateKeys(arg);
  return result[0].ToString();
}

internal async Task<string> GenerateRowKey(Dictionary<string, EntityProperty> arg)
{
  var result = await GenerateKeys(arg);
  return result[1].ToString();
}


private async Task<List<object>> GenerateKeys(Dictionary<string, EntityProperty> args)
{
  DataRulesPSScripts ds = new DataRulesPSScripts();
  var results = await ds.RunScriptBlock(args);
  return results.ToList();
}

Sample call ( obj is the object that contains the methods above; assumes a simplified EntityProperty class with property .Value ):示例调用( obj是包含上述方法的 object;假设简化的EntityProperty class 具有属性.Value ):

Console.WriteLine(
  obj.GenerateRowKey(
    new Dictionary<string, EntityProperty> { ["ParamA"] = new EntityProperty { Value = "bar" },  ["ParamB"] = new EntityProperty { Value = "baz" } }
  ).Result
);

The above should yield something like:上面应该产生类似的东西:

@{Row=demo.EntityProperty; Key=2}

This is the string representation of the 2nd custom object output by the script block.这是脚本块对第二个自定义 object output 的字符串表示。


[1] In PowerShell script code , by contrast, ScriptBlock instances are used directly, typically in the form of script-block literals ( {... } ), invoked with & , the call operator . [1] 相比之下,在PowerShell 脚本代码中,直接使用ScriptBlock实例,通常脚本块文字( {... } ) 的形式,通过调用运算符&调用。

[2] Here's a quick demonstration from PowerShell: [2] 下面是来自 PowerShell 的快速演示:
$ps=[PowerShell]::Create(); $sb = $ps.AddScript("{ 'hi'; Get-Item nosuchfile }").Invoke()[0]; "call result: $($sb.Invoke())"; "had errors: $($ps.HadErrors)"; "error stream: $($ps.Streams.Error)"
Even though the call produced a non-terminating error, .HadErrors reports $false , and the .Streams.Error is empty.即使调用产生了非终止错误, .HadErrors报告$false ,并且.Streams.Error是空的。

暂无
暂无

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

相关问题 由于C#控制台应用程序中对象的当前状态,因此该操作无效 - Operation is not valid due to the current state of the object in C# console application C# - Autocad api - 由于 ZA8CFDE6331BD59EB2ACZ6B891 的当前 state,操作无效 - C# - Autocad api - Operation is not valid due to the current state of the object 由于C#实体框架中对象的当前状态,因此该操作无效 - Operation is not valid due to the current state of the object in C# Entity Framework 由于对象的当前状态,操作无效。 在C#中 - Operation is not valid due to the current state of the object. in C# 使用C#将记录插入Oracle数据库时出现错误-由于对象的当前状态,操作无效 - Error on while inserting record to Oracle database using C# - Operation is not valid due to the current state of the object 为什么收到此错误{“由于对象的当前状态,操作无效。”} C# - Why am I getting this error {“Operation is not valid due to the current state of the object.”} C# C#和Oracle,登录表单:由于对象的当前状态,操作无效 - C# and Oracle, login Form: Operation is not valid due to the current state of the object Npgsql C#throw由于对象的当前状态突然无效 - Npgsql C# throwing Operation is not valid due to the current state of the object suddenly 由于对象的当前状态,IEquatable Operation无效 - IEquatable Operation is not valid due to the current state of the object 由于对象的当前状态,操作无效 - Operation is not valid due to the current state of the object
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM