Bagikan melalui


Identity kustomisasi model di ASP.NET Core

Oleh Arthur Vickers

ASP.NET Core Identity menyediakan kerangka kerja untuk mengelola dan menyimpan akun pengguna di aplikasi ASP.NET Core. Identity ditambahkan ke proyek Anda saat Akun Pengguna Individual dipilih sebagai mekanisme autentikasi. Secara default, Identity gunakan model data Core Entity Framework (EF). Artikel ini menjelaskan cara mengkustomisasi Identity model.

Identity dan EF Core Migrasi

Sebelum memeriksa model, berguna untuk memahami cara Identity kerja EF Core Migrasi untuk membuat dan memperbarui database. Di tingkat atas, prosesnya adalah:

  1. Tentukan atau perbarui model data dalam kode.
  2. Tambahkan Migrasi untuk menerjemahkan model ini ke dalam perubahan yang dapat diterapkan ke database.
  3. Periksa apakah Migrasi mewakili niat Anda dengan benar.
  4. Terapkan Migrasi untuk memperbarui database agar sinkron dengan model.
  5. Ulangi langkah 1 hingga 4 untuk lebih menyempurnakan model dan menjaga database tetap sinkron.

Gunakan salah satu pendekatan berikut untuk menambahkan dan menerapkan Migrasi:

  • Jendela Package Manager Console (PMC) jika menggunakan Visual Studio. Untuk informasi selengkapnya, lihat EF Core Alat PMC.
  • .NET CLI jika menggunakan baris perintah. Untuk informasi selengkapnya, lihat EF Core alat baris perintah .NET.
  • Mengklik tombol Terapkan Migrasi pada halaman kesalahan saat aplikasi dijalankan.

ASP.NET Core memiliki handler halaman kesalahan waktu pengembangan. Handler dapat menerapkan migrasi saat aplikasi dijalankan. Aplikasi produksi biasanya menghasilkan skrip SQL dari migrasi dan menyebarkan perubahan database sebagai bagian dari penyebaran aplikasi dan database yang dikontrol.

Saat aplikasi baru menggunakan Identity dibuat, langkah 1 dan 2 di atas telah selesai. Artinya, model data awal sudah ada, dan migrasi awal telah ditambahkan ke proyek. Migrasi awal masih perlu diterapkan ke database. Migrasi awal dapat diterapkan melalui salah satu pendekatan berikut:

  • Jalankan Update-Database di PMC.
  • Jalankan dotnet ef database update dalam shell perintah.
  • Klik tombol Terapkan Migrasi pada halaman kesalahan saat aplikasi dijalankan.

Ulangi langkah-langkah sebelumnya saat perubahan dilakukan pada model.

Model Identity

Jenis entitas

Model ini Identity terdiri dari jenis entitas berikut.

Jenis entitas Deskripsi
User Mewakili pengguna.
Role Mewakili peran.
UserClaim Mewakili klaim yang dimiliki pengguna.
UserToken Mewakili token autentikasi untuk pengguna.
UserLogin Mengaitkan pengguna dengan login.
RoleClaim Mewakili klaim yang diberikan kepada semua pengguna dalam peran.
UserRole Entitas gabungan yang mengaitkan pengguna dan peran.

Hubungan jenis entitas

Jenis entitas terkait satu sama lain dengan cara berikut:

  • Masing-masing User dapat memiliki banyak UserClaims.
  • Masing-masing User dapat memiliki banyak UserLogins.
  • Masing-masing User dapat memiliki banyak UserTokens.
  • Masing-masing Role dapat memiliki banyak terkait RoleClaims.
  • Masing-masing User dapat memiliki banyak terkait Roles, dan masing-masing Role dapat dikaitkan dengan banyak Users. Ini adalah hubungan banyak ke banyak yang memerlukan tabel gabungan dalam database. Tabel gabungan diwakili oleh UserRole entitas.

Konfigurasi model default

