モデルの一括構成

ある側面を複数のエンティティ型について同じ方法で構成する必要がある場合は、次の手法を使うと、コードの重複を減らし、ロジックを統合できます。

以下で示すコード スニペットを含む完全なサンプル プロジェクトを参照してください。

OnModelCreating での一括構成

ModelBuilder から返されるすべてのビルダー オブジェクトにより、モデルを構成するオブジェクトへの低レベルのアクセスを提供する Model または Metadata プロパティが公開されます。 特に、モデル内の特定のオブジェクトを反復処理し、それらに共通の構成を適用できるメソッドがあります。

次の例のモデルには、カスタム値型 Currency が含まれています。

public readonly struct Currency
{
    public Currency(decimal amount)
        => Amount = amount;

    public decimal Amount { get; }

    public override string ToString()
        => $"${Amount}";
}

現在の EF プロバイダーは、この型のプロパティをデータベース型にマップする方法を知らないため、既定ではそれは検出されません。 この OnModelCreating のスニペットでは、Currency 型のすべてのプロパティが追加され、サポートされている decimal 型への値コンバーターが構成されます。

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    foreach (var propertyInfo in entityType.ClrType.GetProperties())
    {
        if (propertyInfo.PropertyType == typeof(Currency))
        {
            entityType.AddProperty(propertyInfo)
                .SetValueConverter(typeof(CurrencyConverter));
        }
    }
}
public class CurrencyConverter : ValueConverter<Currency, decimal>
{
    public CurrencyConverter()
        : base(
            v => v.Amount,
            v => new Currency(v))
    {
    }
}

Metadata API の欠点

  • Fluent API とは異なり、モデルに対するすべての変更を明示的に行う必要があります。 たとえば、一部の Currency プロパティが規則によってナビゲーションとして構成されている場合は、それに対するエンティティ型プロパティを追加する前にまず、CLR プロパティを参照するナビゲーションを削除する必要があります。 #9117 によりこれが改善されます。
  • 各変更の後で、規則が実行されます。 規則によって検出されたナビゲーションを削除すると、規則が再度実行され、それを追加し直す可能性があります。 この問題が発生しないようにするには、DelayConventions() を呼び出し、後で返されたオブジェクトを破棄することにより、プロパティが追加された後まで規則を遅延させるか、AddIgnored を使用して無視されるように CLR プロパティをマークする必要があります。
  • この繰り返しが行われた後でエンティティ型が追加される可能性があり、それらには構成が適用されません。 これは、通常、OnModelCreating の末尾にこのコードを配置することで防ぐことができますが、2 つの相互に依存する構成のセットがある場合は、それらを一貫して適用できる順序がない可能性があります。

規則の前の構成

EF Core では、特定の CLR 型に対してマッピング構成を 1 回指定できます。その後、その構成は、モデルで検出されたその型のすべてのプロパティに適用されます。 これが "規則の前のモデル構成" と呼ばれるのは、モデル構築規則を実行できるようになる前に、それによってモデルの側面が構成されるためです。 このような構成は、DbContext から派生した型の ConfigureConventions をオーバーライドすることによって適用されます。

この例では、Currency 型のすべてのプロパティを、値コンバーターを持つように構成する方法を示します。

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder
        .Properties<Currency>()
        .HaveConversion<CurrencyConverter>();
}

そして、この例では、string 型のすべてのプロパティで一部のファセットを構成する方法を示します。

configurationBuilder
    .Properties<string>()
    .AreUnicode(false)
    .HaveMaxLength(1024);

Note

ConfigureConventions からの呼び出しで指定される型には、基本データ型、インターフェイス、またはジェネリック型定義を指定できます。 一致するすべての構成が、最も具体的ではないものから順に適用されます。

  1. インターフェイス
  2. 基本型
  3. ジェネリック型定義
  4. null 非許容値型
  5. 正確な型

重要

規則の前の構成は、一致するオブジェクトがモデルに追加されるとすぐに適用される明示的な構成と同じです。 すべての規則とデータ注釈がオーバーライドされます。 たとえば、上の構成では、すべての文字列外部キー プロパティが、MaxLength が 1024 の非 Unicode として作成されます (それがプリンシパル キーと一致しない場合でも)。

型の無視

