Megosztás a következőn keresztül:


Identity modell testreszabása az ASP.NET Core-ban

Készítette: Arthur Vickers

ASP.NET Core Identity keretrendszert biztosít a felhasználói fiókok kezeléséhez és tárolásához ASP.NET Core-alkalmazásokban. Identity akkor lesz hozzáadva a projekthez, ha az egyéni fiókok ki lesznek választva hitelesítési mechanizmusként. Alapértelmezés szerint Identity egy Entity Framework (EF) Core adatmodellt használ. Ez a cikk ismerteti a Identity modell testreszabását.

Identity és EF Core migrálások

A modell vizsgálata előtt hasznos megérteni, hogyan Identity működik a MigrálásokkalEF Core egy adatbázis létrehozása és frissítése. A legfelső szinten a folyamat a következő:

  1. Adatmodell definiálása vagy frissítése kódban.
  2. Adjon hozzá egy migrálást, amely lefordítja ezt a modellt az adatbázisra alkalmazható módosításokra.
  3. Ellenőrizze, hogy az áttelepítés helyesen jelöli-e a szándékait.
  4. A migrálás alkalmazásával frissítse az adatbázist, hogy szinkronban legyen a modellel.
  5. Ismételje meg az 1–4. lépést a modell további finomításához és az adatbázis szinkronban tartásához.

A migrálások hozzáadásához és alkalmazásához használja az alábbi módszerek egyikét:

  • A Package Manager-konzol (PMC) ablaka a Visual Studio használata esetén. További információ: EF Core PMC-eszközök.
  • A .NET parancssori felület, ha a parancssort használja. További információ: EF Core .NET parancssori eszközök.
  • Az alkalmazás futtatásakor kattintson az Áttelepítés alkalmazása gombra a hibalapon.

ASP.NET Core rendelkezik egy fejlesztési idejű hibaoldal-kezelővel. A kezelő migrálásokat alkalmazhat az alkalmazás futtatásakor. Az éles alkalmazások jellemzően SQL-szkripteket hoznak létre az áttelepítésekből, és az adatbázis-módosításokat egy kontrollált alkalmazás- és adatbázis-telepítés részeként helyezik üzembe.

Új alkalmazás Identity létrehozásakor a fenti 1. és 2. lépés már befejeződött. Vagyis a kezdeti adatmodell már létezik, és a kezdeti migrálás hozzá lett adva a projekthez. A kezdeti migrálást továbbra is alkalmazni kell az adatbázisra. A kezdeti migrálás az alábbi módszerek egyikével alkalmazható:

  • Futtatás Update-Database a PMC-ben.
  • Futtassa a dotnet ef database update parancssorban.
  • Az alkalmazás futtatásakor kattintson az Áttelepítések alkalmazása gombra a hibalapon.

Ismételje meg az előző lépéseket a modell módosításakor.

Fontos

Ha Identity az alapul szolgáló EF Core modellt befolyásoló beállítások vannak konfigurálva (például options.Stores.MaxLengthForKeys, vagy options.Stores.SchemaVersion), a tervezéskor is alkalmazni kell ezeket a beállításértékeket EF Core, hogy a migrációk a megfelelő modellalakzatot generálják. Ha az EF-eszközök a beállítások konfigurálása nélkül futnak, a létrehozott migrálások kihagyhatják a kívánt módosításokat. További információ: efcore#36314. Annak érdekében, hogy a Identity beállítások következetesen alkalmazva legyenek a migráció-generálás során, használja az alábbi módszerek egyikét:

  • Az indítási projekt beállítása: Futtassa dotnet ef a parancsokat (vagy PMC-parancsokat) az indítási projektként hívott AddDefaultIdentity vagy AddIdentityCore beállított alkalmazásprojekttel. Ha például parancsokat futtat egy osztálytárprojektből, adja meg az indítási projektet dotnet ef migrations add {MIGRATION_NAME} --startup-project {PATH_TO_APP_PROJECT}, ahol a {MIGRATION_NAME} helyőrző az áttelepítés neve, a {PATH_TO_APP_PROJECT} helyőrző pedig az alkalmazásprojekt elérési útja.
  • Implementálás IDesignTimeDbContextFactory: Másik lehetőségként implementáljon egy olyan környezetet IDesignTimeDbContextFactory<TContext> , amely felépíti a környezetet, és alkalmazza az ezzel egyenértékű Identity beállításkonfigurációt. A Aspire-barát megoldásért lásd: efcore#35285 (megjegyzés).