Identity menentukan banyak kelas konteks yang mewarisi dari DbContext untuk mengonfigurasi dan menggunakan model. Konfigurasi ini dilakukan menggunakan EF Core CODE First Fluent API dalam OnModelCreating metode kelas konteks. Konfigurasi defaultnya adalah:

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

Jenis generik model

Identitymenentukan jenis Common Language Runtime (CLR) default untuk setiap jenis entitas yang tercantum di atas. Semua jenis ini diawali dengan Identity:

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

Daripada menggunakan jenis ini secara langsung, jenisnya dapat digunakan sebagai kelas dasar untuk jenis aplikasi sendiri. Kelas DbContext yang ditentukan oleh Identity bersifat umum, sehingga jenis CLR yang berbeda dapat digunakan untuk satu atau beberapa jenis entitas dalam model. Jenis generik ini juga memungkinkan User jenis data kunci primer (PK) diubah.

Saat menggunakan Identity dengan dukungan untuk peran, IdentityDbContext kelas harus digunakan. Contohnya:

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

Anda juga dapat menggunakan Identity tanpa peran (hanya klaim), dalam hal ini IdentityUserContext<TUser> kelas harus digunakan:

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

Menyesuaikan model

Titik awal untuk kustomisasi model adalah berasal dari jenis konteks yang sesuai. Lihat bagian Jenis generik model. Jenis konteks ini biasanya disebut ApplicationDbContext dan dibuat oleh templat ASP.NET Core.

Konteks digunakan untuk mengonfigurasi model dengan dua cara:

  • Menyediakan entitas dan jenis kunci untuk parameter jenis generik.
  • Mengambil alih OnModelCreating untuk mengubah pemetaan jenis ini.

Saat mengambil alih OnModelCreating, base.OnModelCreating harus dipanggil terlebih dahulu; konfigurasi penimpaan harus dipanggil berikutnya. EF Core umumnya memiliki kebijakan last-one-wins untuk konfigurasi. Misalnya, jika ToTable metode untuk jenis entitas dipanggil terlebih dahulu dengan satu nama tabel dan kemudian lagi nanti dengan nama tabel yang berbeda, nama tabel dalam panggilan kedua digunakan.

CATATAN: Jika DbContext tidak berasal dari IdentityDbContext, AddEntityFrameworkStores mungkin tidak menyimpulkan jenis POCO yang benar untuk TUserClaim, TUserLogin, dan TUserToken. Jika AddEntityFrameworkStores tidak menyimpulkan jenis POCO yang benar, solusinya adalah langsung menambahkan jenis yang benar melalui services.AddScoped<IUser/RoleStore<TUser> dan UserStore<...>>.

Data pengguna kustom

Data pengguna kustom didukung dengan mewarisi dari IdentityUser. Ini adalah kebiasaan untuk memberi nama jenis ApplicationUserini :

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

ApplicationUser Gunakan jenis sebagai argumen generik untuk konteks:

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

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

Tidak perlu mengambil alih OnModelCreating di ApplicationDbContext kelas. EF CoreCustomTag memetakan properti menurut konvensi. Namun, database perlu diperbarui untuk membuat kolom baru CustomTag . Untuk membuat kolom, tambahkan migrasi, lalu perbarui database seperti yang dijelaskan di Identity dan EF Core Migrasi.

Perbarui Pages/Shared/_LoginPartial.cshtml dan ganti IdentityUser dengan ApplicationUser:

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

Perbarui Areas/Identity/IdentityHostingStartup.cs atau Startup.ConfigureServices dan ganti IdentityUser dengan ApplicationUser.

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

AddDefaultIdentity Panggilan setara dengan kode berikut:

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 disediakan sebagai Razor Pustaka Kelas. Untuk informasi selengkapnya, lihat Perancah Identity dalam proyek ASP.NET Core. Akibatnya, kode sebelumnya memerlukan panggilan ke AddDefaultUI. Identity Jika perancah digunakan untuk menambahkan Identity file ke proyek, hapus panggilan ke AddDefaultUI. Untuk informasi selengkapnya, lihat:

Mengubah jenis kunci primer