規則の前の構成では、型を無視して、エンティティ型またはエンティティ型のプロパティとして規則によって検出されないようにすることもできます。

configurationBuilder
    .IgnoreAny(typeof(IList<>));

型の既定のマッピング

一般に、EF では、プロバイダーによってサポートされていない型のプロパティに値コンバーターを指定しておくと、その型の定数を持つクエリを変換できます。 ただし、この型のプロパティを含まないクエリでは、EF が正しい値コンバーターを見つける方法はありません。 この場合は、DefaultTypeMapping を呼び出して、プロバイダー型のマッピングを追加またはオーバーライドできます。

configurationBuilder
    .DefaultTypeMapping<Currency>()
    .HasConversion<CurrencyConverter>();

規則の前の構成の制限事項

  • この方法では、構成できない側面が多くあります。 #6787 では、これがさらに多くの型に拡張されます。
  • 現在、構成は CLR 型によってのみ決定されます。 #20418 では、カスタム述語が許可されます。
  • この構成は、モデルが作成される前に実行されます。 適用時に競合が発生した場合、例外スタック トレースには ConfigureConventions メソッドが含まれないため、原因を見つけるのが難しいことがあります。

規約

Note

カスタム モデル構築規則は、EF Core 7.0 で導入されました。

EF Core のモデル構築規則は、構築中のモデルに対して行われた変更に基づいてトリガーされるロジックを含むクラスです。 これにより、明示的な構成が行われ、マッピング属性が適用され、その他の規則が実行されるため、モデルは最新の状態に維持されます。 これに加わるには、すべての規則で、対応するメソッドがトリガーされるタイミングを決定するインターフェイスを 1 つ以上実装します。 たとえば、IEntityTypeAddedConvention 実装する規則は、新しいエンティティ型がモデルに追加されるたびにトリガーされます。 同様に、IForeignKeyAddedConventionIKeyAddedConvention の両方を実装する規則は、キーまたは外部キーがモデルに追加されるたびにトリガーされます。

モデル構築規則は、モデルの構成を制御するための強力な方法ですが、複雑で、正しく使用するのが難しい場合があります。 多くの場合、規則の前のモデル構成を代わりに使って、プロパティと型の共通構成を簡単に指定できます。

新しい規則の追加

例: 識別子プロパティの長さを制限する

Table-Per-Hierarchy 継承マッピング戦略では、特定の行で表される型を指定するために識別子列が必要です。 既定では、EF は識別子に無制限の文字列列を使用します。これにより、どのような長さの識別子でも機能することが保証されます。 ただし、識別子文字列の最大長を制限すると、記憶域とクエリをいっそう効率的にできます。 それを行う新しい規則を作成しましょう。

EF Core のモデル構築規則は、構築中のモデルに対して行われた変更に基づいてトリガーされます。 これにより、明示的な構成が行われ、マッピング属性が適用され、その他の規則が実行されるため、モデルは最新の状態に維持されます。 これに加わるため、すべての規則で、規則がトリガーされるタイミングを決定するインターフェイスを 1 つ以上実装します。 たとえば、IEntityTypeAddedConvention 実装する規則は、新しいエンティティ型がモデルに追加されるたびにトリガーされます。 同様に、IForeignKeyAddedConventionIKeyAddedConvention の両方を実装する規則は、キーまたは外部キーがモデルに追加されるたびにトリガーされます。

ある時点でモデルに対して行われた構成は、後で変更または削除される可能性があるため、実装すべきインターフェイスがわかりにくい場合があります。 たとえば、規則によってキーが作成されても、後で別のキーを明示的に構成すると置き換えられる可能性があります。

識別子の長さの規則の最初の実装を試みることで、これをもう少し具体的に見てみましょう。

public class DiscriminatorLengthConvention1 : IEntityTypeBaseTypeChangedConvention
{
    public void ProcessEntityTypeBaseTypeChanged(
        IConventionEntityTypeBuilder entityTypeBuilder,
        IConventionEntityType? newBaseType,
        IConventionEntityType? oldBaseType,
        IConventionContext<IConventionEntityType> context)
    {
        var discriminatorProperty = entityTypeBuilder.Metadata.FindDiscriminatorProperty();
        if (discriminatorProperty != null
            && discriminatorProperty.ClrType == typeof(string))
        {
            discriminatorProperty.Builder.HasMaxLength(24);
        }
    }
}

