エンティティの種類

ある型の DbSet をコンテキストに含めることは、それが EF Core のモデルに含められることを意味します。このような型は、通常 "エンティティ" といいます。 EF Core は、データベースに対してエンティティ インスタンスの読み取りと書き込みを行うことができます。リレーショナル データベースを使用している場合、EF Core では移行によってエンティティのテーブルを作成することができます。

モデルへの型の包含

慣例により、コンテキストの DbSet プロパティで公開される型は、エンティティとしてモデルに含められます。 OnModelCreating メソッドに指定されているエンティティ型も、他の検出されたエンティティ型のナビゲーション プロパティを再帰的に調べることによって検出されるすべての型と同様に含められます。

以下のコード サンプルでは、すべての型が含められます。

  • Blog は、コンテキストの DbSet プロパティで公開されているため、含められます。
  • Post は、Blog.Posts ナビゲーション プロパティ経由で検出されるため、含められます。
  • AuditEntry (OnModelCreating で指定されるため)。
internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AuditEntry>();
    }
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }

    public List<Post> Posts { get; set; }
}

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public Blog Blog { get; set; }
}

public class AuditEntry
{
    public int AuditEntryId { get; set; }
    public string Username { get; set; }
    public string Action { get; set; }
}

モデルから型を除外する

モデルに型を含めない場合は、その型を除外することができます。

[NotMapped]
public class BlogMetadata
{
    public DateTime LoadedFromDatabase { get; set; }
}

移行からの除外

同じエンティティ型を複数の DbContext 型にマップすると便利な場合があります。 これは、境界付けられたコンテキストを使用する場合に特に当てはまります。境界付けられたコンテキストでは、それぞれ異なる DbContext 型を持つことが一般的です。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<IdentityUser>()
        .ToTable("AspNetUsers", t => t.ExcludeFromMigrations());
}

この構成では、移行によって AspNetUsers テーブルは作成されませんが、IdentityUser は引き続きモデルに含められ、通常どおり使用できます。

移行を使用してテーブルの管理を再開する必要がある場合は、AspNetUsers が除外されない新しい移行を作成する必要があります。 次の移行では、テーブルに加えられた変更がすべて含まれるようになります。

テーブル名

慣例により、各エンティティ型は、エンティティを公開する DbSet プロパティと同じ名前を持つデータベース テーブルにマッピングされるように設定されます。 指定されたエンティティに DbSet が存在しない場合は、クラス名が使用されます。

テーブル名は手動で構成できます。

[Table("blogs")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

テーブル スキーマ

リレーショナル データベースを使用する場合、テーブルは慣例により、データベースの既定のスキーマで作成されます。 たとえば、Microsoft SQL Server は dbo スキーマを使用します (SQLite はスキーマをサポートしていません)。

次のように、特定のスキーマで作成されるテーブルを構成できます。

[Table("blogs", Schema = "blogging")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

各テーブルのスキーマを指定するのではなく、Fluent API を使用してモデル レベルで既定のスキーマを定義することもできます。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasDefaultSchema("blogging");
}

既定のスキーマを設定すると、シーケンスなどの他のデータベース オブジェクトも影響を受けることに注意してください。

マッピングの表示

エンティティ型は Fluent API を使用してデータベース ビューにマップできます。

Note

EF では、参照されているビューが既にデータベースに存在することを前提としており、ビューは移行時に自動的に作成されません。

modelBuilder.Entity<Blog>()
    .ToView("blogsView", schema: "blogging");

ビューへのマッピングにより、既定のテーブル マッピングが削除されますが、エンティティ型をテーブルに明示的にマップすることもできます。 この場合、クエリにはクエリ マッピングが使用され、更新にはテーブル マッピングが使用されます。

ヒント

メモリ内プロバイダーを使用して、ビューにマップされたキーなしエンティティ型をテストするには、ToInMemoryQuery を使用してクエリにマップします。 詳細については、メモリ内プロバイダーのドキュメントを参照してください。

テーブル値関数のマッピング

データベース内のテーブルではなくテーブル値関数 (TVF) にエンティティ型をマップすることができます。 これを説明するために、複数の投稿を含むブログを表す別のエンティティを定義してみましょう。 この例では、エンティティはキーなしですが、そうである必要はありません。

public class BlogWithMultiplePosts
{
    public string Url { get; set; }
    public int PostCount { get; set; }
}

次に、データベースに次のテーブル値関数を作成します。これにより、複数の投稿を含むブログと、これらの各ブログに関連付けられている投稿の数が返されます。

CREATE FUNCTION dbo.BlogsWithMultiplePosts()
RETURNS TABLE
AS
RETURN
(
    SELECT b.Url, COUNT(p.BlogId) AS PostCount
    FROM Blogs AS b
    JOIN Posts AS p ON b.BlogId = p.BlogId
    GROUP BY b.BlogId, b.Url
    HAVING COUNT(p.BlogId) > 1
)

次に、次のようにしてエンティティ BlogWithMultiplePosts をこの関数にマップできます。

modelBuilder.Entity<BlogWithMultiplePosts>().HasNoKey().ToFunction("BlogsWithMultiplePosts");

Note

エンティティをテーブル値関数にマップするには、関数をパラメーターなしにする必要があります。

規則として、エンティティのプロパティは、TVF によって返される一致する列にマップされます。 TVF によって返される列の名前がエンティティ プロパティと異なる場合は、通常のテーブルにマップする場合と同様に、HasColumnName メソッドを使用してエンティティの列を構成できます。

エンティティ型をテーブル値関数にマップすると、クエリは次のようになります。

var query = from b in context.Set<BlogWithMultiplePosts>()
            where b.PostCount > 3
            select new { b.Url, b.PostCount };

これは次の SQL を生成します。

SELECT [b].[Url], [b].[PostCount]
FROM [dbo].[BlogsWithMultiplePosts]() AS [b]
WHERE [b].[PostCount] > 3

テーブルのコメント

データベース列に対して設定される任意のテキスト コメントを設定でき、データベース内のスキーマを文書化することができます。

[Comment("Blogs managed on the website")]
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

共有型のエンティティ型

同じ CLR 型を使用するエンティティ型は、共有型のエンティティ型と呼ばれます。 これらのエンティティ型は、CLR 型に加えて、共有型のエンティティ型を使用するときに必ず指定する必要がある一意の名前を使用して構成する必要があります。 これは、Set 呼び出しを使用して、対応する DbSet プロパティを実装する必要があることを意味します。

internal class MyContext : DbContext
{
    public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.SharedTypeEntity<Dictionary<string, object>>(
            "Blog", bb =>
            {
                bb.Property<int>("BlogId");
                bb.Property<string>("Url");
                bb.Property<DateTime>("LastUpdated");
            });
    }
}