Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
ASP.NET Core Identity tillhandahåller ett ramverk för att hantera och lagra användarkonton i ASP.NET Core-appar. Identity läggs till i projektet när enskilda konton väljs som autentiseringsmekanism. Som standardinställning använder Identity en Entity Framework (EF) Core-datamodell. I den här artikeln beskrivs hur modellen Identity kan anpassas.
Identity och EF Core migreringar
Innan du undersöker modellen är det bra att förstå hur Identity det fungerar med EF Core migreringar för att skapa och uppdatera en databas. På den översta nivån är processen:
- Definiera eller uppdatera en datamodell i kod.
- Lägg till en migrering för att översätta den här modellen till ändringar som kan tillämpas på databasen.
- Kontrollera att migreringen representerar dina avsikter korrekt.
- Använd migreringen för att uppdatera databasen så att den är synkroniserad med modellen.
- Upprepa steg 1 till och med 4 för att ytterligare förfina modellen och hålla databasen synkroniserad.
Använd någon av följande metoder för att lägga till och tillämpa migreringar:
- Fönstret Package Manager Console (PMC) om du använder Visual Studio. Mer information finns i EF Core PMC-verktyg.
- .NET CLI om du använder kommandoraden. Mer information finns i EF Core .NET-kommandoradsverktyg.
- Klicka på knappen Tillämpa migreringar på felsidan när appen körs.
ASP.NET Core har en sidhanterare för utvecklingstidsfel. Hanteraren kan använda migreringar när appen körs. Produktionsappar genererar vanligtvis SQL-skript från migreringarna och distribuerar databasändringar som en del av en kontrollerad app- och databasdistribution.
När en ny app som använder Identity skapas har steg 1 och 2 ovan redan slutförts. Den första datamodellen finns alltså redan och den första migreringen har lagts till i projektet. Den inledande migreringen måste fortfarande tillämpas på databasen. Den inledande migreringen kan tillämpas via någon av följande metoder:
- Kör
Update-Databasei PMC. - Kör
dotnet ef database updatei ett kommandogränssnitt. - Klicka på knappen Tillämpa migreringar på felsidan när appen körs.
Upprepa föregående steg när ändringar görs i modellen.
Modellen Identity
Entitetstyper
Modellen Identity består av följande entitetstyper.
| Entitetstyp | Beskrivning |
|---|---|
User |
Representerar användaren. |
Role |
Representerar en roll. |
UserClaim |
Representerar ett anspråk som en användare har. |
UserToken |
Representerar en autentiseringstoken för en användare. |
UserLogin |
Associerar en användare med en inloggning. |
RoleClaim |
Representerar ett anspråk som beviljas alla användare inom en roll. |
UserRole |
En kopplingsentitet som associerar användare och roller. |
Relationer av entitetstyp
Entitetstyperna är relaterade till varandra på följande sätt:
- Var och en
Userkan ha mångaUserClaims. - Var och en
Userkan ha mångaUserLogins. - Var och en
Userkan ha mångaUserTokens. - Var och en
Rolekan ha många associeradeRoleClaims. - Varje
Userkan ha många associeradeRoles, och varjeRolekan vara associerad med mångaUsers. Det här är en många-till-många-relation som kräver en kopplingstabell i databasen. Kopplingstabellen representeras av entitetenUserRole.
Standardmodellkonfiguration
Identity definierar många kontextklasser som ärver från DbContext för att konfigurera och använda modellen. Den här konfigurationen görs med api:et EF Core Code First Fluent i OnModelCreating -metoden för kontextklassen. Standardkonfigurationen är:
builder.Entity<TUser>(b =>
{
// Primary key
b.HasKey(u => u.Id);
// Indexes for "normalized" username and email, to allow efficient lookups
b.HasIndex(u => u.NormalizedUserName).HasName("UserNameIndex").IsUnique();
b.HasIndex(u => u.NormalizedEmail).HasName("EmailIndex");
// Maps to the AspNetUsers table
b.ToTable("AspNetUsers");
// A concurrency token for use with the optimistic concurrency checking
b.Property(u => u.ConcurrencyStamp).IsConcurrencyToken();
// Limit the size of columns to use efficient database types
b.Property(u => u.UserName).HasMaxLength(256);
b.Property(u => u.NormalizedUserName).HasMaxLength(256);
b.Property(u => u.Email).HasMaxLength(256);
b.Property(u => u.NormalizedEmail).HasMaxLength(256);
// The relationships between User and other entity types
// Note that these relationships are configured with no navigation properties
// Each User can have many UserClaims
b.HasMany<TUserClaim>().WithOne().HasForeignKey(uc => uc.UserId).IsRequired();
// Each User can have many UserLogins
b.HasMany<TUserLogin>().WithOne().HasForeignKey(ul => ul.UserId).IsRequired();
// Each User can have many UserTokens
b.HasMany<TUserToken>().WithOne().HasForeignKey(ut => ut.UserId).IsRequired();
// Each User can have many entries in the UserRole join table
b.HasMany<TUserRole>().WithOne().HasForeignKey(ur => ur.UserId).IsRequired();
});
builder.Entity<TUserClaim>(b =>
{
// Primary key
b.HasKey(uc => uc.Id);
// Maps to the AspNetUserClaims table
b.ToTable("AspNetUserClaims");
});
builder.Entity<TUserLogin>(b =>
{
// Composite primary key consisting of the LoginProvider and the key to use
// with that provider
b.HasKey(l => new { l.LoginProvider, l.ProviderKey });
// Limit the size of the composite key columns due to common DB restrictions
b.Property(l => l.LoginProvider).HasMaxLength(128);
b.Property(l => l.ProviderKey).HasMaxLength(128);
// Maps to the AspNetUserLogins table
b.ToTable("AspNetUserLogins");
});
builder.Entity<TUserToken>(b =>
{
// Composite primary key consisting of the UserId, LoginProvider and Name
b.HasKey(t => new { t.UserId, t.LoginProvider, t.Name });
// Limit the size of the composite key columns due to common DB restrictions
b.Property(t => t.LoginProvider).HasMaxLength(maxKeyLength);
b.Property(t => t.Name).HasMaxLength(maxKeyLength);
// Maps to the AspNetUserTokens table
b.ToTable("AspNetUserTokens");
});
builder.Entity<TRole>(b =>
{
// Primary key
b.HasKey(r => r.Id);
// Index for "normalized" role name to allow efficient lookups
b.HasIndex(r => r.NormalizedName).HasName("RoleNameIndex").IsUnique();
// Maps to the AspNetRoles table
b.ToTable("AspNetRoles");
// A concurrency token for use with the optimistic concurrency checking
b.Property(r => r.ConcurrencyStamp).IsConcurrencyToken();
// Limit the size of columns to use efficient database types
b.Property(u => u.Name).HasMaxLength(256);
b.Property(u => u.NormalizedName).HasMaxLength(256);
// The relationships between Role and other entity types
// Note that these relationships are configured with no navigation properties
// Each Role can have many entries in the UserRole join table
b.HasMany<TUserRole>().WithOne().HasForeignKey(ur => ur.RoleId).IsRequired();
// Each Role can have many associated RoleClaims
b.HasMany<TRoleClaim>().WithOne().HasForeignKey(rc => rc.RoleId).IsRequired();
});
builder.Entity<TRoleClaim>(b =>
{
// Primary key
b.HasKey(rc => rc.Id);
// Maps to the AspNetRoleClaims table
b.ToTable("AspNetRoleClaims");
});
builder.Entity<TUserRole>(b =>
{
// Primary key
b.HasKey(r => new { r.UserId, r.RoleId });
// Maps to the AspNetUserRoles table
b.ToTable("AspNetUserRoles");
});
Generiska typer i modeller
Identity definierar clr-standardtyper ( Common Language Runtime ) för var och en av de entitetstyper som anges ovan. De här typerna är alla prefixade med Identity:
IdentityUserIdentityRoleIdentityUserClaimIdentityUserTokenIdentityUserLoginIdentityRoleClaimIdentityUserRole
I stället för att använda dessa typer direkt kan typerna användas som basklasser för appens egna typer. Klasserna DbContext som definieras av Identity är generiska, så att olika CLR-typer kan användas för en eller flera av entitetstyperna i modellen. Dessa allmänna typer tillåter User också att datatypen primärnyckel (PK) ändras.
När du använder Identity med stöd för roller ska en IdentityDbContext klass användas. Till exempel:
// Uses all the built-in Identity types
// Uses `string` as the key type
public class IdentityDbContext
: IdentityDbContext<IdentityUser, IdentityRole, string>
{
}
// Uses the built-in Identity types except with a custom User type
// Uses `string` as the key type
public class IdentityDbContext<TUser>
: IdentityDbContext<TUser, IdentityRole, string>
where TUser : IdentityUser
{
}
// Uses the built-in Identity types except with custom User and Role types
// The key type is defined by TKey
public class IdentityDbContext<TUser, TRole, TKey> : IdentityDbContext<
TUser, TRole, TKey, IdentityUserClaim<TKey>, IdentityUserRole<TKey>,
IdentityUserLogin<TKey>, IdentityRoleClaim<TKey>, IdentityUserToken<TKey>>
where TUser : IdentityUser<TKey>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey>
{
}
// No built-in Identity types are used; all are specified by generic arguments
// The key type is defined by TKey
public abstract class IdentityDbContext<
TUser, TRole, TKey, TUserClaim, TUserRole, TUserLogin, TRoleClaim, TUserToken>
: IdentityUserContext<TUser, TKey, TUserClaim, TUserLogin, TUserToken>
where TUser : IdentityUser<TKey>
where TRole : IdentityRole<TKey>
where TKey : IEquatable<TKey>
where TUserClaim : IdentityUserClaim<TKey>
where TUserRole : IdentityUserRole<TKey>
where TUserLogin : IdentityUserLogin<TKey>
where TRoleClaim : IdentityRoleClaim<TKey>
where TUserToken : IdentityUserToken<TKey>
Det är också möjligt att använda Identity utan roller (endast anspråk), i vilket fall en IdentityUserContext<TUser> klass ska användas:
// Uses the built-in non-role Identity types except with a custom User type
// Uses `string` as the key type
public class IdentityUserContext<TUser>
: IdentityUserContext<TUser, string>
where TUser : IdentityUser
{
}
// Uses the built-in non-role Identity types except with a custom User type
// The key type is defined by TKey
public class IdentityUserContext<TUser, TKey> : IdentityUserContext<
TUser, TKey, IdentityUserClaim<TKey>, IdentityUserLogin<TKey>,
IdentityUserToken<TKey>>
where TUser : IdentityUser<TKey>
where TKey : IEquatable<TKey>
{
}
// No built-in Identity types are used; all are specified by generic arguments, with no roles
// The key type is defined by TKey
public abstract class IdentityUserContext<
TUser, TKey, TUserClaim, TUserLogin, TUserToken> : DbContext
where TUser : IdentityUser<TKey>
where TKey : IEquatable<TKey>
where TUserClaim : IdentityUserClaim<TKey>
where TUserLogin : IdentityUserLogin<TKey>
where TUserToken : IdentityUserToken<TKey>
{
}
Anpassa modellen
Utgångspunkten för modellanpassning är att härleda från lämplig kontexttyp. Se avsnittet Generiska typer för modell. Den här kontexttypen anropas ApplicationDbContext vanligtvis och skapas av ASP.NET Core-mallarna.
Kontexten används för att konfigurera modellen på två sätt:
- Tillhandahåller entitets- och nyckeltyper för parametrar av allmän typ.
- Att åsidosätta
OnModelCreatingför att modifiera mappningen av dessa typer.
Vid åsidosättande av OnModelCreating ska base.OnModelCreating anropas först; den åsidosättande konfigurationen ska anropas därefter.
EF Core har i allmänhet en princip för sista-vinner för konfiguration. Om ToTable metoden för en entitetstyp till exempel anropas först med ett tabellnamn och sedan igen senare med ett annat tabellnamn används tabellnamnet i det andra anropet.
Obs! Om DbContext inte härleds från IdentityDbContextkan AddEntityFrameworkStores det hända att de inte härleder rätt POCO-typer för TUserClaim, TUserLoginoch TUserToken. Om AddEntityFrameworkStores inte härleder rätt POCO-typer är en lösning att lägga till rätt typer direkt via services.AddScoped<IUser/RoleStore<TUser> och UserStore<...>>.
Anpassade användardata
Anpassade användardata stöds genom att ärva från IdentityUser. Det är vanligt att namnge den här typen ApplicationUser:
public class ApplicationUser : IdentityUser
{
public string CustomTag { get; set; }
}
Använd typen ApplicationUser som ett allmänt argument för kontexten:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
Det finns inget behov av att åsidosätta OnModelCreating i ApplicationDbContext klassen.
EF Core mappar egenskapen CustomTag efter konvention. Databasen måste dock uppdateras för att skapa en ny CustomTag kolumn. Om du vill skapa kolumnen lägger du till en migrering och uppdaterar sedan databasen enligt beskrivningen i Identity och EF Core Migreringar.
Uppdatera Pages/Shared/_LoginPartial.cshtml och ersätt IdentityUser med ApplicationUser:
@using Microsoft.AspNetCore.Identity
@using WebApp1.Areas.Identity.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
Uppdatera Areas/Identity/IdentityHostingStartup.cs eller Startup.ConfigureServices ersätt IdentityUser med ApplicationUser.
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
Anrop AddDefaultIdentity motsvarar följande kod:
services.AddAuthentication(o =>
{
o.DefaultScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies(o => { });
services.AddIdentityCore<TUser>(o =>
{
o.Stores.MaxLengthForKeys = 128;
o.SignIn.RequireConfirmedAccount = true;
})
.AddDefaultUI()
.AddDefaultTokenProviders();
Identity tillhandahålls som ett Razor klassbibliotek. Mer information finns i Scaffold Identity i ASP.NET Core-projekt. Därför kräver föregående kod ett anrop till AddDefaultUI.
Identity Om byggnadsställningen användes för att lägga Identity till filer i projektet tar du bort anropet till AddDefaultUI. Mer information finns i:
Ändra primärnyckeltyp
En ändring av PK-kolumnens datatyp efter att databasen har skapats är problematisk i många databassystem. Att ändra primärnyckeln (PK) innebär vanligtvis att tabellen tas bort och återskapas. Därför bör nyckeltyper anges i den första migreringen när databasen skapas.
Följ dessa steg för att ändra PK-typen:
Om databasen skapades före PK-ändringen, kör
Drop-Database(PMC) ellerdotnet ef database drop(.NET CLI) för att ta bort det.När du har bekräftat borttagningen av databasen tar du bort den första migreringen med
Remove-Migration(PMC) ellerdotnet ef migrations remove(.NET CLI).Uppdatera
ApplicationDbContext-klassen så att den härleds från IdentityDbContext<TUser,TRole,TKey>. Ange den nya nyckeltypen förTKey. Om du till exempel vill använda enGuidnyckeltyp:public class ApplicationDbContext : IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }I föregående kod måste de allmänna klasserna IdentityUser<TKey> och IdentityRole<TKey> anges för att använda den nya nyckeltypen.
Startup.ConfigureServicesmåste uppdateras för att använda den generiska användaren:services.AddDefaultIdentity<IdentityUser<Guid>>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();Om en anpassad
ApplicationUserklass används uppdaterar du klassen så att den ärver frånIdentityUser. Till exempel:using System; using Microsoft.AspNetCore.Identity; public class ApplicationUser : IdentityUser<Guid> { public string CustomTag { get; set; } }Uppdatera
ApplicationDbContextför att referera till den anpassadeApplicationUserklassen:public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }Registrera den anpassade databaskontextklassen när du Identity lägger till tjänsten i
Startup.ConfigureServices:services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();Den primära nyckelns datatyp härleds genom att analysera DbContext objektet.
Identity tillhandahålls som ett Razor klassbibliotek. Mer information finns i Scaffold Identity i ASP.NET Core-projekt. Därför kräver föregående kod ett anrop till AddDefaultUI. Identity Om byggnadsställningen användes för att lägga Identity till filer i projektet tar du bort anropet till
AddDefaultUI.Om en anpassad
ApplicationRoleklass används uppdaterar du klassen så att den ärver frånIdentityRole<TKey>. Till exempel:using System; using Microsoft.AspNetCore.Identity; public class ApplicationRole : IdentityRole<Guid> { public string Description { get; set; } }Uppdatera
ApplicationDbContextför att referera till den anpassadeApplicationRoleklassen. Följande klass refererar till exempel till en anpassadApplicationUseroch en anpassadApplicationRole:using System; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }Registrera den anpassade databaskontextklassen när du Identity lägger till tjänsten i
Startup.ConfigureServices:public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<ApplicationUser, ApplicationRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultUI() .AddDefaultTokenProviders(); services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Version_2_1); }Den primära nyckelns datatyp härleds genom att analysera DbContext objektet.
Identity tillhandahålls som ett Razor klassbibliotek. Mer information finns i Scaffold Identity i ASP.NET Core-projekt. Därför kräver föregående kod ett anrop till AddDefaultUI. Identity Om byggnadsställningen användes för att lägga Identity till filer i projektet tar du bort anropet till
AddDefaultUI.
Lägga till navigeringsegenskaper
Det kan vara svårare att ändra modellkonfigurationen för relationer än att göra andra ändringar. Var noga med att ersätta befintliga relationer i stället för att skapa nya, ytterligare relationer. I synnerhet måste den ändrade relationen ange samma egenskap för främmande nyckel (FK) som den befintliga relationen. Till exempel anges relationen mellan Users och UserClaims som standard enligt följande:
builder.Entity<TUser>(b =>
{
// Each User can have many UserClaims
b.HasMany<TUserClaim>()
.WithOne()
.HasForeignKey(uc => uc.UserId)
.IsRequired();
});
FK:n för den här relationen anges som egenskapen UserClaim.UserId.
HasMany och WithOne anropas utan argument för att skapa relationen utan navigeringsegenskaper.
Lägg till en navigeringsegenskap för ApplicationUser som gör det möjligt att associerad UserClaims kan refereras från användaren.
public class ApplicationUser : IdentityUser
{
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
}
För TKeyIdentityUserClaim<TKey> är den typ som angetts för PK för användare. I det här fallet TKey är string för att standardvärdena används. Det är inte PK-typen för entitetstypen UserClaim .
Nu när navigeringsegenskapen finns måste den konfigureras i OnModelCreating:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(b =>
{
// Each User can have many UserClaims
b.HasMany(e => e.Claims)
.WithOne()
.HasForeignKey(uc => uc.UserId)
.IsRequired();
});
}
}
Observera att relationen har konfigurerats exakt som den var tidigare, endast med en navigeringsegenskap som anges i anropet till HasMany.
Navigeringsegenskaperna finns bara i EF-modellen, inte databasen. Eftersom FK:n för relationen inte har ändrats kräver inte den här typen av modelländring att databasen uppdateras. Detta kan kontrolleras genom att lägga till en migrering när du har gjort ändringen. Metoderna Up och Down är tomma.
Lägg till alla användarnavigeringsegenskaper
Med hjälp av avsnittet ovan som vägledning konfigurerar följande exempel enkelriktade navigeringsegenskaper för alla relationer på användaren:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }
public virtual ICollection<IdentityUserRole<string>> UserRoles { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(b =>
{
// Each User can have many UserClaims
b.HasMany(e => e.Claims)
.WithOne()
.HasForeignKey(uc => uc.UserId)
.IsRequired();
// Each User can have many UserLogins
b.HasMany(e => e.Logins)
.WithOne()
.HasForeignKey(ul => ul.UserId)
.IsRequired();
// Each User can have many UserTokens
b.HasMany(e => e.Tokens)
.WithOne()
.HasForeignKey(ut => ut.UserId)
.IsRequired();
// Each User can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne()
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
}
}
Lägg till navigeringsegenskaper för användare och roll
Med hjälp av avsnittet ovan som vägledning konfigurerar följande exempel navigeringsegenskaper för alla relationer för användare och roll:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }
public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationRole : IdentityRole
{
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationDbContext
: IdentityDbContext<
ApplicationUser, ApplicationRole, string,
IdentityUserClaim<string>, ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(b =>
{
// Each User can have many UserClaims
b.HasMany(e => e.Claims)
.WithOne()
.HasForeignKey(uc => uc.UserId)
.IsRequired();
// Each User can have many UserLogins
b.HasMany(e => e.Logins)
.WithOne()
.HasForeignKey(ul => ul.UserId)
.IsRequired();
// Each User can have many UserTokens
b.HasMany(e => e.Tokens)
.WithOne()
.HasForeignKey(ut => ut.UserId)
.IsRequired();
// Each User can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.User)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
modelBuilder.Entity<ApplicationRole>(b =>
{
// Each Role can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.Role)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
});
}
}
Anteckningar:
- I det här exemplet ingår även kopplingsentiteten
UserRole, som behövs för att navigera i många-till-många-relationen från användare till roller. - Kom ihåg att ändra typerna av navigeringsegenskaper så att de återspeglar att
Application{...}typerna nu används i stället förIdentity{...}typer. - Kom ihåg att använda
Application{...}i den allmännaApplicationContextdefinitionen.
Lägg till alla navigeringsegenskaper
Med hjälp av avsnittet ovan som vägledning konfigurerar följande exempel navigeringsegenskaper för alla relationer på alla entitetstyper:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<ApplicationUserClaim> Claims { get; set; }
public virtual ICollection<ApplicationUserLogin> Logins { get; set; }
public virtual ICollection<ApplicationUserToken> Tokens { get; set; }
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
}
public class ApplicationRole : IdentityRole
{
public virtual ICollection<ApplicationUserRole> UserRoles { get; set; }
public virtual ICollection<ApplicationRoleClaim> RoleClaims { get; set; }
}
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationUserClaim : IdentityUserClaim<string>
{
public virtual ApplicationUser User { get; set; }
}
public class ApplicationUserLogin : IdentityUserLogin<string>
{
public virtual ApplicationUser User { get; set; }
}
public class ApplicationRoleClaim : IdentityRoleClaim<string>
{
public virtual ApplicationRole Role { get; set; }
}
public class ApplicationUserToken : IdentityUserToken<string>
{
public virtual ApplicationUser User { get; set; }
}
public class ApplicationDbContext
: IdentityDbContext<
ApplicationUser, ApplicationRole, string,
ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
ApplicationRoleClaim, ApplicationUserToken>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>(b =>
{
// Each User can have many UserClaims
b.HasMany(e => e.Claims)
.WithOne(e => e.User)
.HasForeignKey(uc => uc.UserId)
.IsRequired();
// Each User can have many UserLogins
b.HasMany(e => e.Logins)
.WithOne(e => e.User)
.HasForeignKey(ul => ul.UserId)
.IsRequired();
// Each User can have many UserTokens
b.HasMany(e => e.Tokens)
.WithOne(e => e.User)
.HasForeignKey(ut => ut.UserId)
.IsRequired();
// Each User can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.User)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
modelBuilder.Entity<ApplicationRole>(b =>
{
// Each Role can have many entries in the UserRole join table
b.HasMany(e => e.UserRoles)
.WithOne(e => e.Role)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
// Each Role can have many associated RoleClaims
b.HasMany(e => e.RoleClaims)
.WithOne(e => e.Role)
.HasForeignKey(rc => rc.RoleId)
.IsRequired();
});
}
}
Använda sammansatta nycklar
Föregående avsnitt visade hur du ändrar vilken typ av nyckel som används i Identity modellen. Att ändra nyckelmodellen så att den Identity använder sammansatta nycklar stöds inte eller rekommenderas inte. Att använda en sammansatt nyckel med Identity innebär att ändra hur Identity hanteringskoden interagerar med modellen. Den här anpassningen ligger utanför det här dokumentets omfång.
Ändra tabell-/kolumnnamn och fasetter
Om du vill ändra namnen på tabeller och kolumner anropar du base.OnModelCreating. Lägg sedan till konfiguration för att åsidosätta någon av standardvärdena. Om du till exempel vill ändra namnet på alla Identity tabeller:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>(b =>
{
b.ToTable("MyUsers");
});
modelBuilder.Entity<IdentityUserClaim<string>>(b =>
{
b.ToTable("MyUserClaims");
});
modelBuilder.Entity<IdentityUserLogin<string>>(b =>
{
b.ToTable("MyUserLogins");
});
modelBuilder.Entity<IdentityUserToken<string>>(b =>
{
b.ToTable("MyUserTokens");
});
modelBuilder.Entity<IdentityRole>(b =>
{
b.ToTable("MyRoles");
});
modelBuilder.Entity<IdentityRoleClaim<string>>(b =>
{
b.ToTable("MyRoleClaims");
});
modelBuilder.Entity<IdentityUserRole<string>>(b =>
{
b.ToTable("MyUserRoles");
});
}
I de här exemplen används standardtyperna Identity . Om du använder en apptyp som ApplicationUser, konfigurerar du den typen i stället för standardtypen.
I följande exempel ändras några kolumnnamn:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>(b =>
{
b.Property(e => e.Email).HasColumnName("EMail");
});
modelBuilder.Entity<IdentityUserClaim<string>>(b =>
{
b.Property(e => e.ClaimType).HasColumnName("CType");
b.Property(e => e.ClaimValue).HasColumnName("CValue");
});
}
Vissa typer av databaskolumner kan konfigureras med vissa fasetter (till exempel den maximala string tillåtna längden). I följande exempel anges maximala kolumnlängder för flera string egenskaper i modellen:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUser>(b =>
{
b.Property(u => u.UserName).HasMaxLength(128);
b.Property(u => u.NormalizedUserName).HasMaxLength(128);
b.Property(u => u.Email).HasMaxLength(128);
b.Property(u => u.NormalizedEmail).HasMaxLength(128);
});
modelBuilder.Entity<IdentityUserToken<string>>(b =>
{
b.Property(t => t.LoginProvider).HasMaxLength(128);
b.Property(t => t.Name).HasMaxLength(128);
});
}
Mappa till ett annat schema
Scheman kan bete sig annorlunda mellan databasprovidrar. För SQL Server är standardinställningen att skapa alla tabeller i dbo-schemat . Tabellerna kan skapas i ett annat schema. Till exempel:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("notdbo");
}
Fördröjd laddning
I det här avsnittet läggs stöd för fördröjd läsning av proxy i Identity-modellen till. Lazy-loading är användbart eftersom det gör att navigeringsegenskaperna kan användas utan att försäkra sig om att de är inlästa först.
Entitetstyper kan göras lämpliga för lat inläsning på flera sätt, enligt beskrivningen i dokumentationenEF Core. För enkelhetens skull använder du proxyservrar med fördröjd inläsning, som kräver:
- Installation av paketet Microsoft.EntityFrameworkCore.Proxies .
- Ett anrop till UseLazyLoadingProxies inuti AddDbContext.
- Offentliga entitetstyper med
public virtualnavigeringsegenskaper.
I exemplet nedan visas hur man anropar UseLazyLoadingProxies i Startup.ConfigureServices:
services
.AddDbContext<ApplicationDbContext>(
b => b.UseSqlServer(connectionString)
.UseLazyLoadingProxies())
.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Se föregående exempel för vägledning om hur du lägger till navigeringsegenskaper till entitetstyperna.
Varning
Den här artikeln visar användningen av anslutningssträngar. Med en lokal databas behöver användaren inte autentiseras, men i produktion innehåller anslutningssträngar ibland ett lösenord för att autentisera. En resursägares lösenordsautentiseringsuppgifter (ROPC) är en säkerhetsrisk som bör undvikas i produktionsdatabaser. Produktionsappar bör använda det säkraste tillgängliga autentiseringsflödet. Mer information om autentisering för appar som distribueras till test- eller produktionsmiljöer finns i Säkra autentiseringsflöden.
Ytterligare resurser
ASP.NET Core