Perubahan pada tipe data kolom PK setelah database dibuat bermasalah pada banyak sistem database. Mengubah PK biasanya melibatkan penghapusan dan pembuatan ulang tabel. Oleh karena itu, jenis kunci harus ditentukan dalam migrasi awal saat database dibuat.

Ikuti langkah-langkah berikut untuk mengubah jenis PK:

  1. Jika database dibuat sebelum perubahan PK, jalankan Drop-Database (PMC) atau dotnet ef database drop (.NET CLI) untuk menghapusnya.

  2. Setelah mengonfirmasi penghapusan database, hapus migrasi awal dengan Remove-Migration (PMC) atau dotnet ef migrations remove (.NET CLI).

  3. ApplicationDbContext Perbarui kelas untuk berasal dari IdentityDbContext<TUser,TRole,TKey>. Tentukan jenis kunci baru untuk TKey. Misalnya, untuk menggunakan Guid jenis kunci:

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

    Dalam kode sebelumnya, kelas IdentityUser<TKey> generik dan IdentityRole<TKey> harus ditentukan untuk menggunakan jenis kunci baru.

    Startup.ConfigureServices harus diperbarui untuk menggunakan pengguna generik:

    services.AddDefaultIdentity<IdentityUser<Guid>>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
    
  4. Jika kelas kustom ApplicationUser sedang digunakan, perbarui kelas untuk mewarisi dari IdentityUser. Contohnya:

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

    Perbarui ApplicationDbContext untuk mereferensikan kelas kustom ApplicationUser :

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

    Daftarkan kelas konteks database kustom saat menambahkan Identity layanan di Startup.ConfigureServices:

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

    Jenis data kunci primer disimpulkan dengan menganalisis DbContext objek.

    Identity disediakan sebagai Razor Pustaka Kelas. Untuk informasi selengkapnya, lihat Perancah Identity dalam proyek ASP.NET Core. Akibatnya, kode sebelumnya memerlukan panggilan ke AddDefaultUI. Identity Jika perancah digunakan untuk menambahkan Identity file ke proyek, hapus panggilan ke AddDefaultUI.

  5. Jika kelas kustom ApplicationRole sedang digunakan, perbarui kelas untuk mewarisi dari IdentityRole<TKey>. Contohnya:

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

    Perbarui ApplicationDbContext untuk mereferensikan kelas kustom ApplicationRole . Misalnya, kelas berikut mereferensikan kustom ApplicationUser dan kustom 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)
        {
        }
    }
    

    Daftarkan kelas konteks database kustom saat menambahkan Identity layanan di 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);
    }
    

    Jenis data kunci primer disimpulkan dengan menganalisis DbContext objek.

    Identity disediakan sebagai Razor Pustaka Kelas. Untuk informasi selengkapnya, lihat Perancah Identity dalam proyek ASP.NET Core. Akibatnya, kode sebelumnya memerlukan panggilan ke AddDefaultUI. Identity Jika perancah digunakan untuk menambahkan Identity file ke proyek, hapus panggilan ke AddDefaultUI.

Menambahkan properti navigasi

Mengubah konfigurasi model untuk hubungan bisa lebih sulit daripada membuat perubahan lain. Perawatan harus diambil untuk menggantikan hubungan yang ada daripada membuat hubungan tambahan baru. Secara khusus, hubungan yang diubah harus menentukan properti kunci asing (FK) yang sama dengan hubungan yang ada. Misalnya, hubungan antara Users dan UserClaims , secara default, ditentukan sebagai berikut:

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

FK untuk hubungan ini ditentukan sebagai UserClaim.UserId properti . HasMany dan WithOne dipanggil tanpa argumen untuk membuat hubungan tanpa properti navigasi.

Tambahkan properti navigasi ke ApplicationUser yang memungkinkan terkait UserClaims untuk dirujuk dari pengguna:

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

untuk TKeyIdentityUserClaim<TKey> adalah jenis yang ditentukan untuk PK pengguna. Dalam hal ini, TKey adalah string karena default sedang digunakan. Ini bukan jenis PK untuk UserClaim jenis entitas.

