2016-05-09 21 views
2

In meinem ASP.NET MVC-Anwendung habe ich einige Code, der ziemlich trivial sein sollte:UserManager.AddToRole nicht funktioniert - Foreign Key Fehler

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

bekomme ich nur diesen Fehler ...

Die INSERT-Anweisung steht im Konflikt mit der FOREIGN KEY-Einschränkung "FK_dbo.AspNetUserRoles_dbo.AspNetRoles_RoleId". Der Konflikt trat in der Datenbank "TestDatabase", Tabelle "dbo.AspNetRoles", Spalte "Id" auf.

Meine ASP.NET Identität Framework ist Brauch, dass alles Guid als Schlüssel statt int oder string verwendet.

Irgendwelche Ideen, was das verursacht?

redigiert, wie pro Benutzer Kommentare ...

Benutzerklasse

public class User : IdentityUser<Guid, UserLogin, UserRole, UserClaim> 
{ 
    [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public override Guid Id 
    { 
     get { return base.Id; } 
     set { base.Id = value; } 
    } 
} 

Rollenklasse

public class Role : IdentityRole<Guid, UserRole> 
{ 
    public const string Admininstrator = "Administrator"; 

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

Userrole Klasse

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 Klasse

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 Klasse

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; 
    } 
} 

Roles Klasse

internal class RoleStore : RoleStore<Role, Guid, UserRole> 
{ 
    public RoleStore(DbContext context) : base(context) 
    { 
    } 
} 

UPDATE 1: speziell

Der Täter hier liegt ...

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; } 

I ersetzt dies mit einem schmutzigen Hack, was funktioniert

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

Wenn das jemand Hilfe ist? persönlich würde ich lieber keinen dreckigen hack benutzen!

+0

Ist die Rolle "Admin" bereits in der Tabelle Rollen vorhanden? –

+0

@martin_costello ja – series0ne

+0

Ich gehe davon aus, dass Sie OWIN-Identität verwenden. Wenn ja, welche Version ist Ihr Microsoft.AspNet.Identity.Owin NuGet-Paket? –

Antwort

1

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

mit fließend api. In benutzerdefinierte IdentityDbContext Klasse hinzufügen

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); 
} 
+0

Machen diese nicht dasselbe? – trailmax

0

Nach dem letzten Update ist es jetzt klar - ich vermute, dass DatabaseGenerated(DatabaseGeneratedOption.Identity) hinzugefügt wurde, nachdem die Tabellen erstellt wurden. Und die tatsächliche Datenbank weiß nicht, dass sie einen Primärschlüssel generieren muss. Sie erwartet, dass die Clients den Schlüssel bereitstellen.

Wenn Sie in SSMS schauen auf ApplicationRoles Tisch (oder was auch immer entsprechende Tabellennamen Sie haben) (Rechtsklick auf dem Tisch -> „Design“ und suchen Sie auf „Standardwert oder Bindung“ auf dem Feld Id wird es leer sein, aber. es sollte newid() sagen werden: Default Value or Binding is set to be <code>newid()</code>

Migrationen nicht immer diese Änderung ab und bringt Sie mit Fehlern hängen am Ende wie Sie erhalten die Lösungen für dieses andere EF Migration, zu versuchen, das Hinzufügen, sollte es Ihnen so etwas wie dieses geben.:

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

Obwohl das nicht immer funktioniert. Manchmal musste ich die Tabelle komplett löschen und neu erstellen, damit EF die ID für mich erstellen konnte.

Oder (Ich denke, das eine bessere Lösung ist) entfernen DatabaseGenerated(DatabaseGeneratedOption.Identity) aus dem Attribut oben Id Feld und halten

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

Auf diese Weise können Sie den Schlüssel selbst definieren, bevor Sie den Datensatz in der DB zu schaffen. Dies ist besser, wenn Sie die CQRS-Architektur verwenden. Aber EF mag das Entfernen des Attributs witzig machen und wird nach einer weiteren Migration fragen. Ersetzen