Aktualisieren von Benutzerdaten - ASP.NET-Identität

75

Ich habe der ApplicationUserKlasse benutzerdefinierte Felder hinzugefügt.
Ich habe auch ein Formular erstellt, über das der Benutzer die Felder eingeben / bearbeiten kann.
Aus irgendeinem Grund kann ich die Felder in der Datenbank jedoch nicht aktualisieren.

[HttpPost]
[ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Manage(EditProfileViewModel model)
{
    if (ModelState.IsValid)
    {
        // Get the current application user
        var user = User.Identity.GetApplicationUser();

        // Update the details
        user.Name = new Name { First = model.FirstName, Last = model.LastName, Nickname = model.NickName };
        user.Birthday = model.Birthdate;

        // This is the part that doesn't work
        var result = await UserManager.UpdateAsync(user);

        // However, it always succeeds inspite of not updating the database
        if (!result.Succeeded)
        {
            AddErrors(result);
        }
    }

    return RedirectToAction("Manage");
}

Mein Problem ähnelt den benutzerdefinierten Eigenschaften von MVC5 ApplicationUser , es scheint jedoch eine ältere Version von Identity zu verwenden, da die IdentityManager-Klasse anscheinend nicht vorhanden ist.

Kann mich jemand anleiten, wie man UserInformationen in der Datenbank aktualisiert ?

UPDATE: Wenn ich alle Felder in das Registerformular einbinde, werden alle Werte im entsprechenden Feld in einem neuen Datensatz der UsersTabelle aus der Datenbank gespeichert.

Ich kann keine Änderungen an den Feldern eines vorhandenen Benutzers vornehmen (Zeile in der usersTabelle). UserManager.UpdateAsync(user)funktioniert nicht

Beachten Sie auch, dass mein Problem mehr identitätsorientiert ist als EntityFramework

Galdin
quelle
Haben Sie nach Änderungen an ApplicationUser überprüft, ob die Update-Datenbank ausgeführt werden soll? Auch "Name" ist eine Klassenreferenz. Sie muss im OnModelCreating entsprechend serialisiert oder zugeordnet werden.
jd4u
Nein, damit gibt es kein Problem, die Datenbankstruktur ist genauso wie ApplicationUser. Die Namensfelder sind eine Spalte in der Datenbank (als Name_First, Name_Last und Name_NickName). Mein Problem ist, dass die Dateidatenbank beim Aufruf nicht mit den neuen Werten aktualisiert wird UserManager.UpdateAsync(user). Ich möchte nur wissen, wie ich den ApplicationUser (Benutzertabelle) aktualisieren soll
Galdin
Code-Snippets von ApplicationUser und OnModelCreating können hilfreich sein, um dieses Problem zu lösen. Aus Ihrem Kommentar geht hervor, dass es sich um ein Zuordnungsproblem für die Name-Eigenschaft und ihre Teile handelt.
JD4U
@ jd4u Mit der Aktualisierung der ApplicationUser (Users-Tabelle) meinte ich die Werte in der Tabelle und nicht die Struktur der Tabelle. Die Zuordnungen sind korrekt und funktionieren mit dem Registerformular. Die Informationen können einfach nicht aktualisiert werden.
Galdin

Antworten:

99

OK ... Ich habe stundenlang versucht herauszufinden, warum userManager.updateAsyncdie von uns bearbeiteten Benutzerdaten nicht beibehalten werden ... bis ich zu folgendem Ergebnis gekommen bin:

Die Verwirrung ergibt sich aus der Tatsache, dass wir das UserManagerin einer Zeile wie folgt erstellen :

var manager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new MyDbContext()));

... dann verwenden wir, manager.UpdateAsync( user );aber das aktualisiert den Benutzer im Kontext, und dann müssen wir Änderungen am Datenbankkontext der Identität speichern. Die Frage ist also, wie der Identity DBcontext auf einfachste Weise abgerufen werden kann.

Um dies zu lösen, sollten wir das nicht UserManagerin einer Zeile erstellen ... und so mache ich es:

var store = new UserStore<ApplicationUser>(new MyDbContext());
var manager = new UserManager(store);

dann nach dem Aktualisieren des Benutzers durch Aufrufen

manager.UpdateAsync(user);

dann gehst du zum Kontext

var ctx = store.context;

dann

ctx.saveChanges();

wahooooooo ... beharrte :)

Hoffe, das hilft jemandem, der ein paar Stunden an den Haaren gezogen hat: P.

