web-dev-qa-db-fra.com

Comment mettre à jour IdentityUser avec des propriétés personnalisées à l'aide de MVC5 et du framework d'entité

J'utilise le cadre d'identité intégré pour la gestion des utilisateurs et je voudrais ajouter quelques personnalisations à la table AspNetUsers. Jusqu'à présent, la solution à chaque problème que j'ai rencontré provoque un autre problème.

Si j'apporte une modification au modèle utilisateur (par exemple, en ajoutant une propriété de code postal et un champ correspondant dans la table AspNetUsers), puis j'appelle UserManager.UpdateAsync (utilisateur), cela réussit mais ne met pas à jour le champ Code postal dans la base de données.

Au moins n autre SO question a essayé de résoudre ce problème. Mais les correctifs suggérés cassent d'autres choses:

1) La création d'une autre instance de UserDbContext et la tentative d'attacher l'objet utilisateur amènent le framework d'entité à se plaindre qu '"un objet entité ne peut pas être référencé par plusieurs instances de IEntityChangeTracker"

2) La désactivation de la création de proxy supprime le problème répertorié dans # 1, mais empêche le dbcontext de charger des objets enfants (comme AspNetUserLogins, qui sont plutôt importants).

Une autre solution serait d'accéder au contexte créé dans le Controller. Considérez les méthodes du constructeur AccountController par défaut avec une nouvelle application Web ASP .NET utilisant le modèle MVC (version 5):

 public AccountController()
            : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
        {
        }

        public AccountController(UserManager<ApplicationUser> userManager)
        {
            UserManager = userManager;
        }

L'application DB Context est créée, mais il n'y a aucun moyen d'y accéder via UserManager (car la propriété privée 'Store' de UserManager).

Cela ne ressemble pas à la science des fusées, donc je suppose que je fais quelque chose de fondamentalement mauvais en ce qui concerne la gestion/compréhension du cycle de vie de dbcontext.

Donc: comment accéder/utiliser correctement le dbcontext pour enregistrer et mettre à jour les AspNetUsers, les propriétés personnalisées associées et préserver les objets enfants (comme AspNetUserLogins)?

MODIFIER -------

Encore une chose que j'ai essayé ...

J'ai mis à jour le constructeur de AccountController par défaut:

    public AccountController(UserManager<ApplicationUser> userManager)
    {
       UserManager = userManager;
    }

pour ça:

    public AccountController(UserManager<ApplicationUser> userManager)
    {
        userDbContext= new UserDbContext();
        UserStore<ApplicationUser> store = new UserStore<ApplicationUser>();
        UserManager<ApplicationUser> manager = new UserManager<ApplicationUser>(store);

        manager.UserValidator = new CustomUserValidator<ApplicationUser>(UserManager);

       // UserManager = userManager;
        UserManager = manager;

    }

Pour tenter de conserver le dbcontext. Plus tard, dans le corps d'une méthode de tâche asynchrone publique, j'essaie d'appeler:

  var updated = await UserManager.UpdateAsync(user);

  if (updated.Succeeded)
  {
    userDbContext.Entry(user).State = System.Data.Entity.EntityState.Modified;
    await userDbContext.SaveChangesAsync();
  }

Cependant, la tentative de mise à jour de l'état lève une exception:

"Il existe déjà un type de proxy généré pour le type de couche objet 'xyz.Models.ApplicationUser'. Cela se produit lorsque le même type de couche objet est mappé par deux modèles différents ou plus dans un AppDomain."

Cela ne semble pas correct ... c'est le même dbcontext assigné dans le constructeur.

EDIT # 2 -----

Voici le modèle ApplicationUser:

using Microsoft.AspNet.Identity.EntityFramework;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using System.Data.Entity;

namespace xyz.App.Models
{
    // You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.Microsoft.com/fwlink/?LinkID=317594 to learn more.
    public class ApplicationUser : IdentityUser
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string ZipCode { get; set; }
        public string PasswordResetToken { get; set; }
        public System.DateTime? PasswordResetTokenExpiry { get; set; }

        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
        {
            // Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            // Add custom user claims here
            return userIdentity;
        }

        public ApplicationUser() { }

    }




    public class UserDbContext : IdentityDbContext<ApplicationUser>
    {
        public UserDbContext()
            : base("DefaultConnection")
        {

        }

    }
}

Édition finale ----------------------------

D'accord, après quelques allers-retours dans les commentaires, j'ai réalisé que je posais la question de la mauvaise façon. Ma question était vraiment: comment utiliser les migrations Code-first plutôt que Database-first. Venant de l'école Hibernate, j'avais l'habitude de mapper manuellement des objets à des tables via XML ou des annotations en Java.

Donc, en survolant cet article , j'ai raté les étapes importantes de la migration. Leçon apprise.

20
GojiraDeMonstah

Donc, vous utilisez d'abord la base de données au lieu du code d'abord. Ma recommandation est d'utiliser le code en premier. Comme vous, j'ai commencé par utiliser la base de données en premier lieu. Si vous n'utilisez pas le code en premier, supprimez la table des migrations dans votre base de données. Sinon, cela causera des problèmes.

Si possible, je recommande de suivre ce didacticiel en premier code, qui m'a beaucoup aidé. Vous pouvez facilement ajouter des champs personnalisés à votre identité et avoir une intégration complète avec le cadre en ce qui concerne l'authentification.

http://blogs.msdn.com/b/webdev/archive/2013/10/16/customizing-profile-information-in-asp-net-identity-in-vs-2013-templates.aspx

10
user1477388

J'ai fait face au même problème. Un moyen facile de surmonter cela était simplement de prendre les propriétés que je voulais mettre à jour à partir du modèle et de les sauvegarder dans un objet extrait du UserManager;

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(ApplicationUser model)
    {
        if (ModelState.IsValid)
        {
            ApplicationUser u = UserManager.FindById(model.Id);
            u.UserName = model.Email;
            u.Email = model.Email;
            u.StaffName = model.StaffName; // Extra Property
            UserManager.Update(u);
            return RedirectToAction("Index");
        }
        return View(model);
    }
32
Josh