Identity přizpůsobení modelu v ASP.NET Core
Autor: Arthur Vickers
ASP.NET Core Identity poskytuje architekturu pro správu a ukládání uživatelských účtů v aplikacích ASP.NET Core. Identity se přidá do projektu, když jako mechanismus ověřování vyberete jednotlivé uživatelské účty . Ve výchozím nastavení Identity se používá datový model Entity Framework (EF) Core. Tento článek popisuje, jak model přizpůsobit Identity .
Identity a EF Core migrace
Než model prozkoumáte, je užitečné pochopit, jak Identity funguje s EF Core migracemi při vytváření a aktualizaci databáze. Na nejvyšší úrovni je proces následující:
- Definujte nebo aktualizujte datový model v kódu.
- Přidejte migraci, která tento model přeloží na změny, které se dají použít v databázi.
- Zkontrolujte, jestli migrace správně představuje vaše záměry.
- Pomocí migrace aktualizujte databázi, která se má synchronizovat s modelem.
- Opakujte kroky 1 až 4, abyste model dále zpřesní a zachovali databázi v synchronizaci.
K přidání a použití migrací použijte jeden z následujících přístupů:
- Okno konzoly Správce balíčků (PMC), pokud používáte Visual Studio. Další informace najdete v tématu EF Core Nástroje PMC.
- Rozhraní příkazového řádku .NET CLI, pokud používáte příkazový řádek. Další informace najdete v tématu EF Core Nástroje příkazového řádku .NET.
- Po spuštění aplikace klikněte na tlačítko Použít migraci na chybové stránce.
ASP.NET Core má obslužnou rutinu chybové stránky v době vývoje. Obslužná rutina může při spuštění aplikace použít migrace. Produkční aplikace obvykle generují skripty SQL z migrací a nasazují změny databáze jako součást řízeného nasazení aplikace a databáze.
Po vytvoření nové aplikace Identity se už dokončily kroky 1 a 2 výše. To znamená, že počáteční datový model již existuje a počáteční migrace byla přidána do projektu. Počáteční migrace se stále musí použít pro databázi. Počáteční migraci je možné použít pomocí jednoho z následujících přístupů:
- Spusťte
Update-Database
v PMC. - Spusťte
dotnet ef database update
v příkazovém prostředí. - Po spuštění aplikace klikněte na tlačítko Použít migraci na chybové stránce.
Opakujte předchozí kroky, protože se v modelu provádějí změny.
Model Identity
Typy entit
Model Identity se skládá z následujících typů entit.
Typ entity | Popis |
---|---|
User |
Představuje uživatele. |
Role |
Představuje roli. |
UserClaim |
Představuje deklaraci identity, kterou má uživatel. |
UserToken |
Představuje ověřovací token pro uživatele. |
UserLogin |
Přidruží uživatele k přihlášení. |
RoleClaim |
Představuje deklaraci identity, která je udělena všem uživatelům v rámci role. |
UserRole |
Entita spojení, která přidruží uživatele a role. |
Relace typu entity
Typy entit spolu vzájemně souvisejí následujícími způsoby:
- Každý
User
může mít mnohoUserClaims
. - Každý
User
může mít mnohoUserLogins
. - Každý
User
může mít mnohoUserTokens
. - Každý
Role
může mít mnoho přidruženýchRoleClaims
. - Každý
User
může mít mnoho přidruženýchRoles
a každýRole
může být přidružen k mnohaUsers
. Jedná se o relaci M:N, která vyžaduje tabulku spojení v databázi. Tabulka spojení je reprezentována entitouUserRole
.
Výchozí konfigurace modelu
Identity definuje mnoho kontextových tříd , které dědí z DbContext konfigurace a používání modelu. Tato konfigurace se provádí pomocí EF Core rozhraní API Code First Fluent v OnModelCreating metodě třídy kontextu. Výchozí konfigurace je:
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");
});
Obecné typy modelů
Identity definuje výchozí typy CLR (Common Language Runtime ) pro každý z výše uvedených typů entit. Všechny tyto typy mají předponu Identity:
IdentityUser
IdentityRole
IdentityUserClaim
IdentityUserToken
IdentityUserLogin
IdentityRoleClaim
IdentityUserRole
Místo přímého použití těchto typů je možné tyto typy použít jako základní třídy pro vlastní typy aplikace. Třídy DbContext
definované Identity obecnými typy, jako jsou různé typy CLR lze použít pro jeden nebo více typů entit v modelu. Tyto obecné typy také umožňují změnit datový typ primárního User
klíče (PK).
Při použití Identity s podporou pro role IdentityDbContext by se měla použít třída. Příklad:
// 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>
Je také možné použít Identity bez rolí (pouze deklarací identity), v takovém případě IdentityUserContext<TUser> by se měla použít třída:
// 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>
{
}
Přizpůsobení modelu
Výchozím bodem pro přizpůsobení modelu je odvození z příslušného typu kontextu. Viz část Obecné typy modelu. Tento typ kontextu je vlastní volána ApplicationDbContext
a je vytvořena šablonami ASP.NET Core.
Kontext se používá ke konfiguraci modelu dvěma způsoby:
- Zadání entit a typů klíčů pro parametry obecného typu
- Přepsání
OnModelCreating
pro úpravu mapování těchto typů
Při přepsání OnModelCreating
base.OnModelCreating
by se mělo nejprve volat; přepsání konfigurace by se měla volat dále. EF Core Obecně platí, že pro konfiguraci platí zásada posledního jednoho vítězství. Pokud ToTable
je například metoda pro typ entity volána jako první s názvem jedné tabulky a pak znovu později s jiným názvem tabulky, použije se název tabulky ve druhém volání.
POZNÁMKA: Pokud DbContext
není odvozen z IdentityDbContext
, AddEntityFrameworkStores
nemusí odvodit správné typy POCO pro TUserClaim
, TUserLogin
a TUserToken
. Pokud AddEntityFrameworkStores
neodvozuje správné typy POCO, je alternativním řešením přidat přímo správné typy prostřednictvím services.AddScoped<IUser/RoleStore<TUser>
a UserStore<...>>
.
Vlastní uživatelská data
Vlastní uživatelská data jsou podporována děděním z IdentityUser
. Tento typ ApplicationUser
je obvykle pojmenován:
public class ApplicationUser : IdentityUser
{
public string CustomTag { get; set; }
}
ApplicationUser
Pro kontext použijte typ jako obecný argument:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
Ve třídě není nutné přepisovat OnModelCreating
ApplicationDbContext
. EF Core mapuje CustomTag
vlastnost podle konvence. Databáze se ale musí aktualizovat, aby se vytvořil nový CustomTag
sloupec. Pokud chcete vytvořit sloupec, přidejte migraci a pak aktualizujte databázi, jak je popsáno v Identity části Migrace a EF Core Migrace.
Aktualizujte Pages/Shared/_LoginPartial.cshtml
a nahraďte IdentityUser
:ApplicationUser
@using Microsoft.AspNetCore.Identity
@using WebApp1.Areas.Identity.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
Aktualizujte Areas/Identity/IdentityHostingStartup.cs
nebo Startup.ConfigureServices
nahraďte IdentityUser
.ApplicationUser
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
Volání AddDefaultIdentity je ekvivalentní následujícímu kódu:
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 je k dispozici jako Razor knihovna tříd. Další informace najdete v tématu Generování uživatelského rozhraní Identity v projektech ASP.NET Core. V důsledku toho předchozí kód vyžaduje volání AddDefaultUI. Pokud byl Identity scaffolder použit k přidání Identity souborů do projektu, odeberte volání AddDefaultUI
. Další informace naleznete v tématu:
Změna typu primárního klíče
Změna datového typu sloupce PK po vytvoření databáze je v mnoha databázových systémech problematická. Změna infrastruktury veřejných klíčů obvykle zahrnuje vyřazení a opětovné vytvoření tabulky. Proto by se při počáteční migraci při vytváření databáze měly zadat typy klíčů.
Pokud chcete změnit typ infrastruktury veřejných klíčů, postupujte takto:
Pokud byla databáze vytvořena před změnou PK, spusťte
Drop-Database
(PMC) nebodotnet ef database drop
(.NET CLI) a odstraňte ji.Po potvrzení odstranění databáze odeberte počáteční migraci pomocí
Remove-Migration
(PMC) nebodotnet ef migrations remove
(.NET CLI).ApplicationDbContext
Aktualizujte třídu tak, aby byla odvozena od IdentityDbContext<TUser,TRole,TKey>. Zadejte nový typ klíče proTKey
. Pokud chcete například použítGuid
typ klíče:public class ApplicationDbContext : IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }
V předchozím kódu musí být obecné třídy IdentityUser<TKey> a IdentityRole<TKey> musí být zadány pro použití nového typu klíče.
Startup.ConfigureServices
musí být aktualizován, aby používal obecného uživatele:services.AddDefaultIdentity<IdentityUser<Guid>>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
Pokud se používá vlastní
ApplicationUser
třída, aktualizujte třídu tak, aby dědila zIdentityUser
. Příklad:using System; using Microsoft.AspNetCore.Identity; public class ApplicationUser : IdentityUser<Guid> { public string CustomTag { get; set; } }
Aktualizujte
ApplicationDbContext
odkaz na vlastníApplicationUser
třídu:public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }
Zaregistrujte třídu kontextu vlastní databáze při přidávání Identity služby do
Startup.ConfigureServices
:services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddEntityFrameworkStores<ApplicationDbContext>();
Datový typ primárního klíče je odvozen analýzou objektu DbContext .
Identity je k dispozici jako Razor knihovna tříd. Další informace najdete v tématu Generování uživatelského rozhraní Identity v projektech ASP.NET Core. V důsledku toho předchozí kód vyžaduje volání AddDefaultUI. Pokud byl Identity scaffolder použit k přidání Identity souborů do projektu, odeberte volání
AddDefaultUI
.Pokud se používá vlastní
ApplicationRole
třída, aktualizujte třídu tak, aby dědila zIdentityRole<TKey>
. Příklad:using System; using Microsoft.AspNetCore.Identity; public class ApplicationRole : IdentityRole<Guid> { public string Description { get; set; } }
Aktualizujte
ApplicationDbContext
odkaz na vlastníApplicationRole
třídu. Například následující třída odkazuje na vlastníApplicationUser
a vlastníApplicationRole
:using System; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, Guid> { public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } }
Zaregistrujte třídu kontextu vlastní databáze při přidávání Identity služby do
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); }
Datový typ primárního klíče je odvozen analýzou objektu DbContext .
Identity je k dispozici jako Razor knihovna tříd. Další informace najdete v tématu Generování uživatelského rozhraní Identity v projektech ASP.NET Core. V důsledku toho předchozí kód vyžaduje volání AddDefaultUI. Pokud byl Identity scaffolder použit k přidání Identity souborů do projektu, odeberte volání
AddDefaultUI
.
Přidání navigačních vlastností
Změna konfigurace modelu pro relace může být obtížnější než provádění jiných změn. Je potřeba věnovat pozornost nahrazení existujících relací, nikoli vytváření nových a dalších relací. Konkrétně musí změněná relace zadat stejnou vlastnost cizího klíče (FK) jako existující relace. Například relace mezi Users
a UserClaims
je ve výchozím nastavení určená takto:
builder.Entity<TUser>(b =>
{
// Each User can have many UserClaims
b.HasMany<TUserClaim>()
.WithOne()
.HasForeignKey(uc => uc.UserId)
.IsRequired();
});
FK pro tuto relaci je určena jako UserClaim.UserId
vlastnost. HasMany
a WithOne
jsou volána bez argumentů k vytvoření relace bez navigačních vlastností.
Přidejte navigační vlastnost, která ApplicationUser
umožňuje odkazovat UserClaims
od uživatele:
public class ApplicationUser : IdentityUser
{
public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
}
Hodnota TKey
for IdentityUserClaim<TKey>
je typ určený pro PK uživatelů. V tomto případě je string
to proto, TKey
že se používají výchozí hodnoty. Nejedná se o typ PK pro UserClaim
typ entity.
Teď, když vlastnost navigace existuje, musí být nakonfigurována v 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();
});
}
}
Všimněte si, že relace je nakonfigurovaná přesně tak, jak byla předtím, pouze s navigační vlastností zadanou ve volání HasMany
.
Navigační vlastnosti existují pouze v modelu EF, nikoli v databázi. Vzhledem k tomu, že se sada FK pro relaci nezměnila, tento druh změny modelu nevyžaduje aktualizaci databáze. Můžete to zkontrolovat přidáním migrace po provedení změny. Metody Up
a Down
metody jsou prázdné.
Přidání všech vlastností navigace uživatele
Použití výše uvedené části jako pokynů v následujícím příkladu konfiguruje jednosměrné navigační vlastnosti pro všechny relace uživatele:
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();
});
}
}
Přidání vlastností navigace uživatele a role
Použití výše uvedené části jako doprovodné materiály, následující příklad konfiguruje navigační vlastnosti pro všechny relace uživatele a role:
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();
});
}
}
Poznámky:
- Tento příklad obsahuje také entitu
UserRole
spojení, která je potřebná k přechodu mezi relacemi M:N od uživatelů k rolím. - Nezapomeňte změnit typy navigačních vlastností tak, aby odrážely, že
Application{...}
se teď místo typů používajíIdentity{...}
typy. - Nezapomeňte použít
Application{...}
v obecnéApplicationContext
definici.
Přidání všech vlastností navigace
Následující příklad pomocí výše uvedené části konfiguruje navigační vlastnosti pro všechny relace u všech typů entit:
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();
});
}
}
Použití složených klíčů
Předchozí části ukazují změnu typu klíče použitého Identity v modelu. Změna klíčového modelu tak, Identity aby používala složené klíče, se nepodporuje ani nedoporučuje. Použití složeného klíče Identity zahrnuje změnu způsobu Identity interakce kódu správce s modelem. Toto přizpůsobení je nad rámec tohoto dokumentu.
Změna názvů tabulek a sloupců a omezujících vlastností
Chcete-li změnit názvy tabulek a sloupců, zavolejte base.OnModelCreating
. Potom přidejte konfiguraci, která přepíše kteroukoli z výchozích hodnot. Pokud chcete například změnit název všech Identity tabulek:
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");
});
}
Tyto příklady používají výchozí Identity typy. Pokud používáte typ aplikace, například ApplicationUser
, nakonfigurujte tento typ místo výchozího typu.
Následující příklad změní některé názvy sloupců:
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");
});
}
Některé typy databázových sloupců je možné nakonfigurovat s určitými omezujícími vlastnostmi (například maximální string
povolenou délkou). Následující příklad nastaví maximální délku sloupce pro několik string
vlastností v modelu:
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);
});
}
Mapování na jiné schéma
Schémata se můžou u zprostředkovatelů databáze chovat odlišně. Pro SQL Server je výchozím nastavením vytvořit všechny tabulky ve schématu dbo . Tabulky lze vytvořit v jiném schématu. Příklad:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.HasDefaultSchema("notdbo");
}
Opožděné načítání
V této části se přidá podpora pro opožděné načítání proxy serverů v Identity modelu. Opožděné načítání je užitečné, protože umožňuje používat navigační vlastnosti bez toho, aby se napřed zajistilo jejich načtení.
Typy entit lze označit jako vhodné pro opožděné načítání několika způsoby, jak je popsáno EF Core v dokumentaci. Pro zjednodušení používejte opožděné načítání proxy serverů, které vyžadují:
- Instalace balíčku Microsoft.EntityFrameworkCore.Proxies
- Volání dovnitř UseLazyLoadingProxies AddDbContext.
- Typy veřejných entit s navigačními vlastnostmi
public virtual
Následující příklad ukazuje voláníUseLazyLoadingProxies
:Startup.ConfigureServices
services
.AddDbContext<ApplicationDbContext>(
b => b.UseSqlServer(connectionString)
.UseLazyLoadingProxies())
.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>();
Pokyny k přidání navigačních vlastností do typů entit najdete v předchozích příkladech.
Upozorňující
Tento článek ukazuje použití připojovací řetězec. U místní databáze nemusí být uživatel ověřený, ale v produkčním prostředí připojovací řetězec někdy obsahují heslo k ověření. Přihlašovací údaje vlastníka prostředku (ROPC) jsou bezpečnostní riziko, kterému byste se měli vyhnout v produkčních databázích. Produkční aplikace by měly používat nejbezpečnější dostupný tok ověřování. Další informace o ověřování pro aplikace nasazené do testovacího nebo produkčního prostředí najdete v tématu Zabezpečené toky ověřování.