Udostępnij za pośrednictwem


Konwencje oparte na modelu

Uwaga

Tylko rozwiązanie EF6 i nowsze wersje — Funkcje, interfejsy API itp. omówione na tej stronie zostały wprowadzone w rozwiązaniu Entity Framework 6. Jeśli korzystasz ze starszej wersji, niektóre lub wszystkie podane informacje nie mają zastosowania.

Konwencje oparte na modelu to zaawansowana metoda konfiguracji modelu opartego na konwencji. W większości scenariuszy należy użyć interfejsu API niestandardowej konwencji code first w programie DbModelBuilder . Przed użyciem konwencji zaleca się zrozumienie interfejsu API DbModelBuilder dla konwencji.

Konwencje oparte na modelu umożliwiają tworzenie konwencji mających wpływ na właściwości i tabele, które nie można konfigurować za pomocą konwencji standardowych. Przykłady z nich to kolumny dyskryminujące w tabeli na modele hierarchii i kolumny Niezależne skojarzenie.

Tworzenie konwencji

Pierwszym krokiem tworzenia konwencji opartej na modelu jest wybranie, kiedy w potoku należy zastosować konwencję do modelu. Istnieją dwa typy konwencji modelu: Koncepcyjne (C-Space) i Store (S-Space). Konwencja C-Space jest stosowana do modelu tworzonego przez aplikację, natomiast konwencja S-Space jest stosowana do wersji modelu reprezentującego bazę danych i kontroluje takie elementy, jak nazwane kolumny generowane automatycznie.

Konwencja modelu to klasa, która rozszerza model IConceptualModelConvention lub IStoreModelConvention. Oba interfejsy akceptują typ ogólny, który może być typu MetadataItem, który służy do filtrowania typu danych, do którego ma zastosowanie konwencja.

Dodawanie konwencji

Konwencje modelu są dodawane w taki sam sposób, jak klasy konwencji regularnych. W metodzie OnModelCreating dodaj konwencję do listy konwencji dla modelu.

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

Konwencję można również dodać w odniesieniu do innej konwencji przy użyciu metod Conventions.AddBefore<> lub Conventions.AddAfter<> . Aby uzyskać więcej informacji na temat konwencji stosowana przez program Entity Framework, zobacz sekcję notatek.

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

Przykład: Dyskryminująca konwencja modelu

Zmiana nazwy kolumn generowanych przez platformę EF jest przykładem czegoś, czego nie można zrobić z innymi interfejsami API konwencji. Jest to sytuacja, w której używanie konwencji modelu jest jedyną opcją.

Przykładem sposobu używania konwencji opartej na modelu do konfigurowania wygenerowanych kolumn jest dostosowywanie sposobu, w jaki są nazwane kolumny dyskryminujące. Poniżej przedstawiono przykład prostej konwencji opartej na modelu, która zmienia nazwę każdej kolumny w modelu o nazwie "Dyskryminujące" na "EntityType". Obejmuje to kolumny, które deweloper nazwał po prostu "Dyskryminującym". Ponieważ kolumna "Dyskryminująca" jest wygenerowaną kolumną, musi być uruchamiana w obszarze 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";  
        }  
    }  
}

Przykład: Ogólna konwencja zmiany nazw IA

Innym bardziej skomplikowanym przykładem konwencji opartych na modelu w działaniu jest skonfigurowanie sposobu, w jaki nazwy są nazywane niezależnymi skojarzeniami (IA). Jest to sytuacja, w której mają zastosowanie konwencje modelu, ponieważ wystąpienia IA są generowane przez platformę EF i nie są obecne w modelu, do którego może uzyskać dostęp interfejs API DbModelBuilder.

Gdy program EF generuje IA, tworzy kolumnę o nazwie EntityType_KeyName. Na przykład w przypadku skojarzenia o nazwie Klient z kolumną klucza o nazwie CustomerId wygeneruje kolumnę o nazwie Customer_CustomerId. Poniższa konwencja usuwa znak "_" z nazwy kolumny wygenerowanej dla 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);
            }                 
        }
    }
}

Rozszerzanie istniejących konwencji

Jeśli musisz napisać konwencję podobną do jednej z konwencji, które program Entity Framework już stosuje do modelu, zawsze można rozszerzyć taką konwencję, aby uniknąć konieczności ponownego zapisywania go od podstaw. Przykładem jest zastąpienie istniejącej konwencji dopasowywania identyfikatorów niestandardowym. Dodatkową zaletą zastąpienia konwencji klucza jest to, że metoda zastąpiona zostanie wywołana tylko wtedy, gdy nie wykryto już klucza lub jawnie skonfigurowano. Lista konwencji używanych przez program Entity Framework jest dostępna tutaj: 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;
    }
}

Następnie musimy dodać nową konwencję przed istniejącą konwencją klucza. Po dodaniu elementu CustomKeyDiscoveryConvention możemy usunąć element IdKeyDiscoveryConvention. Jeśli nie usuniemy istniejącej konwencji IdKeyDiscoveryConvention, nadal będzie pierwszeństwo przed konwencją odnajdywania identyfikatorów, ponieważ jest ona uruchamiana jako pierwsza, ale w przypadku, gdy nie zostanie znaleziona żadna właściwość "klucz", zostanie uruchomiona konwencja "id". Widzimy to zachowanie, ponieważ każda konwencja widzi model zgodnie z poprzednią konwencją (zamiast działać na nim niezależnie i wszystkie połączone razem), tak aby na przykład poprzednia konwencja zaktualizowała nazwę kolumny, aby dopasować coś interesującego do konwencji niestandardowej (gdy wcześniej nazwa nie była interesująca), zostanie ona zastosowana do tej kolumny.

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

Uwagi

Lista konwencji, które są obecnie stosowane przez program Entity Framework, jest dostępna w dokumentacji MSDN tutaj: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Ta lista jest pobierana bezpośrednio z naszego kodu źródłowego. Kod źródłowy dla programu Entity Framework 6 jest dostępny w witrynie GitHub , a wiele konwencji używanych przez program Entity Framework to dobre punkty wyjścia dla niestandardowych konwencji opartych na modelu.