UserManager.AddToRole 不工作 - 外键错误
UserManager.AddToRole not working - Foreign Key error
在我的 ASP.NET MVC 应用程序中,我有一些代码应该相当简单:
UserManager.AddToRole(user.id, "Admin");
我刚收到这个错误...
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId". The conflict occurred
in database "TestDatabase", table "dbo.AspNetRoles", column 'Id'.
我的 ASP.NET 身份框架是自定义的,因为所有内容都使用 Guid
作为键而不是 int
或 string
.
知道是什么原因造成的吗?
编辑,根据用户评论...
用户class
public class User : IdentityUser<Guid, UserLogin, UserRole, UserClaim>
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override Guid Id
{
get { return base.Id; }
set { base.Id = value; }
}
}
角色class
public class Role : IdentityRole<Guid, UserRole>
{
public const string Admininstrator = "Administrator";
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public new Guid Id { get; set; }
}
用户角色class
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 class
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);
}
}
用户管理器class
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;
}
}
角色库class
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; }
我用 肮脏的 hack 替换了它
public Role()
{
Id = Guid.NewGuid();
}
如果这对任何人有帮助?就个人而言,我宁愿不要使用肮脏的 hack!
在上次更新之后,现在很清楚了——我猜 DatabaseGenerated(DatabaseGeneratedOption.Identity)
是在 table 创建之后添加的。而实际的数据库并不知道自己需要生成主键,它希望客户端提供主键。
如果您在 ApplicationRoles
table(或任何相应的 table 名称)上查看 SSMS(右键单击 table -> "Design" 并查看字段 Id
上的 "Default Value or Binding" 它将是空的。但它应该说 newid()
:
迁移并不总能接受此更改,您最终会遇到类似的错误。解决方案是尝试添加另一个 EF 迁移,它应该给你这样的东西:
AlterColumn("dbo.ApplicationRoles", "Id", c => c.Guid(nullable: false, defaultValueSql: "newid()"));
虽然这并不总是有效。有时我不得不完全删除并重新创建 table,以便 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
class 添加
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);
}
这可能对自定义默认 IdentityUser 的其他人有用
虽然我在不同的配置下遇到了同样的错误。实际问题是 AddToRoleAsync()
方法从默认 IdentityUser
指向 Id
而不是我的自定义用户,它当然是从 IdentityUser<int>
继承的,PK 为 UserId
。
当 AddToRoleAsync()
方法从默认 IdentityUser
指向 Id
时,但我们为自定义用户设置了不同的主键,我们在 Id
列 IdentityUser
。这就是它因参照完整性而抛出错误的原因。这种参照完整性要求外键也必须存在于父 table 中。在这种情况下,父 table User
没有主键值 0
.
因此,我只是删除了 UserId
并使用了来自 IdentityUser
的现有 Id
。因为我不期待修改 UserManager
class.
祝你好运!
在我的 ASP.NET MVC 应用程序中,我有一些代码应该相当简单:
UserManager.AddToRole(user.id, "Admin");
我刚收到这个错误...
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId". The conflict occurred in database "TestDatabase", table "dbo.AspNetRoles", column 'Id'.
我的 ASP.NET 身份框架是自定义的,因为所有内容都使用 Guid
作为键而不是 int
或 string
.
知道是什么原因造成的吗?
编辑,根据用户评论...
用户class
public class User : IdentityUser<Guid, UserLogin, UserRole, UserClaim>
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public override Guid Id
{
get { return base.Id; }
set { base.Id = value; }
}
}
角色class
public class Role : IdentityRole<Guid, UserRole>
{
public const string Admininstrator = "Administrator";
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public new Guid Id { get; set; }
}
用户角色class
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 class
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);
}
}
用户管理器class
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;
}
}
角色库class
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; }
我用 肮脏的 hack 替换了它
public Role()
{
Id = Guid.NewGuid();
}
如果这对任何人有帮助?就个人而言,我宁愿不要使用肮脏的 hack!
在上次更新之后,现在很清楚了——我猜 DatabaseGenerated(DatabaseGeneratedOption.Identity)
是在 table 创建之后添加的。而实际的数据库并不知道自己需要生成主键,它希望客户端提供主键。
如果您在 ApplicationRoles
table(或任何相应的 table 名称)上查看 SSMS(右键单击 table -> "Design" 并查看字段 Id
上的 "Default Value or Binding" 它将是空的。但它应该说 newid()
:
迁移并不总能接受此更改,您最终会遇到类似的错误。解决方案是尝试添加另一个 EF 迁移,它应该给你这样的东西:
AlterColumn("dbo.ApplicationRoles", "Id", c => c.Guid(nullable: false, defaultValueSql: "newid()"));
虽然这并不总是有效。有时我不得不完全删除并重新创建 table,以便 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
class 添加
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);
}
这可能对自定义默认 IdentityUser 的其他人有用
虽然我在不同的配置下遇到了同样的错误。实际问题是 AddToRoleAsync()
方法从默认 IdentityUser
指向 Id
而不是我的自定义用户,它当然是从 IdentityUser<int>
继承的,PK 为 UserId
。
当 AddToRoleAsync()
方法从默认 IdentityUser
指向 Id
时,但我们为自定义用户设置了不同的主键,我们在 Id
列 IdentityUser
。这就是它因参照完整性而抛出错误的原因。这种参照完整性要求外键也必须存在于父 table 中。在这种情况下,父 table User
没有主键值 0
.
因此,我只是删除了 UserId
并使用了来自 IdentityUser
的现有 Id
。因为我不期待修改 UserManager
class.
祝你好运!