为了让这个问题简单化,我将描述更高层次的问题,然后在需要时讨论任何实现细节。
我在开发中的应用程序中使用 ASP.NET 标识。在一系列请求的特定场景中,UserManager 首先获取当前用户(至少一个 FindById 请求),从中获取用户。在后续请求中,我更新了由 UserManager.Update 保存的关于该用户的信息,我可以看到数据库中保存的更改。
这里的问题是,在进一步的后续请求中,从 FindById 获取的用户对象没有更新。这很奇怪,但可能与我不明白的 UserManager 中的缓存有关。但是,当我跟踪数据库调用时,我发现 UserManager 确实正在向数据库发送 sql 请求以获取用户。
这就是它变得非常奇怪的地方 - 即使数据库被确认是最新的,UserManager 仍然以某种方式从这个过程中返回一个旧对象。当我自己运行直接跟踪到数据库的完全相同的查询时,我得到了预期的更新数据。
这是什么黑魔法?
很明显,有些东西缓存在某个地方,但为什么它会查询数据库,而忽略它获得的更新数据呢?
示例
这个下面的例子更新了数据库中对 Controller 操作的每个请求的预期的一切,当 GetUserDummyTestClass 在 UserManager 的另一个实例上调用 findById 时,我可以跟踪 sql 请求,并且可以直接测试这些到数据库并验证他们返回更新的数据。但是,从同一行代码返回的用户对象仍然具有旧值(在这种情况下,应用程序启动后的第一次编辑,无论调用了多少次测试操作)。
Controller
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。但是,如果我将 GetUserDummyTest 中的 UserManager 绑定(bind)到 HttpContext(根据 HttpRequest 保留它)以防我想在请求期间多次使用它,它仍然缓存它通过 Id 获取的第一个 User 对象,并忽略任何更新来自另一个 UserManager。因此表明真正的问题确实是我使用了两个不同的 UserManagers 作为 trailmax 建议,并且它不是为这种用法而设计的。
在我上面的示例中,如果我将 GetUserDummyTestClass 中的 UserManager 保留在 HttpRequest 上,添加一个更新方法并仅在 Controller 中使用它,一切都会按预期正常工作。
因此,如果要得出结论,如果我想在 Controller 范围之外使用来自 UserManager 的逻辑,我必须将 UserManager 实例全局化到一个适当的类中,我可以在其中绑定(bind)实例到 HttpContext,如果我想避免为一次性使用创建和处置实例?
更新 2
进一步调查,我意识到我确实打算为每个请求使用一个实例,并且实际上已经为 Startup.Auth 中的 OwinContext 设置了这个实例,之后可以像这样访问:
using Microsoft.AspNet.Identity.Owin;
// Controller
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>()
// Other scopes
HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>()
从所提供的默认 AccountController 的设置来看,这实际上是非常明显的,但我猜上面描述的相当奇怪和意外的行为被证明是相当分散注意力的。尽管如此,了解这种行为的原因还是很有趣的,即使使用 OwinContext.GetUserManager 不再是问题。
最佳答案
你的问题是你使用了两个不同的 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);
}
}
关于c# - .Net Identity 中 UserManager 的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25701942/
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
两个gsub产生不同的结果。谁能解释一下为什么?代码也可在https://gist.github.com/franklsf95/6c0f8938f28706b5644d获得.ver=9999str="\tCFBundleDevelopmentRegion\n\ten\n\tCFBundleVersion\n\t0.1.190\n\tAppID\n\t000000000000000"putsstr.gsub/(CFBundleVersion\n\t.*\.).*()/,"#{$1}#{ver}#{$2}"puts'--------'putsstr.gsub/(CFBundleVersio
我在一段非常简单的代码(如我所想)中得到了一个错误的值:org=4caseorgwhenorg=4val='H'endputsval=>nil请不要生气,我希望我错过了一些非常明显的东西,但我真的想不通。谢谢。 最佳答案 这是典型的Ruby错误。case有两种被调用的方法,一种是你传递一个东西作为分支的基础,另一种是你不传递的东西。如果您确实在case中指定了一个表达式语句然后评估所有其他条件并与===进行比较.在这种情况下org评估为false和org===false显然不是真的。所有其他情况也是如此,它们要么是真的,要么是假的。
假设您在Ruby中执行此操作:ar=[1,2]x,y=ar然后,x==1和y==2。是否有一种方法可以在我自己的类中定义,从而产生相同的效果?例如rb=AllYourCode.newx,y=rb到目前为止,对于这样的赋值,我所能做的就是使x==rb和y=nil。Python有这样一个特性:>>>classFoo:...def__iter__(self):...returniter([1,2])...>>>x,y=Foo()>>>x1>>>y2 最佳答案 是的。定义#to_ary。这将使您的对象被视为要分配的数组。irb>o=Obje
我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject
有没有人用ruby解决这个问题:假设我们有:a=8.1999999我们想将它四舍五入为2位小数,即8.20,然后乘以1,000,000得到8,200,000我们是这样做的;(a.round(2)*1000000).to_i但是我们得到的是8199999,为什么?奇怪的是,如果我们乘以1000、100000或10000000而不是1000000,我们会得到正确的结果。有人知道为什么吗?我们正在使用ruby1.9.2并尝试使用1.9.3。谢谢! 最佳答案 每当你在计算中得到时髦的数字时使用bigdecimalrequire'bi
defreverse(ary)result=[]forresult[0,0]inaryendresultendassert_equal["baz","bar","foo"],reverse(["foo","bar","baz"])这行得通,我想了解原因。有什么解释吗? 最佳答案 如果我使用each而不是for/in重写它,它看起来像这样:defreverse(ary)result=[]#forresult[0,0]inaryary.eachdo|item|result[0,0]=itemendresultendforainb基本上就