【发布时间】:2012-07-19 16:00:43
【问题描述】:
我有一个 MVC Web 应用程序,它在我们的 AD 中显示有关用户的一些信息。 AD 与 Office 365 同步,因此使用 UPN,我可以使用 Windows PowerShell cmdlets for Office 365 从 Office 365 检索许可证信息。基本上这一切都很好。
由于初始化 cmdlet Connect-MsolService 需要一些时间才能完成,我正在为我的 Office365Connector 类使用一种单例模式。在我的Global.asax 中的Application_Start() 中,我初始化了单例实例,在Application_End() 中我处理了它。连接器类恰好使用了我的PowerShellInvoker 类的一个实例——顾名思义——它封装了PowerShell 调用。 PowerShellInvoker 构造函数中的 PowerShell 初始化代码如下所示:
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 的 cmdlet。我保留了 _runspace 和 _invoker 字段以供以后执行命令。这两个字段都将在PowerShellInvoker 的Dispose 方法中进行处理(在处理Office365Connector 类时调用该方法)。脚本执行由这行代码完成:
_invoker.Invoke(scriptText);
介绍就这么多——现在真正的问题来了:
在我的应用程序中,我有一个用户列表。当我单击用户时,会使用 AJAX 请求加载附加信息。在此请求中,我的应用程序使用 Office365Connector 类的单例实例来检索用户的许可证信息。在大多数情况下,这一切都很完美。但有时 AJAX 请求以代码 500 结束。调试我的源代码时,我偶然发现在上面“调用”行的PowerShellInvoker 中抛出了一个异常,告诉我运行空间不再打开,我可以不知道为什么。我什至无法真正复制它。有时,当我单击第二个用户时会发生错误。有时,错误发生在第 10 位或第 15 位用户身上。我已经想过 MVC 使用的一些奇怪的清理、超时或垃圾收集技术,但我还没有得出结论。恕我直言,Runspace 关闭不能基于时间,因为“用户点击”之间的时间只有几秒钟。
Connect-MsolService cmdlet 创建到 Office 365 的连接,但它不返回任何内容。因此,如果需要重新创建运行空间不是一种解决方法,因为这将由 PowerShellInvoker 类完成,而 Office365Connector 不会知道它必须重新连接到 Office 365。(而且这不会 解决这个问题。)结合这两个类也不是一个解决方案,因为PowerShellInvoker也在其他地方使用。
那么谁能告诉我如何防止运行空间关闭或为什么它被关闭?
编辑:更多代码
完整的PowerShellInvoker类可以在here找到。
在Office365Connector 类中,目前有很多开销。以下是一些sn-ps:
在构造函数中初始化:
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();
}
如您所见,我在方法返回值中添加了一些ToLists(尤其是在PowerShellInvoker 中)。我这样做是因为我想防止可枚举的延迟执行,因为我认为这可能是关闭 Runspace 的原因。
【问题讨论】:
-
我没有答案,但我有兴趣尝试找出答案。您可以分享更多代码吗?另外,您是否尝试过在打开运行空间后立即启动 PowerShell 脚本?看看它是否记录了任何有用的东西会很有趣。
-
@ElijahW.Gagne 我编辑了问题并添加了更多代码。我会尽快尝试转录的想法。
-
感谢老兄的问答 - 在尝试为 PS 提供商编写测试时帮助了我!
标签: c# asp.net-mvc-3 powershell office365