この規則で実装する IEntityTypeBaseTypeChangedConvention は、エンティティ型のマップされた継承階層が変更されるたびにトリガーされることを意味します。 その後、この規則は階層の文字列識別子プロパティを見つけて構成します。

この規則を使うには、ConfigureConventionsAdd を呼び出します。

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Add(_ =>  new DiscriminatorLengthConvention1());
}

Note

Add メソッドは、規則のインスタンスを直接追加するのではなく、規則のインスタンスを作成するためのファクトリを受け取ります。 これにより、規則で EF Core の内部サービス プロバイダーから依存関係を使用できます。 この規則には依存関係がないため、サービス プロバイダー パラメーターには、使われないことを示す _ という名前が付けられています。

モデルを構築して、Post エンティティ型を確認すると、これが機能したことがわかります。識別子プロパティは、最大長 24 で構成されるようになりました。

 Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)

しかし、異なる識別子プロパティを明示的に構成した場合はどうなるでしょうか。 次に例を示します。

modelBuilder.Entity<Post>()
    .HasDiscriminator<string>("PostTypeDiscriminator")
    .HasValue<Post>("Post")
    .HasValue<FeaturedPost>("Featured");

モデルのデバッグ ビューを見ると、識別子の長さが構成されなくなったことがわかります。

 PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw

これは、規則で構成した識別子プロパティが、後でカスタム識別子が追加されたときに削除されたためです。 別のインターフェイスを規則に実装して識別子の変更に対応することで、この修正を試みることはできますが、実装するインターフェイスを見極めるのは簡単ではありません。

幸い、もっと簡単な方法があります。 多くの場合、最終的なモデルが正しい限り、構築中のモデルの見た目は問題ではありません。 さらに、多くの場合、適用する構成で他の規則をトリガーして対応する必要はありません。 したがって、この規則では IModelFinalizingConvention を実装できます。 "モデル最終処理規則" は、他のすべてのモデル構築が完了した後で実行されるため、モデルのほぼ最終的な状態にアクセスできます。 これと対照的なのは "対話型規則" であり、モデルが変更されるたびに対応して、OnModelCreating メソッドがいつ実行されても、モデルが最新の状態になっているようにします。 モデル最終処理規則は、通常、モデル要素を構成するモデル全体を反復処理します。 したがって、この場合は、モデル内のすべての識別子を見つけて構成します。

public class DiscriminatorLengthConvention2 : IModelFinalizingConvention
{
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
    {
        foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
                     .Where(entityType => entityType.BaseType == null))
        {
            var discriminatorProperty = entityType.FindDiscriminatorProperty();
            if (discriminatorProperty != null
                && discriminatorProperty.ClrType == typeof(string))
            {
                discriminatorProperty.Builder.HasMaxLength(24);
            }
        }
    }
}

この新しい規則を使ってモデルを構築した後は、識別子の長さがカスタマイズされていても正しく構成されるようになることがわかります。

PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)

もう一歩進んで、最大長を最も長い識別子の値の長さに設定できます。

public class DiscriminatorLengthConvention3 : IModelFinalizingConvention
{
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
    {
        foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
                     .Where(entityType => entityType.BaseType == null))
        {
            var discriminatorProperty = entityType.FindDiscriminatorProperty();
            if (discriminatorProperty != null
                && discriminatorProperty.ClrType == typeof(string))
            {
                var maxDiscriminatorValueLength =
                    entityType.GetDerivedTypesInclusive().Select(e => ((string)e.GetDiscriminatorValue()!).Length).Max();

                discriminatorProperty.Builder.HasMaxLength(maxDiscriminatorValueLength);
            }
        }
    }
}

今度は、識別子列の最大長が 8 になりました。これは、使われている最も長い識別子の値である "Featured" の長さです。

PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(8)

例: すべての文字列プロパティの既定の長さ

最終処理規則を使用できるもう 1 つの例を見てみましょう。"任意の" 文字列プロパティに対して既定の最大長を設定します。 この規則は、前の例によく似ています。

