我有一个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字段以供以后执行命令。这两个字段都将以DisposePowerShellInvoker方法处理(在处理Office365Connector类时会调用此方法)。脚本执行通过以下代码行完成:

_invoker.Invoke(scriptText);

这么多的介绍-现在是真正的问题:

在我的应用程序中,我有一个用户列表。当我单击用户时,将使用AJAX请求加载其他信息。在此请求中,我的应用程序使用Office365Connector类的单例实例来检索用户的许可证信息。在大多数情况下,这一切都很好。但是有时AJAX请求最终以代码500结尾。在调试我的源代码时,我偶然发现上面“调用”行上的PowerShellInvoker中引发了异常,告诉我运行空间不再打开,并且我不能找出原因。我什至无法真正复制它。有时,当我单击第二个用户时,会发生错误。有时,错误发生在第10位或第15位用户上。我已经考虑过MVC使用的一些怪异的清理,超时或垃圾收集技术,但是我尚未得出结论。恕我直言,运行空间的关闭不能基于时间,因为“用户单击”之间的时间只有几秒钟。
Connect-MsolService cmdlet创建与Office 365的连接,但不返回任何内容。因此,如果需要的话,重新创建运行空间不是一种解决方法,因为这将由PowerShellInvoker类完成,而Office365Connector将不知道必须重新连接到Office365。(这也无法解决问题。)这两个类都不是解决方案,因为PowerShellInvoker也用在其他地方。

那么谁能告诉我如何防止运行空间关闭或为什么关闭它?

编辑:更多代码

完整的PowerShellInvoker类可以在here中找到。

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中)。我这样做是因为我想防止延迟枚举的执行,因为我认为这可能是封闭Runspace的原因。

最佳答案

好的,这是我自己的一个愚蠢错误-尽管我不太了解MVC为什么这样做:

在解决问题的众多尝试之一中,我将Office365Connector的处置代码移动到应用程序的Dispose方法中(在Global.asax.cs中)。在那之后,我显然解决了原始错误,但是由于处理错误而无法解决。显然,应用程序实例比“结束”实例的处置频率更高。当我将代码移回属于它的Application_End()时,一切正常。

这使我原来的问题有点无效,因为我写道我的处置代码已经在Application_End()中。 :-\

关于c# - 不知道为什么C#PowerShell Runspace不定期关闭,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11564774/

10-11 09:30