Aby uprościć to pytanie, opiszę problem na wyższym poziomie, a następnie w razie potrzeby przejdę do szczegółów implementacji.Dziwne zachowanie użytkownika UserManager w .Net Identity
Używam tożsamości ASP.NET w mojej aplikacji w fazie rozwoju. W określonym scenariuszu dla szeregu zgłoszeń menedżer UserManager otrzymuje najpierw bieżącego użytkownika (co najmniej jedno żądanie FindById), w którym użytkownik jest pobierany. Na kolejne żądanie aktualizuję informacje o tym użytkowniku, które są zapisywane przez UserManager.Update i widzę, że zmiana trwała w bazie danych.
Problem polega na tym, że przy kolejnych kolejnych żądaniach obiekt użytkownika pobrany z FindById nie jest aktualizowany. To dziwne, ale może być coś z buforowaniem w UserManager, którego nie rozumiem. Jednak podczas śledzenia wywołań bazy danych widzę, że UserManager rzeczywiście wysyła zapytania sql do bazy danych w celu pobrania użytkownika.
I tu robi się naprawdę dziwnie - mimo że baza danych jest potwierdzona, że jest aktualna, UserManager nadal w jakiś sposób zwraca stary obiekt z tego procesu. Kiedy sam uruchomię dokładnie to samo zapytanie, które jest bezpośrednio śledzone w bazie danych, otrzymuję zaktualizowane dane zgodnie z oczekiwaniami.
Czym jest ta czarna magia?
Oczywiście coś jest gdzieś zbuforowane, ale dlaczego robi zapytanie do bazy danych, tylko po to, aby zignorować zaktualizowane dane, które dostaje?
Przykład
To poniżej przykład aktualizuje wszystko zgodnie z oczekiwaniami w dB dla każdego żądania do działania kontrolera, a kiedy GetUserDummyTestClass dzwoni findById drugiej instancji UserManager mogę śledzić SQL żądań, a może przetestuj je bezpośrednio na db i sprawdź, czy zwracają zaktualizowane dane. Jednak obiekt użytkownika zwrócony z tego samego wiersza kodu nadal ma stare wartości (w tym scenariuszu pierwsza edycja po uruchomieniu aplikacji, bez względu na to, ile razy wywoływana jest akcja testowa).
Kontroler
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;
}
}
Aktualizacja
Jak zauważył Erik, nie powinienem używać statycznych UserManagers. Jeśli jednak zachowam menedżera UserManager w GetUserDummyTest powiązanego z HttpContext (utrzymując go przez HttpRequest) w przypadku, gdy chcę go użyć kilka razy podczas żądania, to nadal buforuje pierwszy obiekt użytkownika, który otrzymuje przez Id, i pomijając wszelkie aktualizacje z innego menedżera UserManager. Sugeruje to, że prawdziwym problemem jest to, że używam dwóch różnych UserManagerów, jak sugeruje trailmax, i że nie jest on przeznaczony do tego rodzaju użycia.
W powyższym przykładzie, jeśli utrzymuję UserManager w GetUserDummyTestClass trwały przez HttpRequest, dodać metodę Update i używać tylko w kontroler, wszystko działa poprawnie zgodnie z oczekiwaniami.
Więc jeśli dojdziemy do wniosku, czy słusznie byłoby stwierdzić, że jeśli chcę używać logiki z UserManagera poza zakresem kontrolera, muszę dokonać globalizacji instancji UserManager w odpowiedniej klasie, gdzie mogę powiązać instancji do HttpContext, jeśli chcę uniknąć tworzenia i usuwania instancji do jednorazowego użycia?
Aktualizacja 2
Badanie trochę dalej, zdałem sobie sprawę, że jestem rzeczywiście przeznaczone do używania jednej instancji na żądanie, i że to już rzeczywiście jest skonfigurowana dla OwinContext w Startup.Auth a później uzyskać jak to:
using Microsoft.AspNet.Identity.Owin;
// Controller
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>()
// Other scopes
HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>()
to jest rzeczywiście żenująco oczywiste patrząc na konfiguracji domyślnej AccountController dostarczonych, ale myślę, że raczej dziwne i nieoczekiwane zachowanie opisane powyżej okazały się bardzo rozpraszać. Mimo to, byłoby interesujące zrozumieć przyczynę tego zachowania, nawet jeśli nie będzie to już problemem przy użyciu OwinContext.GetUserManager.
wiem, że trzeba Po pojawieniu się nowego pytania poczekaj chwilę, ale moja strefa czasowa niestety zmusza mnie do opuszczenia tego na chwilę. Jeśli ktoś ma pytania, prosimy o cierpliwość. – Alex
Czy możesz wysłać przykładowy kod, który powiela twój problem? – trailmax
@trailmax Zaktualizowany – Alex