2014-09-06 46 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