Visual Studio のテンプレートで認証を「個別のユーザーアカウント」としてプロジェクトを作成すると ASP.NET Identity を使った認証システムが実装されます。それにエンティティを追加する方法を書きます。この記事の例では下の画像の左上の Post エンティティがそれです。
Post というのは投稿という意味です。複数のユーザーが複数の投稿をするアプリで、投稿内容を SQL Server データベースで保持し、Post エンティティクラスを定義して Entity Framework 経由で書き込み、読み出し、編集などを行うという想定です。
ユーザーの誰が投稿を書いたかを識別できるように、上の画像のように Post と ApplicationUser をナビゲーションプロパティで紐づけます。(結果、EF Code First で生成されるデータベースでは、ApplicationUser に該当する dbo.AspNetUsers テーブルの Id 列に、dbo.Posts テーブルの ApplicationUserId 列から FK 制約が張られます)
上の画像の Post エンティティ以外の基本のエンティティクラスとコンテキストクラスは ASP.NET Identity の中で定義済みです。(.NET Framework 版の場合は Models/IdentityModels.cs に ApplicationUser クラスと ApplicationDbContext クラスが含まれていて、その継承元で定義されています)
それに Post エンティティを追加してナビゲーションプロパティを張るには Post クラスを追加するだけではダメで、既存の基本のエンティティクラスとコンテキストクラスにも手を加える必要があります。それをどのようにするかというのがこの記事の話です。
.NET Framework 版の場合は自動生成された Models/IdentityModels.cs にコンテキストクラスとエンティティクラスが定義されていますので、それに手を加えて Migration を実行することになります。
手を加えた Models/IdentityModels.cs のサンプルコードは以下の通りです。コードの中で「追加」とコメントした部分を追加しているだけです。(Core 版は場所が違うので注意。この記事の下の方の説明と画像を見てください)
using System.Data.Entity;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
// 追加
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Mvc5App3.Models
{
// ApplicationUser クラスにさらにプロパティを追加すると、ユーザーの
// プロファイル データを追加できます。詳細については、
// https://go.microsoft.com/fwlink/?LinkID=317594 を参照してください。
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(
UserManager<ApplicationUser> manager)
{
// authenticationType が CookieAuthenticationOptions
// .AuthenticationType で定義されているものと一致して
// いる必要があります
var userIdentity = await manager.CreateIdentityAsync(
this, DefaultAuthenticationTypes.ApplicationCookie);
// ここにカスタム ユーザー クレームを追加します
return userIdentity;
}
// 追加
public virtual IList<Post> Posts { get; set; }
}
// 追加
public class Post
{
[Key, Required]
public int PostId { get; set; }
[Required, MaxLength(128)]
public string Title { get; set; }
[Required, MaxLength(1024)]
public string Content { get; set; }
[Required, ForeignKey(nameof(User))]
public string ApplicationUserId { get; set; }
public virtual ApplicationUser User { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
// 追加
public DbSet<Post> Posts { get; set; }
}
}
上のコードの通りコンテキストクラスとエンティティクラスに手を加えてから Migration 操作 (Add-Migration, Update-Database) を行うと以下のテーブルが SQL Server データベースに生成されます。
dbo.Posts テーブルの ApplicationUserId 列から ApplicationUser に相当するテーブル dbo.AspNetUsers の Id 列に FK 制約が張られます。もちろん Models/IdentityModels.cs のコードに定義したナビゲーションプロパティも期待通り働きます。
以上は .NET Framework 版の話です。Core 版の場合も上記とほぼ同じようにして既存のデータベースに Posts テーブルを追加し、既存の AspNetUsers テーブルの Id 列に FK 制約を張ることができます。
ただし、Core 版では、Visual Studio のテンプレートで「個別のユーザーアカウント」を選んでプロジェクトを作成しても、ASP.NET Core Identity 関係のソースコードは含まれないことに注意してください。Razor Class Library (RCL) として実装されますのでコンテキストクラスとエンティティクラスに手を加えることができません。
なので、Core 版でこの記事のようなことを行う場合は、認証なしでプロジェクトを作成した後でスキャフォールディング機能を使ってソースコードと共に ASP.NET Core Identity を実装するのが良さそうです。
そうした場合、コンテキストクラスとエンティティクラスは下の画像の場所に生成されます。
上の画像ではクラス名が ApplicationDbContext, ApplicationUser となっていますが、デフォルトの設定のまま進めると Application の部分がプロジェクト名になります。スキャフォールディングを行う際に任意に設定できますので、この記事では .NET Framework 版と同じ名前に設定しています。
上の画像の ApplicationDbContext, ApplicationUser クラスに、上のサンプルコードで書いたようにナビゲーションプロパティを追加します。
Post クラスはプロジェクトの既存の Models フォルダにクラスファイルを追加してそれに定義するのが良いと思います。