キーなしエンティティ型

Note

この機能は、クエリの種類の名前の下に追加されました。 その後、キーなしエンティティ型に名前が変更されました。

EF Core モデルには、通常のエンティティ型に加えて "キーなしエンティティ型" を含めることができます。これを使用すると、キー値が含まれていないデータに対してデータベース クエリを実行できます。

キーなしエンティティ型の定義

キーなしエンティティ型は、次のように定義できます。

[Keyless]
public class BlogPostsCount
{
    public string BlogName { get; set; }
    public int PostCount { get; set; }
}

キーなしエンティティ型の特性

キーなしエンティティ型では、継承マッピングやナビゲーション プロパティなど、通常のエンティティ型と同じマッピング機能の多くがサポートされています。 リレーショナル ストアでは、Fluent API メソッドまたはデータ注釈を使用して、対象のデータベース オブジェクトと列を構成できます。

ただし、これらは以下の点で、通常のエンティティ型とは異なります。

  • キーを定義することはできません。
  • DbContext の変更に対して追跡されないため、データベースで挿入、更新、または削除されることはありません。
  • 慣例により検出されません。
  • ナビゲーション マッピング機能のサブセットのみをサポートしています。具体的には、次のようになります。
    • これらはリレーションシップのプリンシパル End としては機能しません。
    • 所有しているエンティティに対するナビゲーションが含まれていない可能性があります
    • これらには、標準エンティティを指す参照ナビゲーションプロパティのみを含めることができます。
    • エンティティには、キーなしエンティティ型へのナビゲーション プロパティを含めることはできません。
  • [Keyless] データ注釈または .HasNoKey() メソッド呼び出しを使用して構成する必要があります。
  • "クエリの定義" にマップされる。 定義クエリは、キーなしエンティティ型のデータソースとして機能するモデルで宣言されたクエリです。
  • 階層を持つことができますが、TPH としてマップする必要があります。
  • テーブル分割またはエンティティ分割を使用できません。

使用シナリオ

キーなしエンティティ型の主な使用シナリオの一部を次に示します。

  • SQL クエリのための戻り値の型として機能します。
  • 主キーが含まれていないデータベース ビューへのマッピング。
  • 主キーが定義されていないテーブルへのマッピング。
  • モデルで定義されているクエリへのマッピング。

データベース オブジェクトへのマッピング

キーなしエンティティ型のデータベース オブジェクトへのマッピングは、ToTable または ToView Fluent API を使用して実現されます。 EF Core の観点から見ると、このメソッドで指定されるデータベース オブジェクトはビューであり、読み取り専用のクエリ ソースとして扱われ、update、insert、delete の各操作の対象にすることはできません。 ただし、これはデータベース オブジェクトが実際にデータベース ビューである必要があることを意味するわけではありません。 または、読み取り専用として扱われるデータベース テーブルにすることもできます。 逆に、通常のエンティティ型の場合、EF Core は、ToTable メソッドで指定されたデータベース オブジェクトをテーブルとして扱うことができることを前提としています。つまり、クエリ ソースとして使用でき、update、delete、および insert 操作の対象にすることができます。 実際には、ToTable でデータベース ビューの名前を指定できます。また、ビューがデータベースで更新可能になるように構成されていれば、すべてが正常に動作します。

次の例では、キーなしエンティティ型を使用してデータベース ビューに対してクエリを実行する方法を示します。

ヒント

この記事のサンプルは GitHub で確認できます。

まず、単純なブログと投稿のモデルを定義します。

public class Blog
{
    public int BlogId { get; set; }
    public string Name { get; set; }
    public string Url { get; set; }
    public ICollection<Post> Posts { get; set; }
}

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

次に、各ブログに関連付けられている投稿の数を照会できる単純なデータベース ビューを定義します。

db.Database.ExecuteSqlRaw(
    @"CREATE VIEW View_BlogPostCounts AS
                SELECT b.Name, Count(p.PostId) as PostCount
                FROM Blogs b
                JOIN Posts p on p.BlogId = b.BlogId
                GROUP BY b.Name");

次に、データベース ビューの結果を保持するクラスを定義します。

public class BlogPostsCount
{
    public string BlogName { get; set; }
    public int PostCount { get; set; }
}

次に、HasNoKey API を使用して、OnModelCreating にキーなしエンティティ型を構成します。 Fluent 構成 API を使用して、キーなしエンティティ型のマッピングを構成します。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<BlogPostsCount>(
            eb =>
            {
                eb.HasNoKey();
                eb.ToView("View_BlogPostCounts");
                eb.Property(v => v.BlogName).HasColumnName("Name");
            });
}

次に、DbSet<T> を含めるように DbContext を構成します。

public DbSet<BlogPostsCount> BlogPostCounts { get; set; }

最後に、標準的な方法でデータベース ビューに対してクエリを実行できます。

var postCounts = db.BlogPostCounts.ToList();

foreach (var postCount in postCounts)
{
    Console.WriteLine($"{postCount.BlogName} has {postCount.PostCount} posts.");
    Console.WriteLine();
}

ヒント

また、この型に対するクエリのルートとして機能するコンテキスト レベルのクエリ プロパティ (DbSet) も定義されています。

ヒント

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