C# UserManager.AddToRole不工作-外键错误

C# UserManager.AddToRole不工作-外键错误,c#,asp.net,sql-server,asp.net-identity,role,C#,Asp.net,Sql Server,Asp.net Identity,Role,在我的ASP.NET MVC应用程序中,我有一些代码,这些代码应该非常简单: UserManager.AddToRole(user.id, "Admin"); 我只是得到这个错误 INSERT语句与外键约束冲突 “FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId”。冲突发生了 在数据库“TestDatabase”的表“dbo.AspNetRoles”的列“Id”中 我的ASP.NET身份框架是定制的,因为所有东西都使用Guid作为键,而不是int或st

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

UserManager.AddToRole(user.id, "Admin");
我只是得到这个错误

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

我的ASP.NET身份框架是定制的,因为所有东西都使用
Guid
作为键,而不是
int
string

你知道这是什么原因吗

根据用户评论进行编辑…

用户类

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; }
}
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>()));
    }
}
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);
    }
}
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;
    }
}
internal class RoleStore : RoleStore<Role, Guid, UserRole>
{
    public RoleStore(DbContext context) : base(context)
    {
    }
}
我用一个肮脏的hack替换了它,它可以工作

public Role()
{
    Id = Guid.NewGuid();
}

如果这对任何人都有帮助的话?就我个人而言,我宁愿不使用肮脏的黑客

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

如果您在SSMS中查看
ApplicationRoles
表(或您拥有的任何相应表名)(右键单击表->“设计”并查看字段
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删除该属性可能会很有趣,并将要求进行另一次迁移。

替换

[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);
}
模型创建时受保护的覆盖无效(DbModelBuilder modelBuilder)
{
基于模型创建(modelBuilder);
//身份
modelBuilder.Entity().Property(r=>r.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity().Property(r=>r.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}

这可能对正在自定义默认标识用户的其他人有用

但是,我在不同的配置中遇到了相同的错误。实际的问题是,
AddToRoleAsync()
方法从默认的
IdentityUser
指向
Id
,而不是我的自定义用户,它当然是从
IdentityUser
继承的,PK是
UserId

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

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


祝你好运!

角色表中是否已经存在
管理员
角色?@martin_costello Yes我假设你使用的是OWIN标识。如果是,你的Microsoft.AspNet.Identity.OWIN NuGet软件包的版本是什么?如果你使用的是自定义IdentityUser和/或IdentityRole类,请显示them@trailmax试过了-SQL看起来有点混乱所以我做了一些探索性测试。通过SSMS手动添加条目有效。-有关更多详细信息,请参阅我的最新更新(更新1)。这些不是在做同样的事情吗?
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public new Guid Id { get; set; }
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);
}