MVC5:UserManager.AddToRole():“将用户添加到角色时出错:找不到用户 ID”?

Posted

技术标签:

【中文标题】MVC5:UserManager.AddToRole():“将用户添加到角色时出错:找不到用户 ID”?【英文标题】:MVC5: UserManager.AddToRole(): "Error Adding User to Role: UserId not found"? 【发布时间】:2014-08-14 20:42:30 【问题描述】:

我一直在试验 MVC5/EF6 并尝试使用 Code-First 迁移的新身份验证。解决方案中的所有内容当前正在构建中,我可以添加Migration,但是当我在 VS2013 中通过package manager console 执行update-database 时,我的Configuration.cs 文件无法将我的测试数据完全处理到我的表中并输出@ 987654325@.

我已尝试显式设置用户 ID 并让它由管理器生成(如在某些示例中所示),但每次我都会收到相同的错误消息。我知道我的#region User & User Roles 文件的#region User & User Roles 中的错误失败了,但我不知道为什么:

using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
using PersonalPortfolio2.Helper;
using PersonalPortfolio2.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Diagnostics;
using System.Linq;

namespace PersonalPortfolio2.Models

    public sealed class Configuration : DbMigrationsConfiguration<PersonalPortfolio2.Models.ApplicationDbContext>
    
        public Configuration()
        
            AutomaticMigrationsEnabled = false;
        

        protected override void Seed(PersonalPortfolio2.Models.ApplicationDbContext context)
        
            BlobHelper bh = new BlobHelper();
            //LocationHelper lh = new LocationHelper();
            ApplicationDbContext db = new ApplicationDbContext();

            #region Roles
            try
            
                List<string> myRoles = new List<string>(new string[]  "Root", "Admin", "Outsider", "Client", "Primary" );
                var RoleManager = new RoleManager<IdentityRole>(new RoleStore<IdentityRole>(context));

                foreach (string r in myRoles)
                
                    RoleManager.Create(new IdentityRole(r));
                
            
            catch (Exception ex)
            
                throw new Exception("Error Create Roles: " + ex.Message);
            
            #endregion

            #region User & User Roles
            var store = new UserStore<ApplicationUser>(context);
            var manager = new UserManager<ApplicationUser>(store);

            List<ApplicationUser> myUsers = GetTestUsers();
            var passwordHasher = new PasswordHasher();

            foreach (var u in myUsers)
            
                var userExists = db.Users.Where(a => a.Email == u.Email).FirstOrDefault();
                if (userExists == null)
                
                    var user = new ApplicationUser
                    
                        Email = u.Email,
                        PasswordHash = passwordHasher.HashPassword("P@ssword1"),
                        LockoutEnabled = false,
                        Name = u.Name,
                        Position = u.Position,
                        RegisteredDate = DateTime.Now,
                        LastVisitDate = DateTime.Now,
                        OrganizationId = u.OrganizationId,
                        ProfilePictureSrc = u.ProfilePictureSrc,
                    ;

                    try
                    
                        var userCreateResult = manager.Create(user);
                    
                    catch (Exception ex)
                    
                        throw new Exception("Error Add User: " + ex.Message + "\n" + ex.InnerException);
                    

                    // Add User to Roles
                    List<string> usersRoles = GetUserRoles(u.Email);
                    bool codeHit = false;
                    foreach (string role in usersRoles)
                    
                        try
                        
                            codeHit = true;
                            manager.AddToRole(user.Id, role);
                        
                        catch (Exception ex)
                        
                            // ERROR!
                            throw new Exception("Error Adding User to Role: " + ex.Message + "\n" + ex.Data + "\n" + ex.InnerException + "\nName: " + user.Name + "\nEmail: " + user.Email + "\nuser.ID: " + user.Id + "\nu.Id: " + u.Id + "\nRole: " + role + "\nCodeHit: " + codeHit);
                        
                    
                

            
            #endregion



            #region Helpers
            private List<ApplicationUser> GetTestUsers()
            
                List<ApplicationUser> testUsers = new List<ApplicationUser>
                
                    new ApplicationUser
                    
                        Id = "1",
                        Email = "Admin@abc.com",
                        Name = "Admin User",
                        RegisteredDate = System.DateTime.Now,
                        LastVisitDate = System.DateTime.Now,
                        Position = "Site Administrator",
                        PhoneNumber = "1234564321",
                    ,
                    new ApplicationUser
                    
                        Id = "2",
                        Email = "first.last@hotmail.com",
                        Name = "James Woods",
                        RegisteredDate = System.DateTime.Now,
                        LastVisitDate = System.DateTime.Now,
                        Position = "Software Developer / Web Designer",
                        PhoneNumber = "1234567890",
                    ,
                    new ApplicationUser
                    
                        Id = "3",
                        Email = "tyler.perry@gmail.com",
                        Name = "Tyler Perry",
                        RegisteredDate = System.DateTime.Now,
                        LastVisitDate = System.DateTime.Now,
                        Position = "Company Contact",
                        PhoneNumber = "1234567890",
                    
                ;
                return testUsers;
            


            public List<string> GetUserRoles(string user)
            
                List<string> myRoles = new List<string>();
                switch (user)
                
                        //"Root", "Admin", "Outsider", "Client", "Primary"
                    case "Admin@abc.com":
                        myRoles = new List<string>(new string[]  "Root", "Admin" );
                        break;
                    case "first.last@hotmail.com":
                        myRoles = new List<string>(new string[]  "Admin" );
                        break;
                    case "tyler.perry@gmail.com":
                        myRoles = new List<string>(new string[]  "Client", "Outsider" );
                        break;
                    default:
                        myRoles = new List<string>(new string[] "[user] not found.");
                        break;
                
                return myRoles;
            
            #endregion

    