Egy példa konfiguráció Identity a következő formában Program.cs:

builder.Services
    .AddDefaultIdentity<ApplicationUser>(options =>
    {
        options.Stores.SchemaVersion = IdentitySchemaVersions.Version2;
        options.Stores.MaxLengthForKeys = 256;
    })
    .AddEntityFrameworkStores<ApplicationDbContext>();

Példa a tervezési idő gyárára:

public class DesignTimeApplicationDbContextFactory
    : IDesignTimeDbContextFactory<ApplicationDbContext>
{
    public ApplicationDbContext CreateDbContext(string[] args)
    {
        var optionsBuilder = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseSqlServer("{CONNECTION_STRING}");

        return new ApplicationDbContext(optionsBuilder.Options);
    }
}

Megjegyzés:

A options.Stores.MaxLengthForKeys közvetlenül nem érhető el a OnModelCreating belül, mert a függőséginjektálás nem érhető el tervezési időben. Ehelyett adja meg a konfigurált értéket közvetlenül (például HasMaxLength(256)), vagy használjon egy tervezési idő mechanizmust a beállítások megadásához, ha szükséges. További részletekért lásd: ASP.NET Core Identitykonfigurálása.

Jótanács

Az áttelepítés hozzáadása után mindig ellenőrizze, hogy az eredményként kapott modell-pillanatkép tükrözi-e a kívánt kulcshosszokat vagy sémaverziót.

A Identity modell

Entitástípusok

A Identity modell az alábbi entitástípusokból áll.

Entitástípus Leírás
User A felhasználót jelöli.
Role Egy szerepkört jelöl.
UserClaim Egy felhasználó által birtokolt jogcímet jelöl.
UserToken Egy felhasználó hitelesítési jogkivonatát jelöli.
UserLogin A felhasználó összekapcsolása a bejelentkezéssel.
RoleClaim Olyan jogcímet jelöl, amely egy szerepkörben lévő összes felhasználó számára biztosított.
UserRole Felhasználókat és szerepköröket összekötő entitás.

Entitástípus-kapcsolatok

Az entitástípusok a következő módokon kapcsolódnak egymáshoz:

  • Minden Userhez több UserClaims tartozhat.
  • Minden Userhez több UserLogins tartozhat.
  • Minden Userhez több UserTokens tartozhat.
  • Mindegyik Role számos RoleClaims kapcsolattal rendelkezhet.
  • Mindegyik User-hez több Roles társítható, és mindegyik Role több Users-hoz is társítható. Ez egy több-az-többhöz kapcsolat, amelyhez kapcsolótábla szükséges az adatbázisban. Az illesztőtáblát az UserRole entitás képviseli.

Alapértelmezett modellkonfiguráció

Identity számos környezeti osztályt definiál, amelyek a DbContext-ből öröklődnek a modell konfigurálásához és használatához. Ez a konfiguráció a EF Core Code First Fluent API használatával történik a OnModelCreating környezeti osztály metódusában. Az alapértelmezett konfiguráció a következő:

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

Általános modelltípusok

Identity Az alapértelmezett Common Language Runtime (CLR) típusokat határozza meg a fent felsorolt entitástípusok mindegyikéhez. Ezek a típusok mind a következő előtaggal vannak elnevezettek Identity:

  • IdentityUser
  • IdentityRole
  • IdentityUserClaim
  • IdentityUserToken
  • IdentityUserLogin
  • IdentityRoleClaim
  • IdentityUserRole

Az ilyen típusok közvetlen használata helyett a típusok használhatók alaposztályként az alkalmazás saját típusaihoz. A DbContext definiált Identity osztályok általánosak, így különböző CLR-típusok használhatók a modell egy vagy több entitástípusához. Ezek az általános típusok lehetővé teszik az User elsődleges kulcs (PK) adattípusának módosítását is.

Amikor a szerepkörök támogatásával használja a Identity elemet, szükséges egy IdentityDbContext osztályt használni. Például:

// 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>

Szerepkörök (csak jogcímek) nélkül is használható Identity , ebben az esetben egy IdentityUserContext<TUser> osztályt kell használni:

// 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>
{
}

A modell testreszabása

A modell testreszabásának kiindulópontja a megfelelő környezettípusból való származtatás. Lásd a Modell általános típusai szakaszt. Ezt a környezettípust szokás nevezni ApplicationDbContext , és a ASP.NET Core-sablonok hozzák létre.

