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í:

  1. Definujte nebo aktualizujte datový model v kódu.
  2. Přidejte migraci, která tento model přeloží na změny, které se dají použít v databázi.
  3. Zkontrolujte, jestli migrace správně představuje vaše záměry.
  4. Pomocí migrace aktualizujte databázi, která se má synchronizovat s modelem.
  5. 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 Core 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 mnoho UserClaims.
  • Každý User může mít mnoho UserLogins.
  • Každý User může mít mnoho UserTokens.
  • Každý Role může mít mnoho přidružených RoleClaims.
  • Každý User může mít mnoho přidružených Rolesa každý Role může být přidružen k mnoha Users. Jedná se o relaci M:N, která vyžaduje tabulku spojení v databázi. Tabulka spojení je reprezentována entitou UserRole .

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í 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:

  1. Pokud byla databáze vytvořena před změnou PK, odstraňte ji spuštěním Drop-Database příkazu (PMC) nebo dotnet ef database drop (.NET Core CLI).

  2. Po potvrzení odstranění databáze odeberte počáteční migraci pomocí Remove-Migration (PMC) nebo dotnet ef migrations remove (.NET Core CLI).

  3. ApplicationDbContext Aktualizujte třídu tak, aby byla odvozena od IdentityDbContext<TUser,TRole,TKey>. Zadejte nový typ klíče pro TKey. Pokud chcete například použít Guid 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>();
    
  4. Pokud se používá vlastní ApplicationUser třída, aktualizujte třídu tak, aby dědila z IdentityUser. 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.

  5. Pokud se používá vlastní ApplicationRole třída, aktualizujte třídu tak, aby dědila z IdentityRole<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í:

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.

Další prostředky