Sekarang setelah properti navigasi ada, properti harus dikonfigurasi di 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();
        });
    }
}

Perhatikan bahwa hubungan dikonfigurasi persis seperti sebelumnya, hanya dengan properti navigasi yang ditentukan dalam panggilan ke HasMany.

Properti navigasi hanya ada dalam model EF, bukan database. Karena FK untuk hubungan tidak berubah, perubahan model semacam ini tidak mengharuskan database diperbarui. Ini dapat diperiksa dengan menambahkan migrasi setelah melakukan perubahan. Metode Up dan Down kosong.

Menambahkan semua properti navigasi Pengguna

Menggunakan bagian di atas sebagai panduan, contoh berikut mengonfigurasi properti navigasi searah untuk semua hubungan pada Pengguna:

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

Menambahkan properti navigasi Pengguna dan Peran

Menggunakan bagian di atas sebagai panduan, contoh berikut mengonfigurasi properti navigasi untuk semua hubungan pada Pengguna dan Peran:

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

    }
}

Catatan:

  • Contoh ini juga mencakup UserRole entitas gabungan, yang diperlukan untuk menavigasi hubungan banyak ke banyak dari Pengguna ke Peran.
  • Ingatlah untuk mengubah jenis properti navigasi untuk mencerminkan bahwa Application{...} jenis sekarang digunakan alih-alih Identity{...} jenis.
  • Ingatlah untuk menggunakan Application{...} dalam definisi generik ApplicationContext .

Menambahkan semua properti navigasi

Menggunakan bagian di atas sebagai panduan, contoh berikut mengonfigurasi properti navigasi untuk semua hubungan pada semua jenis entitas:

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

Menggunakan kunci komposit

Bagian sebelumnya menunjukkan perubahan jenis kunci yang digunakan dalam Identity model. Mengubah Identity model kunci untuk menggunakan kunci komposit tidak didukung atau direkomendasikan. Menggunakan kunci komposit dengan Identity melibatkan perubahan bagaimana Identity kode manajer berinteraksi dengan model. Penyesuaian ini berada di luar cakupan dokumen ini.

Mengubah nama dan faset tabel/kolom

Untuk mengubah nama tabel dan kolom, panggil base.OnModelCreating. Kemudian, tambahkan konfigurasi untuk mengambil alih salah satu default. Misalnya, untuk mengubah nama semua Identity tabel:

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

Contoh-contoh ini menggunakan jenis default Identity . Jika menggunakan jenis aplikasi seperti ApplicationUser, konfigurasikan jenis tersebut alih-alih jenis default.

Contoh berikut mengubah beberapa nama kolom:

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

Beberapa jenis kolom database dapat dikonfigurasi dengan faset tertentu (misalnya, panjang maksimum string yang diizinkan). Contoh berikut mengatur panjang maksimum kolom untuk beberapa string properti dalam model:

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

Memetakan ke skema yang berbeda

Skema dapat berulah secara berbeda di seluruh penyedia database. Untuk SQL Server, defaultnya adalah membuat semua tabel dalam skema dbo . Tabel dapat dibuat dalam skema yang berbeda. Contohnya:

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

    modelBuilder.HasDefaultSchema("notdbo");
}

Pemuatan lambat

Di bagian ini, dukungan untuk proksi pemuatan malas dalam Identity model ditambahkan. Pemuatan malas berguna karena memungkinkan properti navigasi digunakan tanpa terlebih dahulu memastikannya dimuat.

Jenis entitas dapat dibuat cocok untuk pemuatan malas dalam beberapa cara, seperti yang EF Core dijelaskan dalam dokumentasi. Untuk kesederhanaan, gunakan proksi pemuatan malas, yang memerlukan:

Contoh berikut menunjukkan panggilan UseLazyLoadingProxies di Startup.ConfigureServices:

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

Lihat contoh sebelumnya untuk panduan tentang menambahkan properti navigasi ke jenis entitas.

Sumber Daya Tambahan: