Sdílet prostřednictvím


Modelové konvence

Poznámka

Pouze EF6 a novější – Funkce, rozhraní API atd. popsané na této stránce byly představeny v Entity Framework 6. Pokud používáte starší verzi, některé nebo všechny informace nemusí být platné.

Konvence založené na modelu představují pokročilou metodu konfigurace modelu založeného na konvencích. Ve většině scénářů by se mělo použít rozhraní API Custom Code First Convention pro DbModelBuilder . Před použitím konvencí založených na modelu se doporučuje porozumět rozhraní API DbModelBuilder pro konvence.

Modelové konvence umožňují vytvářet konvence, které ovlivňují vlastnosti a tabulky, které se nedají konfigurovat prostřednictvím standardních konvencí. Příklady těchto sloupců jsou diskriminující sloupce v tabulce na modely hierarchie a sloupce Nezávislé přidružení.

Vytvoření konvence

Prvním krokem při vytváření konvence založené na modelu je výběr, kdy se v kanálu má konvence použít pro model. Existují dva typy konvencí modelu, Conceptual (C-Space) a Store (S-Space). Konvence prostoru jazyka C se použije na model, který aplikace sestaví, zatímco konvence S-Space se použije na verzi modelu, která představuje databázi a řídí věci, jako je například název automaticky generovaných sloupců.

Konvence modelu je třída, která se rozšiřuje z IConceptualModelConvention nebo IStoreModelConvention. Obě tato rozhraní přijímají obecný typ, který může být typu MetadataItem, který se používá k filtrování datového typu, na který se konvence vztahuje.

Přidání konvence

Konvence modelu se přidávají stejným způsobem jako třídy pravidelných konvencí. V OnModelCreating metoda přidejte konvenci do seznamu konvencí pro 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>();  
    }  
}

Konvenci lze také přidat ve vztahu k jiné konvenci pomocí metod Conventions.AddBefore<> nebo Conventions.AddAfter<> . Další informace o konvencích, které entity Framework používá, najdete v části poznámek.

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

Příklad: Diskriminátorská konvence modelu

Přejmenování sloupců generovaných ef je příkladem něčeho, co nemůžete dělat s jinými rozhraními API konvencí. Jedná se o situaci, kdy je použití konvencí modelu vaší jedinou možností.

Příkladem použití konvence založené na modelu ke konfiguraci generovaných sloupců je přizpůsobení způsobu, jakým jsou diskriminující sloupce pojmenovány. Níže je příklad jednoduché konvence založené na modelu, která přejmenuje každý sloupec v modelu s názvem "Diskriminovat" na EntityType. To zahrnuje sloupce, které vývojář jednoduše pojmenoval "Diskriminovat". Vzhledem k tomu, že "diskriminátor" je vygenerovaný sloupec, musí se spouštět v prostoru 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";  
        }  
    }  
}

Příklad: Obecná konvence přejmenování IA

Dalším složitějším příkladem konvencí založených na modelu v akci je konfigurace způsobu pojmenování nezávislých přidružení (IA). Jedná se o situaci, kdy se používají konvence modelu, protože ef vygenerují IA a nejsou přítomné v modelu, ke kterému má rozhraní DBModelBuilder API přístup.

Když EF vygeneruje IA, vytvoří sloupec s názvem EntityType_KeyName. Například pro přidružení s názvem Zákazník s klíčovým sloupcem s názvem CustomerId by vygeneroval sloupec s názvem Customer_CustomerId. Následující konvence odstraní znak _z názvu sloupce, který se vygeneruje pro 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);
            }                 
        }
    }
}

Rozšíření existujících konvencí

Pokud potřebujete napsat konvenci, která je podobná jedné z konvencí, které už entity Framework platí pro váš model, můžete tuto konvenci vždy rozšířit, abyste ji nemuseli přepisovat úplně od začátku. Příkladem je nahrazení stávající konvence porovnávání ID vlastní konvencí. Přidanou výhodou přepsání konvence klíče je, že přepsáná metoda se zavolá pouze v případě, že neexistuje žádný klíč, který je již zjištěn nebo explicitně nakonfigurovaný. Seznam konvencí, které používá Entity Framework, je k dispozici zde: 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;
    }
}

Pak musíme přidat novou konvenci před stávající klíčovou konvenci. Po přidání CustomKeyDiscoveryConvention můžeme idKeyDiscoveryConvention odebrat. Pokud bychom stávající idKeyDiscoveryConvention neodebrali, bude mít tato konvence přednost před konvencí zjišťování ID, protože se spustí jako první, ale v případě, že se nenajde žádná vlastnost "klíč", spustí se konvence ID. Toto chování vidíme, protože každá konvence vidí model aktualizovaný předchozím konvencem (místo toho, aby na něm fungoval nezávisle a všechny dohromady) tak, aby pokud například předchozí konvence aktualizovala název sloupce tak, aby odpovídal něčemu zajímavému pro vaši vlastní konvenci (kdy před tím, než název nebyl zájem), použije se na tento sloupec.

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

Poznámky

Seznam konvencí, které entity Framework aktuálně používá, je k dispozici v dokumentaci MSDN zde: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Tento seznam se načte přímo ze zdrojového kódu. Zdrojový kód pro Entity Framework 6 je k dispozici na GitHubu a mnoho konvencí, které entity Framework používá, jsou dobrými výchozími body pro vlastní konvence založené na modelu.