[英]Microsoft.Graph GetAsync() hangs indefinitely
我正在開發一個 ASP.NET 應用程序,除其他外,它應該從 Azure Active Directory 中檢索用戶。 為此,我使用的是 Microsoft Graph 1.14.0 版預覽庫,可在 此處找到。
由於此庫僅提供用於檢索用戶的異步方法,因此我使用以下(偽)代碼來同步運行它。
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應用程序的controller中的代碼完全相同。 使用單元測試,上述代碼的執行時間約為 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
完全相同的方法。
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 中的代碼沒有,而且它實際上是相同的代碼。 誰能解釋這是為什么?
我有同樣的問題( 見這里)。 我通過恢復Microsoft.Graph
和Microsoft.Graph.Core
版本 1.12.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
避免在函數中使用Result
而是直接與用戶打交道
User user = await gServiceClient.Users[ID].Request().GetAsync();
我知道在 99.9% 的情況下絕對不應該這樣做。 但有些時候你別無選擇。 例如,在 Sitefinity 中,您通過 controller 類制作小部件。 這些 controller 類不支持異步操作(Sitefinity 不支持此操作)並且 microsoft graph sdk 只為您提供異步選項來獲取數據。 所以我必須找到一種方法來使異步變為同步。 所以對我來說,我不得不做這個討厭的解決方法。 關鍵是 ConfigureAwait(false)。 它可以防止死鎖 ( https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html )。 我希望它能幫助那些和我一樣被困在同一個 position 的人。
public IList<UserModel> GetAllUsers()
{
var client = _graphClientFactory.GetClient();
// This bad looking hack is to be able to call the async method synchronously without deadlocking.
// It is necessary because Sitefinity does not support async controller actions...
var task = Task.Run(() =>
{
return client.Users.Request().GetAsync();
}).ConfigureAwait(false);
var result = task.GetAwaiter().GetResult();
// Build models
return new List<UserModel>();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.