Stackunderflow
quelle
4
Verknüpfung zu den letzten beiden Schritten: store.context.saveChanges ();
Stackunderflow
3
endlich .. für diejenigen, die eine vollständige Implementierung benötigen, ist dieser Link ausgezeichnet weblogs.asp.net/imranbaloch/archive/2013/12/12/…
stackunderflow
2
Ich denke, es ist falsch, updateAsync aufzurufen und dann zwei Zeilen Kontextänderungen zu speichern. Wird das nicht eine Rennbedingung sein?
Mac10688
1
Sie meinen, wir sollten verwendenawait
Stackunderflow
1
Ist es wirklich notwendig, ctx.saveChanges () auszuführen? nach UpdateAsync ()? Das glaube ich nicht. Und vergessen Sie nicht, "new MyDbContext ()" in den Block zu setzen.
Alexander
58

Wenn Sie eines der Felder für ApplicationUser ODER IdentityUser null lassen, wird das Update als erfolgreich zurückgegeben, die Daten werden jedoch nicht in der Datenbank gespeichert.

Beispiellösung:

ApplicationUser model = UserManager.FindById(User.Identity.GetUserId())

Fügen Sie die neu aktualisierten Felder hinzu:

model.Email = AppUserViewModel.Email;
model.FName = AppUserViewModel.FName;
model.LName = AppUserViewModel.LName;
model.DOB = AppUserViewModel.DOB;
model.Gender = AppUserViewModel.Gender;

Rufen Sie UpdateAsync auf

IdentityResult result = await UserManager.UpdateAsync(model);

Ich habe das getestet und es funktioniert.

JoshdeVries
quelle
Ich habe eine ähnliche Option erhalten. Ich habe gerade Automapper für das Mapping-Modell verwendet. Es sieht so aus: Mapper.Map (AppUserViewModel, Model);
Oleksii Aza
Das heißt, ich kann nicht zulassen, dass eines der Felder null bleibt
galdin
Ich denke, es sollte IdentityResult result = await UserManager.UpdateAsync(model);klein m für Modell sein
Jammer
12

Im OWIN-Kontext können Sie den Datenbankkontext abrufen. Scheint mir bisher gut zu funktionieren, und schließlich kam mir die Idee aus der ApplciationUserManager-Klasse, die dasselbe tut.

    internal void UpdateEmail(HttpContext context, string userName, string email)
    {
        var manager = context.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var user = manager.FindByName(userName);
        user.Email = email;
        user.EmailConfirmed = false;
        manager.Update(user);
        context.GetOwinContext().Get<ApplicationDbContext>().SaveChanges();
    }
Atters
quelle
3
Ich glaube nicht, dass die letzte Zeile mehr benötigt wird (diese Frage wurde gestellt, als Identität neu war). Ich habe gerade die Quellcodeverwaltung eines kürzlich durchgeführten Projekts überprüft und festgestellt, dass dies nur der Fall ist await UserManager.UpdateAsync(user)und die Änderung weiterhin besteht. Könnten Sie bitte bestätigen?
Galdin
1
Ich kann bestätigen, dass für dieses Beispiel die letzte Zeile unbedingt erforderlich war, um die Änderungen an der Datenbank beizubehalten. Hierbei wird die neueste stabile Version von Identity 2 verwendet, die für eine Webforms-Anwendung verfügbar ist. Vielleicht funktionieren die Async-Methoden anders?
Atters
Ich verwende Owin mit IOC (mit Update()Aufruf von innerhalb einer nicht asynchronen Hilfsfunktion) und dies ist die einzige Lösung, die mit dem Standard-Owin-Kontext funktioniert hat. Prost für die Rettung meines Tages und +1
Gone Coding
5

Der UserManager funktionierte nicht und wie @ Kevin Junghans schrieb,

UpdateAsync schreibt das Update nur in den Kontext. Sie müssen den Kontext noch speichern, damit er in die Datenbank übernommen wird

Hier ist eine schnelle Lösung (vor den neuen Funktionen in ASP.net Identity v2), die ich in einem Webformular-Projekt verwendet habe. Das

class AspNetUser :IdentityUser

Wurde von SqlServerMembership aspnet_Users migriert. Und der Kontext ist definiert:

public partial class MyContext : IdentityDbContext<AspNetUser>

