[英]Don't know why C# PowerShell Runspace is closing irregularly
I have a MVC web application which shows some information about users in our AD. 我有一个MVC Web应用程序,其中显示了有关我们AD中用户的一些信息。 The AD is synchronized with Office 365, so using the UPN I can retrieve the license information from Office 365 using the Windows PowerShell cmdlets for Office 365 .
AD与Office 365同步,因此使用UPN可以使用Office 365的Windows PowerShell cmdlet从Office 365检索许可证信息。 Basically this all works fine.
基本上,这一切都很好。
As the initialization cmdlet Connect-MsolService
takes some time to finish, I'm using kind of a singleton pattern for my Office365Connector
class. 由于初始化cmdlet
Connect-MsolService
需要一些时间才能完成,因此我在Office365Connector
类中使用了一种单例模式。 In my Global.asax
in Application_Start()
I initialize the singleton instance, in Application_End()
I dispose it. 在
Application_Start()
Global.asax
中,我初始化单例实例,在Application_End()
,将其处置。 The connector class uses exactly one instance of my PowerShellInvoker
class which - as the name implies - encapsulates PowerShell invoking. 连接器类恰好使用了我的
PowerShellInvoker
类的一个实例,顾名思义,该实例封装了PowerShell调用。 The PowerShell initialization code inside the PowerShellInvoker
constructor looks like this: 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);
}
The Office365Connector
class calls this constructor with "MSOnline"
as parameter. Office365Connector
类使用"MSOnline"
作为参数调用此构造函数。 The MSOnline
module contains the cmdlets for Office 365. I keep the _runspace
and _invoker
fields for command execution at a later time. MSOnline
模块包含Office 365的cmdlet。我保留_runspace
和_invoker
字段以供以后执行命令。 Both fields will be disposed in the Dispose
method of PowerShellInvoker
(which is called when the Office365Connector
class is being disposed). 这两个字段都将在
PowerShellInvoker
的Dispose
方法中进行处置(在Dispose
Office365Connector
类时会调用此Office365Connector
)。 Script execution is done by this line of code: 脚本执行通过以下代码行完成:
_invoker.Invoke(scriptText);
So much for the introduction - now here comes the real problem: 这么多的介绍-现在是真正的问题:
In my application, I have a user list. 在我的应用程序中,我有一个用户列表。 When I click a user, additional information is loaded using an AJAX request.
当我单击用户时,将使用AJAX请求加载其他信息。 Within this request, my app uses the singleton instance of the
Office365Connector
class to retrieve the license information for the user. 在此请求中,我的应用程序使用
Office365Connector
类的单例实例来检索用户的许可证信息。 In most cases, this all works perfectly. 在大多数情况下,这一切都很好。 But sometimes the AJAX request ends up with a code 500. Debugging my source code, I stumbled upon an Exception being thrown in the
PowerShellInvoker
on the "Invoke" line above, telling me that the Runspace is not open anymore, and I can't figure out why. 但是有时AJAX请求最终以代码500结束。在调试我的源代码时,我偶然发现
PowerShellInvoker
在上面“调用”行上引发了异常,告诉我运行空间不再打开,并且我不能找出原因。 I can't even really reproduce it. 我什至无法真正复制它。 Sometimes, the error occurs when I click the second user.
有时,当我单击第二个用户时,会发生错误。 Sometimes, the error occurs on the 10th or 15th user.
有时,错误发生在第10位或第15位用户上。 I already thought about some weird clean-up, timeout or garbage collection techniques used by MVC, but I haven't come to a conclusion.
我已经考虑过MVC使用的一些怪异的清理,超时或垃圾收集技术,但是我尚未得出结论。 IMHO, the Runspace closing can't be time-based because the time between the "user clicks" is just a few seconds.
恕我直言,运行空间的关闭不能基于时间,因为“用户单击”之间的时间只有几秒钟。
The Connect-MsolService
cmdlet creates a connection to Office 365, but it doesn't return anything. Connect-MsolService
cmdlet创建与Office 365的连接,但不返回任何内容。 So re-creating the Runspace if needed is not a work-around because this would be done by the PowerShellInvoker
class and the Office365Connector
wouldn't know that it has to reconnect to Office 365. (Also this would not solve the problem.) Combining the two classes isn't a solution either because the PowerShellInvoker
is also used elsewhere. 因此,如果需要的话,重新创建运行空间不是一种解决方法,因为这将由
PowerShellInvoker
类完成,并且Office365Connector
不会知道它必须重新连接至Office365Connector
。(这也无法解决问题。)这两个类都不是解决方案,因为PowerShellInvoker
也用在其他地方。
So can anyone tell me how to prevent the Runspace from closing or why it is closed? 那么谁能告诉我如何防止运行空间关闭或为什么关闭它?
Edit: More code 编辑:更多代码
The full PowerShellInvoker
class can be found here . 完整的
PowerShellInvoker
类可在此处找到。
In the Office365Connector
class, there is currently much overhead. 在
Office365Connector
类中,当前有很多开销。 Here are some snippets: 以下是一些摘要:
Initialization in constructor: 在构造函数中初始化:
var cred = new PSCredential(adminUpn, adminPassword);
_psi = new PowerShellInvoker("MSOnline");
_psi.ExecuteCommand("Connect-MsolService", new { Credential = cred });
Method to retrieve the license for a UPN: 检索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();
}
As you can see, I added some ToList
s to the method return values (especially in the PowerShellInvoker
). 如您所见,我在方法的返回值中添加了一些
ToList
(特别是在PowerShellInvoker
)。 I did this because I wanted to prevent lazy execution of the enumerables because I thought this could be the reason for the closed Runspace. 我这样做是因为我想防止延迟枚举的执行,因为我认为这可能是封闭Runspace的原因。
OK, this was a stupid mistake by myself - although I don't really understand why MVC is doing this: 好的,这是我自己一个愚蠢的错误-尽管我不太了解MVC为何这样做:
In one of my many attempts to solve the problem I moved the disposal code of my Office365Connector
to the Dispose
method of the application (in Global.asax.cs
). 在解决问题的众多尝试之一中,我将
Office365Connector
的处置代码移至了应用程序的Dispose
方法(位于Global.asax.cs
)。 After that I apparently fixed the original error but it didn't work out because of the disposal stuff. 在那之后,我显然修复了原始错误,但是由于处理错误而无法解决。 Apparently the application instance is disposed more often than it is "ended".
显然,应用程序实例比“结束”实例的处置频率更高。 When I moved the code back to
Application_End()
where it belongs everything worked fine. 当我将代码移回所属的
Application_End()
,一切正常。
That makes my original question kind of invalid because I wrote that my dispose code already was in Application_End()
. 这使我原来的问题变得无效,因为我写道我的处置代码已经在
Application_End()
。 :-\\ : - \\
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.