Состояние текущего экземпляра PowerShell недопустимо для этой операции в C#
У меня есть метод ниже, который вызывается для разных сценариев PS, и я хотел бы, чтобы объект PowerShell был создан только один раз, и для этого я сделал объект Powershell как статический (см. Код ниже). но тогда это дает мне ошибку
Состояние текущего экземпляра PowerShell недопустимо для этой операции.
Как мне с этим справиться? как лучше всего оптимизировать мой код ниже?NB: приведенный ниже код отлично работает, если я удалю статику.
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;
}
}
}
это вызывается отсюда:
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;
}
1 ответ
Пока можно создать экземпляров в коде C#, вы не можете напрямую выполнять их там - это возможно только черезэкземпляр (или из кода PowerShell). Нет причин создавать и взаимодействовать с
ScriptBlock
экземпляры прямо в вашем коде C#.
Вот пример того, как неявно создать блок скрипта с помощью <tcode id="2672794"></tcode>, и как передать ему аргумент :
// 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);
}
}
Однако приведенное выше не может использоваться повторно , потому что после добавления аргументов или параметров с помощью или вы не можете удалить их и указать другие для выполнения другого вызова - насколько мне известно.
Обходной путь - использовать ввод конвейера PowerShell (как предоставляется через необязательный
input
параметр, который вы можете передать
<tcode id="2672799"></tcode>, так как это позволяет повторять вызовы с разными входными данными. Однако ваш блок сценария должен быть построен соответствующим образом:
// 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);
}
}
}
В качестве альтернативы, если это возможно, подумайте о том, чтобы обойтись без блоков сценариев , поскольку они требуют синтаксического анализа (хотя в данном случае как разовые накладные расходы) и - в Windows - подчиняются эффективной <em>политике выполнения</em> , которая может помешать их выполнению.
Без блоков скрипта вам пришлось бы вызывать одну или несколько команд по отдельности, используя <tcode id="2672801"></tcode> вызовы, разделяя несколько независимых команд с помощью <tcode id="2672802"></tcode>.
Если одна команда или конвейер команд принимает весь ввод через конвейер , вы можете использовать тот же подход, что и выше.
В противном случае - если
.AddParameter(s)
/.AddArgument()
нужны - вам нужно будет позвонитьps.Commands.Clear()
и повторно добавляйте команды перед каждым (повторным) вызовом ; однако по сравнению с вызовом.AddScript()
, это должно привести к небольшим накладным расходам.
Адаптация многоразовой техники к вашему коду :
Учебный класс
DataRulesPSScripts
, который использует статический
PowerShell
instance и один раз добавляет блок скрипта в свой статический конструктор.
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() {
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 });
}
}
Код, использующий класс, который передает параметры через конвейер :
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();
}
Образец звонка (
obj
- объект, содержащий указанные выше методы; предполагает упрощенный
EntityProperty
класс с собственностью
.Value
):
Console.WriteLine(
obj.GenerateRowKey(
new Dictionary<string, EntityProperty> { ["ParamA"] = new EntityProperty { Value = "bar" }, ["ParamB"] = new EntityProperty { Value = "baz" } }
).Result
);
Вышеупомянутое должно дать что-то вроде:
@{Row=demo.EntityProperty; Key=2}
Это строковое представление второго настраиваемого объекта, выводимого блоком сценария.