次の方法で共有


モデルベース慣例

EF6 以降のみ - このページで説明されている機能、API などが Entity Framework 6 で導入されました。 以前のバージョンを使用している場合、一部またはすべての情報は適用されません。

モデル ベースの規則は、規則ベースのモデル構成の高度な方法です。 ほとんどのシナリオでは、 DbModelBuilder のカスタム コードファースト規約 API を使用する必要があります。 モデル ベースの規則を使用する前に、DbModelBuilder API の規則について理解しておくことをお勧めします。

モデル ベースの規則を使用すると、標準規則では構成できないプロパティとテーブルに影響を与える規則を作成できます。 これらの例としては、階層モデルごとのテーブル内の識別子列と独立した関連付け列があります。

規則の作成

モデル ベースの規則を作成する最初の手順は、パイプライン内で規則をモデルに適用する必要があるタイミングを選択することです。 モデル規則には、概念 (C 空間) とストア (S 空間) の 2 種類があります。 C 空間規則は、アプリケーションが構築するモデルに適用されますが、S 空間規則は、データベースを表すモデルのバージョンに適用され、自動生成される列の名前付け方法などを制御します。

モデル規則は、IConceptualModelConvention または IStoreModelConvention から拡張されるクラスです。 これらのインターフェイスはどちらも、規則が適用されるデータ型をフィルター処理するために使用される MetadataItem 型のジェネリック型を受け入れます。

規約の追加

モデル規則は、通常の規則クラスと同じ方法で追加されます。 OnModelCreating メソッドで、モデルの規則の一覧に規則を追加します。

using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;

public class BlogContext : DbContext  
{  
    public DbSet<Post> Posts { get; set; }  
    public DbSet<Comment> Comments { get; set; }  

    protected override void OnModelCreating(DbModelBuilder modelBuilder)  
    {  
        modelBuilder.Conventions.Add<MyModelBasedConvention>();  
    }  
}

また、Conventions.AddBefore<> または Conventions.AddAfter<> メソッドを使用して、別の規則に関連して規則を追加することもできます。 Entity Framework が適用する規則の詳細については、「メモ」セクションを参照してください。

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.AddAfter<IdKeyDiscoveryConvention>(new MyModelBasedConvention());
}

例: 識別モデル規約

EF によって生成された列の名前変更は、他の規則 API では実行できない例です。 これは、モデル規則を使用することが唯一の選択肢である状況です。

モデル ベースの規則を使用して生成された列を構成する方法の例として、識別子列の名前付け方法をカスタマイズする方法があります。 "識別子" という名前のモデル内のすべての列の名前を "EntityType" に変更する単純なモデル ベースの規則の例を次に示します。 これには、開発者が "Discriminator" という名前を付けた列が含まれます。 "識別子" 列は生成された列であるため、S 空間で実行する必要があります。

using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;

class DiscriminatorRenamingConvention : IStoreModelConvention<EdmProperty>  
{  
    public void Apply(EdmProperty property, DbModel model)  
    {            
        if (property.Name == "Discriminator")  
        {  
            property.Name = "EntityType";  
        }  
    }  
}

例: 一般的な IA 名前変更規則

モデル ベースの規則のもう 1 つのより複雑な例は、独立した関連付け (IA) の名前を付ける方法を構成することです。 これは、ID が EF によって生成され、DbModelBuilder API がアクセスできるモデルに存在しないため、モデル規則が適用される状況です。

EF が IA を生成すると、EntityType_KeyNameという名前の列が作成されます。 たとえば、Customer という名前の関連付けに CustomerId という名前のキー列がある場合、Customer_CustomerIdという名前の列が生成されます。 次の規則では、IA に対して生成される列名から '_' 文字を削除します。

using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;

