Creación y configuración de un modelo

EF Core usa un modelo de metadatos para describir cómo se asignan los tipos de entidad de la aplicación a la base de datos subyacente. Este modelo se crea con un conjunto de convenciones: heurística que busca patrones comunes. Después, el modelo se puede personalizar mediante atributos de asignación (también conocidos como anotaciones de datos) o llamadas a los métodos ModelBuilder(también conocido como API fluida) en OnModelCreating; ambos reemplazarán la configuración que realizan las convenciones.

La mayoría de la configuración se puede aplicar a un modelo que tenga como destino cualquier almacén de datos. Los proveedores también pueden habilitar la configuración específica de un almacén de datos determinado, así como omitir la configuración que no es compatible o no es aplicable. Para obtener documentación sobre la configuración específica del proveedor, vea la sección Proveedores de bases de datos.

Sugerencia

Puede ver ejemplos de este artículo en GitHub.

Uso de la API fluida para configurar un modelo

Puede reemplazar el método OnModelCreating del contexto derivado y usar API fluida para configurar el modelo. Este es el método más eficaz de configuración y permite especificar la configuración sin modificar las clases de entidad. La configuración de API fluida tiene la prioridad más alta y reemplaza las anotaciones de datos y las convenciones. La configuración se aplica en el orden en que se llama a los métodos y, en caso de conflictos, la llamada más reciente invalidará la configuración especificada anteriormente.

using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.FluentAPI.Required;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    #region Required
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property(b => b.Url)
            .IsRequired();
    }
    #endregion
}

public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
}

Sugerencia

Para aplicar la misma configuración a varios objetos del modelo, vea configuración masiva.

Configuración de agrupación

Para reducir el tamaño del método OnModelCreating, toda la configuración de un tipo de entidad se puede extraer en una clase independiente que implemente IEntityTypeConfiguration<TEntity>.

public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
    public void Configure(EntityTypeBuilder<Blog> builder)
    {
        builder
            .Property(b => b.Url)
            .IsRequired();
    }
}

A continuación, basta con invocar el método Configure desde OnModelCreating.

new BlogEntityTypeConfiguration().Configure(modelBuilder.Entity<Blog>());

Aplicación de todas las configuraciones en un ensamblado

Es posible aplicar toda la configuración especificada en tipos que implementen IEntityTypeConfiguration en un ensamblado determinado.

modelBuilder.ApplyConfigurationsFromAssembly(typeof(BlogEntityTypeConfiguration).Assembly);

Nota

El orden en que se aplicarán las configuraciones está sin definir, por lo que solo debe usarse este método si el orden no importa.

Uso de EntityTypeConfigurationAttribute en tipos de entidad

En lugar de llamar explícitamente a Configure, en su lugar se puede colocar un elemento EntityTypeConfigurationAttribute en el tipo de entidad para que EF Core pueda encontrar y usar la configuración adecuada. Por ejemplo:

[EntityTypeConfiguration(typeof(BookConfiguration))]
public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Isbn { get; set; }
}

Este atributo significa que EF Core usará la implementación de IEntityTypeConfiguration especificada siempre que el tipo de entidad Book se incluya en un modelo. El tipo de entidad se incluye en un modelo mediante uno de los mecanismos normales. Por ejemplo, mediante la creación de una propiedad DbSet<TEntity> para el tipo de entidad:

public class BooksContext : DbContext
{
    public DbSet<Book> Books { get; set; }

    //...

O bien, mediante su registro en OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Book>();
}

Nota

Los tipos EntityTypeConfigurationAttribute no se detectarán automáticamente en un ensamblado. Los tipos de entidad se deben agregar al modelo antes de que se detecte el atributo en ese tipo de entidad.

Uso de anotaciones de datos para configurar un modelo

También puede aplicar determinados atributos (conocidos como anotaciones de datos) a las clases y propiedades. Las anotaciones de datos reemplazarán a las convenciones, pero la configuración de la API fluida también las reemplazará.

using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;

namespace EFModeling.EntityProperties.DataAnnotations.Annotations;

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
}

[Table("Blogs")]
public class Blog
{
    public int BlogId { get; set; }

    [Required]
    public string Url { get; set; }
}

Convenciones integradas

EF Core incluye muchas convenciones de creación de modelos habilitadas de manera predeterminada. Puede encontrar todos ellos en la lista de clases que implementan la interfaz IConvention. Pero esa lista no incluye convenciones que hayan introducido proveedores de bases de datos de terceros ni complementos.

Las aplicaciones pueden quitar o reemplazar cualquiera de estas convenciones, así como agregar nuevas convenciones personalizadas que apliquen la configuración de los patrones que EF no reconoce de fábrica.

Sugerencia

El código que se muestra debajo procede de ModelBuildingConventionsSample.cs.

Eliminación de una convención existente

A veces, es posible que una de las convenciones integradas no sea adecuada para la aplicación, en cuyo caso se puede quitar.

Sugerencia

Si el modelo no usa atributos de asignación (también conocidos como anotaciones de datos) para la configuración, todas las convenciones cuyo nombre termine en AttributeConvention se pueden quitar de forma segura para acelerar la creación de modelos.

Ejemplo: no cree índices para columnas de clave externa

Normalmente tiene sentido crear índices para columnas de clave externa (FK) y, por lo tanto, hay una convención integrada para este proceso: ForeignKeyIndexConvention. Al examinar la vista de depuración del modelo para obtener un tipo de entidad Post con relaciones con Blog y Author, podemos ver que se crean dos índices: uno para el elemento BlogId de FK y el otro para el elemento AuthorId de FK.

  EntityType: Post
    Properties:
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      AuthorId (no field, int?) Shadow FK Index
      BlogId (no field, int) Shadow Required FK Index
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade
    Indexes:
      AuthorId
      BlogId

Pero los índices tienen sobrecarga y es posible que no siempre sea adecuado crearlos para todas las columnas de FK. Para lograrlo, se puede quitar ForeignKeyIndexConvention al compilar el modelo:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder.Conventions.Remove(typeof(ForeignKeyIndexConvention));
}

Al examinar ahora la vista de depuración del modelo para Post, vemos que no se han creado los índices en los elementos FK:

  EntityType: Post
    Properties:
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      AuthorId (no field, int?) Shadow FK
      BlogId (no field, int) Shadow Required FK
    Navigations:
      Author (Author) ToPrincipal Author Inverse: Posts
      Blog (Blog) ToPrincipal Blog Inverse: Posts
    Keys:
      Id PK
    Foreign keys:
      Post {'AuthorId'} -> Author {'Id'} ToDependent: Posts ToPrincipal: Author ClientSetNull
      Post {'BlogId'} -> Blog {'Id'} ToDependent: Posts ToPrincipal: Blog Cascade

Cuando se quiera, los índices todavía se pueden crear de forma explícita para columnas de clave externa, ya sea mediante IndexAttribute o con la configuración en OnModelCreating.

Vista de depuración

Se puede acceder a la vista de depuración del generador de modelos en el depurador del IDE. Por ejemplo, con Visual Studio:

Accessing the model builder debug view from the Visual Studio debugger

También se puede acceder directamente desde el código, por ejemplo, para enviar la vista de depuración a la consola:

Console.WriteLine(context.Model.ToDebugString());

La vista de depuración tiene un formato corto y uno largo. El formato largo también incluye todas las anotaciones, lo que podría ser útil si necesita ver metadatos relacionales o específicos del proveedor. También se puede acceder a la vista larga desde el código:

Console.WriteLine(context.Model.ToDebugString(MetadataDebugStringOptions.LongDefault));