public class MaxStringLengthConvention : IModelFinalizingConvention
{
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
    {
        foreach (var property in modelBuilder.Metadata.GetEntityTypes()
                     .SelectMany(
                         entityType => entityType.GetDeclaredProperties()
                             .Where(
                                 property => property.ClrType == typeof(string))))
        {
            property.Builder.HasMaxLength(512);
        }
    }
}

この規則は非常に簡単です。 モデル内ですべての文字列プロパティを検索し、その最大長を 512 に設定します。 デバッグ ビューで Post のプロパティを見ると、すべての文字列プロパティの最大長が 512 になっていることがわかります。

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
    Content (string) Required MaxLength(512)
    Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(512)
    PublishedOn (DateTime) Required
    Title (string) Required MaxLength(512)

Note

規則の前の構成でも同じことができますが、規則を使うと、適用可能なプロパティをさらにフィルター処理したり、データ注釈で構成をオーバーライドしたりできます。

最後に、この例を終了する前に、MaxStringLengthConventionDiscriminatorLengthConvention3 の両方を同時に使うとどうなるでしょう。 答えは、モデルの最終処理規則はそれが追加された順序で実行されるため、追加された順序によって異なります。 したがって、MaxStringLengthConvention が最後に追加された場合は、それが最後に実行されて、識別子プロパティの最大長を 512 に設定します。 したがって、この場合により適しているのは、識別子プロパティについてだけ既定の最大長をオーバーライドし、他のすべての文字列プロパティは 512 のままにできるように、DiscriminatorLengthConvention3 を最後に追加することです。

既存の規則の置き換え

既存の規則を完全に削除するのではなく、行うことは基本的に同じでも動作が異なる規則に置き換えたいことがあります。 これは、適切にトリガーされるために必要なインターフェイスが既存の規則によって既に実装されるため便利です。

例: プロパティー マッピングをオプトインする

EF Core では、すべてのパブリック読み取り/書き込みプロパティが規則によってマップされます。 これは、エンティティ型の定義方法に対して適していない場合があります。 これを変更するには、プロパティが OnModelCreating で明示的にマップされているか、または Persist という新しい属性でマークされているのでない限りプロパティをマップしない独自の実装に、PropertyDiscoveryConvention を置き換えることができます。

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class PersistAttribute : Attribute
{
}

新しい規則は次にようになります。

public class AttributeBasedPropertyDiscoveryConvention : PropertyDiscoveryConvention
{
    public AttributeBasedPropertyDiscoveryConvention(ProviderConventionSetBuilderDependencies dependencies)
        : base(dependencies)
    {
    }

    public override void ProcessEntityTypeAdded(
        IConventionEntityTypeBuilder entityTypeBuilder,
        IConventionContext<IConventionEntityTypeBuilder> context)
        => Process(entityTypeBuilder);

    public override void ProcessEntityTypeBaseTypeChanged(
        IConventionEntityTypeBuilder entityTypeBuilder,
        IConventionEntityType? newBaseType,
        IConventionEntityType? oldBaseType,
        IConventionContext<IConventionEntityType> context)
    {
        if ((newBaseType == null
             || oldBaseType != null)
            && entityTypeBuilder.Metadata.BaseType == newBaseType)
        {
            Process(entityTypeBuilder);
        }
    }

    private void Process(IConventionEntityTypeBuilder entityTypeBuilder)
    {
        foreach (var memberInfo in GetRuntimeMembers())
        {
            if (Attribute.IsDefined(memberInfo, typeof(PersistAttribute), inherit: true))
            {
                entityTypeBuilder.Property(memberInfo);
            }
            else if (memberInfo is PropertyInfo propertyInfo
                     && Dependencies.TypeMappingSource.FindMapping(propertyInfo) != null)
            {
                entityTypeBuilder.Ignore(propertyInfo.Name);
            }
        }

        IEnumerable<MemberInfo> GetRuntimeMembers()
        {
            var clrType = entityTypeBuilder.Metadata.ClrType;

            foreach (var property in clrType.GetRuntimeProperties()
                         .Where(p => p.GetMethod != null && !p.GetMethod.IsStatic))
            {
                yield return property;
            }

            foreach (var property in clrType.GetRuntimeFields())
            {
                yield return property;
            }
        }
    }
}

ヒント

