AD のユーザーに関する情報を表示する MVC Web アプリケーションがあります。AD は Office 365 と同期されているため、UPN を使用して、Office 365用の Windows PowerShell コマンドレットを使用して Office 365 からライセンス情報を取得できます。基本的に、これはすべて正常に機能します。
初期化コマンドレットConnect-MsolService
が完了するまでに時間がかかるため、Office365Connector
クラスに一種のシングルトン パターンを使用しています。私のGlobal.asax
中でApplication_Start()
シングルトンインスタンスを初期化し、Application_End()
それを破棄します。コネクタ クラスはPowerShellInvoker
、名前が示すように、PowerShell の呼び出しをカプセル化するクラスのインスタンスを 1 つだけ使用します。コンストラクター内の PowerShell 初期化コードはPowerShellInvoker
次のようになります。
public PowerShellInvoker(params string[] modules)
{
var iss = InitialSessionState.CreateDefault();
iss.ImportPSModule(modules);
iss.ThrowOnRunspaceOpenError = true;
_runspace = RunspaceFactory.CreateRunspace(iss);
_runspace.Open();
_invoker = new RunspaceInvoke(_runspace);
}
クラスは、パラメータとしてOffice365Connector
このコンストラクタを呼び出し"MSOnline"
ます。このMSOnline
モジュールには、Office 365 のコマンドレットが含まれています。後でコマンドを実行するために、 フィールド_runspace
とフィールドを保持しています。_invoker
両方のフィールドは、(クラスが破棄されるときに呼び出される) のDispose
メソッドで破棄されます。スクリプトの実行は、次のコード行によって行われます。PowerShellInvoker
Office365Connector
_invoker.Invoke(scriptText);
紹介は以上です - ここで本当の問題が発生します。
私のアプリケーションには、ユーザー リストがあります。ユーザーをクリックすると、AJAX リクエストを使用して追加情報が読み込まれます。このリクエスト内で、アプリはクラスのシングルトン インスタンスを使用してOffice365Connector
、ユーザーのライセンス情報を取得します。ほとんどの場合、これはすべて完全に機能します。しかし、AJAX リクエストがコード 500 で終了することがあります。ソース コードをデバッグしていると、例外がPowerShellInvoker
上記の「呼び出し」行で、実行空間がもう開いていないことを教えてくれますが、その理由はわかりません。私は本当にそれを再現することさえできません。2 番目のユーザーをクリックすると、エラーが発生することがあります。場合によっては、10 番目または 15 番目のユーザーでエラーが発生します。MVC で使用される奇妙なクリーンアップ、タイムアウト、またはガベージ コレクションの手法については既に考えましたが、結論には達していません。IMHO、「ユーザーのクリック」の間の時間はわずか数秒であるため、Runspace の終了を時間ベースにすることはできません。
コマンドレットは Office 365 へのConnect-MsolService
接続を作成しますが、何も返しません。そのため、必要に応じて実行空間を再作成することは回避策ではありません。これは、PowerShellInvoker
クラスによって実行され、 Office365Connector
Office 365 に再接続する必要があることを認識しないためです。(これでも問題は解決しません。)クラスは他のPowerShellInvoker
場所でも使用されているため、解決策ではありません。
では、Runspace が閉じないようにする方法、または閉じられる理由を誰か教えてもらえますか?
編集:より多くのコード
完全なPowerShellInvoker
クラスはここにあります。
クラスでは、Office365Connector
現在多くのオーバーヘッドがあります。ここにいくつかのスニペットがあります:
コンストラクターでの初期化:
var cred = new PSCredential(adminUpn, adminPassword);
_psi = new PowerShellInvoker("MSOnline");
_psi.ExecuteCommand("Connect-MsolService", new { Credential = cred });
UPN のライセンスを取得する方法:
public IEnumerable<string> GetUserLicenses(string upn)
{
PSObject[] licenses = _psi.ExecuteScript(string.Format("(Get-MsolUser -UserPrincipalName \"{0}\").Licenses | % {{ $_.AccountSkuId }}", upn)).ToArray();
// no licenses: a list with one element (which is null) is returned.
if (licenses.Length == 1 && licenses[0] == null)
{
licenses = new PSObject[0];
}
return licenses.Select(pso => pso.ToString()).ToList();
}
ご覧のとおりToList
、メソッドの戻り値 (特にPowerShellInvoker
) にいくつかの s を追加しました。これを行ったのは、これが閉じられた実行空間の理由である可能性があると考えたため、列挙型の遅延実行を防ぎたかったからです。