A környezet két módon konfigurálja a modellt:

  • Entitás- és kulcstípusok megadása az általános típusparaméterekhez.
  • Ezeknek a típusoknak a leképezését felül kell bírálni OnModelCreating.

Felülíráskor először OnModelCreating-et kell meghívni, majd a felülíró konfigurációt. EF Core konfigurálási szabályzata szerint az utolsó módosítás élvez előnyt. Ha például egy ToTable entitástípus metódusát először egy táblanévvel, majd később egy másik táblanévvel hívja meg a rendszer, a második hívásban a tábla nevét használja a rendszer.

MEGJEGYZÉS: Ha a DbContext nem származik IdentityDbContext-ból, előfordulhat, hogy a AddEntityFrameworkStores nem tudja helyesen megállapítani a megfelelő POCO típusokat a TUserClaim, TUserLogin és TUserToken számára. Ha AddEntityFrameworkStores nem a megfelelő POCO típusokra következtet, a megkerülő megoldás az, ha közvetlenül hozzáadja a megfelelő típusokat services.AddScoped<IUser/RoleStore<TUser> és UserStore<...>>.

Egyéni felhasználói adatok

Az egyéni felhasználói adatokat az IdentityUser-ből való öröklés támogatja. Ezt a típust ApplicationUserszokás nevezni:

public class ApplicationUser : IdentityUser
{
    public string CustomTag { get; set; }
}

Használja a ApplicationUser típust a környezet általános argumentumaként:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    }
}

Nincs szükség OnModelCreating felülírására az ApplicationDbContext osztályban. EF Core konvenció szerint leképezi a CustomTag tulajdonságot. Az adatbázist azonban frissíteni kell egy új CustomTag oszlop létrehozásához. A oszlop létrehozásához adjon hozzá egy migrációt, majd frissítse az adatbázist a Identity és EF Core Áttelepítések szakaszokban leírtak szerint.

Frissítse Pages/Shared/_LoginPartial.cshtml, és cserélje le IdentityUserApplicationUser-re:

@using Microsoft.AspNetCore.Identity
@using WebApp1.Areas.Identity.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

Frissítse a Areas/Identity/IdentityHostingStartup.cs vagy a Startup.ConfigureServices és cserélje le a IdentityUser-t a ApplicationUser-ra.

services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();                                    

A hívás AddDefaultIdentity egyenértékű a következő kóddal:

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 osztálykódtárként Razor van megadva. További információ: Állványzat Identity ASP.NET Core-projektekben. Ennek következtében az előző kódhoz szükség van hívásra AddDefaultUI. Ha az Identity állványzatot Identity fájlok projekthez való hozzáadására használták, távolítsa el a AddDefaultUI hívását. További információkért lásd:

Az elsődleges kulcs típusának módosítása

A PK-oszlop adattípusának módosítása az adatbázis létrehozása után számos adatbázisrendszer esetében problémás. A PK megváltoztatása jellemzően megkívánja a tábla elvetését és újbóli létrehozását. Ezért az adatbázis létrehozásakor a kezdeti migrálás során meg kell adni a kulcstípusokat.

