ASP.NET MVC 5 - Tożsamość. Jak uzyskać bieżący ApplicationUser

237

W moim projekcie jest podmiot z artykułami, który ma ApplicationUserwłaściwość o nazwie Author. Jak mogę uzyskać pełny obiekt aktualnie zalogowanego ApplicationUser? Podczas tworzenia nowego artykułu muszę ustawić Authorwłaściwość Articlena bieżącą ApplicationUser.

W starym mechanizmie członkostwa było to proste, ale w nowym podejściu do tożsamości nie wiem, jak to zrobić.

Próbowałem to zrobić w ten sposób:

  • Dodaj instrukcję przy użyciu rozszerzeń tożsamości: using Microsoft.AspNet.Identity;
  • Następnie próbuję uzyskać bieżącego użytkownika: ApplicationUser currentUser = db.Users.FirstOrDefault(x => x.Id == User.Identity.GetUserId());

Ale dostaję następujący wyjątek:

LINQ to Entities nie rozpoznaje metody „System.String GetUserId (System.Security.Principal.IIdentity)” i tej metody nie można przetłumaczyć na wyrażenie sklepu. Źródło = EntityFramework

Ellbar
źródło

Odpowiedzi:

448

Nie trzeba wykonywać zapytań bezpośrednio do bazy danych dla bieżącego ApplicationUser.

Wprowadza to nową zależność od posiadania dodatkowego kontekstu na początek, ale w przyszłości tabele bazy danych użytkowników zmieniają się (3 razy w ciągu ostatnich 2 lat), ale interfejs API jest spójny. Na przykład userstabela jest teraz nazywana AspNetUsersw systemie tożsamości, a nazwy kilku pól kluczy podstawowych ciągle się zmieniają, więc kod w kilku odpowiedziach nie będzie już działał w niezmienionej postaci .

Innym problemem jest to, że podstawowy dostęp OWIN do bazy danych będzie korzystał z osobnego kontekstu, więc zmiany z oddzielnego dostępu SQL mogą dawać nieprawidłowe wyniki (np. Nie widzieć zmian wprowadzonych w bazie danych). Ponownie rozwiązaniem jest praca z dostarczonym interfejsem API, a nie próba obejścia go.

Prawidłowy sposób dostępu do bieżącego obiektu użytkownika w tożsamości ASP.Net (na ten dzień) to:

var user = UserManager.FindById(User.Identity.GetUserId());

lub, jeśli masz akcję asynchroniczną, coś takiego:

var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

FindByIdwymaga posiadania następującej instrukcji using, aby UserManagerdostępne były metody niesynchroniczne (są to metody rozszerzenia dla UserManager, więc jeśli go nie uwzględnisz, zobaczysz tylko FindByIdAsync):

using Microsoft.AspNet.Identity;

Jeśli w ogóle nie jesteś w kontrolerze (np. Używasz iniekcji IOC), identyfikator użytkownika jest pobierany w całości z:

System.Web.HttpContext.Current.User.Identity.GetUserId();

Jeśli nie jesteś w standardowym kontrolerze konta, musisz dodać (na przykład) następujące elementy do kontrolera:

1. Dodaj te dwie właściwości:

    /// <summary>
    /// Application DB context
    /// </summary>
    protected ApplicationDbContext ApplicationDbContext { get; set; }

    /// <summary>
    /// User manager - attached to application DB context
    /// </summary>
    protected UserManager<ApplicationUser> UserManager { get; set; }

2. Dodaj to do konstruktora kontrolera:

    this.ApplicationDbContext = new ApplicationDbContext();
    this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.ApplicationDbContext));

Aktualizacja marca 2015 r

Uwaga: Najnowsza aktualizacja struktury tożsamości zmienia jedną z podstawowych klas używanych do uwierzytelniania. Możesz teraz uzyskać do niego dostęp z kontekstu Owin bieżącego HttpContent.

ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());

Uzupełnienie:

Korzystając z EF i platformy tożsamości z platformą Azure, przez zdalne połączenie z bazą danych (np. Testowanie hosta lokalnego z bazą danych Azure), możesz losowo trafić na przerażający „błąd: 19 - Połączenie fizyczne nie jest użyteczne”. Ponieważ przyczyna jest ukryta w programie Identity Framework, w którym nie można dodawać ponownych prób (lub czegoś, co wydaje się brakować .Include(x->someTable)), należy zaimplementować niestandardowy SqlAzureExecutionStrategyprojekt.

Gone Coding
źródło
5
@TBA - dzięki, zrozumiałem później, że jest to metoda rozszerzenia. Musisz dodać Microsoft.AspNet.Identity za pomocą. jeszcze raz dziękuję
Sentinel
2
Nie można znaleźć typu lub nazwapliku UserStore. Dodałem za pomocą Microsft.AspNet.Indentity
Wasfa
2
@Zapnologica: Brzmi jak nowe pytanie (sugeruj, żebyś je opublikował). Możesz rozszerzyć ApplicationUserklasę (specyficzną dla aplikacji) i AspNetUserstabelę równolegle, a one zapewnią nowe pola. Ponownie: nie trafiaj bezpośrednio do bazy danych! :)
Gone Coding
2
@ LifeH2O: The ApplicationUser zwrócony przez FindById jest twoja klasa, wraz ze swoimi dodatkowych właściwościach. Proszę spróbować
Gone Coding
1
Oczekiwanie na nowe obejście: P
Anup Sharma
60