Ich entschuldige mich für die Reflexion und den synchronen Code. Wenn Sie dies in eine asynchrone Methode einfügen, verwenden Sie diese awaitfür die asynchronen Aufrufe und entfernen Sie die Aufgaben und Wait (). Das Argument, Requisiten, enthält die Namen der zu aktualisierenden Eigenschaften.

 public static void UpdateAspNetUser(AspNetUser user, string[] props)
 {
     MyContext context = new MyContext();
     UserStore<AspNetUser> store = new UserStore<AspNetUser>(context);
     Task<AspNetUser> cUser = store.FindByIdAsync(user.Id); 
     cUser.Wait();
     AspNetUser oldUser = cUser.Result;

    foreach (var prop in props)
    {
        PropertyInfo pi = typeof(AspNetUser).GetProperty(prop);
        var val = pi.GetValue(user);
        pi.SetValue(oldUser, val);
    }

    Task task = store.UpdateAsync(oldUser);
    task.Wait();

    context.SaveChanges();
 }
subsci
quelle
3

Ich hatte auch Probleme mit UpdateAsync bei der Entwicklung einer Version von SimpleSecurity , die ASP.NET Identity verwendet. Zum Beispiel habe ich eine Funktion hinzugefügt, mit der ein Kennwort zurückgesetzt werden kann, um den Benutzerinformationen ein Kennwortrücksetz-Token hinzuzufügen. Zuerst habe ich versucht, UpdateAsync zu verwenden, und es wurden die gleichen Ergebnisse erzielt wie bei Ihnen. Am Ende habe ich die Benutzerentität in ein Repository-Muster eingewickelt und zum Laufen gebracht. Sie können sich das SimpleSecurity-Projekt als Beispiel ansehen . Nachdem Sie mehr mit ASP.NET Identity gearbeitet haben (Dokumentation ist noch nicht vorhanden), denke ich, dass UpdateAsync das Update nur in den Kontext festschreibt. Sie müssen den Kontext noch speichern, damit es in die Datenbank übernommen wird.

