Model Tabanlı Kurallar

Dekont

Yalnızca EF6'ya Doğru - Bu sayfada ele alınan özellikler, API'ler vb. Entity Framework 6'da sunulmuştur. Önceki bir sürümü kullanıyorsanız, bilgilerin bir kısmı veya tümü geçerli değildir.

Model tabanlı kurallar, kural tabanlı model yapılandırmasının gelişmiş bir yöntemidir. Çoğu senaryoda DbModelBuilder'da Özel Kod İlk Kural API'sinin kullanılması gerekir. Model tabanlı kuralları kullanmadan önce, kurallar için DbModelBuilder API'sinin anlaşılması önerilir.

Model tabanlı kurallar, standart kurallar aracılığıyla yapılandırılamayan özellikleri ve tabloları etkileyen kuralların oluşturulmasına olanak sağlar. Bunlara örnek olarak hiyerarşi modellerine göre tablodaki ayrımcı sütunlar ve Bağımsız İlişkilendirme sütunları verilebilir.

Kural Oluşturma

Model tabanlı bir kural oluşturmanın ilk adımı işlem hattında kuralın modele ne zaman uygulanması gerektiğini seçmektir. Kavramsal (C-Boşluk) ve Mağaza (S-Space) olmak üzere iki tür model kuralı vardır. Uygulamanın oluşturduğu modele bir C-Space kuralı uygulanırken, modelin veritabanını temsil eden ve otomatik olarak oluşturulan sütunların nasıl adlandırıldığını denetleyebilen bir S-Space kuralı uygulanır.

Model kuralı, IConceptualModelConvention veya IStoreModelConvention'dan genişleten bir sınıftır. Bu arabirimlerin her ikisi de, kuralın geçerli olduğu veri türünü filtrelemek için kullanılan MetadataItem türünde olabilecek genel bir türü kabul eder.

Kural Ekleme

Model kuralları, normal kural sınıflarıyla aynı şekilde eklenir. OnModelCreating yönteminde, kuralı bir model için kurallar listesine ekleyin.

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

Bir kural, Conventions.AddBefore<> veya Conventions.AddAfter<> yöntemleri kullanılarak başka bir kuralla ilişkili olarak da eklenebilir. Entity Framework'ün uyguladığı kurallar hakkında daha fazla bilgi için notlar bölümüne bakın.

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

Örnek: Ayrımcı Model Kuralı

EF tarafından oluşturulan sütunların yeniden adlandırılması, diğer kural API'leriyle gerçekleştiremezseniz bir örnektir. Bu, model kurallarını kullanmanın tek seçeneğiniz olduğu bir durumdur.

Oluşturulan sütunları yapılandırmak için model tabanlı bir kuralın nasıl kullanılacağını gösteren bir örnek, ayrımcı sütunların adlandırılması şeklini özelleştirmektir. Aşağıda, modeldeki "Discriminator" adlı her sütunu "EntityType" olarak yeniden adlandıran basit bir model tabanlı kural örneği verilmiştir. Bu, geliştiricinin yalnızca "Discriminator" olarak adlandırdığı sütunları içerir. "Discriminator" sütunu oluşturulan bir sütun olduğundan, bunun S-Space'te çalıştırılması gerekir.

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

Örnek: Genel IA Yeniden Adlandırma Kuralı

Model tabanlı kuralların iş başındaki diğer bir karmaşık örneği de, Bağımsız İlişkilendirmelerin (IA) adlandırıldığını yapılandırmaktır. Bu durum, IA'lar EF tarafından oluşturulduğundan ve DbModelBuilder API'sinin erişebileceği modelde bulunmadığından Model kurallarının geçerli olduğu bir durumdur.

EF bir IA oluşturduğunda, EntityType_KeyName adlı bir sütun oluşturur. Örneğin CustomerId adlı anahtar sütunu olan Customer adlı bir ilişkilendirme için Customer_CustomerId adlı bir sütun oluşturur. Aşağıdaki kural, '_' karakterini IA için oluşturulan sütun adından çıkarır.

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

Mevcut Kuralları Genişletme

Entity Framework'ün modeliniz için zaten geçerli olduğu kurallardan birine benzer bir kural yazmanız gerekiyorsa, sıfırdan yeniden yazmak zorunda kalmamak için her zaman bu kuralı genişletebilirsiniz. Bunun bir örneği, mevcut kimlik eşleştirme kuralını özel bir kuralla değiştirmektir. Anahtar kuralını geçersiz kılmanın bir avantajı, geçersiz kılınan yöntemin yalnızca önceden algılanmış veya açıkça yapılandırılmış bir anahtar yoksa çağrılabilmesidir. Entity Framework tarafından kullanılan kuralların listesini burada bulabilirsiniz: 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;
    }
}

Ardından mevcut anahtar kuralından önce yeni kuralımızı eklememiz gerekir. CustomKeyDiscoveryConvention'ı ekledikten sonra IdKeyDiscoveryConvention'ı kaldırabiliriz. Mevcut IdKeyDiscoveryConvention'ı kaldırmadıysak, bu kural ilk çalıştırıldığından kimlik bulma kuralından daha öncelikli olacaktır, ancak "anahtar" özelliğinin bulunmaması durumunda "id" kuralı çalıştırılır. Bu davranışın nedeni, her kuralın modeli önceki kural tarafından güncelleştirilmiş olarak görmesi (modelin bağımsız olarak çalıştırılması ve tümünün birlikte birleştirilmesi yerine) olmasıdır. Örneğin, önceki bir kural bir sütun adını özel kuralınızla ilgili bir değerle eşleşecek şekilde güncelleştirdiyse (bu tarihten önce adın ilgi çekici olmadığı durumlarda) bu sütuna uygulanır.

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

Notlar

Şu anda Entity Framework tarafından uygulanan kuralların listesi şu MSDN belgelerinde bulunabilir: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Bu liste doğrudan kaynak kodumuzdan çekilir. Entity Framework 6 için kaynak kodu GitHub'da kullanılabilir ve Entity Framework tarafından kullanılan kuralların çoğu özel model tabanlı kurallar için iyi başlangıç noktalarıdır.