// Provides a convention for fixing the independent association (IA) foreign key column names.  
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{

    public void Apply(AssociationType association, DbModel model)
    {
        // Identify ForeignKey properties (including IAs)  
        if (association.IsForeignKey)
        {
            // rename FK columns  
            var constraint = association.Constraint;
            if (DoPropertiesHaveDefaultNames(constraint.FromProperties, constraint.ToRole.Name, constraint.ToProperties))
            {
                NormalizeForeignKeyProperties(constraint.FromProperties);
            }
            if (DoPropertiesHaveDefaultNames(constraint.ToProperties, constraint.FromRole.Name, constraint.FromProperties))
            {
                NormalizeForeignKeyProperties(constraint.ToProperties);
            }
        }
    }

    private bool DoPropertiesHaveDefaultNames(ReadOnlyMetadataCollection<EdmProperty> properties, string roleName, ReadOnlyMetadataCollection<EdmProperty> otherEndProperties)
    {
        if (properties.Count != otherEndProperties.Count)
        {
            return false;
        }

        for (int i = 0; i < properties.Count; ++i)
        {
            if (!properties[i].Name.EndsWith("_" + otherEndProperties[i].Name))
            {
                return false;
            }
        }
        return true;
    }

    private void NormalizeForeignKeyProperties(ReadOnlyMetadataCollection<EdmProperty> properties)
    {
        for (int i = 0; i < properties.Count; ++i)
        {
            int underscoreIndex = properties[i].Name.IndexOf('_');
            if (underscoreIndex > 0)
            {
                properties[i].Name = properties[i].Name.Remove(underscoreIndex, 1);
            }                 
        }
    }
}

既存の規則の拡張

Entity Framework が既にモデルに適用している規則のいずれかに似た規則を記述する必要がある場合は、その規則をいつでも拡張して、ゼロから書き直す必要がなくなります。 この例では、既存の ID 照合規則をカスタム ID に置き換えます。 キー規則をオーバーライドする利点として、既に検出または明示的に構成されているキーがない場合にのみ、オーバーライドされたメソッドが呼び出されます。 Entity Framework で使用される規則の一覧については、 http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspxを参照してください。

using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;  

// Convention to detect primary key properties.
// Recognized naming patterns in order of precedence are:
// 1. 'Key'
// 2. [type name]Key
// Primary key detection is case insensitive.
public class CustomKeyDiscoveryConvention : KeyDiscoveryConvention
{
    private const string Id = "Key";

    protected override IEnumerable<EdmProperty> MatchKeyProperty(
        EntityType entityType, IEnumerable<EdmProperty> primitiveProperties)
    {
        Debug.Assert(entityType != null);
        Debug.Assert(primitiveProperties != null);

        var matches = primitiveProperties
            .Where(p => Id.Equals(p.Name, StringComparison.OrdinalIgnoreCase));

        if (!matches.Any())
       {
            matches = primitiveProperties
                .Where(p => (entityType.Name + Id).Equals(p.Name, StringComparison.OrdinalIgnoreCase));
        }

        // If the number of matches is more than one, then multiple properties matched differing only by
        // case--for example, "Key" and "key".  
        if (matches.Count() > 1)
        {
            throw new InvalidOperationException("Multiple properties match the key convention");
        }

        return matches;
    }
}

次に、既存のキー規則の前に新しい規則を追加する必要があります。 CustomKeyDiscoveryConvention を追加したら、IdKeyDiscoveryConvention を削除できます。 既存の IdKeyDiscoveryConvention を削除しなかった場合でも、この規則は最初に実行されるため Id 検出規則よりも優先されますが、"key" プロパティが見つからない場合は、"id" 規則が実行されます。 この動作は、各規則が以前の規則によってモデルが更新されたと見なすために起きます。たとえば、以前の規則で列名が変更され、その変更後にカスタム規則にとって関心のある名前に一致するようになった場合、その列に適用されます。

public class BlogContext : DbContext
{
    public DbSet<Post> Posts { get; set; }
    public DbSet<Comment> Comments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.AddBefore<IdKeyDiscoveryConvention>(new CustomKeyDiscoveryConvention());
        modelBuilder.Conventions.Remove<IdKeyDiscoveryConvention>();
    }
}

メモ

Entity Framework で現在適用されている規則の一覧については、MSDN のドキュメント「 http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx」を参照してください。 このリストは、ソース コードから直接取得されます。 Entity Framework 6 のソース コードは GitHub で入手できます。Entity Framework で使用される規則の多くは、カスタム モデル ベースの規則の出発点として適しています。