问题在于,尽管应用程序运行良好,但如果我尝试同时使用多个线程为两个租户运行应用程序,则会发生冲突。该模块似乎不支持多线程。
我已经创建了一个服务,并通过 .Net DI 注入为短暂对象。该服务创建 HostedRunspace 类,用于执行 PowerShell 的状态管理。
public class HostedRunspace : IDisposable
{
private Runspace runspace;
public void Initialize(string[] modulesToLoad = null)
{
InitialSessionState defaultSessionState = InitialSessionState.CreateDefault();
defaultSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.RemoteSigned;
if (modulesToLoad != null)
{
foreach (string moduleName in modulesToLoad)
{
defaultSessionState.ImportPSModule(moduleName);
}
}
runspace = RunspaceFactory.CreateRunspace(defaultSessionState);
runspace.ThreadOptions = PSThreadOptions.UseNewThread;
runspace.ApartmentState = ApartmentState.STA;
runspace.Open();
}
public async Task<List<PSObject>> RunScript(string scriptContents, Dictionary<string, object> scriptParameters = null)
{
if (runspace == null)
{
throw new ApplicationException("Runspace must be initialized before calling RunScript().");
}
PSDataCollection<PSObject> pipelineObjects;
using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(runspace))
{
ps.AddScript(scriptContents);
if (scriptParameters != null)
{
ps.AddParameters(scriptParameters);
}
ps.Streams.Error.DataAdded += Error_DataAdded;
ps.Streams.Warning.DataAdded += Warning_DataAdded;
ps.Streams.Information.DataAdded += Information_DataAdded;
// execute the script and await the result.
pipelineObjects = await ps.InvokeAsync().ConfigureAwait(false);
// print the resulting pipeline objects to the console.
Console.WriteLine("----- Pipeline Output below this point -----");
foreach (PSObject item in pipelineObjects)
{
Console.WriteLine(item.BaseObject.ToString());
}
}
List<PSObject> psObjects = new List<PSObject>();
foreach (PSObject pipelineObject in pipelineObjects)
{
psObjects.Add(pipelineObject);
}
return psObjects;
}
当收集租户的PowerShell数据时,会创建一个新线程,如下所示:
IOnlineDataTaskRunner taskRunner = serviceProvider.GetRequiredService<IOnlineDataTaskRunner>();
Thread thread = new Thread(() => taskRunner.RunAsync(dataTask));
thread.Start();
这里我获取了我的PowerShell服务的短暂版本,它本身将新建一个HostedRunspace。 我创建了一个新线程,提供了一些配置并启动该线程。
当线程运行时,我首先必须使用证书连接到Exchange Online。
string command = $"Connect-ExchangeOnline -CertificateThumbprint \"{Thumbprint}\" -AppId \"{ClientId}\" -ShowBanner:$false -Organization {tenant}";
await runspace.RunScript(command);
接着,我使用PowerShell模块执行各种其他数据检索任务,包括获取邮箱信息、存储大小等。这些任务也是通过执行的。
await runspace.RunScript(command);
如上所述,如果我一次只运行一个线程,就不会出现问题。但是,如果将线程1连接到租户A,将线程2连接到租户B,则最初的Connect-ExchangeOnline将不会出现任何问题。
但是,如果检索邮箱信息,例如,两个线程都将为最后连接的任何租户获取数据。这表明该模块或我的实现可能存在线程问题。