A PK típusának módosításához kövesse az alábbi lépéseket:

  1. Ha az adatbázis a PK módosítása előtt lett létrehozva, futtassa Drop-Database a (PMC) vagy dotnet ef database drop a (.NET CLI) parancsot a törléshez.

  2. Az adatbázis törlésének megerősítése után távolítsa el a kezdeti migrálást a PMC (Remove-Migration) vagy a .NET CLI (dotnet ef migrations remove) használatával.

  3. Frissítse az ApplicationDbContext osztályt, hogy az a IdentityDbContext<TUser,TRole,TKey> osztályból származzon. Adja meg a következő új kulcstípust TKey: . Például a Guid kulcstípus használatához szükséges:

    public class ApplicationDbContext
        : IdentityDbContext<IdentityUser<Guid>, IdentityRole<Guid>, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    Az előző kódban az általános osztályokat IdentityUser<TKey>IdentityRole<TKey> kell megadni az új kulcstípus használatához.

    Startup.ConfigureServices az általános felhasználó használatához frissíteni kell:

    services.AddDefaultIdentity<IdentityUser<Guid>>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
    
  4. Ha egyéni ApplicationUser osztályt használ, frissítse az osztályt, hogy örököljön a IdentityUser osztályból. Például:

    using System;
    using Microsoft.AspNetCore.Identity;
    
    public class ApplicationUser : IdentityUser<Guid>
    {
        public string CustomTag { get; set; }
    }
    

    Frissítés ApplicationDbContext az egyéni ApplicationUser osztályra való hivatkozáshoz:

    public class ApplicationDbContext
        : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    Regisztrálja az egyéni adatbázis-konteksztus osztályt, amikor hozzáadja a(z) Identity szolgáltatást Startup.ConfigureServices-ben.

    services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
    

    Az elsődleges kulcs adattípusa az DbContext objektum elemzéséből következik.

    Identity osztálykódtárként Razor van megadva. További információ: Állványzat Identity ASP.NET Core-projektekben. Ennek következtében az előző kódhoz szükség van hívásra AddDefaultUI. Ha az Identity állványzatot Identity fájlok projekthez való hozzáadására használták, távolítsa el a AddDefaultUI hívását.

  5. Ha egyéni ApplicationRole osztályt használ, frissítse az osztályt, hogy örököljön a IdentityRole<TKey> osztályból. Például:

    using System;
    using Microsoft.AspNetCore.Identity;
    
    public class ApplicationRole : IdentityRole<Guid>
    {
        public string Description { get; set; }
    }
    

    Frissítse ApplicationDbContext hivatkozását az egyéni ApplicationRole osztályra. Az alábbi osztály például egy egyedi ApplicationUser-re és egy egyedi ApplicationRole-re hivatkozik:

    using System;
    using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
    using Microsoft.EntityFrameworkCore;
    
    public class ApplicationDbContext :
        IdentityDbContext<ApplicationUser, ApplicationRole, Guid>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
    

    Regisztrálja az egyéni adatbázis-konteksztus osztályt, amikor hozzáadja a(z) Identity szolgáltatást Startup.ConfigureServices-ben.

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

    Az elsődleges kulcs adattípusa az DbContext objektum elemzéséből következik.

    Identity osztálykódtárként Razor van megadva. További információ: Állványzat Identity ASP.NET Core-projektekben. Ennek következtében az előző kódhoz szükség van hívásra AddDefaultUI. Ha az Identity állványzatot Identity fájlok projekthez való hozzáadására használták, távolítsa el a AddDefaultUI hívását.

Navigációs tulajdonságok hozzáadása

A kapcsolatok modellkonfigurációjának módosítása nehezebb lehet, mint más módosítások végrehajtása. Ügyeljen arra, hogy a meglévő kapcsolatokat cserélje le, és ne hozzon létre új, további kapcsolatokat. A módosított kapcsolatnak különösen ugyanazt az idegenkulcs-tulajdonságot (FK) kell megadnia, mint a meglévő kapcsolatot. A kapcsolat UsersUserClaims például alapértelmezés szerint az alábbiak szerint van megadva:

builder.Entity<TUser>(b =>
{
    // Each User can have many UserClaims
    b.HasMany<TUserClaim>()
     .WithOne()
     .HasForeignKey(uc => uc.UserId)
     .IsRequired();
});

A kapcsolat FK-értéke tulajdonságként UserClaim.UserId van megadva. HasMany és WithOne argumentumok nélkül hívjuk meg, hogy navigációs tulajdonságok nélkül hozza létre a kapcsolatot.

Adjon hozzá egy navigációs tulajdonságot a ApplicationUser-hez, amely lehetővé teszi a társított UserClaims hivatkozását a felhasználó által.

public class ApplicationUser : IdentityUser
{
    public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }
}

A TKey for IdentityUserClaim<TKey> a felhasználók PK-jának megadott típusa. Ebben az esetben TKey az string, mert alapértelmezett értékeket használ a rendszer. Nem a entitástípushoz tartozó PK típus.

Most, hogy a navigációs tulajdonság létezik, szükséges annak konfigurálása a következőben 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();
        });
    }
}

Figyelje meg, hogy a kapcsolat pontosan úgy van konfigurálva, mint korábban, csak a hívásban HasMany megadott navigációs tulajdonsággal.

A navigációs tulajdonságok csak az EF-modellben léteznek, az adatbázisban nem. Mivel a kapcsolat FK-jához tartozó FK nem változott, az ilyen típusú modellmódosítás nem igényli az adatbázis frissítését. Ezt úgy ellenőrizheti, hogy migrálást ad hozzá a módosítás után. A Up metódusok és Down a metódusok üresek.

Az összes felhasználói navigációs tulajdonság hozzáadása

