我正在开发一个ASP.NET应用程序,除其他外,该应用程序应该从Azure Active Directory检索用户。为此,我使用的是Microsoft Graph版本1.14.0预览库,可以在here中找到。
由于该库仅提供用于检索用户的异步方法,因此我正在使用以下(伪)代码来同步运行它。
string userPrincipalName = "test.user@intuneforeducation.com";
var task = Task.Run(async () => await _graphServiceClient.Users[userPrincipalName].Request().GetAsync());
while (!task.IsCompleted)
Thread.Sleep(200);
User retrievedUser = task.Result;
我现在面临的问题是,从ASP.NET应用程序调用这段代码后,task.IsCompleted
会永远false
保留。现在,这里有一个奇怪的部分,我无法理解:该代码可以在控制台应用程序和单元测试(使用NUnit)中完美运行。
人们可能会认为GraphServiceClient实例在这些版本中的构建方式有所不同,但我100%肯定不是。组成它的信息是从数据库中加载的,单元测试中的代码与ASP.NET应用程序的控制器中的代码完全相同。使用单元测试,以上代码将在1.5秒内执行。在ASP.NET应用程序中,我将其运行了长达30分钟,没有任何结果,没有错误,没有超时,什么也没有。
我意识到这可能是一个小问题,但我确实希望有人遇到了同样的问题并能够解决。
我设法解决了这个问题。奇怪的是,将我所有的方法都转换为异步任务无法正常工作,甚至await
一直挂着。但是,我不完全理解为什么我的解决方案现在可以正常工作。看来我的伪代码不完全准确,解决方案就在那里。
此代码将永久保留在while (!runTask.IsCompleted)
中。
object GetResult<TResult>(Task<TResult> task)
{
using (task)
using (var runTask = Task.Run(async () => await task))
{
while (!runTask.IsCompleted)
Thread.Sleep(SleepTime);
if (runTask.Exception != null)
throw runTask.Exception.InnerException ?? runTask.Exception;
return runTask.Result;
}
}
User GetUser(string userPrincipalName)
{
return (User)GetResult(_graphServiceClient.Users[userPrincipalName].Request().GetAsync());
}
此方法在执行await
行之后保持挂起状态。
async Task<User> GetUser(string userPrincipalName)
{
User user = await _graphServiceClient.Users[userPrincipalName].Request().GetAsync();
return user;
}
此代码与尝试#1中的代码基本相同,唯一的区别是它不使用GetResult
方法,但确实使用了与GetResult
完全相同的方法。 / p>
User GetUser(string userPrincipalName)
{
using(var task = Task.Run(async () => await _graphServiceClient.Users[userPrincipalName].Request().GetAsync()))
{
while (!task.IsCompleted)
Thread.Sleep(200);
return task.Result;
}
}
虽然这种方法可能不被视为最佳实践,但它确实有效。我对这种方法为何有效感到非常困惑,因为尝试#1中的代码无效,并且实际上是相同的代码。有人可以解释为什么吗?
答案 0 :(得分:0)
简短的答案是使方法async
并执行以下操作:
string userPrincipalName = "test.user@intuneforeducation.com";
User retrievedUser = await _graphServiceClient.Users[userPrincipalName].Request().GetAsync();
每当您使用.Result
(通常称为“异步同步”)时,如果您不太谨慎,就有可能出现死锁的风险。死锁意味着两个任务正在互相等待完成,这意味着什么也没发生。
尤其是在ASP.NET中,最好始终使用async
/ await
:在此方法中一直使用它,直到控制器为止。是:
如果您想深入了解并确切了解死锁的原因,Stephen Cleary上有一篇很棒的文章:Don't Block on Async Code
答案 1 :(得分:0)
我遇到了同样的问题(see here)。我通过还原kubectl describe rs <NAME>
和Microsoft.Graph
1.12.0版来解决了这个问题。
答案 2 :(得分:0)
避免在函数中使用 Result
但直接与用户打交道
User user = await gServiceClient.Users[ID].Request().GetAsync();