Comparteix a través de


Convenciones Basadas en Modelos

Nota:

Solo ef6 y versiones posteriores : las características, las API, etc. que se describen en esta página se introdujeron en Entity Framework 6. Si usa una versión anterior, no se aplica parte o toda la información.

Las convenciones basadas en modelos son un método avanzado de configuración del modelo basado en convención. Para la mayoría de los escenarios, se debe usar custom Code First Convention API en DbModelBuilder . Se recomienda comprender la API dbModelBuilder para las convenciones antes de usar convenciones basadas en modelos.

Las convenciones basadas en modelos permiten la creación de convenciones que afectan a propiedades y tablas que no se pueden configurar mediante convenciones estándar. Algunos ejemplos de estos son las columnas discriminadoras en modelos de tabla por jerarquía y las columnas de asociaciones independientes.

Creación de una convención

Al crear una convención modelo basada, el primer paso consiste en elegir en qué parte del flujo de trabajo se debe aplicar la convención al modelo. Hay dos tipos de convenciones de modelo, Conceptual (C-Space) y Almacenamiento (S-Space). Se aplica una convención de espacio C al modelo que compila la aplicación, mientras que se aplica una convención S-Space a la versión del modelo que representa la base de datos y controla aspectos como el nombre de las columnas generadas automáticamente.

Una convención de modelo es una clase que se extiende desde IConceptualModelConvention o IStoreModelConvention. Estas interfaces aceptan un tipo genérico que puede ser de tipo MetadataItem, que se usa para filtrar el tipo de datos al que se aplica la convención.

Agregar una convención

Las convenciones de modelo se agregan de la misma manera que las clases de convenciones normales. En el método OnModelCreating , agregue la convención a la lista de convenciones de un modelo.

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

También se puede agregar una convención en relación con otra convención mediante los métodos Conventions.AddBefore<> o Conventions.AddAfter<> . Para obtener más información sobre las convenciones que entity Framework aplica, consulte la sección de notas.

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

Ejemplo: Convención del modelo discriminador

Cambiar el nombre de las columnas generadas por EF es un ejemplo de algo que no se puede hacer con las otras API de convenciones. Esta es una situación en la que el uso de convenciones de modelo es la única opción.

Un ejemplo de cómo usar una convención basada en modelos para configurar columnas generadas es personalizar la forma en que se denominan columnas discriminadora. A continuación se muestra un ejemplo de una convención basada en modelo simple que cambia el nombre de todas las columnas del modelo denominada "Discriminador" a "EntityType". Esto incluye columnas que el desarrollador simplemente llamó "Discriminador". Dado que la columna "Discriminador" es una columna generada, debe ejecutarse en 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";  
        }  
    }  
}

Ejemplo: Convención general de cambio de nombre de IA

Otro ejemplo más complicado de convenciones basadas en modelos en acción es configurar la manera en que se nombran las Asociaciones Independientes (AIs). Se trata de una situación en la que las convenciones de modelo son aplicables porque las Asociaciones Indirectas (AIs) son generadas por EF y no están presentes en el modelo al que puede acceder la API DbModelBuilder.

Cuando EF genera una IA, crea una columna denominada EntityType_KeyName. Por ejemplo, para una asociación denominada Customer con una columna de clave denominada CustomerId, generaría una columna denominada Customer_CustomerId. La siguiente convención quita el carácter "_" del nombre de columna que se genera para la 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);
            }                 
        }
    }
}

Extensión de convenciones existentes

Si necesita escribir una convención similar a una de las convenciones que Entity Framework ya se aplica al modelo, siempre puede extender esa convención para evitar tener que volver a escribirla desde cero. Un ejemplo de esto es reemplazar la convención de coincidencia de identificador existente por una personalizada. Una ventaja adicional para invalidar la convención de clave es que solo se llamará al método invalidado si no hay ninguna clave ya detectada o configurada explícitamente. Una lista de convenciones que usa Entity Framework está disponible aquí: 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;
    }
}

A continuación, debemos agregar nuestra nueva convención antes de la convención principal existente. Después de agregar CustomKeyDiscoveryConvention, podemos quitar IdKeyDiscoveryConvention. Si no quitamos la idKeyDiscoveryConvention existente, esta convención seguirá teniendo prioridad sobre la convención de detección de identificadores, ya que se ejecuta primero, pero en el caso de que no se encuentre ninguna propiedad "key", se ejecutará la convención "id". Vemos este comportamiento porque cada convención ve el modelo como actualizado por la convención anterior, en lugar de operar en él de forma independiente y combinarse todos juntos, de modo que, si por ejemplo, una convención anterior actualizó un nombre de columna para que sea relevante para su convención personalizada (cuando antes el nombre no era relevante), se aplicará a esa columna.

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

Notas

Encontrará una lista de convenciones que actualmente aplica Entity Framework en la documentación de MSDN: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Esta lista se extrae directamente del código fuente. El código fuente de Entity Framework 6 está disponible en GitHub y muchas de las convenciones usadas por Entity Framework son buenos puntos de partida para las convenciones basadas en modelos personalizados.