如何使用MVC5和实体框架使用自定义属性更新IdentityUser

时间:2014-04-02 17:22:31

标签: c# asp.net-mvc entity-framework

我使用内置的身份框架进行用户管理,并希望为AspNetUsers表添加一些自定义。到目前为止,我遇到的每个问题的解决方案都会导致另一个问题。

如果我对用户模型进行了更改(例如,通过在AspNetUsers表中添加邮政编码属性和匹配字段),然后调用UserManager.UpdateAsync(user),它会成功,但不会更新邮政编码字段数据库。

至少one other SO question试图解决这个问题。但建议的修复措施打破了其他一些事情:

1)创建UserDbContext的另一个实例并尝试附加用户对象会导致实体框架抱怨“实体对象不能被多个IEntityChangeTracker实例引用”

2)关闭代理创建摆脱了#1,but causes the dbcontext to not load child objects中列出的问题(如AspNetUserLogins,这一点非常重要)。

另一种解决方案是访问Controller中创建的上下文。使用MVC(版本5)模板考虑使用新的ASP .NET Web应用程序的默认AccountController的构造函数方法:

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

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

创建了应用程序数据库上下文,但无法通过UserManager访问它(因为UserManager的“存储”私有属性)。

这似乎不像是火箭科学,所以我的猜测是我在处理/理解dbcontext生命周期方面做了一些基本上错误的事情。

那么:我如何正确访问/使用dbcontext来保存和更新AspNetUsers,关联的自定义属性以及保留子对象(如AspNetUserLogins)?

编辑-------

我尝试了一件事......

我更新了AccountController的默认构造函数:

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

到此:

    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;

    }

试图挂起dbcontext。稍后,在公共异步任务方法的主体中,我尝试调用:

  var updated = await UserManager.UpdateAsync(user);

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

但是,尝试更新状态会引发异常:

“对象图层类型'xyz.Models.ApplicationUser'已经生成了代理类型。当AppDomain中的两个或多个不同模型映射相同的对象图层类型时,会发生这种情况。”

这似乎不正确......它与构造函数中指定的dbcontext相同。

编辑#2 -----

这是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")
        {

        }

    }
}

最终修改----------------------------

好的,经过评论中的一些来回,我意识到我是以错误的方式提问。我的问题是:如何使用代码优先而不是数据库优先迁移。来自Hibernate学校,我习惯于通过XML或Java中的注释手动将对象映射到表。

因此,在略读this article时,我错过了有关迁移的重要步骤。经验教训。

2 个答案:

答案 0 :(得分:32)

我遇到了同样的问题。解决这个问题的一个简单方法就是从模型中获取我想要更新的属性,并将它们保存回从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);
    }

答案 1 :(得分:10)

因此,您使用的是数据库优先而不是代码优先。我的建议是使用代码优先。我和你一样,首先开始使用数据库。如果您没有使用代码优先,则删除数据库中的迁移表。否则,会引起问题。

如果可能的话,我建议遵循这个代码优先的教程,这对我帮助很大。您可以轻松地将自定义字段添加到您的身份中,并在身份验证过程中与框架完全集成。

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