モデルの作成と構成

EF Core では、メタデータ "モデル" を使って、アプリケーションのエンティティ型が基になるデータベースにどのようにマップされるかを記述します。 このモデルは、一般的なパターンを探すヒューリスティックという一連の"規則"を使用して構築されています。 その後、OnModelCreatingマッピング属性 ("データ注釈" とも呼ばれます)ModelBuilder メソッド (Fluent API とも呼ばれます) の呼び出しを使用して、モデルをカスタマイズできます。どちらも規則によって実行される構成をオーバーライドします。

ほとんどの構成は、いずれかのデータ ストアを対象とするモデルに適用できます。 プロバイダーは、特定のデータ ストアに固有の構成を有効にすることもできます。また、サポートされていない構成や適用できない構成を無視することもできます。 プロバイダー固有の構成については、「データベース プロバイダー」セクションを参照してください。

ヒント

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

fluent API を使用してモデルを構成する

派生コンテキストで OnModelCreating メソッドをオーバーライドし、Fluent API を使用してモデルを構成できます。 これは最も強力な構成方法であり、エンティティ クラスを変更しなくても構成を指定できます。 Fluent API 構成には一番上の優先度が与えられ、規則やデータ注釈をオーバーライドします。 構成はメソッドが呼び出される順序で適用され、競合がある場合は、最後の呼び出しが以前に指定した構成をオーバーライドします。

using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.FluentAPI.Required;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    #region Required
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .IsRequired();
    }
    #endregion
}

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

ヒント

モデル内の複数のオブジェクトに同じ構成を適用する場合は、「一括構成」を参照してください。

構成のグループ化

OnModelCreating メソッドのサイズを小さくするため、エンティティ型のすべての構成を、IEntityTypeConfiguration<TEntity> を実装する個別のクラスに抽出できます。

public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder
            .Property(b => b.Url)
            .IsRequired();
    }
}

その後、OnModelCreating から Configure メソッドを呼び出すだけです。

new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());

アセンブリ内のすべての構成の適用

特定のアセンブリ内の IEntityTypeConfiguration を実装する型で指定されたすべての構成を適用できます。

modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);

注意

構成が適用される順序は定義されていないため、このメソッドは順序が重要でない場合にのみ使用する必要があります。

エンティティ型での EntityTypeConfigurationAttribute の使用

明示的に Configure を呼び出す代わりに、エンティティ型に対して EntityTypeConfigurationAttribute を配置することで、EF Core が適切な構成を見つけて使用できるようにすることができます。 例:

[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Isbn { get; set; }
}

この属性は、Book エンティティ型がモデルに含まれている場合、指定した IEntityTypeConfiguration の実装が EF Core で使用されることを意味します。 エンティティ型は、通常のいずれかのメカニズムを使用してモデルに含められます。 たとえば、エンティティ型に対して DbSet<TEntity> を使用します。

public class BooksContext : DbContext
{
    public DbSet<Book> Books { get; set; }

    //...

または、OnModelCreating において登録します。

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

注意

EntityTypeConfigurationAttribute 型は、アセンブリ内で自動的に検出されることはありません。 エンティティ型の属性が検出される前に、エンティティ型をモデルに追加する必要があります。

データ注釈を使用してモデルを構成する

クラスやプロパティに特定の属性 ("データ注釈" と呼ばれています) を適用することもできます。 データ注釈は規則をオーバーライドしますが、Fluent API 構成で上書きされます。

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.DataAnnotations.Annotations;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
}

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

    [Required]
    public string Url { get; set; }
}

組み込みの規約

EF Core には、既定で有効になっている多くのモデル構築規則が含まれています。 これらはすべて、IConvention インターフェイスを実装するクラスの一覧にあります。 ただし、この一覧には、サードパーティーのデータベース プロバイダーとプラグインによって導入された規則は含まれていません。

アプリケーションでは、これらの規則のいずれかを削除または置き換えたり、EF ですぐに認識されないパターンの構成を適用する新しいカスタム規則を追加したりできます。

ヒント

下で示すコードは ModelBuildingConventionsSample.cs のものです。

既存の規則の削除

組み込み規則の 1 つがアプリケーションに適していないことがあり、その場合は削除できます。

ヒント

モデルで構成にマッピング属性 (データ注釈とも呼ばれます) が使われていない場合は、名前の末尾が AttributeConvention のすべての規則を安全に削除して、モデルの構築を高速化できます。

例: 外部キー列のインデックスを作成しない

通常、外部キー (FK) 列のインデックスを作成することは理にかなっており、そのため次の規則が組み込まれています: ForeignKeyIndexConventionBlogAuthor に対するリレーションシップを持つ Post エンティティ型のモデル デバッグ ビューを見ると、BlogId FK 用と AuthorId FK 用に 2 つのインデックスが作成されていることがわかります。

  EntityType: Post
    Properties:
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      AuthorId (no field, int?) Shadow FK Index
      BlogId (no field, int) Shadow Required FK Index
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade
    Indexes:
      AuthorId
      BlogId

ただし、インデックスにはオーバーヘッドがあり、すべての FK 列にインデックスを作成するのが常に適切であるとは限りません。 これを実現するには、モデルを構築するときに ForeignKeyIndexConvention を削除できます。

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}

ここで、Post のモデルのデバッグ ビューを見ると、FK のインデックスが作成されていないことがわかります。

  EntityType: Post
    Properties:
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      AuthorId (no field, int?) Shadow FK
      BlogId (no field, int) Shadow Required FK
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade

必要に応じて、IndexAttribute または OnModelCreating の構成のどちらかを使用して、外部キー列にインデックスを明示的に作成できます。

デバッグ ビュー

モデル ビルダーのデバッグ ビューには、IDE のデバッガーでアクセスできます。 たとえば、Visual Studio の場合は次のようになります。

Accessing the model builder debug view from the Visual Studio debugger

また、コードから直接アクセスすることもできます。たとえば、コンソールにデバッグ ビューを送信することができます。

Console.WriteLine(context.Model.ToDebugString());

デバッグ ビューには、短い形式と長い形式があります。 長い形式にはすべての注釈も含まれています。これは、リレーショナルまたはプロバイダー固有のメタデータを表示する必要がある場合に便利です。 長いビューには、コードからもアクセスできます。

Console.WriteLine(context.Model.ToDebugString(MetadataDebugStringOptions.LongDefault));