简体   繁体   English

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

[英]How to update IdentityUser with custom properties using MVC5 and entity framework

I am using the built in identity framework for user management, and would like to add a few customizations to the AspNetUsers table. 我使用内置的身份框架进行用户管理,并希望为AspNetUsers表添加一些自定义。 So far the solution to each problem I've encountered causes another problem. 到目前为止,我遇到的每个问题的解决方案都会导致另一个问题。

If I make a change to the user model (say, by adding a zip code property and matching field in the AspNetUsers table), then call UserManager.UpdateAsync(user), it succeeds but does not update the zip code field in the database. 如果我更改用户模型(例如,通过在AspNetUsers表中添加邮政编码属性和匹配字段),然后调用UserManager.UpdateAsync(用户),它会成功,但不会更新数据库中的邮政编码字段。

At least one other SO question has tried to deal with this. 至少有一个其他的SO问题试图解决这个问题。 But the suggested fixes there break other things: 但建议修复那里打破其他事情:

1) Creating another instance of the UserDbContext and trying to attach the user object causes entity framework to complain that “An entity object cannot be referenced by multiple instances of IEntityChangeTracker” 1)创建UserDbContext的另一个实例并尝试附加用户对象导致实体框架抱怨“实体对象不能被IEntityChangeTracker的多个实例引用”

2) Turning off proxy creation gets rid of the problem listed in #1, but causes the dbcontext to not load child objects (like AspNetUserLogins, which are rather important). 2)关闭代理创建摆脱了#1中列出的问题, 但导致dbcontext不加载子对象 (如AspNetUserLogins,这是非常重要的)。

Another solution would be to access the context created in the Controller. 另一种解决方案是访问在Controller中创建的上下文。 Consider the default AccountController's constructor methods with a new ASP .NET Web Application using the MVC (version 5) template: 使用MVC(版本5)模板考虑使用新的ASP .NET Web应用程序的默认AccountController的构造函数方法:

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

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

The application DB Context is created, but there is no way to access it via the UserManager (because the 'Store' private property of UserManager). 创建应用程序数据库上下文,但无法通过UserManager访问它(因为UserManager的“存储”私有属性)。

This doesn't seem like rocket science, so my guess is that I am doing something basically wrong around handling/understanding the dbcontext lifecycle. 这看起来不像是火箭科学,所以我的猜测是我在处理/理解dbcontext生命周期时做了一些基本上错误的事情。

So: how do I correctly access/use the dbcontext to save and update AspNetUsers, associated custom properties, and preserve child objects (like AspNetUserLogins)? 那么:我如何正确访问/使用dbcontext来保存和更新AspNetUsers,关联的自定义属性以及保留子对象(如AspNetUserLogins)?

EDIT ------- 编辑-------

One more thing I tried... 还有一件事我试过......

I updated the AccountController's constructor from the default: 我从默认更新了AccountController的构造函数:

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

to this: 对此:

    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;

    }

In an attempt to hang on to the dbcontext. 试图挂起dbcontext。 Later, in the body of a public async Task method, I attempt to call: 后来,在公共异步任务方法的主体中,我试图调用:

  var updated = await UserManager.UpdateAsync(user);

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

However, the attempt to update the state throws an exception: 但是,尝试更新状态会引发异常:

"There is already a generated proxy type for the object layer type 'xyz.Models.ApplicationUser'. This occurs when the same object layer type is mapped by two or more different models in an AppDomain." “对象层类型'xyz.Models.ApplicationUser'已经生成了代理类型。当AppDomain中的两个或多个不同模型映射相同的对象层类型时,会发生这种情况。”

That doesn't seem right... it's the same dbcontext assigned in the constructor. 这似乎不正确......它与构造函数中指定的dbcontext相同。

EDIT #2 ----- 编辑#2 -----

Here is the ApplicationUser model: 这是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")
        {

        }

    }
}

Final edit ---------------------------- 最终编辑----------------------------

Ok, after some back and forth in the comments, I realized I was asking the question in the wrong way. 好吧,经过评论中的一些来回,我意识到我是以错误的方式提出问题。 My question was really: How to use Code-first rather than Database-first migrations. 我的问题是:如何使用代码优先而不是数据库优先迁移。 Coming from the Hibernate school, I had been used to manually mapping objects to tables via XML or annotations in Java. 来自Hibernate学校,我习惯于通过XML或Java中的注释手动将对象映射到表。

So, in skimming this article , I missed the important steps around migration. 因此,在略读这篇文章时 ,我错过了有关迁移的重要步骤。 Lesson learned. 学过的知识。

I faced the same problem. 我遇到了同样的问题。 An easy way to overcome this was just to take the properties I wanted to update from the model and save them back to an object pulled from the UserManager; 解决这个问题的一个简单方法就是从模型中获取我想要更新的属性,并将它们保存回从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);
    }

So, you are using database-first instead of code-first. 因此,您使用的是数据库优先而不是代码优先。 My recommendation is to use code-first. 我的建议是使用代码优先。 I, like you, started out using database first, instead. 我和你一样,首先开始使用数据库。 If you aren't using code-first, then delete the migrations table in your database. 如果您不使用代码优先,则删除数据库中的迁移表。 Otherwise, it will cause problems. 否则,会引起问题。

If possible, I recommend following this code-first tutorial, which has helped me greatly. 如果可能的话,我建议遵循这个代码优先的教程,这对我帮助很大。 You can easily add custom fields to your identity and have full integration with the framework as far as authentication goes. 您可以轻松地将自定义字段添加到您的身份中,并在身份验证过程中与框架完全集成。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM