Bagikan melalui


Konvensi Berbasis Model

Catatan

Hanya EF6 dan seterusnya - Fitur, API, dll. yang dibahas di halaman ini dimasukkan dalam Entity Framework 6. Jika Anda menggunakan versi yang lebih lama, beberapa atau semua informasi tidak berlaku.

Konvensi berbasis model adalah metode lanjutan konfigurasi model berbasis konvensi. Untuk sebagian besar skenario , API Konvensi Pertama Kode Kustom pada DbModelBuilder harus digunakan. Pemahaman tentang API DbModelBuilder untuk konvensi direkomendasikan sebelum menggunakan konvensi berbasis model.

Konvensi berbasis model memungkinkan pembuatan konvensi yang memengaruhi properti dan tabel yang tidak dapat dikonfigurasi melalui konvensi standar. Contohnya adalah kolom diskriminator dalam tabel per model hierarki dan kolom Asosiasi Independen.

Membuat Konvensi

Langkah pertama dalam membuat konvensi berbasis model adalah memilih ketika dalam alur konvensi perlu diterapkan ke model. Ada dua jenis konvensi model, Konseptual (C-Space) dan Store (S-Space). Konvensi C-Space diterapkan ke model yang dibangun aplikasi, sedangkan konvensi S-Space diterapkan ke versi model yang mewakili database dan mengontrol hal-hal seperti bagaimana kolom yang dihasilkan secara otomatis dinamai.

Konvensi model adalah kelas yang diperluas dari IConceptualModelConvention atau IStoreModelConvention. Antarmuka ini menerima jenis generik yang dapat berjenis MetadataItem yang digunakan untuk memfilter jenis data yang diterapkan konvensi.

Menambahkan Konvensi

Konvensi model ditambahkan dengan cara yang sama seperti kelas konvensi reguler. Dalam metode OnModelCreating, tambahkan konvensi ke daftar konvensi untuk model.

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>();  
    }  
}

Konvensi juga dapat ditambahkan sehubungan dengan konvensi lain menggunakan metode Conventions.AddBefore<> atau Conventions.AddAfter<> . Untuk informasi selengkapnya tentang konvensi yang diterapkan Kerangka Kerja Entitas, lihat bagian catatan.

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

Contoh: Konvensi Model Diskriminator

Mengganti nama kolom yang dihasilkan oleh EF adalah contoh sesuatu yang tidak dapat Anda lakukan dengan API konvensi lainnya. Ini adalah situasi di mana menggunakan konvensi model adalah satu-satunya pilihan Anda.

Contoh cara menggunakan konvensi berbasis model untuk mengonfigurasi kolom yang dihasilkan adalah menyesuaikan cara kolom diskriminator diberi nama. Di bawah ini adalah contoh konvensi berbasis model sederhana yang mengganti nama setiap kolom dalam model bernama "Discriminator" menjadi "EntityType". Ini termasuk kolom yang hanya diberi nama pengembang "Diskriminator". Karena kolom "Diskriminator" adalah kolom yang dihasilkan, ini perlu dijalankan di S-Space.

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";  
        }  
    }  
}

Contoh: Konvensi Penggantian Nama IA Umum

Contoh lain yang lebih rumit dari konvensi berbasis model yang beraksi adalah mengonfigurasi cara Asosiasi Independen (IAs) diberi nama. Ini adalah situasi di mana konvensi Model berlaku karena IA dihasilkan oleh EF dan tidak ada dalam model yang dapat diakses oleh API DbModelBuilder.

Saat EF menghasilkan IA, ia membuat kolom bernama EntityType_KeyName. Misalnya, untuk asosiasi bernama Pelanggan dengan kolom kunci bernama CustomerId, itu akan menghasilkan kolom bernama Customer_CustomerId. Konvensi berikut menghapus karakter '_' dari nama kolom yang dihasilkan untuk 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);
            }                 
        }
    }
}

Memperluas Konvensi yang Ada

Jika Anda perlu menulis konvensi yang mirip dengan salah satu konvensi yang sudah diterapkan Kerangka Kerja Entitas ke model Anda, Anda selalu dapat memperluas konvensi tersebut untuk menghindari harus menulis ulang dari awal. Contohnya adalah mengganti konvensi pencocokan Id yang ada dengan yang kustom. Manfaat tambahan untuk mengambil alih konvensi kunci adalah bahwa metode penimpaan akan dipanggil hanya jika tidak ada kunci yang sudah terdeteksi atau dikonfigurasi secara eksplisit. Daftar konvensi yang digunakan oleh Kerangka Kerja Entitas tersedia di sini: 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;
    }
}

Kita kemudian perlu menambahkan konvensi baru kita sebelum konvensi kunci yang ada. Setelah menambahkan CustomKeyDiscoveryConvention, kita dapat menghapus IdKeyDiscoveryConvention. Jika kami tidak menghapus IdKeyDiscoveryConvention yang ada, konvensi ini masih akan lebih diutamakan daripada konvensi penemuan Id sejak dijalankan terlebih dahulu, tetapi dalam kasus di mana tidak ada properti "kunci" yang ditemukan, konvensi "id" akan berjalan. Kami melihat perilaku ini karena setiap konvensi melihat model seperti yang diperbarui oleh konvensi sebelumnya (daripada beroperasi di atasnya secara independen dan semua digabungkan bersama-sama) sehingga jika misalnya, konvensi sebelumnya memperbarui nama kolom untuk mencocokkan sesuatu yang menarik bagi konvensi kustom Anda (ketika sebelum itu nama tidak menarik) maka akan berlaku untuk kolom tersebut.

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>();
    }
}

Catatan

Daftar konvensi yang saat ini diterapkan oleh Kerangka Kerja Entitas tersedia dalam dokumentasi MSDN di sini: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Daftar ini ditarik langsung dari kode sumber kami. Kode sumber untuk Entity Framework 6 tersedia di GitHub dan banyak konvensi yang digunakan oleh Entity Framework adalah titik awal yang baik untuk konvensi berbasis model kustom.