Poznámka:
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
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é úč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-Databasev PMC. - Spusťte
dotnet ef database updatev 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.
Důležité
Pokud jsou nakonfigurované možnosti, které ovlivňují základní Identity model (například EF Core nebo options.Stores.MaxLengthForKeys), musí být tyto hodnoty možností použity také v době návrhu, aby options.Stores.SchemaVersion migrace vygenerovala správný tvar modelu. Pokud se nástroje EF spustí bez nakonfigurovaných možností, generované migrace můžou vynechat zamýšlené změny. Další informace najdete v tématu efcore#36314.
Pokud chcete během generování migrace zajistit konzistentní uplatňování možností, použijte jeden z následujících přístupů:
-
Nastavte spouštěný projekt: Spusťte
dotnet efpříkazy (nebo příkazy PMC) s projektem aplikace, který voláAddDefaultIdentityneboAddIdentityCorenastavuje jako spouštěný projekt. Například při spouštění příkazů z projektu knihovny tříd zadejte výchozí projekt pomocídotnet ef migrations add {MIGRATION_NAME} --startup-project {PATH_TO_APP_PROJECT}, kde{MIGRATION_NAME}je zástupný symbol pro název migrace a{PATH_TO_APP_PROJECT}je zástupný symbol pro cestu k projektu aplikace. -
Implementace
IDesignTimeDbContextFactory: Alternativně implementujteIDesignTimeDbContextFactory<TContext>, který vytvoří kontext a použije ekvivalentní Identity konfiguraci možností. Pro řešení přátelské pro Aspire viz efcore#35285 (komentář).
Příklad Identity konfigurace v Program.cs:
builder.Services
.AddDefaultIdentity<ApplicationUser>(options =>
{
options.Stores.SchemaVersion = IdentitySchemaVersions.Version2;
options.Stores.MaxLengthForKeys = 256;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
Příklad návrhové továrny:
public class DesignTimeApplicationDbContextFactory
: IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer("{CONNECTION_STRING}");
return new ApplicationDbContext(optionsBuilder.Options);
}
}
Poznámka:
Nelze získat přístup options.Stores.MaxLengthForKeys přímo uvnitř OnModelCreating , protože injektáž závislostí není v době návrhu k dispozici. Místo toho zadejte nakonfigurovanou hodnotu přímo (například HasMaxLength(256)) nebo použijte mechanismus návrhu, který v případě potřeby předá nastavení.
Další podrobnosti najdete v tématu Konfigurace ASP.NET Core Identity.
Návod
Po přidání migrace vždy ověřte, že výsledný snímek modelu odráží zamýšlené délky klíčů nebo verzi schématu.
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ý
Usermůže mít mnohoUserClaims. - Každý
Usermůže mít mnohoUserLogins. - Každý
Usermůže mít mnohoUserTokens. - Každý
Rolemůže mít mnoho přidruženýchRoleClaims. - Každý
Usermůže mít mnoho přidruženýchRolesa každýRolemůž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:
IdentityUserIdentityRoleIdentityUserClaimIdentityUserTokenIdentityUserLoginIdentityRoleClaimIdentityUserRole
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í
OnModelCreatingpro úpravu mapování těchto typů
Při přepsání OnModelCreatingbase.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, TUserLogina 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 ApplicationUserje 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 OnModelCreatingApplicationDbContext .
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).ApplicationDbContextAktualizujte třídu tak, aby byla odvozena od IdentityDbContext<TUser,TRole,TKey>. Zadejte nový typ klíče proTKey. Pokud chcete například použítGuidtyp 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.ConfigureServicesmusí 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í
ApplicationUsertří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
ApplicationDbContextodkaz na vlastníApplicationUsertří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í
ApplicationRoletří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
ApplicationDbContextodkaz na vlastníApplicationRoletřídu. Například následující třída odkazuje na vlastníApplicationUsera 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 TKey to proto, string ž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
UserRolespojení, 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éApplicationContextdefinici.
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ř UseLazyLoadingProxiesAddDbContext.
- 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í.