2014-09-06 50 views
12

爲了簡化這個問題,我將描述更高級別的問題,然後根據需要進入任何實現細節。用戶管理器在.Net標識中的奇怪行爲

我在開發中使用我的應用程序中的ASP.NET身份。在一系列請求的特定場景中,UserManager首先獲取當前用戶(至少一個FindById請求),用戶被抓取到。在後續的請求中,我更新了由UserManager.Update保存的此用戶的信息,我可以看到數據庫中保留的更改。

問題在於,在進一步的後續請求中,從FindById獲取的用戶對象不會更新。這很奇怪,但可能是關於在UserManager中緩存的內容我不明白。但是,當我跟蹤數據庫調用時,我發現UserManager確實正在向數據庫發送sql-requests以獲取用戶。

這就是它真正奇怪的地方 - 即使數據庫被確認爲最新的,UserManager仍然以某種方式從這個過程中返回一箇舊對象。當我自己運行直接跟蹤到數據庫的完全相同的查詢時,我按預期得到更新的數據。

這是什麼黑魔法?

很明顯,某些東西被緩存在某處,但爲什麼它會對數據庫進行查詢,只是爲了忽略它所獲取的更新數據?

此示例下面更新一切按預期在db爲每個請求到控制器的動作,和時GetUserDummyTestClass呼籲findById上的UserManager的另一實例我可以跟蹤SQL請求,並能直接測試這些數據庫並驗證它們是否返回更新的數據。但是,從同一行代碼中返回的用戶對象仍然具有舊值(在這種情況下,應用程序啓動後的第一次編輯,無論調用Test操作多少次)。

控制器

public ActionResult Test() 
{ 
    var userId = User.Identity.GetUserId(); 
    var user = UserManager.FindById(userId); 

    user.RealName = "name - " + DateTime.Now.ToString("mm:ss:fff"); 
    UserManager.Update(user); 
    return View((object)userId); 
} 

Test.cshtml

@model string 

@{ 
    var user = GetUserDummyTestClass.GetUser(Model); 
} 

@user.RealName; 

GetUserDummyTestClass

public class GetUserDummyTestClass 
{ 
    private static UserManager<ApplicationUser> _userManager; 

    private static UserManager<ApplicationUser> UserManager 
    { 
     get { return _userManager ?? (_userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))); } 
    } 

    public static ApplicationUser GetUser(string id) 
    { 
     var user = UserManager.FindById(id); 
     return user; 
    } 
} 

更新

正如Erik指出的,我不應該使用靜態UserManagers。但是,如果我保持UserManager在GetUserDummyTest中綁定到HttpContext(每個HttpRequest持久化它),以防在請求期間多次使用它,它仍然緩存它通過Id獲取的第一個User對象,並忽略任何更新從另一個用戶管理器。因此表明真正的問題確實是我使用兩種不同的UserManagers作爲trailmax建議的,並且它不是爲這種用法而設計的。

在我上面的示例中,如果我將UserManager保留在GetUserDummyTestClass持久性HttpRequest上,請添加Update-method並僅在控制器中使用它,一切正常。

因此,如果要得出結論,說明如果我想使用控制器範圍之外的UserManager中的邏輯,我必須將UserManager實例全局化爲可綁定的適當類HttpContext的實例,如果我想避免創建和處置一次性使用的實例?

更新2

調查遠一點,我意識到,我確實打算每個請求使用一個實例,這實際上已經被設置爲OwinContext在Startup.Auth後來像訪問這樣的:

using Microsoft.AspNet.Identity.Owin; 

// Controller 
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>() 

// Other scopes 
HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>() 

這實際上是令人尷尬的明顯看提供的默認的AccountController的設置,但我想以上證明:相當分散描述的相當奇怪和意外的行爲。不過,理解這種行爲的原因是很有趣的,儘管使用OwinContext.GetUserManager不會再成爲問題了。

+1

我知道一個人應該在發佈了一個新問題後,我會在一段時間後掛斷,但不幸的是,我的時區迫使我暫時離開。如果有人有問題,請耐心等待。 – Alex 2014-09-06 16:25:31

+0

你可以發佈一個示例代碼來重現你的問題嗎? – trailmax 2014-09-07 16:54:02

+0

@trailmax更新 – Alex 2014-09-08 16:15:08

回答

7

你的問題是你使用兩個不同的UserManager實例,它看起來像它們都是靜態定義的(這是Web應用程序中的一個巨大的禁忌,因爲它們在所有線程和用戶之間共享系統不是線程安全的,你甚至無法讓他們線程安全受到周圍鎖定,因爲它們含有某種狀態的用戶)

更改GetUserDummyTestClass這樣:

private static UserManager<ApplicationUser> UserManager 
{ 
    get { return new UserManager<ApplicationUser>(
      new UserStore<ApplicationUser>(new ApplicationDbContext())); } 
} 

public static ApplicationUser GetUser(string id) 
{ 
    using (var userManager = UserManager) 
    { 
     return UserManager.FindById(id); 
    } 
} 
+1

謝謝你提示它是靜態的,我不知道它的狀態是特定於某個用戶的。但至少我應該注意到它包含一個dbcontext的實例,並且這絕對不應該是靜態的。 但是,出於好奇,如何解釋GetUserDummyTestClass中的UserManager對數據庫執行查詢以忽略它所獲取的更新數據?或者,當僅使用未指定的靜態實例時(以及肯定不相關)的行爲? – Alex 2014-09-09 00:46:26

+0

再次更新問題。如果我想通過HttpRequest(在HttpContext中)持久保存UserManager,我只需要在執行流程中只使用一個實例? – Alex 2014-09-09 01:34:26

+1

這仍然是你正確的方式來創建一個UserManager的新實例嗎? – jasdefer 2016-11-09 19:33:34