UserManager.AddToRole 不工作 - 外键错误

Posted

技术标签:

【中文标题】UserManager.AddToRole 不工作 - 外键错误【英文标题】:UserManager.AddToRole not working - Foreign Key error 【发布时间】:2016-09-04 09:27:12 【问题描述】:

在我的 ASP.NET MVC 应用程序中,我有一些代码应该相当简单:

UserManager.AddToRole(user.id, "Admin");

我刚刚收到这个错误...

INSERT 语句与 FOREIGN KEY 约束冲突 “FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId”。发生了冲突 在数据库“TestDatabase”、表“dbo.AspNetRoles”、“Id”列中。

我的 ASP.NET 身份框架是自定义的,因为所有内容都使用 Guid 作为键,而不是 intstring

任何想法是什么原因造成的?

根据用户 cmets 进行编辑...

用户类

public class User : IdentityUser<Guid, UserLogin, UserRole, UserClaim>

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public override Guid Id
    
        get  return base.Id; 
        set  base.Id = value; 
    

角色类

public class Role : IdentityRole<Guid, UserRole>

    public const string Admininstrator = "Administrator";

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public new Guid Id  get; set; 

UserRole 类

public class UserRole : IdentityUserRole<Guid>



internal class RoleManager : RoleManager<Role, Guid>

    public RoleManager(IRoleStore<Role, Guid> roleStore) : base(roleStore)
    
    

    public static RoleManager Create(IdentityFactoryOptions<RoleManager> options, IOwinContext context)
    
        return new RoleManager(new RoleStore(context.Get<ApplicationDataContext>()));
    

SignInManager 类

internal class SignInManager : SignInManager<User, Guid>

    public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager) : base(userManager, authenticationManager)
    
    

    public override Task<ClaimsIdentity> CreateUserIdentityAsync(User user)
    
        return user.GenerateUserIdentityAsync((UserManager)UserManager);
    

    public static SignInManager Create(IdentityFactoryOptions<SignInManager> options, IOwinContext context)
    
        return new SignInManager(context.GetUserManager<UserManager>(), context.Authentication);
    

UserManager 类

internal class UserManager : UserManager<User, Guid>

    public UserManager(IUserStore<User, Guid> store) : base(store)
    
    

    public static UserManager Create(IdentityFactoryOptions<UserManager> options, IOwinContext context)
    
        var manager = new UserManager(new UserStore<User, Role, Guid, UserLogin, UserRole, UserClaim>(context.Get<ApplicationDataContext>()));
        // Configure validation logic for usernames
        manager.UserValidator = new UserValidator<User, Guid>(manager)
        
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        ;

        // Configure validation logic for passwords
        manager.PasswordValidator = new PasswordValidator
        
            RequiredLength = 6,
            RequireNonLetterOrDigit = true,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        ;

        // Configure user lockout defaults
        manager.UserLockoutEnabledByDefault = true;
        manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
        manager.MaxFailedAccessAttemptsBeforeLockout = 5;

        // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
        // You can write your own provider and plug it in here.
        manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<User, Guid>
        
            MessageFormat = "Your security code is 0"
        );
        manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<User, Guid>
        
            Subject = "Security Code",
            BodyFormat = "Your security code is 0"
        );
        manager.EmailService = new EmailService();
        manager.SmsService = new SmsService();
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        
            manager.UserTokenProvider = new DataProtectorTokenProvider<User, Guid>(dataProtectionProvider.Create("ASP.NET Identity"));
        

        return manager;
    

RoleStore 类

internal class RoleStore : RoleStore<Role, Guid, UserRole>

    public RoleStore(DbContext context) : base(context)
    
    

更新 1:

罪魁祸首在这里……

public class Role : IdentityRole<Guid, UserRole>

    public const string Admininstrator = "Administrator";

    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public new Guid Id  get; set; 

...具体来说...

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public new Guid Id  get; set; 

我用 dirty hack 替换了它,它可以工作

public Role()

    Id = Guid.NewGuid();

如果这对任何人有帮助吗?就个人而言,我不希望使用肮脏的黑客!

【问题讨论】:

Admin 角色是否已经存在于 Roles 表中? @martin_costello 是的 我假设您使用的是 OWIN Identity。如果是,您的 Microsoft.AspNet.Identity.Owin NuGet 包是什么版本? 如果您使用自定义 IdentityUser 和/或 IdentityRole 类,请显示它们 @trailmax 试过了——SQL 看起来有点晦涩,所以我做了一些探索性测试。通过 SSMS 手动添加条目有效。 - 有关详细信息,请参阅我的最新更新(更新 1)。 【参考方案1】:

在上次更新之后,现在很清楚了——我猜DatabaseGenerated(DatabaseGeneratedOption.Identity) 是在创建表之后添加的。而实际的数据库并不知道自己需要生成主键,而是希望客户端提供主键。

如果您在ApplicationRoles 表(或您拥有的任何相应表名)上查看 SSMS(右键单击表 ->“设计”并查看字段 Id 上的“默认值或绑定”,它将是空。但应该说newid()

迁移并不总是能接受这种变化,最终你会遇到类似的错误。解决方案是尝试添加另一个 EF 迁移,它应该会给你这样的东西:

AlterColumn("dbo.ApplicationRoles", "Id", c => c.Guid(nullable: false, defaultValueSql: "newid()"));

虽然这并不总是有效。有时我不得不完全删除并重新创建表,以便 EF 获取我希望 DB 为我生成 ID。

或者(我认为这是一个更好的解决方案)从Id 字段上方的属性中删除DatabaseGenerated(DatabaseGeneratedOption.Identity) 并保留

public Role()

    Id = Guid.NewGuid();

这允许您在数据库中创建记录之前自己定义键。如果您使用的是 CQRS 架构,这会更好。但是 EF 可能对删除该属性很有趣,并会要求再次迁移。

【讨论】:

【参考方案2】:

替换

[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public new Guid Id  get; set; 

使用流畅的 api。在自定义IdentityDbContext类中添加

protected override void OnModelCreating(DbModelBuilder modelBuilder)

       base.OnModelCreating(modelBuilder);
       // identity
       modelBuilder.Entity<User>().Property(r=>r.Id)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
       modelBuilder.Entity<Role>().Property(r=>r.Id)
          .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

【讨论】:

这些不是在做同样的事情吗?【参考方案3】:

这可能对自定义默认 IdentityUser 的其他人有用

虽然我在不同的配置下遇到了同样的错误。实际问题是AddToRoleAsync() 方法从默认IdentityUser 指向Id,而不是我的自定义用户,它当然继承自IdentityUser&lt;int&gt;,PK 为UserId

AddToRoleAsync() 方法从默认IdentityUser 指向Id,但我们为自定义用户设置了不同的主键时,IdentityUserId 列中有0。这就是由于参照完整性而引发错误的原因。这种参照完整性要求外键也必须存在于父表中。在这种情况下,父表User 没有主键值0

因此,我只是删除了我的UserId 并使用来自IdentityUser 的现有Id。因为我并不期待修改UserManager 类。

祝你好运!

【讨论】:

以上是关于UserManager.AddToRole 不工作 - 外键错误的主要内容,如果未能解决你的问题,请参考以下文章

模拟服务工作者/节点不工作,我不明白为啥

马上五十岁了,工作得很不开心,非常郁闷,工作重要还是生活重要?

SKSpriteNode .run 不工作或不完全工作

2017除夕夜的感悟:学习工作不分家,工作生活不分家,读书用兵不分家

cakePHP 准备好的语句不工作(获取所有不工作)

无法返回 - popViewController Animated 不工作,向后滑动也不工作