Mój błąd, nie powinienem był używać metody wewnątrz zapytania LINQ.

Poprawny kod:

using Microsoft.AspNet.Identity;


string currentUserId = User.Identity.GetUserId();
ApplicationUser currentUser = db.Users.FirstOrDefault(x => x.Id == currentUserId);
Ellbar
źródło
2
User.Identiy.GetUserId dla mnie nie istnieje. czy to moja metoda niestandardowa? Wstaję tylko do User.Identity
Gerrie Pretorius
9
Nieważne ... potrzebujesz „za pomocą Microsoft.AspNet.Identity;” aby ta metoda była dostępna.
Gerrie Pretorius
4
Uwaga: obiekt użytkownika jest widoczny tylko w kontrolerach.
Miro J.
8
Z pewnością powinieneś używać UserManagermetod, a nie uderzać bezpośrednio w bazę danych?
Gone Coding
3
@Josh Bjelovuk: Nigdy nie uderzaj bezpośrednio w bazę danych, gdy interfejs API jest dostępny. Wprowadza to nową zależność od posiadania dodatkowego kontekstu na początek, ale w przyszłości tabele bazy danych użytkowników zmieniają się (3 razy w ciągu ostatnich 2 lat), ale interfejs API jest spójny.
Gone Coding
33

Jest w komentarzach odpowiedzi, ale nikt nie opublikował tego jako rzeczywistego rozwiązania.

Wystarczy dodać instrukcję using u góry:

using Microsoft.AspNet.Identity;
rtpHarry
źródło
2
Przybyłem tutaj z tym wyjątkiem, rozwiązałem go z tym using. Widząc, że 15 tys. Osób odwiedziło pytanie, uznałem, że to była pomocna odpowiedź :)
rtpHarry
2
@TrueBlueAussie, mimo że nie jest bezpośrednią odpowiedzią na pytanie PO, wydaje mi się, że wzmianka o użyciu jest bardzo przydatnym dodatkiem.
StuartQ
1
Dla jasności to dlatego, że .GetUserId()jest to metoda rozszerzenia
FSCKur
11

Kod Ellbar działa! Musisz tylko dodać za pomocą.

1 - using Microsoft.AspNet.Identity;

I ... kod Ellbar:

2 - string currentUserId = User.Identity.GetUserId(); ApplicationUser currentUser = db.Users.FirstOrDefault(x => x.Id == currentUserId);

Za pomocą tego kodu (in currentUser) pracujesz na ogólnych danych podłączonego użytkownika, jeśli chcesz dodatkowych danych ... zobacz ten link

Diego Borges
źródło
5
Może „działać”, ale z pewnością nie jest zalecane ominięcie dostarczonego interfejsu API i bezpośrednie trafienie do bazy danych. Jeśli używałeś interfejsu API, nie potrzebowałbyś dodatkowej pracy, aby uzyskać dodatkowe dane, ponieważ byłyby już w ApplicationUserobiekcie
Gone Coding
Zgadzam się! Jednak uciekłem się do tego, ponieważ miałem już dzisiaj system z uruchomioną bazą danych i potrzebuję prostego rozwiązania, aby rozwiązać ten problem! Z pewnością we wczesnym systemie umieściłem obiekty w odpowiednich klasach i tożsamościach.
Diego Borges
6

Począwszy od ASP.NET Identity 3.0.0, zostało to przekształcone w

//returns the userid claim value if present, otherwise returns null
User.GetUserId();
Seth IK
źródło
6
ApplicationDbContext context = new ApplicationDbContext();
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
ApplicationUser currentUser = UserManager.FindById(User.Identity.GetUserId());

string ID = currentUser.Id;
string Email = currentUser.Email;
string Username = currentUser.UserName;
Majid joghataey
źródło
3

W przypadku MVC 5 wystarczy zajrzeć do metody EnableTwoFactorAuthentication ManageController w rusztowaniu szablonów WebApplication, gdzie odbywa się tam:

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> EnableTwoFactorAuthentication()
        {
            await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
            var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
            if (user != null)
            {
                await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
            }
            return RedirectToAction("Index", "Manage");
        }

Odpowiedź jest tam, jak sugeruje sam Microsoft:

var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());

Będzie miał wszystkie dodatkowe właściwości zdefiniowane w klasie ApplicationUser.

Paceman
źródło
5
Już pokryte. Sprawdź, czy identyczna odpowiedź nie jest jeszcze opublikowana (lub dodaj komentarz do istniejącej odpowiedzi) :)
Gone Coding
3

Obecnie szablon projektu asp.mvc tworzy kontroler konta, który pobiera menedżera użytkowników w ten sposób:

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

Poniższe działa dla mnie:

ApplicationUser user = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(User.Identity.GetUserId());
Holger Thiemann
źródło
0

Udało mi się uzyskać użytkownika aplikacji, postępując zgodnie z częścią kodu

var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
            var user = manager.FindById(User.Identity.GetUserId());
            ApplicationUser EmpUser = user;
Abdul Hannan
źródło
0

W przypadku, gdy ktoś pracuje z Identityużytkownikami web forms, działam tak:

var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var user = manager.FindById(User.Identity.GetUserId());
Jamshaid Kamran
źródło