組み込み規則を置き換えるときは、新しい規則の実装で既存の規則のクラスから継承する必要があります。 一部の規則には、リレーショナルまたはプロバイダー固有の実装があり、その場合、新しい規則の実装では、使われているデータベース プロバイダーの最も具体的な既存の規則クラスから継承する必要があることに注意してください。

その後、ConfigureConventionsReplace メソッドを使って規則を登録されます。

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Replace<PropertyDiscoveryConvention>(
        serviceProvider => new AttributeBasedPropertyDiscoveryConvention(
            serviceProvider.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
}

ヒント

これは、ProviderConventionSetBuilderDependencies 依存関係オブジェクトによって表される依存関係が既存の規則にある場合です。 これらは、GetRequiredService を使って内部サービス プロバイダーから取得されて、規則のコンストラクターに渡されます。

この規則では、[Persist] でマークされているフィールドを (プロパティに加えて) マップできることに注意してください。 つまり、モデルでプライベート フィールドを非表示キーとして使用できます。

たとえば、次のようなエンティティ型を考えます。

public class LaundryBasket
{
    [Persist]
    [Key]
    private readonly int _id;

    [Persist]
    public int TenantId { get; init; }

    public bool IsClean { get; set; }

    public List<Garment> Garments { get; } = new();
}

public class Garment
{
    public Garment(string name, string color)
    {
        Name = name;
        Color = color;
    }

    [Persist]
    [Key]
    private readonly int _id;

    [Persist]
    public int TenantId { get; init; }

    [Persist]
    public string Name { get; }

    [Persist]
    public string Color { get; }

    public bool IsClean { get; set; }

    public LaundryBasket? Basket { get; set; }
}

これらのエンティティ型から構築されたモデルは次のようになります。

Model:
  EntityType: Garment
    Properties:
      _id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      Basket_id (no field, int?) Shadow FK Index
      Color (string) Required
      Name (string) Required
      TenantId (int) Required
    Navigations:
      Basket (LaundryBasket) ToPrincipal LaundryBasket Inverse: Garments
    Keys:
      _id PK
    Foreign keys:
      Garment {'Basket_id'} -> LaundryBasket {'_id'} ToDependent: Garments ToPrincipal: Basket ClientSetNull
    Indexes:
      Basket_id
  EntityType: LaundryBasket
    Properties:
      _id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      TenantId (int) Required
    Navigations:
      Garments (List<Garment>) Collection ToDependent Garment Inverse: Basket
    Keys:
      _id PK

通常、IsClean はマップされていましたが、[Persist] でマークされていないため、マップされないプロパティとして扱われるようになりました。

ヒント

プロパティがをマップされた後でそれをさらに構成するために実行する必要がある既存のモデル最終処理規則があるため、この規則をモデル最終処理規則として実装することはできません。

規則の実装に関する考慮事項

EF Core は、構成のすべての部分がどのように行われたかを追跡しています。 これは、ConfigurationSource 列挙型によって表されます。 構成の種類は次のとおりです。

  • Explicit: そのモデル要素は、OnModelCreating で明示的に構成されました
  • DataAnnotation: そのモデル要素は、CLR 型のマッピング属性 (別名データ注釈) を使って構成されました
  • Convention: そのモデル要素は、モデル構築規則によって構成されました

DataAnnotation または Explicit として行われた構成を、規則でオーバーライドしてはなりません。 これは、たとえば Builder プロパティから取得される IConventionPropertyBuilder のような、"規則ビルダー" を使って実現されます。 次に例を示します。

property.Builder.HasMaxLength(512);

規則ビルダーで HasMaxLength を呼び出すと、"マッピング属性または OnModelCreating でまだ構成されていない場合" にのみ、最大長が設定されます。

このようなビルダー メソッドには、2 番目のパラメーター fromDataAnnotation もあります。 規則がマッピング属性に代わって構成を行っている場合、これを true に設定します。 次に例を示します。

property.Builder.HasMaxLength(512, fromDataAnnotation: true);

これにより、ConfigurationSourceDataAnnotation に設定されます。つまり、値は、OnModelCreating での明示的なマッピングによってオーバーライドできるようになりましたが、マッピング属性以外の規則ではオーバーライドできません。

現在の構成をオーバーライドできない場合、メソッドは null を返します。さらに構成を実行する必要がある場合は、これを考慮する必要があります。

property.Builder.HasMaxLength(512)?.IsUnicode(false);

Unicode の構成をオーバーライドできない場合でも、最大長が設定されることに注意してください。 両方の呼び出しが成功したときにのみファセットを構成する必要がある場合は、CanSetMaxLengthCanSetIsUnicode を呼び出すことによって、これを先に確認できます。

public class MaxStringLengthNonUnicodeConvention : IModelFinalizingConvention
{
    public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
    {
        foreach (var property in modelBuilder.Metadata.GetEntityTypes()
                     .SelectMany(
                         entityType => entityType.GetDeclaredProperties()
                             .Where(
                                 property => property.ClrType == typeof(string))))
        {
            var propertyBuilder = property.Builder;
            if (propertyBuilder.CanSetMaxLength(512)
                && propertyBuilder.CanSetIsUnicode(false))
            {
                propertyBuilder.HasMaxLength(512)!.IsUnicode(false);
            }
        }
    }
}

ここでは、HasMaxLength の呼び出しで null が返されないことを確認できます。 その場合でも、propertyBuilder と異なる可能性があるため、HasMaxLength から返されるビルダー インスタンスを使うことをお勧めします。

Note

他の規則は、規則が変更を行った直後にトリガーされるのではなく、すべての規則による現在の変更の処理が完了するまで遅延されます。

IConventionContext

すべての規則メソッドには、IConventionContext<TMetadata> パラメーターもあります。 これは、特定のケースで役に立つ可能性のあるメソッドを提供します。

例: NotMappedAttribute 規則

この規則は、モデルに追加された型で NotMappedAttribute を検索し、モデルからそのエンティティ型の削除を試みます。 ただし、エンティティ型がモデルから削除された場合、ProcessEntityTypeAdded を実装する他の規則を実行する必要はなくなります。 これは、StopProcessing() を呼び出すことで実現できます。

public virtual void ProcessEntityTypeAdded(
    IConventionEntityTypeBuilder entityTypeBuilder,
    IConventionContext<IConventionEntityTypeBuilder> context)
{
    var type = entityTypeBuilder.Metadata.ClrType;
    if (!Attribute.IsDefined(type, typeof(NotMappedAttribute), inherit: true))
    {
        return;
    }

    if (entityTypeBuilder.ModelBuilder.Ignore(entityTypeBuilder.Metadata.Name, fromDataAnnotation: true) != null)
    {
        context.StopProcessing();
    }
}

IConventionModel

規則に渡されるすべてのビルダー オブジェクトで、モデルを構成するオブジェクトへの低レベルのアクセスを提供する Metadata プロパティが公開されます。 具体的には、「例: すべての文字列プロパティの既定の長さ」で見たように、モデル内の特定のオブジェクトを反復処理し、それらに共通の構成を適用できるメソッドがあります。 この API は、一括構成で示した IMutableModel と似ています。

注意事項

常に、Builder プロパティとして公開されているビルダーでメソッドを呼び出すことによって、構成を実行することをお勧めします。これは、特定の構成が Fluent API またはデータ注釈を使って既に指定されたものをオーバーライドするかどうかが、ビルダーによってチェックされるためです。

どのようなときに一括構成の各アプローチを使用するか

次のようなときは、Metadata API を使います。

  • 構成を、特定の時点で適用する必要があり、その後のモデルの変更には対応する必要がない。
  • モデルの構築速度が非常に重要である。 Metadata API は、安全性のチェックが少ないため、他のアプローチより若干高速になる可能性がありますが、コンパイル済みモデルを使うと、起動時間がさらに短縮されます。

次のようなときは、規則の前のモデル構成を使います。

  • 適用条件が、型にのみ依存しており、単純である。
  • 特定の型のプロパティがモデルに追加されて、データ注釈と規則をオーバーライドする任意の時点で、構成を適用する必要がある

次のようなときは、最終処理規則を使います。

  • 適用条件が複雑である。
  • データ注釈で指定されている内容を、構成でオーバーライドしてはならない。

次のようなときは、対話型規則を使います。

  • 複数の規則が相互に依存している。 最終処理規則は追加された順序で実行されるため、後の最終処理規則によって行われた変更に対応することはできません。
  • ロジックが複数のコンテキスト間で共有される。 対話型規則は、他のアプローチより安全です。