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:
- Tentukan atau perbarui model data dalam kode.
- Tambahkan Migrasi untuk menerjemahkan model ini ke dalam perubahan yang dapat diterapkan ke database.
- Periksa apakah Migrasi mewakili niat Anda dengan benar.
- Terapkan Migrasi untuk memperbarui database agar sinkron dengan model.
- 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 banyakUserClaims
. - Masing-masing
User
dapat memiliki banyakUserLogins
. - Masing-masing
User
dapat memiliki banyakUserTokens
. - Masing-masing
Role
dapat memiliki banyak terkaitRoleClaims
. - Masing-masing
User
dapat memiliki banyak terkaitRoles
, dan masing-masingRole
dapat dikaitkan dengan banyakUsers
. Ini adalah hubungan banyak ke banyak yang memerlukan tabel gabungan dalam database. Tabel gabungan diwakili olehUserRole
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 ApplicationUser
ini :
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:
Jika database dibuat sebelum perubahan PK, jalankan
Drop-Database
(PMC) ataudotnet ef database drop
(.NET CLI) untuk menghapusnya.Setelah mengonfirmasi penghapusan database, hapus migrasi awal dengan
Remove-Migration
(PMC) ataudotnet ef migrations remove
(.NET CLI).ApplicationDbContext
Perbarui kelas untuk berasal dari IdentityDbContext<TUser,TRole,TKey>. Tentukan jenis kunci baru untukTKey
. Misalnya, untuk menggunakanGuid
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>();
Jika kelas kustom
ApplicationUser
sedang digunakan, perbarui kelas untuk mewarisi dariIdentityUser
. Contohnya:using System; using Microsoft.AspNetCore.Identity; public class ApplicationUser : IdentityUser<Guid> { public string CustomTag { get; set; } }
Perbarui
ApplicationDbContext
untuk mereferensikan kelas kustomApplicationUser
: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
.Jika kelas kustom
ApplicationRole
sedang digunakan, perbarui kelas untuk mewarisi dariIdentityRole<TKey>
. Contohnya:using System; using Microsoft.AspNetCore.Identity; public class ApplicationRole : IdentityRole<Guid> { public string Description { get; set; } }
Perbarui
ApplicationDbContext
untuk mereferensikan kelas kustomApplicationRole
. Misalnya, kelas berikut mereferensikan kustomApplicationUser
dan kustomApplicationRole
: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 TKey
IdentityUserClaim<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-alihIdentity{...}
jenis. - Ingatlah untuk menggunakan
Application{...}
dalam definisi generikApplicationContext
.
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:
- Penginstalan paket Microsoft.EntityFrameworkCore.Proxies.
- Panggilan ke UseLazyLoadingProxies dalam AddDbContext.
- Jenis entitas publik dengan
public virtual
properti navigasi.
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:
ASP.NET Core
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk