Identity ASP.NET Core でのモデルのカスタマイズ

提供 : Kers

ASP.NET Core Identity は、Core アプリでユーザー アカウントを管理および格納するための ASP.NET を提供します。 Identity は、認証メカニズムとして [個々の ユーザー アカウント] が選択されている場合にプロジェクトに追加されます。 既定では、 Identity は、EF (EF) Entity Frameworkモデルを使用します。 この記事では、モデルをカスタマイズする方法について説明 Identity します。

Identity および EF Core 移行

モデルを調べる前に、EF Core Migrations を使用してデータベース Identity を作成 および更新する方法を理解すると便利です。 一番上のレベルでは、プロセスは次の処理になります。

  1. コード でデータ モデル を定義または更新します
  2. 移行を追加して、このモデルをデータベースに適用できる変更に変換します。
  3. 移行が意図を正しく表すのを確認します。
  4. 移行を適用して、モデルと同期するデータベースを更新します。
  5. 手順 1 から 4 を繰り返して、モデルをさらに絞り込み、データベースの同期を維持します。

移行を追加して適用するには、次のいずれかの方法を使用します。

  • [パッケージ マネージャー コンソール (PMC) ウィンドウ (使用している場合Visual Studio。 詳細については、「PMC ツール のEF Coreを参照してください
  • コマンド .NET Core CLIを使用している場合は、次のコマンドを実行します。 詳細については、「 EF Core .net コマンドラインツール」を参照してください。
  • アプリの実行時に、エラーページの [ 移行の適用 ] ボタンをクリックします。

ASP.NET Core には、開発時エラーページハンドラーがあります。 ハンドラーは、アプリの実行時に移行を適用できます。 実稼働アプリでは、通常、移行から SQL スクリプトを生成し、制御されたアプリとデータベースの配置の一部としてデータベースの変更をデプロイします。

を使用する新しいアプリを作成すると Identity 、上記の手順 1. と 2. は既に完了しています。 つまり、初期データモデルが既に存在し、初期移行がプロジェクトに追加されています。 初期移行は、引き続きデータベースに適用する必要があります。 最初の移行は、次のいずれかの方法を使用して適用できます。

  • Update-DatabasePMC でを実行します。
  • dotnet ef database updateコマンドシェルでを実行します。
  • アプリの実行時に、エラーページの [ 移行の適用 ] ボタンをクリックします。

モデルが変更されたときに、上記の手順を繰り返します。

Identityモデル

エンティティの種類

モデルは、 Identity 次のエンティティ型で構成されます。

エンティティ型 説明
User ユーザーを表します。
Role ロールを表します。
UserClaim ユーザーが所有するクレームを表します。
UserToken ユーザーの認証トークンを表します。
UserLogin ユーザーをログインに関連付けます。
RoleClaim ロール内のすべてのユーザーに付与される要求を表します。
UserRole ユーザーとロールを関連付ける結合エンティティ。

エンティティ型のリレーションシップ

エンティティ 型は、 次の方法で互いに関連付け合います。

  • User には、多数の を含めできます UserClaims
  • User には、多数の を含めできます UserLogins
  • User には、多数の を含めできます UserTokens
  • 各 には Role 、多数の が関連付けられている RoleClaims 場合があります。
  • User には多数の が関連 Roles 付けられている場合があります。また、各 は Role 多数の に関連付けできます Users 。 これは、データベース内の結合テーブルを必要とする多対多リレーションシップです。 結合テーブルは エンティティによって表 UserRole されます。

既定のモデル構成

Identity は、モデル を構成して使用 するために DbContext から継承する多くのコンテキスト クラスを定義します。 この構成は、コンテキスト クラスのonModelCreating EF Core Code First Fluent APIを使用して行われます。 既定の構成は次の場合です。

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

ジェネリック型のモデル化

Identity では、上記 のエンティティ型ごとに 既定の共通言語ランタイム (CLR) 型が定義されます。 これらの型の先頭には、 が付くものがあります Identity

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

これらの型を直接使用するのではなく、アプリ独自の型の基本クラスとして使用できます。 で定義されるクラスはジェネリックで、モデル内の 1 つ以上のエンティティ型に異なる CLR 型 DbContext Identity を使用できます。 これらのジェネリック型を使用すると、 User 主キー (PK) データ型を変更することもできます。

ロールのサポートを使用する場合は Identity 、 IdentityDbContext クラスを使用する必要があります。 次に例を示します。

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

Identityロールなし (要求のみ) を使用することもできます。この場合、 IdentityUserContext<TUser> クラスを使用する必要があります。

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

モデルをカスタマイズする

モデルのカスタマイズの開始点は、適切なコンテキスト型から派生することです。 「 モデルのジェネリック型 」を参照してください。 このコンテキストの種類は通常と呼ばれ、 ApplicationDbContext ASP.NET Core テンプレートによって作成されます。

このコンテキストは、次の2つの方法でモデルを構成するために使用されます。

  • ジェネリック型パラメーターのエンティティ型とキー型を提供します。
  • をオーバーライドし OnModelCreating て、これらの型のマッピングを変更します。

をオーバーライドする場合は OnModelCreating 、最初にを base.OnModelCreating 呼び出す必要があります。次に、オーバーライドする構成を呼び出す必要があります。 EF Core は、一般に、構成のための最後の1つの wins ポリシーを持っています。 たとえば、 ToTable エンティティ型のメソッドが最初に1つのテーブル名で呼び出され、後で別のテーブル名を使用して呼び出された場合、2番目の呼び出しのテーブル名が使用されます。

カスタムユーザーデータ

カスタムユーザーデータ は、から継承することによってサポートされ IdentityUser ます。 この型には、次のような名前が付いてい ApplicationUser ます。

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

この型は、 ApplicationUser コンテキストの汎用引数として使用します。

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

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

クラスでオーバーライドする必要はありません OnModelCreating ApplicationDbContext 。 EF Core は、 CustomTag 規則に従ってプロパティをマップします。 ただし、新しい列を作成するには、データベースを更新する必要があり CustomTag ます。 列を作成するには、「移行」の説明に従って移行を追加し、データベース Identity EF Coreします

Pages/Shared/_LoginPartial.cshtml を更新し、 を に置き IdentityUser 換える ApplicationUser

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

更新 領域/ Identity / Identity HostingStartup.cs または を に Startup.ConfigureServices 置き換え、 を IdentityUser に置き換える ApplicationUser

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

の呼 AddDefaultIdentity び出しは、次のコードと同じです。

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 はクラス ライブラリとして Razor 提供されます。 詳細については、「IdentityASP.NET Core プロジェクトでのスキャフォールディング」を参照してください。 そのため、前のコードでは、 の呼び出しが必要です AddDefaultUI 。 スキャ Identity フォールディングを使用してプロジェクトにファイルを追加 Identity した場合は、 の呼び出しを削除します AddDefaultUI 。 詳細については、次を参照してください。

主キーの種類を変更する

データベースの作成後に PK 列のデータ型を変更すると、多くのデータベース システムで問題が発生します。 通常、PK を変更するには、テーブルの削除と再作成が必要です。 そのため、データベースの作成時の初期移行では、キーの種類を指定する必要があります。

PK の種類を変更するには、次の手順に従います。

  1. PK 変更の前にデータベースが作成された場合は Drop-Database 、(PMC) または dotnet ef database drop (.NET Core CLI) を実行して削除します。

  2. データベースの削除を確認した後、 (PMC) または (.NET Core CLI) を使用して初期 Remove-Migration dotnet ef migrations remove 移行を削除します。

  3. から派生 ApplicationDbContext する クラスを更新します IdentityDbContext<TUser,TRole,TKey> 。 に新しいキーの種類を指定します TKey 。 たとえば、キーの種類を Guid 使用するには、次のコマンドを実行します。

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

    前のコードでは、新しいキー型を使用するには、ジェネリック クラス と IdentityUser<TKey> IdentityRole<TKey> を指定する必要があります。

    Startup.ConfigureServices 汎用ユーザーを使用するには、次のように更新する必要があります。

    services.AddDefaultIdentity<IdentityUser<Guid>>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();
    
  4. カスタムクラスが使用されている場合は ApplicationUser 、継承元のクラスを更新し IdentityUser ます。 次に例を示します。

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

    ApplicationDbContextカスタムクラスを参照するように更新し ApplicationUser ます。

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

    でサービスを追加するときに、カスタムデータベースコンテキストクラスを登録し Identity Startup.ConfigureServices ます。

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

    主キーのデータ型は、 Dbcontext オブジェクトを分析することによって推論されます。

    Identity はクラスライブラリとして提供され Razor ます。 詳細については、「IdentityASP.NET Core プロジェクトでのスキャフォールディング」を参照してください。 そのため、上記のコードでは、を呼び出す必要があり AddDefaultUI ます。 Scaffolder を Identity 使用してファイルをプロジェクトに追加した場合は、への呼び出しを削除し Identity AddDefaultUI ます。

  5. カスタムクラスが使用されている場合は ApplicationRole 、継承元のクラスを更新し IdentityRole<TKey> ます。 次に例を示します。

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

    ApplicationDbContextカスタムクラスを参照するように更新し ApplicationRole ます。 たとえば、次のクラスはカスタム ApplicationUser とカスタムを参照し 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)
        {
        }
    }
    

    でサービスを追加するときに、カスタムデータベースコンテキストクラスを登録し Identity 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);
    }
    

    主キーのデータ型は、 Dbcontext オブジェクトを分析することによって推論されます。

    Identity はクラスライブラリとして提供され Razor ます。 詳細については、「IdentityASP.NET Core プロジェクトでのスキャフォールディング」を参照してください。 そのため、上記のコードでは、を呼び出す必要があり AddDefaultUI ます。 Scaffolder を Identity 使用してファイルをプロジェクトに追加した場合は、への呼び出しを削除し Identity AddDefaultUI ます。

ナビゲーションプロパティの追加

リレーションシップのモデル構成の変更は、他の変更を行うよりも困難になることがあります。 新しい追加のリレーションシップを作成するのではなく、既存のリレーションシップを置き換える必要があります。 特に、変更されたリレーションシップでは、既存のリレーションシップと同じ外部キー (FK) プロパティを指定する必要があります。 たとえば、 と のリレーションシップは Users UserClaims 、既定では次のように指定されます。

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

このリレーションシップの FK は、 プロパティとして指定 UserClaim.UserId されます。 HasManyWithOne は、ナビゲーション プロパティなしでリレーションシップを作成するために引数なしで呼び出されます。

ユーザーから関連付けられた ApplicationUser を参照 UserClaims できる ナビゲーション プロパティを に追加します。

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

TKeyIdentityUserClaim<TKey> 、ユーザーの PK に指定された型です。 この場合、 TKeystring 、既定値が使用されているためです。 エンティティ型 PK 型 UserClaim ではありません。

ナビゲーション プロパティが存在するので、 で構成する必要があります 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();
        });
    }
}

リレーションシップは、 の呼び出しで指定されたナビゲーション プロパティを使用してのみ、以前とまったく同じ方法で構成されているのに注意してください HasMany

ナビゲーション プロパティは EF モデルにのみ存在し、データベースには存在しません。 リレーションシップの FK は変更されないので、この種のモデル変更ではデータベースを更新する必要はありません。 これは、変更後に移行を追加することで確認できます。 メソッド UpDown メソッドは空です。

すべてのユーザー ナビゲーション プロパティを追加する

上記のセクションをガイダンスとして使用して、次の例では User のすべてのリレーションシップの一方向ナビゲーション プロパティを構成します。

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

ユーザーとロールのナビゲーション プロパティを追加する

上記のセクションをガイダンスとして使用して、次の例では、ユーザーとロールのすべてのリレーションシップのナビゲーション プロパティを構成します。

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

    }
}

メモ:

  • この例には、多対多リレーションシップを [ユーザー] から [ロール] に移動するために必要な結合 UserRole エンティティも含まれています。
  • ナビゲーション プロパティの型を変更して、型ではなく型が使用されるのを反映するように Application{...} 注意 Identity{...} してください。
  • ジェネリック定義では を Application{...} 使用してください ApplicationContext

すべてのナビゲーションプロパティの追加

次の例では、上のセクションをガイダンスとして使用して、すべてのエンティティ型のすべてのリレーションシップに対してナビゲーションプロパティを構成します。

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

複合キーを使用する

前のセクションでは、モデルで使用されるキーの種類の変更について説明し Identity ます。 Identity複合キーを使用するようにキーモデルを変更することはサポートされていないか、推奨されません。 で複合キーを使用する場合は、 Identity マネージャーコードがモデルとどのように連携するかを変更する必要が Identity あります。 このカスタマイズについては、このドキュメントでは説明しません。

テーブル/列の名前とファセットの変更

テーブルと列の名前を変更するには、を呼び出し base.OnModelCreating ます。 次に、構成を追加して、既定値を上書きします。 たとえば、すべてのテーブルの名前を変更するには、次のようにし Identity ます。

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

これらの例では、既定の型を使用し Identity ます。 などのアプリの種類を使用する場合は ApplicationUser 、既定の型ではなく、その型を構成します。

次の例では、いくつかの列名を変更します。

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

一部の種類のデータベース列は、特定の ファセット を使用して構成でき string ます (許容される最大長など)。 次の例では、モデルのいくつかのプロパティの列の最大長を設定し string ます。

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

別のスキーマにマップする

スキーマは、データベースプロバイダーによって動作が異なります。 SQL Server の場合、既定では dbo スキーマのすべてのテーブルが作成されます。 テーブルは、別のスキーマで作成できます。 次に例を示します。

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

    modelBuilder.HasDefaultSchema("notdbo");
}

遅延読み込み

このセクションでは、モデルでの遅延読み込みプロキシ Identity のサポートが追加されます。 遅延読み込みは、ナビゲーション プロパティを最初に読み込まなくても使用することができるので便利です。

エンティティ型は、次のドキュメントで説明するように、いくつかの方法で遅延読み込 EF Coreできます。 わかりやすくするために、遅延読み込みプロキシを使用します。このプロキシには次が必要です。

で を呼び出す例を次に UseLazyLoadingProxies 示します Startup.ConfigureServices

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

エンティティ型にナビゲーション プロパティを追加する方法については、前の例を参照してください。

その他のリソース