A fenti szakaszt útmutatásként használva az alábbi példa egyirányú navigációs tulajdonságokat konfigurál a felhasználó összes kapcsolatához:

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

Felhasználói és szerepkör-navigációs tulajdonságok hozzáadása

A fenti szakaszt útmutatásként használva az alábbi példa a felhasználó és a szerepkör összes kapcsolatához konfigurálja a navigációs tulajdonságokat:

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

    }
}

Megjegyzések:

  • Ez a példa tartalmazza a UserRole összekapcsolási entitást is, amely a felhasználók és a szerepkörök közötti sok-sokhoz kapcsolatok navigálásához szükséges.
  • Ne felejtse el módosítani a navigációs tulajdonságok típusait, hogy azok tükrözzék, hogy most már Application{...} típusok használatosak a Identity{...} típusok helyett.
  • Ne felejtse el használni a Application{...}-t az általános ApplicationContext definícióban.

Az összes navigációs tulajdonság hozzáadása

A fenti szakaszt útmutatásként használva az alábbi példa az összes entitástípushoz tartozó összes kapcsolat navigációs tulajdonságait konfigurálja:

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

Összetett kulcsok használata

Az előző szakaszok a modellben Identity használt kulcs típusának módosítását mutatták be. Identity A kulcsmodell összetett kulcsok használatára történő módosítása nem támogatott vagy ajánlott. Az Identity összetett kulcs használata megváltoztatja a Identity kezelőkód és a modell közötti interakciót. Ez a testreszabás meghaladja a dokumentum hatókörét.

Tábla-/oszlopnevek és -aspektusok módosítása

A táblák és oszlopok nevének módosításához hívja meg a következőt base.OnModelCreating: Ezután adjon hozzá konfigurációt az alapértelmezett értékek felülbírálásához. Például az összes Identity tábla nevének módosításához:

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

Ezek a példák az alapértelmezett Identity típusokat használják. Ha például ApplicationUseralkalmazástípust használ, az alapértelmezett típus helyett ezt a típust konfigurálja.

Az alábbi példa módosít néhány oszlopnevet:

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

Bizonyos típusú adatbázisoszlopok konfigurálhatók bizonyos aspektusokkal (például a megengedett maximális string hosszsal). Az alábbi példa a modell több string tulajdonságához állítja be az oszlop maximális hosszát:

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

Másik sémára való leképezés

A sémák eltérően viselkedhetnek az adatbázis-szolgáltatók között. AZ SQL Server esetében az alapértelmezett beállítás az összes tábla létrehozása a dbo sémában. A táblák egy másik sémában hozhatók létre. Például:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.HasDefaultSchema("notdbo");
}

Késleltetett betöltés

Ebben a szakaszban a lusta betöltési proxyk támogatása lett hozzáadva a Identity modellhez. A lazy-loading hasznos, mivel lehetővé teszi a navigációs tulajdonságok használatát anélkül, hogy először biztosítaná azok betöltését.

Az entitástípusok többféleképpen is alkalmassá tehetők a lusta betöltésre, a EF Core dokumentációban leírtak szerint. Az egyszerűség kedvéért használjon lusta betöltési proxykat, amelyekhez a következőre van szükség:

Az alábbi példa bemutatja a UseLazyLoadingProxies meghívását a Startup.ConfigureServices-ben:

services
    .AddDbContext<ApplicationDbContext>(
        b => b.UseSqlServer(connectionString)
              .UseLazyLoadingProxies())
    .AddDefaultIdentity<ApplicationUser>()
    .AddEntityFrameworkStores<ApplicationDbContext>();

A navigációs tulajdonságok entitástípusokhoz való hozzáadására vonatkozó útmutatásért tekintse meg az előző példákat.

Figyelmeztetés

Ez a cikk a kapcsolati sztringek használatát mutatja be. Helyi adatbázis esetén a felhasználót nem kell hitelesíteni, de éles környezetben a kapcsolati sztringek néha tartalmaznak jelszót a hitelesítéshez. Az erőforrás-tulajdonosi jelszóval történő hitelesítés (ROPC) olyan biztonsági kockázatot jelent, amelyet el kell kerülni a termelési adatbázisokban. A termelésben használt alkalmazásoknak az elérhető legbiztonságosabb hitelesítési folyamatot kell használniuk. A tesztelési vagy éles környezetekben üzembe helyezett alkalmazások hitelesítéséről további információt a Biztonságos hitelesítési folyamatokcímű témakörben talál.

További erőforrások