谁能在这里提供一些我可能忽略的见解?有关详细信息,我当前的 catch 声明正在输出以下内容:

Error Adding User to Role: UserId not found.
System.Collections.ListDictionaryInternal

Name: Admin User
Email: Admin@abc.com
user.ID: 1
u.Id: 1
Role: Root
CodeHit: True

当我为我的管理员用户注释掉明确的 Id = "1", 时,user.IDu.Id 变为:ab753316-3d7b-4f98-a13a-d19f7c926976。我原以为可能是我的GetTestUsers()GetUserRoles(u.Email) 的辅助方法是问题所在,但在我的try/catch 和我正在使用的codeHit 布尔变量之间,我已经验证问题肯定来自@987654339 @。

【问题讨论】:

你能解决你的问题吗? 【参考方案1】:

我将这里留给可能遇到类似问题的任何人。我有完全相同的“症状”。事实证明,与存储的密码相关的问题不符合配置的密码策略(至少一个大写字符、一个小写字符等)。

根据下面的 cmets 和答案,通常,当任何用户创建约束或策略未遵循时,都会引发相同的模糊错误消息。

这可能包括以下内容:

未遵循密码策略(这似乎是最常见的原因) 必填字段作为空字符串/ null 传递 用户名或电子邮件重复

确实有很多问题会导致此错误发生。如果以上方法不能解决你的问题,我建议如下:

    熟悉身份提供者的规则和政策,因为它是为您的解决方案设置的 根据@Ted 下面的回答,在 UserManager.Create 方法上跟踪或设置断点以查看结果,因为这可能会揭示问题的原因。

【讨论】:

很高兴听到您能弄明白! 谢谢@CraftBeerHipsterDude,你的答案很准确。但有时您可能会收到不同的错误(重复的用户电子邮件等),并且它会隐藏在相同的错误消息后面。请参阅下面 Ted 的回答【参考方案2】:

CraftBeerHipsterDude 的回答帮助我找到了相同情况的答案。如果 AddToRole 失败,诀窍是查看从 Usermanager.Create(...) 返回的结果。它可能不会抛出异常,但仍会返回表明创建失败的结果。在我的情况下,它是“用户名已经存在”。