Kevin Junghans
quelle
Ja `UpdateAsync schreibt das Update nur in den Kontext, [...] speichert den Kontext, damit es in die Datenbank übernommen wird '
subsci
3

Ich habe die Funktionalität auf die gleiche Weise ausprobiert und wenn ich die UserManager.Updateasync Methode aufrufe, ist sie erfolgreich, aber es gibt kein Update in der Datenbank. Nachdem ich einige Zeit verbracht hatte, fand ich eine andere Lösung, um die Daten in aspnetusersder folgenden Tabelle zu aktualisieren :

1) Sie müssen eine UserDbContextKlasse erstellen , die von einer IdentityDbContextKlasse wie folgt erbt :

public class UserDbContext:IdentityDbContext<UserInfo>
{
    public UserDbContext():
        base("DefaultConnection")
    {
        this.Configuration.ProxyCreationEnabled = false;
    }
}

2) Aktualisieren Sie dann im Account Controller die Benutzerinformationen wie folgt:

UserDbContext userDbContext = new UserDbContext();
userDbContext.Entry(user).State = System.Data.Entity.EntityState.Modified;
await userDbContext.SaveChangesAsync();

Wo userist Ihre aktualisierte Entität?

hoffe das wird dir helfen.

Ankit Sahrawat
quelle
2

Ausgezeichnet!!!

IdentityResult result = await UserManager.UpdateAsync(user);
Max
quelle
Ich weiß, dass dies jetzt funktioniert, aber damals nicht: p Das Identity Framework war zu dieser Zeit relativ neu
Galdin
Wenn es funktioniert, verwende ich VS 2013 MVC 5 - EntityFramework, Version = 6.
Max
MVC 5 verwendet Identity 2.0 richtig? Diese Frage ist ziemlich alt, wie ich sagte.
Galdin
Ich weiß nicht, ob ich dieses Übel arbeite, das ich benutze oder soll ich es ändern?
Max
Version system.Web.Mvc: 5.2.3.0
Max
1

Basierend auf Ihrer Frage und auch im Kommentar vermerkt.

Kann mich jemand anleiten, wie Benutzerinformationen in der Datenbank aktualisiert werden?

Ja, der Code ist korrekt für die Aktualisierung ApplicationUserder Datenbank.

IdentityResult result = await UserManager.UpdateAsync(user);

  • Überprüfen Sie alle erforderlichen Werte des Felds auf Einschränkungen
  • Überprüfen Sie, ob UserManager mit ApplicationUser erstellt wurde.

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

jd4u
quelle
Werden die Einschränkungen beim Überprüfen nicht überprüft ModelState.IsValid?
Galdin
ModelState sucht nach View Model. Sie nehmen den ApplicationUser mit GetApplicationUser () von Db.
JD4U
Darüber hinaus führt UpdateManager.Update in der Standardimplementierung nur eine Überprüfung des Benutzernamens durch, bevor ein Aktualisierungsaufruf an UserStore gesendet wird. Versuchen Sie, UserStore direkt für den Debug-Zweck zu verwenden.
JD4U
2
@anyone, Während Sie eine Antwort als nicht nützlich markieren, lesen Sie bitte alle und denken Sie an einen kleinen Kontext. Diese Antwort ist kontextbezogen. Darüber hinaus ist die hier ausgewählte Antwort keine Lösung, da ihr alternativer Ansatz das Kernframework des Identity Framework ignoriert und direkt mit der Datenbank arbeitet.
JD4U
0

Das funktioniert bei mir. Ich verwende Identity 2.0. GetApplicationUser scheint nicht mehr vorhanden zu sein.

        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (!string.IsNullOrEmpty(form["FirstName"]))
        {
            user.FirstName = form["FirstName"];
        }
        if (!string.IsNullOrEmpty(form["LastName"]))
        {
            user.LastName = form["LastName"];
        }
        IdentityResult result = await UserManager.UpdateAsync(user);
Yan
quelle
GetApplicationUser()ist eine benutzerdefinierte Erweiterungsmethode
Galdin
0

Ich verwende den neuen EF & Identity Core und habe das gleiche Problem mit dem Zusatz, dass ich diesen Fehler habe:

Die Instanz des Entitätstyps kann nicht verfolgt werden, da bereits eine andere Instanz dieses Typs mit demselben Schlüssel verfolgt wird.

Mit dem neuen DI-Modell habe ich dem Controller des Konstruktors den Kontext zur DB hinzugefügt.

Ich habe erfolglos versucht, den Konflikt zu erkennen _conext.ChangeTracker.Entries()und AsNoTracking()meine Anrufe zu ergänzen.

Ich muss nur den Status meines Objekts ändern (in diesem Fall Identität).

_context.Entry(user).State = EntityState.Modified;
var result = await _userManager.UpdateAsync(user);

Und arbeitete ohne ein anderes Geschäft oder Objekt und Mapping zu erstellen.

Ich hoffe, jemand anderes ist nützlich meine zwei Cent.

Ivan Paniagua
quelle
0

Ich verwende .Net Core 3.1 oder eine höhere Version. Bitte folgen Sie der Lösung:

  public class UpdateAssignUserRole
    {
        public string username { get; set; }
        public string rolename { get; set; }
        public bool IsEdit { get; set; }

    }

 private async Task UpdateSeedUsers(UserManager<IdentityUser> userManager, UpdateAssignUserRole updateassignUsername)
        {
            IList<Users> Users = await FindByUserName(updateassignUsername.username);

            if (await userManager.FindByNameAsync(updateassignUsername.username) != null)
            {
                var user = new IdentityUser
                {
                    UserName = updateassignUsername.username,
                    Email = Users[0].Email,
 
                };
                var result = await userManager.FindByNameAsync(updateassignUsername.username);
                if (result != null)
                {
                    IdentityResult deletionResult = await userManager.RemoveFromRolesAsync(result, await userManager.GetRolesAsync(result));
                    if (deletionResult != null)
                    {
                        await userManager.AddToRoleAsync(result, updateassignUsername.rolename);
                    }
                }
            }

        }
Papun Sahoo
quelle
-1

Fügen Sie Ihrer Datei Startup.Auth.cs unter dem statischen Konstruktor den folgenden Code hinzu:

        UserManagerFactory = () => new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));

        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            AllowInsecureHttp = true
        };

Mit der Codezeile für die UserManagerFactory-Einstellung können Sie Ihren benutzerdefinierten DataContext mit dem UserManager verknüpfen. Sobald Sie dies getan haben, können Sie eine Instanz des UserManager in Ihrem ApiController abrufen und die UserManager.UpdateAsync (Benutzer) -Methode funktioniert, da sie Ihren DataContext verwendet, um die zusätzlichen Eigenschaften zu speichern, die Sie Ihrem benutzerdefinierten Anwendungsbenutzer hinzugefügt haben.

Chris Swain
quelle
Ihr Code enthält einige Dinge, die für die Frage irrelevant sind. Ich schlage vor, Sie räumen auf.
Zero3