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