概括:检查早期操作的结果以追踪真正的错误。

【讨论】:

很奇怪Create 仍然为用户分配一个新的 ID,即使有隐藏的错误。我的错误是“名称不能为空”... 谢谢你,泰德!我试图在Configuration.cs 中植入一个新用户,所以我无法在其上放置一个有效的断点,直到我将代码移动到一个我可以激活的控制器和一个像这样的结果变量:var result = await userManager.CreateAsync(user, "somePassword");。有一个无效的电子邮件地址! "只能包含数字或字母"【参考方案3】:

对问题创建者的回答:不应输入 UserId,将其留给实体框架为用户创建它。您还尝试将其输入为整数,但在基于角色的安全性中为用户输入 Id是字符串形式的 GUID,因此无论如何尝试为需要字符串的列添加整数值都行不通。

现在这对我来说不是问题,但我遇到了同样错误的其他几个问题:

我试图弄清楚我错过了什么,但没有发现任何问题。

所以我使用了另一个 VS 实例作为种子代码的调试器(在另一个线程中找到了这个):

    if (System.Diagnostics.Debugger.IsAttached == false)
    System.Diagnostics.Debugger.Launch();

我注意到用户在添加到角色时确实有一个 Id。 最后,我能够通过添加一个空的 catch 块来解决问题。由于错误错误,代码不会引发异常,并且用户被添加到正确的角色:

    try
    
        var newUser = userManager.FindByEmail(superuserToInsert.Email);
        userManager.AddToRole(newUser.Id, "Super users");
    
    catch 
    
    

在我尝试将新创建的用户添加到角色之前,种子似乎回滚了用户的创建,并且从未在用户数据库中创建用户。

但是当我完全删除添加到角色部分时,用户被添加到数据库中,并且在第二次运行稍微编辑配置代码时,可以将用户添加到角色中,导致我认为它是aa抛出错误错误..使用空catch块忽略此错误错误似乎也忽略了用户创建的回滚(必须在添加到角色部分之前的幕后发生)导致用户在添加到角色时存在部分代码。

希望对某人有所帮助

编辑:

以上解决了我几乎所有的头痛问题,但我注意到仍有大约 300 个帐户无法创建(但没有引发错误)。一个简单的 FULL OUTER JOIN 和对源数据库表的用户数据库的检查让我认为在“UserName”和“Email”列的值中都有破折号(-)字符的用户阻止了用户的创建,这是这样的。

这是一个简单的解决方法,只需将用户管理器配置为使用多个字母数字字符: (即使属性名称似乎只指向“用户名”,它也会影响“电子邮件”)

    var userManager = new UserManager<ApplicationUser>(userStore);

    userManager.UserValidator = new UserValidator<ApplicationUser(userManager)
    
        AllowOnlyAlphanumericUserNames = false,
    ; 

//错了

【讨论】:

【参考方案4】:

我会避免在您的 Seed 方法中使用 UserManagerRoleManager。相反,我只会使用上下文。我使用的是以下内容,它创建了一个用户并将他分配给一个角色:

protected override void Seed(DbModelContext context)

    if (context == null)
    
        throw new ArgumentNullException("context", "Context must not be null.");
    

    const string UserName = "User";
    const string RoleName = "UserRole";

    var userRole = new IdentityRole  Name = RoleName, Id = Guid.NewGuid().ToString() ;
    context.Roles.Add(userRole);

    var hasher = new PasswordHasher();

    var user = new IdentityUser
                   
                       UserName = UserName,
                       PasswordHash = hasher.HashPassword(UserName),
                       Email = "test@test.com",
                       EmailConfirmed = true,
                       SecurityStamp = Guid.NewGuid().ToString()
                   ;

    user.Roles.Add(new IdentityUserRole  RoleId = userRole.Id, UserId = user.Id );

    context.Users.Add(user);

    base.Seed(context);

Entity 类是自定义实现(因为我想使用 GUID 作为 ID),但它们派生自框架类。如果您将它们更改为适当的框架类,它应该以相同的方式工作。

编辑

我删除了自定义类并将它们切换到框架类,因为有些混乱。

【讨论】:

感谢您回复地平线。我相信我们在这里的经验水平不同,我很难实施与您建议的自定义类类似的解决方案。 @AnalyticLunatic 自定义类与此示例无关。它们只是框架实现之上的一层。为避免混淆,我将示例更新为框架类。就像您看到的那样,它只是类名称中的一个开关。请记住,您可能需要将 ID 从 GUID 更改为字符串(只需添加 ToString()),具体取决于您用作 ID 的数据类型。 那么,我应该放弃我正在尝试的当前方法吗?当我当前的代码失败时,我的 [AspNetRoles] 表具有我的所有角色,但是在 var userCreateResult = manager.Create(user); 处理时,AspNetUsersAspNetUserRoles 都没有一个条目。 嘿Horizo​​n,我想通了,但也许你能提供一些见解?当我比较我的示例应用程序时,我终于发现我所做的不同。该示例使用UserName = u.Email 初始化我的所有new ApplicationUser,我认为这是我的应用程序中不需要的用户名字段。当我将鼠标悬停在该字段上时,它会显示string IdentityUser&lt;string,IdentityUserLogin,IdentityUserRole,IdentityUserClaim&gt;.UserName。关于为什么设置Username = u.Email 解决了我的UserId not found 问题的任何想法? @AnalyticLunatic 不太清楚你做了什么。你能用新代码更新你的问题吗?我正在努力解决同样的问题。【参考方案5】:

这节省了我的第二天。谢谢@达菲朋克。就我而言,问题是密码。之前是“abc”。

var studentPassword = "abcABC_9";
                        var user = new ApplicationUser  UserName = "abc", Email = 1@gmail.com ;
                        var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context));
                        var result = await UserManager.CreateAsync(user, studentPassword.ToString());
                        await UserManager.AddToRoleAsync(user.Id, "Student");

【讨论】:

【参考方案6】:

我的问题是我使用用户的(用户名)而不是 ID,工作代码如下:

 public async Task<ActionResult> Register(RegisterViewModel model)
        
            if (ModelState.IsValid)
            
                var user = new ApplicationUser  UserName = model.Username, Email = model.Username, workshopId =model.workshopId;
                var rolesList = _context.Roles.ToList();
                string role = rolesList.FirstOrDefault(r => r.Id == model.SelectedRoleId).Name;
                var result = await UserManager.CreateAsync(user, model.Password);

                // Add User to the selected role from the list in .cshtml
                if (result.Succeeded)
                
                    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
                    result =await UserManager.AddToRoleAsync(user.Id, role);
                    // For more information on how to enable account confirmation and password reset please visit https://go.microsoft.com/fwlink/?LinkID=320771
                    // Send an email with this link
                    // string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
                    // var callbackUrl = Url.Action("ConfirmEmail", "Account", new  userId = user.Id, code = code , protocol: Request.Url.Scheme);
                    // await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

                    return RedirectToAction("Index", "Home");
                
                AddErrors(result);
            

            // If we got this far, something failed, redisplay form
            return View(model);
        

【讨论】:

以上是关于MVC5:UserManager.AddToRole():“将用户添加到角色时出错:找不到用户 ID”?的主要内容,如果未能解决你的问题,请参考以下文章

如何从一个 MVC5 应用程序获取访问令牌到另一个 MVC5 应用程序

C# mvc5使用mvc5 +bootstrap+EF6搭建一个权限管理系统的心得体会

《ASP.NET MVC 5 破境之道》:第一境 ASP.Net MVC5项目初探 — 第二节:MVC5项目结构

MVC5 和 Ninject:无参数构造函数错误

mvc5整合Autofac

MVC5 HTML将列作为表头?