0% encontró este documento útil (0 votos)
1K vistas978 páginas

Guía Completa de Entity Framework y EF Core

Entity Framework (EF) es un asignador relacional de objetos que permite a los desarrolladores de .NET trabajar con una base de datos mediante objetos .NET. Existen dos versiones principales: EF Core, que es una versión ligera y multiplataforma, y EF6, que es una versión más estable pero solo para .NET Framework. La documentación compara las características y funcionalidades de ambas versiones y proporciona información sobre su instalación, uso y referencia de API.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
0% encontró este documento útil (0 votos)
1K vistas978 páginas

Guía Completa de Entity Framework y EF Core

Entity Framework (EF) es un asignador relacional de objetos que permite a los desarrolladores de .NET trabajar con una base de datos mediante objetos .NET. Existen dos versiones principales: EF Core, que es una versión ligera y multiplataforma, y EF6, que es una versión más estable pero solo para .NET Framework. La documentación compara las características y funcionalidades de ambas versiones y proporciona información sobre su instalación, uso y referencia de API.
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd

Contents

Entity Framework
EF Core y EF6
Comparar EF Core y EF6
Portabilidad de EF6 a EF Core
Información general
Portabilidad de un modelo basado en EDMX
Portabilidad de un modelo basado en código
EF6 y EF Core en la misma aplicación
Entity Framework Core
Información general
Versiones y planeamiento (plan de desarrollo)
Versiones actuales y planeadas
Proceso de planeamiento de versiones
EF Core 5.0
Plan de alto nivel
Novedades
EF Core 3.0
Nuevas características
Últimos cambios
EF Core 2.2
EF Core 2.1
EF Core 2.0
EF Core 1.1
EF Core 1.0
Actualización desde versiones anteriores
De 1.0 RC1 a RC2
De 1.0 RC2 a RTM
De 1.x a 2.0
Introducción
Tutorial de EF Core
Instalación de EF Core
Tutorial de [Link] Core >>
Aspectos básicos
Cadenas de conexión
Registro
Resistencia de la conexión
Prueba
Información general
Pruebas con SQLite
Pruebas con InMemory
Configuración de un DbContext
Tipos de referencia que aceptan valores NULL
Crear un modelo
Información general
Tipos de entidades
Propiedades de entidad
Claves
Valores generados
Tokens de simultaneidad
Propiedades reemplazadas
Relaciones
Índices
Herencia
Secuencias
Campos de respaldo
Conversiones de valores
Propagación de datos
Constructores de tipos de entidad
División de tablas
Tipos de entidad en propiedad
Tipos de entidad sin llave
Alternancia de modelos con el mismo DbContext
Datos espaciales
Administración de esquemas de base de datos
Información general
Migraciones
Información general
Entornos de equipo
Operaciones personalizadas
Uso de un proyecto independiente
Varios proveedores
Tabla de historial personalizada
Creación y eliminación de API
Utilización de técnicas de ingeniería inversa (scaffolding)
Consultar datos
Información general
Diferencias entre la evaluación de cliente y servidor
Diferencias entre seguimiento y sin seguimiento
Operadores de consulta complejos
Carga de datos relacionados
Consultas asincrónicas
Consultas SQL sin formato
Filtros de consulta global
Etiquetas de consulta
Funcionamiento de las consultas
Guardar datos
Información general
Guardado básico
Datos relacionados
Eliminación en cascada
Conflictos de simultaneidad
Transacciones
Guardado asincrónico
Entidades desconectadas
Valores explícitos para propiedades generadas
Implementaciones de .NET compatibles
Proveedores de bases de datos
Información general
Microsoft SQL Server
Información general
Tablas optimizadas para memoria
Especificación de opciones de Azure SQL Database
SQLite
Información general
Limitaciones de SQLite
Cosmos
Información general
Trabajo con datos no estructurados
Limitaciones de Cosmos
InMemory (para pruebas)
Escritura de un proveedor de base de datos
Cambios que afectan al proveedor
Herramientas y extensiones
Referencia de la línea de comandos
Información general
Consola del Administrador de paquetes (Visual Studio)
CLI de .NET Core
Creación de DbContext en tiempo de diseño
Servicios en tiempo de diseño
Referencia de la API de EF Core >>
Entity Framework 6
Información general
Novedades
Información general
Versiones anteriores
Actualización a EF6
Versiones de Visual Studio
Introducción
Aspectos básicos
Obtener Entity Framework
Trabajar con DbContext
Descripción de las relaciones
Consulta asincrónica y guardado
Configuración
Basada en código
Archivo config
Cadenas de conexión
Resolución de dependencias
Administración de conexiones
Resistencia de la conexión
Lógica de reintento
Errores de confirmación de transacciones
Enlace de datos
WinForms
WPF
Entidades desconectadas
Información general
Entidades de autoseguimiento
Información general
Tutorial
Registro e intercepción
Rendimiento
Consideraciones sobre el rendimiento (notas del producto)
Uso de NGEN
Uso de vistas generadas previamente
Proveedores
Información general
Modelo de proveedor de EF6
Compatibilidad de elementos espaciales con los proveedores
Uso de servidores proxy
Pruebas con EF6
Uso de la simulación
Escritura de duplicados de pruebas propios
Capacidad de prueba con EF4 (artículo)
Crear un modelo
Información general
Uso de Code First
Workflows
Con una base de datos nueva
Con una base de datos existente
Anotaciones de datos
DbSets
Tipos de datos
Enumeraciones
Espacial
Convenciones
Convenciones integradas
Convenciones personalizadas
Convenciones de modelo
Configuración de Fluent
Relaciones
Tipos y propiedades
Uso en Visual Basic
Asignación de procedimientos almacenados
Migraciones
Información general
Migraciones automáticas
Trabajo con bases de datos existentes
Personalización del historial de migraciones
Uso de [Link]
Migraciones en entornos de equipo
Uso de EF Designer
Workflows
Model-First
Database-First
Tipos de datos
Tipos complejos
Enumeraciones
Espacial
División de asignaciones
División de entidades
División de tablas
Asignaciones de herencia
Tabla por jerarquía
Tabla por tipo
Asignación de procedimientos almacenados
Consultar
Actualizar
Asignación de relaciones
Varios diagramas
Selección de la versión del entorno de ejecución
Generación de código
Información general
ObjectContext heredado
Avanzado
Formato de archivo EDMX
Definición de consulta
Varios conjuntos de resultados
Funciones con valores de tabla
Accesos directos del teclado
Consultar datos
Información general
Load (Método)
Datos locales
Consultas de seguimiento y no seguimiento
Uso de consultas SQL sin formato
Consulta de datos relacionados
Guardar datos
Información general
seguimiento de cambios
Detección de cambios automática
Estado de la entidad
Valores de propiedad
Control de conflictos de simultaneidad
Uso de transacciones
Validación de datos
Recursos adicionales
Blogs
Casos prácticos
Contribuciones
Obtener ayuda
Glosario
Base de datos de ejemplo School
Herramientas y extensiones
Licencias
EF5
Chino simplificado
Chino tradicional
Alemán
Inglés
Español
Francés
Italiano
Japonés
Coreano
Ruso
EF6
Versión preliminar
Chino simplificado
Chino tradicional
Alemán
Inglés
Español
Francés
Italiano
Japonés
Coreano
Ruso
Referencia de la API de EF6 >>
Documentación de Entity Framework
Entity Framework

Entity Framework es un asignador relacional de objetos (O/RM) que permite a los


desarrolladores de .NET trabajar con una base de datos mediante objetos .NET.
Elimina la necesidad de usar la mayoría del código de acceso a datos que los
programadores suelen tener que escribir.

Entity Framework Core


EF Core es una versión ligera, extensible y multiplataforma de Entity Framework.

Entity Framework 6
EF 6 es una tecnología de acceso a datos probada con muchos años de características
y estabilización.

Elección
Descubra qué versión de EF es adecuada en su caso.

Portabilidad a EF Core
Guía sobre la portabilidad de una aplicación existente de EF 6 a EF Core.
EF Core
todo

EF Core es una versión ligera, extensible y multiplataforma de Entity Framework.

Introducción
Información general
Crear un modelo
Consultar datos
Guardar datos

Tutoriales
más…

Proveedores de bases de datos


SQL Server
MySQL
PostgreSQL
SQLite
Cosmos
más…

Referencia de API
DbContext
DbSet<TEntity>
más…
EF 6

EF 6 es una tecnología de acceso a datos probada con muchos años de características


y estabilización.

Introducción
Aprenda a acceder a los datos con Entity Framework 6.

Referencia de API
Examine la API de Entity Framework 6, organizada por espacio de nombres.
Comparar EF Core y EF6
08/04/2020 • 9 minutes to read • Edit Online

EF Core
Entity Framework Core (EF Core) es un asignador de base de datos de objeto moderno para .NET. Admite consultas
LINQ, seguimiento de cambios, actualizaciones y migraciones de esquemas.
EF Core funciona con SQL Server o SQL Azure, SQLite, Azure Cosmos DB, MySQL, PostgreSQL y muchas otras
bases de datos a través de un modelo de complemento de proveedor de bases de datos.

EF6
Entity Framework 6 (EF6) es un asignador relacional de objetos diseñado para .NET Framework, pero compatible
con .NET Core. EF6 es un producto estable y compatible, pero ya no se desarrolla activamente.

Comparación de características
EF Core ofrece nuevas características que no se implementarán en EF6. Sin embargo, no todas las características de
EF6 están implementadas actualmente en EF Core.
En las tablas siguientes se comparan las características disponibles en EF Core y EF6. Se trata de una comparación
general en la que no se muestran todas las características ni se explican las diferencias entre una misma
característica en las distintas versiones de EF.
La columna EF Core indica la versión del producto en la que la característica apareció por primera vez.
Creación de un modelo
C A RA C T ERÍST IC A EF 6. 4 EF C O RE

Asignación de clase básica Sí 1.0

Constructores con parámetros 2.1

Conversiones de valores de propiedad 2.1

Tipos asignados sin claves 2.1

Convenciones Sí 1.0

Convenciones personalizadas Sí 1.0 (parcial; n.º 214)

Anotaciones de datos Sí 1.0

API fluida Sí 1.0

Herencia: tabla por jerarquía (TPH) Sí 1.0

Herencia: tabla por tipo (TPT) Sí Planeado para la versión 5.0 (n.º 2266)
C A RA C T ERÍST IC A EF 6. 4 EF C O RE

Herencia: tabla por clase concreta (TPC) Sí Stretch para la versión 5.0 (n.º 3170) (1)

Propiedades de estado reemplazadas 1.0

Claves alternativas 1.0

Navegaciones de varios a varios Sí Planeado para la versión 5.0 (n.º 19003)

Varios a varios sin entidad de Sí En el trabajo pendiente (n.º 1368)


combinación

Generación de claves: Base de datos Sí 1.0

Generación de claves: Cliente 1.0

Tipos complejos/de propiedad Sí 2.0

Datos espaciales Sí 2.2

Formato de modelo: Código Sí 1.0

Crear modelo desde base de datos: Sí 1.0


Línea de comandos

Actualizar modelo desde base de datos Parcial En el trabajo pendiente (n.º 831)

Filtros de consulta global 2.0

División de tablas Sí 2.0

División de entidades Sí Stretch para la versión 5.0 (n.º 620) (1)

Asignación de función escalar de base Insuficiente 2.0


de datos

Asignación de campos 1.1

Tipos de referencia que aceptan valores 3.0


NULL (C# 8.0)

Visualización gráfica de modelo Sí No hay soporte técnico planeado (2)

Editor de modelo gráfico Sí No hay soporte técnico planeado (2)

Formato de modelo: EDMX (XML) Sí No hay soporte técnico planeado (2)

Crear modelo desde base de datos: Sí No hay soporte técnico planeado (2)
Asistente de VS

Consulta de datos
C A RA C T ERÍST IC A EF 6. 4 EF C O RE

Consultas LINQ Sí 1.0

SQL generado legible Insuficiente 1.0

Traslación de GroupBy Sí 2.1

Carga de datos relacionados: diligente Sí 1.0

Carga de datos relacionados: Carga 2.1


diligente para tipos derivados

Carga de datos relacionados: Vago Sí 2.1

Carga de datos relacionados: Explicit Sí 1.1

Consultas SQL sin formato: Tipos de Sí 1.0


entidad

Consultas SQL sin formato: Tipos de Sí 2.1


entidad sin llave

Consultas SQL sin formato: Redacción 1.0


con LINQ

Consultas compiladas de manera Insuficiente 2.0


explícita

await foreach (C# 8.0) 3.0

Lenguaje de consulta basado en texto Sí No hay soporte técnico planeado (2)


(Entity SQL)

Guardado de datos
C A RA C T ERÍST IC A EF 6. 4 EF C O RE

Seguimiento de cambios: Depurador de Sí 1.0

Seguimiento de cambios: notificación Sí 1.0

Seguimiento de cambios: Servidores Sí Combinado para la versión 5.0


proxy (n.º 10949)

Acceso al estado con seguimiento Sí 1.0

Simultaneidad optimista Sí 1.0

Transacciones Sí 1.0

Procesamiento de instrucciones por 1.0


lotes
C A RA C T ERÍST IC A EF 6. 4 EF C O RE

Asignación de procedimientos Sí En el trabajo pendiente (n.º 245)


almacenados

Grafo desconectado: API de bajo nivel Insuficiente 1.0

Grafo desconectado: de un extremo a 1.0 (parcial; n.º 5536)


otro

Otras características
C A RA C T ERÍST IC A EF 6. 4 EF C O RE

Migraciones Sí 1.0

API de creación o eliminación de la base Sí 1.0


de datos

Datos de inicialización Sí 2.1

Resistencia de conexión Sí 1.1

Interceptores Sí 3.0

Events Sí 3.0 (parcial; n.º 626)

Registro simple ([Link]) Sí Combinado para la versión 5.0


(n.º 1199)

Agrupación de DbContext 2.0

Proveedores de bases de datos (3)


C A RA C T ERÍST IC A EF 6. 4 EF C O RE

SQL Server Sí 1.0

MySQL Sí 1.0

PostgreSQL Sí 1.0

Oracle Sí 1.0

SQLite Sí 1.0

SQL Server Compact Sí 1.0 (4)

DB2 Sí 1.0

Firebird Sí 2.0

Jet (Microsoft Access) 2.0 (4)


C A RA C T ERÍST IC A EF 6. 4 EF C O RE

Azure Cosmos DB 3.0

En memoria (para pruebas) 1.0

1 Es probable que no se logren los objetivos de Stretch para una versión determinada. Sin embargo, si las cosas

van bien, intentaremos lograrlos.


2 Algunas características de EF6 no se implementarán en EF Core. Estas características dependen del Entity Data
Model (EDM) subyacente de EF6 o son características complejas con una rentabilidad de la inversión relativamente
baja. Siempre agradecemos los comentarios, pero, aunque EF Core permite muchas cosas que no son posibles en
EF6, no es factible que EF Core admita todas las características de EF6.
3 Es posible que los proveedores de bases de datos de EF Core implementados por terceros sufran un retraso en la
actualización a las versiones principales de EF Core. Vea Proveedores de bases de datos para obtener más
información.
4 Los proveedores de SQL Server Compact y Jet solo funcionan en .NET Framework (no en .NET Core).
Plataformas compatibles
EF Core 3.1 se ejecuta en .NET Core y .NET Framework a través del uso de .NET Standard 2.0. Sin embargo, EF
Core 5.0 no se ejecutará en .NET Framework. Consulte Plataformas para obtener más información.
EF6.4 se ejecuta en .NET Core y .NET Framework a través de la compatibilidad con múltiples versiones.

Guía para las aplicaciones nuevas


Use EF Core en .NET Core para todas las aplicaciones nuevas a menos que la aplicación necesite algún elemento
que solo se admita en .NET Framework.

Guía para las aplicaciones existentes de EF6


EF Core no es un reemplazo de EF6. Probablemente, cambiar de EF6 a EF Core requerirá cambios en la aplicación.
Al mover una aplicación de EF6 a .NET Core:
Siga usando EF6 si el código de acceso a datos es estable y no es probable que evolucione o necesite nuevas
características.
Realice el traslado a EF Core si el código de acceso a datos evoluciona o si la aplicación necesita nuevas
características que solo están disponibles en EF Core.
El traslado a EF Core también se realiza a menudo para obtener un mayor rendimiento. Sin embargo, no todos
los escenarios son más rápidos, así que genere primero algunos perfiles.
Para más información, consulteTraslado de EF6 a EF Core.
Portabilidad de EF6 a EF Core
08/04/2020 • 6 minutes to read

Debido a los cambios fundamentales en EF Core, no se recomienda que intente mover una aplicación de EF6 a EF
Core, salvo que tenga una razón convincente para hacerlo. Debe ver la migración de EF6 a EF Core como una
portabilidad en lugar de una actualización.

IMPORTANT
Antes de comenzar el proceso de portabilidad, es importante validar que EF Core cumple los requisitos de acceso a los datos
de la aplicación.

Características que faltan


Asegúrese de que EF Core tenga todas las características que necesita para usar en la aplicación. Vea Comparación
de características para obtener una comparación detallada del conjunto de características entre EF Core y EF6. Si
faltan algunas características necesarias, asegúrese de que puede compensar la falta de estas características antes
de portar a EF Core.

Cambios de comportamiento
Se trata de una lista no exhaustiva de algunos cambios de comportamiento entre EF6 y EF Core. Es importante
tener esto en cuenta cuando porte la aplicación, ya que pueden cambiar la forma en que se comporta la aplicación,
pero no se mostrarán como errores de compilación después de cambiar a EF Core.
[Link]/Attach y comportamiento del grafo
En EF6, llamar [Link]() en una entidad provoca una búsqueda recursiva de todas las entidades a las que se
hace referencia en sus propiedades de navegación. Las entidades que se encuentran, y a las que el contexto todavía
no ha realizado un seguimiento, también se marcarán como agregadas. [Link]() se comporta de la misma
forma, salvo que todas las entidades se marcan como sin cambios.
EF Core realiza una búsqueda recursiva similar, pero con algunas reglas ligeramente diferentes.
La entidad raíz siempre está en el estado de solicitada (agregada para [Link] y sin cambios para
[Link] ).

Para las entidades que se encuentran durante la búsqueda recursiva de propiedades de


navegación:
Si la clave principal de la entidad se genera en el almacén
Si la clave principal no se establece en un valor, el estado se establece en agregada. El valor de
la clave principal se considera "no establecido" si se le asigna el valor predeterminado de CLR
para el tipo de propiedad (por ejemplo, 0 para int , null para string , etc.).
Si la clave principal no se establece en un valor, el estado se establece en sin cambios.
Si la clave principal no se genera en la base de datos, la entidad se coloca en el mismo estado que la
raíz.
Inicialización de la base de datos de Code First
EF6 tiene cier ta magia en torno a la selección de la conexión de base de datos y la inicialización de
la base de datos. Algunas de estas reglas incluyen:
Si no se realiza ninguna configuración, EF6 seleccionará una base de datos en SQL Express o LocalDb.
Si una cadena de conexión con el mismo nombre que el contexto está en el archivo App/[Link] de las
aplicaciones, se usará esta conexión.
Si la base de datos no existe, se creará.
Si no existe ninguna de las tablas del modelo en la base de datos, el esquema del modelo actual se agrega a
la base de datos. Si se habilitan las migraciones, se usan para crear la base de datos.
Si la base de datos existe y EF6 ha creado previamente el esquema, entonces se comprueba la
compatibilidad de dicho esquema con el modelo actual. Se inicia una excepción si el modelo ha cambiado
desde que se creó el esquema.
EF Core no lleva a cabo nada de esta magia.
La conexión de base de datos debe estar explícitamente configurada en el código.
No se realiza ninguna inicialización. Debe usar [Link]() para aplicar las migraciones (o
[Link]() y EnsureDeleted() para crear o eliminar la base de datos sin usar
migraciones).
Convención de nomenclatura de tablas de Code First
EF6 ejecuta el nombre de clase de entidad a través de un servicio de pluralización para calcular el nombre de tabla
predeterminado al que está asignada la entidad.
EF Core usa el nombre de la propiedad DbSet en la que se expone la entidad en el contexto derivado. Si la entidad
no tiene una propiedad DbSet , se utiliza el nombre de clase.
Portabilidad de un modelo basado en EDMX de EF6
a EF Core
08/04/2020 • 2 minutes to read

EF Core no admite el formato de archivo EDMX para los modelos. La mejor opción para realizar la portabilidad de
estos modelos consiste en generar un modelo nuevo basado en código a partir de la base de datos de la aplicación.

Instalación de los paquetes NuGet de EF Core


Instale el paquete NuGet [Link] .

Regeneración del modelo


Ahora puede usar la funcionalidad de ingeniería inversa para crear un modelo basado en la base de datos existente.
Ejecute el comando siguiente en la consola del Administrador de paquetes NuGet (Herramientas –> Administrador
de paquetes NuGet –> Consola del Administrador de paquetes). Vea Consola del Administrador de paquetes
(Visual Studio) para conocer las opciones de comando para aplicar scaffolding a un subconjunto de tablas, etc.

Scaffold-DbContext "<connection string>" <database provider name>

Por ejemplo, este es el comando para aplicar scaffolding a un modelo a partir de la base de datos Blogging en la
instancia de LocalDB de SQL Server.

Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;"
[Link]

Eliminación del modelo de EF6


Ahora se quitará el modelo de EF6 de la aplicación.
No hay problema por dejar el paquete NuGet de EF6 (EntityFramework) instalado, ya que EF Core y EF6 se pueden
usar en paralelo en la misma aplicación. Sin embargo, si no va a usar EF6 en ninguna de las áreas de la aplicación,
la desinstalación del paquete le ayudará a dar errores de compilación en fragmentos de código que requieren
atención.

Actualización del código


En este punto, es cuestión de solucionar los errores de compilación y de revisar el código para ver si los cambios de
comportamiento entre EF6 y EF Core le afectarán.

Prueba del puerto


El hecho de que la aplicación se compile no significa que se haya trasladado correctamente a EF Core. Tendrá que
probar todas las áreas de la aplicación para asegurarse de que ninguno de los cambios de comportamiento afecte
de forma negativa a la aplicación.
Portabilidad de un modelo basado en código de EF6
a EF Core
08/04/2020 • 4 minutes to read

Si ha leído todas las advertencias y está a punto para realizar la portabilidad, estas son algunas instrucciones que le
ayudarán a empezar.

Instalación de los paquetes NuGet de EF Core


Para usar EF Core, instale el paquete NuGet correspondiente al proveedor de base de datos que quiera usar. Por
ejemplo, cuando el destino es SQL Server, tendría que instalar [Link] . Para
obtener más información, vea Proveedores de bases de datos.
Si tiene previsto usar migraciones, también debe instalar el paquete [Link] .
No hay problema por dejar el paquete NuGet de EF6 (EntityFramework) instalado, ya que EF Core y EF6 se pueden
usar en paralelo en la misma aplicación. Sin embargo, si no va a usar EF6 en ninguna de las áreas de la aplicación,
la desinstalación del paquete le ayudará a dar errores de compilación en fragmentos de código que requieren
atención.

Intercambio de espacios de nombres


La mayoría de las API que se usan en EF6 se encuentran en el espacio de nombres [Link] (y los
subespacios de nombres relacionados). El primer cambio de código consiste en cambiar al espacio de nombres
[Link] . Normalmente, empezará con el archivo de código de contexto derivado y, después,
avanzará desde allí y solucionará los errores de compilación a medida que aparezcan.

Configuración del contexto (conexión, etc.)


Como se describe en Asegurarse de que EF Core funcionará para la aplicación, la detección de la base de datos a la
que se va a conectar EF Core tiene menos secretos. Tendrá que reemplazar el método OnConfiguring en el contexto
derivado y usar la API específica del proveedor de base de datos para configurar la conexión a la base de datos.
La mayoría de las aplicaciones EF6 almacenan la cadena de conexión en el archivo App/[Link] de las
aplicaciones. En EF Core, esta cadena de conexión se lee mediante la API ConfigurationManager . Es posible que
tenga que agregar una referencia al ensamblado del marco [Link] para poder usar esta API.

public class BloggingContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
[Link]([Link]["BloggingDatabase"].ConnectionString);
}
}

Actualización del código


En este punto, es cuestión de solucionar los errores de compilación y de revisar el código para ver si los cambios de
comportamiento le afectarán.

Migraciones existentes
Realmente no existe una manera viable de realizar la portabilidad de las migraciones de EF6 existentes a EF Core.
Si es posible, es mejor suponer que todas las migraciones anteriores de EF6 se han aplicado a la base de datos y,
después, iniciar la migración del esquema desde ese punto mediante EF Core. Para ello, use el comando
Add-Migration para agregar una migración una vez que el modelo se haya trasladado a EF Core. Después, podría
quitar todo el código de los métodos Up y Down de la migración con scaffolding. Las migraciones posteriores se
compararán con el modelo cuando se haya aplicado scaffolding a la migración inicial.

Prueba del puerto


El hecho de que la aplicación se compile no significa que se haya trasladado correctamente a EF Core. Tendrá que
probar todas las áreas de la aplicación para asegurarse de que ninguno de los cambios de comportamiento afecte
de forma negativa a la aplicación.
Uso de EF Core y EF6 en la misma aplicación
08/04/2020 • 2 minutes to read • Edit Online

Es posible usar EF Core y EF6 en la misma biblioteca o aplicación al instalar ambos paquetes NuGet.
Algunos tipos tienen los mismos nombres en EF Core y EF6 y solo difieren en el espacio de nombres, lo que puede
complicar el uso de EF Core y EF6 en el mismo archivo de código. La ambigüedad se puede eliminar fácilmente con
directivas de alias de espacios de nombres. Por ejemplo:

using [Link]; // use DbContext for EF Core


using EF6 = [Link]; // use [Link] for the EF6 version

Si traslada una aplicación existente que tiene varios modelos de EF, puede elegir trasladar de manera selectiva
algunos de ellos a EF Core y seguir usando EF6 para los demás.
Entity Framework Core
08/04/2020 • 3 minutes to read • Edit Online

Entity Framework (EF) Core es una versión ligera, extensible, de código abierto y multiplataforma de la popular
tecnología de acceso a datos Entity Framework.
EF Core puede servir como asignador relacional de objetos (O/RM), lo que permite a los desarrolladores de .NET
trabajar con una base de datos mediante objetos .NET y eliminar la mayoría del código de acceso a los datos que
normalmente deben escribir.
EF Core es compatible con muchos motores de base de datos; vea Proveedores de bases de datos para más
información.

El modelo
Con EF Core, el acceso a datos se realiza mediante un modelo. Un modelo se compone de clases de entidad y un
objeto de contexto que representa una sesión con la base de datos, lo que permite consultar y guardar los datos.
Vea Creación de un modelo para más información.
Puede generar un modelo a partir de una base de datos existente, codificar manualmente un modelo para que
coincida con la base de datos o usar migraciones de EF para crear una base de datos a partir del modelo y que
evolucione a medida que cambia el modelo.
using [Link];
using [Link];

namespace Intro
{
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
[Link](
@"Server=(localdb)\mssqllocaldb;Database=Blogging;Integrated Security=True");
}
}

public class Blog


{
public int BlogId { get; set; }
public string Url { get; set; }
public int Rating { get; set; }
public List<Post> Posts { get; set; }
}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}
}

Consultas
Las instancias de las clases de entidad se recuperan de la base de datos mediante Language Integrated Query
(LINQ). Vea Consulta de datos para más información.

using (var db = new BloggingContext())


{
var blogs = [Link]
.Where(b => [Link] > 3)
.OrderBy(b => [Link])
.ToList();
}

Guardado de datos
Los datos se crean, se eliminan y se modifican en la base de datos mediante instancias de las clases de entidad. Vea
Guardado de datos para más información.

using (var db = new BloggingContext())


{
var blog = new Blog { Url = "[Link] };
[Link](blog);
[Link]();
}
Pasos siguientes
Para consultar tutoriales de introducción, vea Introducción a Entity Framework Core.
Versiones y planeamiento de EF Core
09/04/2020 • 4 minutes to read • Edit Online

Versiones estables
M A RC O DE T RA B A JO DE
REL EA SE DEST IN O C O M PAT IB IL IDA D H A STA VÍN C ULO S

EF Core 3.1 .NET Standard 2.0 3 de diciembre de 2022 (LTS) Anuncio

EF Core 3.0 .NET Standard 2.1 Expiró el 3 de marzo de Anuncio / Cambios


2020 importantes

EF Core 2.2 .NET Standard 2.0 Expiró el 23 de diciembre de Anuncio


2019

EF Core 2.1 .NET Standard 2.0 21 de agosto de 2021 (LTS) Anuncio

EF Core 2.0 .NET Standard 2.0 Expiró el 1 de octubre de Anuncio


2018

EF Core 1.1 .NET Standard 1.3 Expiró el 27 de junio de Anuncio


2019

EF Core 1.0 .NET Standard 1.3 Expiró el 27 de junio de Anuncio


2019

Consulte las plataformas compatibles para saber qué plataformas concretas se admiten en cada versión de EF Core.
Consulte la Directiva de compatibilidad de .NET para obtener información sobre la fecha de expiración de la
compatibilidad y las versiones de compatibilidad a largo plazo (LTS).

Instrucciones para actualizar a nuevas versiones


Las versiones admitidas se revisan por motivos de seguridad y para solucionar otros errores críticos. Use
siempre el parche más reciente de una versión determinada. Por ejemplo, para EF Core 2.1, use 2.1.14.
Las actualizaciones de la versión principal (por ejemplo, de EF Core 2 a EF Core 3) suelen tener cambios
importantes. Se recomienda realizar pruebas exhaustivas al cambiar de una versión principal a otra. Use los
vínculos de cambios importantes anteriores para obtener información sobre cómo abordar los cambios
importantes.
Las actualizaciones de versiones secundarias no suelen contener cambios importantes. No obstante, sigue
siendo aconsejable realizar pruebas exhaustivas, ya que las nuevas características pueden introducir
regresiones.

Programación y planeación de versiones


Las versiones de EF Core siguen la programación de envío de .NET Core.
Normalmente, las versiones de revisión se envían mensualmente, pero tienen un largo plazo. Estamos trabajando
para mejorar esto.
Vea el proceso de planeamiento de versiones para obtener más información sobre cómo decidimos qué enviar en
cada versión. Por lo general, no hacemos un planeamiento detallado más allá de la siguiente versión principal o
secundaria.

EF Core 5.0
La siguiente versión estable planeada es EF Core 5.0 , programada para noviembre de 2020.
Se ha creado un plan de alto nivel para EF Core 5.0 siguiendo el proceso de planeamiento de versiones
documentado.
Sus comentarios sobre la planeación son importantes. La mejor manera de indicar la importancia de un problema
es votar (pulgar arriba ) por ese problema en GitHub. Estos datos se introducen en el proceso de planeación de
la próxima versión.
¡Obténgalo ahora!
Los paquetes de EF Core 5.0 están disponibles ahora como
Compilaciones diarias
Todas las características y correcciones de errores más recientes. Normalmente muy estable; se ejecutan
más de 57 000 pruebas en cada compilación.
Versiones preliminares en NuGet
Van a la zaga de las compilaciones diarias, pero están probadas para trabajar con las versiones
preliminares de [Link] Core y .NET Core correspondientes.
Usar las versiones preliminares o las compilaciones diarias es una excelente manera de detectar problemas y
proporcionar comentarios cuanto antes. Cuanto antes recibamos esos comentarios, más probable será que puedan
procesarse antes de la siguiente versión oficial.
Proceso de planeamiento de versiones
08/04/2020 • 12 minutes to read • Edit Online

A menudo nos preguntan cómo se eligen características específicas para incluirlas en una versión concreta. En
este documento se describe el proceso que usamos. El proceso evoluciona continuamente a medida que
encontramos mejores formas de planeación, pero las ideas generales siguen siendo las mismas.

Diferentes tipos de versiones


Los distintos tipos de versión contienen distintos tipos de cambios. Esto significa que, a su vez, el planeamiento de
versiones es diferente para cada tipo de versión.
Versiones de revisión
Las versiones de revisión solo cambian la parte de "revisión" de la versión. Por ejemplo, EF Core 3.1.1 es una
versión en la que se han revisado los problemas encontrados en EF Core 3.1.0 .
Las versiones de revisión están diseñadas para corregir errores críticos para los clientes. Esto significa que las
versiones de revisión no incluyen nuevas características. No se permiten cambios de API en las versiones de
revisión, excepto en circunstancias especiales.
La dificultad para hacer un cambio en una versión de revisión es muy alta. Esto se debe a que es fundamental que
las versiones de revisión no presenten nuevos errores. Por lo tanto, el proceso de toma de decisiones enfatiza el
alto valor y el riesgo bajo.
Es más probable que revisemos un problema si se cumple una de las siguientes condiciones:
Afecta a varios clientes.
Es una regresión de una versión anterior.
El error provoca daños en los datos.
Es menos probable que revisemos un problema si se cumple una de las siguientes condiciones:
Existen soluciones alternativas razonables.
La corrección implica un alto riesgo de interrumpir algo más.
El error es un caso límite.
La dificultad aumenta gradualmente a lo largo de la vigencia de una versión de soporte técnico a largo plazo
(LTS). Esto se debe a que las versiones de LTS enfatizan la estabilidad.
La decisión final sobre si un problema se revisa o no la realizan los directores de .NET en Microsoft.
Versiones secundarias
Las versiones secundarias solo cambian la parte "secundaria" de la versión. Por ejemplo, EF Core 3.1 .0 es una
versión que mejora EF Core 3.0 .0.
Versiones secundarias:
Están diseñadas para mejorar la calidad y las características de la versión anterior.
Normalmente contienen correcciones de errores y nuevas características.
No incluyen cambios importantes intencionados.
Tienen algunas vistas previas de versiones preliminares insertadas en NuGet.
Versiones principales:
Las versiones principales cambian el número de versión "principal" de EF. Por ejemplo, EF Core 3 .0.0 es una
versión principal que da un gran paso adelante con respecto a EF Core 2.2.x.
Versiones principales:
Están diseñadas para mejorar la calidad y las características de la versión anterior.
Normalmente contienen correcciones de errores y nuevas características.
Algunas de las nuevas características pueden ser cambios fundamentales en el funcionamiento de EF
Core.
Normalmente, incluyen cambios importantes intencionados.
Los cambios importantes son parte necesaria de la evolución de EF Core a medida que aprendemos.
Sin embargo, estudiamos mucho la realización de los cambios importantes debido al posible impacto
que puedan tener en el cliente. Es posible que hayamos sido demasiado radicales con cambios
importantes en el pasado. De ahora en adelante, nos esforzaremos por minimizar los cambios que
interrumpan el funcionamiento de las aplicaciones y reducir los cambios que interrumpan el
funcionamiento de los proveedores de bases de datos y las extensiones.
Tienen muchas vistas previas de versiones preliminares insertadas en NuGet.

Planeación de versiones principales o secundarias


Seguimiento de problemas de GitHub
GitHub ([Link] es la fuente fiable de toda la planeación de EF Core.
Los problemas en GitHub cuentan con lo siguiente:
Un estado
Si un problema está abierto significa que no se ha abordado.
Si un problema está cerrado significa que se ha abordado.
Todos los problemas que se han corregido se etiquetan con el estado “closed-fixed” (cerrado:
corregido). Un problema con la etiqueta “closed-fixed” está corregido y combinado, pero es
posible que no se haya publicado.
Las demás etiquetas de closed- indican otras razones por las que se ha cerrado un problema.
Por ejemplo, los duplicados se etiquetan con “closed-duplicate” (cerrado: duplicado).
Un tipo
El tipo Bugs (Errores) representa errores.
El tipo Enhancements (Mejoras) corresponde a nuevas características o una mejor funcionalidad en las
características existentes.
Un hito
Si un problema no tiene hito, significa que el equipo lo está considerando. La decisión sobre qué hacer
con el problema aún no se ha tomado o se está considerando cambiarla.
Los problemas en el hito Backlog (Trabajo pendiente) son elementos en los que el equipo de EF
considerará que debe trabajar en una versión futura.
Es posible que los problemas del trabajo pendiente tengan la etiqueta consider-for-next-release
(considerar en la próxima versión), lo que indica que este elemento de trabajo es una de las
prioridades para la próxima versión.
Los problemas abiertos en un hito con versión son elementos en los que el equipo tiene previsto
trabajar en esa versión. Por ejemplo, estos son los problemas con los que tenemos previsto trabajar en
EF Core 5.0.
Los problemas cerrados en un hito con versión son los que se completan para esa versión. Tenga en
cuenta que es posible que la versión todavía no se haya lanzado. Por ejemplo, estos son los problemas
completados para EF Core 3.0.
Votos
La votación es la mejor manera de que el usuario indique que un problema es importante.
Para votar, solo tiene que agregar un "pulgar" al problema. Por ejemplo, estos son los problemas
más votados.
También debe incluir un comentario con las razones específicas por las que necesita la característica si
cree que puede ser útil. Comentar "+ 1" o algo similar no agrega ningún valor.
Proceso de planeación
El proceso de planeación es más complicado que simplemente tomar las principales características más
solicitadas del trabajo pendiente. Esto se debe a que se recopilan comentarios de varias partes interesadas de
varias maneras. Por lo tanto, formamos una versión basada en lo siguiente:
Comentarios de los clientes
Comentarios de otras partes interesadas
Dirección estratégica
Recursos disponibles
Programación
Algunas de las preguntas que formulamos son:
1. ¿Cuántos desarrolladores creemos que usarán la característica y en qué medida mejorará las
aplicaciones o la experiencia? Para responder a esta pregunta, recopilamos información de varias
fuentes, y los comentarios y los votos son una de ellas. Las involucraciones específicas con clientes
importantes son otra.
2. ¿Qué soluciones alternativas pueden adoptar los usuarios si todavía no se ha implementado
una característica? Por ejemplo, hay muchos desarrolladores que pueden asignar una tabla de unión
para poder trabajar a pesar de la falta de compatibilidad múltiple de forma nativa. Obviamente, no todos
los desarrolladores quieren hacerlo, pero muchos pueden, y eso se considera un factor decisivo.
3. ¿La implementación de esta característica hará evolucionar la arquitectura de EF Core tanto
como para poder implementar otras características? Normalmente tienen preferencia las
características que actúan como bloques de creación de otras características. Por ejemplo, las entidades
contenedoras de propiedades pueden ayudarnos a avanzar hacia la compatibilidad de varios a varios, y los
constructores de entidades han habilitado nuestra compatibilidad de carga diferida.
4. ¿La característica es un punto de extensibilidad? Los puntos de extensibilidad suelten tener
preferencia sobre las características normales porque permiten que los desarrolladores puedan crear sus
propios comportamientos y compensar las funcionalidades que faltan.
5. ¿Cuál es la sinergia de la característica cuando se usa en combinación con otros productos?
Tienen preferencia las características que permiten o mejoran significativamente la experiencia de uso de
EF Core con otros productos, como .NET Core, la última versión de Visual Studio, Microsoft Azure, etc.
6. ¿Cuáles son las habilidades de las personas disponibles para trabajar en una característica y
cómo se aprovechan mejor estos recursos? Todos los miembros del equipo de EF, e incluso los
colaboradores de la comunidad, tienen diferentes niveles de experiencia en varias áreas, y tenemos que
elaborar el plan de acuerdo con ello. Incluso aunque quisiéramos tener a todos trabajando en una
característica específica, como las traducciones de GroupBy o las relaciones múltiples, no sería práctico.
Plan para Entity Framework Core 5.0
08/04/2020 • 18 minutes to read • Edit Online

Como se describe en el proceso de planeamiento, se ha recopilado la información de las partes interesadas en un


plan provisional para la versión EF Core 5.0.

IMPORTANT
Este plan sigue siendo un trabajo en curso. Nada de esto es un compromiso. Este plan es un punto de partida que
evolucionará a medida que se obtenga más información. Es posible que algunos aspectos no planeados en la actualidad se
incorporen a la versión 5.0. Es posible que algunos aspectos planeados en la actualidad se eliminen de la versión 5.0.

Número de versión y fecha de lanzamiento.


En la actualidad, el lanzamiento de EF Core 5.0 está programado al mismo tiempo que .NET 5.0. Se ha elegido la
versión "5.0" para la alineación con .NET 5.0.
Plataformas compatibles
Está previsto que EF Core 5.0 se ejecute en cualquier plataforma .NET 5.0 en función de la convergencia de estas
plataformas a .NET Core. Lo que esto significa en términos de .NET Standard y el TFM real que se usa todavía está
por determinar.
EF Core 5.0 no se ejecutará en .NET Framework.
Cambios importantes
EF Core 5.0 contendrá algunos cambios importantes, pero serán mucho menos graves que en el caso de EF
Core 3.0. El objetivo es permitir que la gran mayoría de las aplicaciones se actualicen sin interrupciones.
Se espera que haya algunos cambios importantes para los proveedores de bases de datos, especialmente
relacionados con la compatibilidad con TPT. Pero se espera que el trabajo para actualizar un proveedor para 5.0
sea menor que el necesario para 3.0.

Temas
Hemos extraído algunas áreas o temas importantes que formarán la base de las grandes inversiones en EF
Core 5.0.

Propiedades de navegación de varios a varios (también denominado


"omitir navegaciones")
Jefes de desarrollo: @smitpatel y @AndriySvyryd
Seguimiento realizado por #19003
Talla de camiseta: L
Estado: En curso
Varios a varios es la característica más solicitada (407 votos aproximadamente) en el trabajo pendiente de GitHub.
Se realiza un seguimiento de la compatibilidad con las relaciones de varios a varios en su totalidad como #10508.
Puede dividirse en tres áreas principales:
Omitir propiedades de navegación. Permiten usar el modelo para las consultas, etc., sin hacer referencia a la
entidad de la tabla de combinación subyacente. (#19003)
Tipos de entidad de contenedor de propiedades. Permiten usar un tipo CLR estándar (por ejemplo, Dictionary )
para las instancias de entidad, de modo que no se necesite un tipo CLR explícito para cada tipo de entidad.
(Stretch para la versión 5.0: #9914).
Facilitar la configuración de relaciones varios a varios. (Stretch para la versión 5.0).
Creemos que el principal obstáculo para los que quieren que se admitan las relaciones varios a varios es la
imposibilidad de usar relaciones "naturales", sin hacer referencia a la tabla de combinación, en la lógica de
negocios, como las consultas. Es posible que el tipo de entidad de tabla de combinación siga existiendo, pero no
debería interferir con la lógica de negocios. Por este motivo hemos optado por abordar la omisión de propiedades
de navegación en la versión 5.0.
En este momento, los demás elementos de las relaciones varios a varios se han convertido en un objetivo de
extensión para EF Core 5.0. Esto significa que actualmente no se incluyen en el plan para la versión 5.0, pero si
todo va bien esperamos incorporarlos.

Asignación de herencia de tabla por tipo (TPT)


Jefe de desarrollo: @AndriySvyryd
Seguimiento realizado por #2266
Talla de camiseta: XL
Estado: En curso
TPT se va a incluir porque se trata de una característica muy solicitada (aproximadamente 254 votos; en tercera
posición) y porque requiere algunos cambios de bajo nivel que consideramos adecuados para la naturaleza
fundamental del plan general de .NET 5. Esperamos que esto genere cambios importantes para los proveedores
de bases de datos, aunque deberían ser mucho menos graves que los necesarios para la versión 3.0.

Inclusión filtrada
Jefe de desarrollo: @maumar
Seguimiento realizado por #1833
Talla de camiseta: M
Estado: En curso
La inclusión filtrada es una característica muy solicitada (aproximadamente 317 votos; en segunda posición) que
no requiere demasiado trabajo y que creemos que desbloqueará o facilitará escenarios que actualmente requieren
filtros de nivel de modelo o consultas más complejas.

Racionalización de ToTable, ToQuery, ToView, FromSql, etc.


Jefes de desarrollo: @maumar y @smitpatel
Seguimiento realizado por #17270
Talla de camiseta: L
Estado: En curso
Se han realizado avances en versiones anteriores hacia la compatibilidad con SQL sin procesar, tipos sin clave y
áreas relacionadas. Pero hay brechas e incoherencias en el funcionamiento en conjunto de todos los elementos. El
objetivo para la versión 5.0 es corregirlos y crear una buena experiencia para definir, migrar y usar otros tipos de
entidades y sus consultas y artefactos de base de datos asociados. Esto también puede implicar actualizaciones de
la API de consulta compilada.
Tenga en cuenta que este elemento puede dar lugar a algunos cambios importantes en el nivel de la aplicación, ya
que algunas de las funcionalidades actuales son demasiado permisivas, lo que puede conducir rápidamente a que
los usuarios cometan errores. Lo más probable es que parte de esta funcionalidad se bloquee y se proporcionen
instrucciones sobre lo que se debe hacer en su lugar.

Mejoras generales de consultas


Jefes de desarrollo: @smitpatel y @maumar
Seguimiento por problemas etiquetados con area-query en el hito 5.0
Talla de camiseta: XL
Estado: En curso
El código de traducción de consultas se ha reescrito de forma exhaustiva para EF Core 3.0. Por este motivo, el
código de consulta tiene un estado mucho más robusto. Para la versión 5.0, no se planean cambios importantes en
las consultas más allá de los necesarios para admitir TPT y la omisión de propiedades de navegación. Pero se
necesita un trabajo importante para corregir las deudas técnicas generadas tras la revisión de la versión 3.0.
También tenemos previsto corregir muchos errores e implementar pequeñas mejoras para mejorar aún más la
experiencia general de las consultas.

Migraciones y experiencia de implementación


Jefes de desarrollo: @bricelam
Seguimiento realizado por #19587
Talla de camiseta: L
Estado: En curso
En la actualidad, muchos desarrolladores migran sus bases de datos en el momento de inicio de la aplicación. Esto
es fácil, pero no se recomienda porque:
Es posible que varios subprocesos, procesos o servidores intenten migrar la base de datos simultáneamente
Es posible que las aplicaciones intenten acceder a un estado incoherente mientras se produce esta operación
Normalmente, no se deben conceder los permisos de base de datos para modificar el esquema para la
ejecución de la aplicación
Es difícil revertir a un estado limpio si se produce algún error
Queremos ofrecer una mejor experiencia que permita migrar la base de datos de forma sencilla en el momento de
la implementación. Esto debería:
Funcionar en Linux, Mac y Windows
Ser una experiencia positiva en la línea de comandos
Admitir escenarios con contenedores
Funcionar con flujos y herramientas de implementación del mundo real que se usan habitualmente
Integrarse al menos en Visual Studio
Lo más probable es que el resultado sea un gran número de pequeñas mejoras en EF Core (por ejemplo, mejores
migraciones en SQLite), junto con instrucciones y colaboraciones a largo plazo con otros equipos para mejorar las
experiencias de un extremo a otro que van más allá de EF exclusivamente.
Experiencia de las plataformas de EF Core
Jefes de desarrollo: @roji y @bricelam
Seguimiento realizado por #19588
Talla de camiseta: L
Estado: Sin iniciar
Disponemos de instrucciones de calidad para usar EF Core en aplicaciones web tradicionales similares a MVC. Las
instrucciones para otras plataformas y modelos de aplicación no existen o no están actualizadas. Para EF Core 5.0,
el objetivo es investigar, mejorar y documentar la experiencia de uso de EF Core con:
Blazor
Xamarin, incluido el uso del artículo de AOT o vinculador
WinForms, WPF, WinUI y posiblemente otras interfaces de usuario frameworks
Lo más probable es que el resultado sea un gran número de pequeñas mejoras en EF Core, junto con instrucciones
y colaboraciones a largo plazo con otros equipos para mejorar las experiencias de un extremo a otro que van más
allá de EF exclusivamente.
Las áreas concretas que tenemos previsto examinar son las siguientes:
Implementación, incluida la experiencia en el uso de herramientas de EF como para migraciones
Modelos de aplicación, como Xamarin y Blazor, y probablemente otros
Experiencias de SQLite, incluida la experiencia espacial y las recompilaciones de tabla
Experiencias de AOT y vinculación
Integración de diagnósticos, incluidos los contadores de rendimiento

Rendimiento
Jefe de desarrollo: @roji
Seguimiento por problemas etiquetados con area-perf en el hito 5.0
Talla de camiseta: L
Estado: En curso
Para EF Core, el plan es mejorar nuestro conjunto de pruebas comparativas de rendimiento y realizar mejoras de
rendimiento dirigidas al tiempo de ejecución. Además, tenemos previsto completar la nueva API de procesamiento
por lotes de [Link], que se ha creado como prototipo durante el ciclo de versiones de 3.0. En el nivel de
[Link] también se planean mejoras de rendimiento adicionales para el proveedor Npgsql.
Como parte de este trabajo, también está previsto agregar contadores de rendimiento de [Link] y EF Core, y
otros diagnósticos según corresponda.

Documentación de arquitectura y colaboradores


Jefe de documentación: @ajcvickers
Seguimiento realizado por #1920
Talla de camiseta: L
Estado: En curso
La idea es facilitar la comprensión de lo que sucede dentro de EF Core. Esto puede ser útil para cualquiera que
utilice EF Core, pero la motivación principal es facilitarlo para los usuarios externos:
Contribuir al código de EF Core
Crear proveedores de bases de datos
Compilar otras extensiones

Documentación de [Link]
Jefe de documentación: @bricelam
Seguimiento realizado por #1675
Talla de camiseta: M
Estado: Completado. La nueva documentación está activa en el sitio de documentación de Microsoft.
El equipo de EF también posee el proveedor de [Link] [Link]. Tenemos previsto documentar
completamente este proveedor como parte de la versión 5.0.

Documentación general
Jefe de documentación: @ajcvickers
Seguimiento mediante problemas en el repositorio de documentación del hito 5.0
Talla de camiseta: L
Estado: En curso
Ya se ha iniciado el proceso de actualización de la documentación de las versiones 3.0 y 3.1. También se está
trabajando en:
Una revisión de la documentación de introducción para que sea más fácil de seguir
La reorganización de la documentación para facilitar la búsqueda y la adición de referencias cruzadas
La incorporación de más detalles y aclaraciones a la documentación existente
La actualización de los ejemplos y la incorporación de otros nuevos

Corrección de errores
Seguimiento por problemas etiquetados con type-bug en el hito 5.0
Desarrolladores: @roji, @maumar, @bricelam, @smitpatel, @AndriySvyryd, @ajcvickers
Talla de camiseta: L
Estado: En curso
En el momento de escribir este documento, se han evaluado 135 errores para corregirlos en la versión 5.0 (ya se
han corregido 62), pero hay una superposición significativa con la sección anterior Mejoras generales de
consultas.
La velocidad de entrada (problemas que acaban como trabajo en un hito) fue de aproximadamente 23 problemas
al mes en el transcurso de la versión 3.0. No todos se tendrán que corregir en la versión 5.0. Como estimación
aproximada, tenemos previsto corregir unos 150 problemas adicionales para la versión 5.0.

Mejoras menores
Seguimiento por problemas etiquetados con type-enhancement en el hito 5.0
Desarrolladores: @roji, @maumar, @bricelam, @smitpatel, @AndriySvyryd, @ajcvickers
Talla de camiseta: L
Estado: En curso
Además de las características más importantes descritas antes, también hay muchas mejoras más pequeñas
programadas para la versión 5.0 a fin de corregir los "elementos excluidos". Tenga en cuenta que muchas de estas
mejoras también se incluyen en los temas más generales descritos antes.

Nivel inferior
Seguimiento por problemas etiquetados con consider-for-next-release

Se trata de correcciones de errores y mejoras no programadas actualmente para la versión 5.0, pero que se
considerarán objetivos de extensión en función del progreso realizado en el trabajo anterior.
Además, durante la planeación siempre se tienen en cuenta los problemas más votados. Excluir cualquiera de
estos problemas de una versión siempre es complicado, pero necesitamos un plan realista para los recursos que
tenemos.

Comentarios
Sus comentarios sobre la planeación son importantes. La mejor manera de indicar la importancia de un problema
es votar (pulgar) por ese problema en GitHub. Estos datos se introducirán después en el proceso de planeación de
la próxima versión.
Novedades en EF Core 5.0
27/03/2020 • 9 minutes to read • Edit Online

EF Core 5.0 está actualmente en desarrollo. Esta página contendrá información general sobre los cambios
interesantes introducidos en cada versión preliminar.
Esta página no duplica el plan para EF Core 5.0. En el plan se describen los temas generales relativos a EF Core 5.0,
incluido todo lo que estamos planeando incluir antes de publicar la versión final.
A medida que se publique el contenido, se agregarán vínculos que redirigirán de esta página a la documentación
oficial.

Versión preliminar 1
Registro sencillo
Esta característica agrega funcionalidad similar a [Link] en EF6. Es decir, proporciona una manera sencilla
de obtener registros de EF Core sin necesidad de configurar ningún tipo de plataforma de registro externa.
La documentación preliminar se incluye en el estado semanal de EF del 5 de diciembre de 2019.
En el problema n.º 2085 se realiza el seguimiento de la documentación adicional.
Forma sencilla de generar contenido SQL
EF Core 5.0 presenta el método de extensión ToQueryString que devolverá el contenido SQL que EF Core generará
al ejecutar una consulta LINQ.
La documentación preliminar se incluye en el estado semanal de EF del 9 de enero de 2020.
En el problema n.º 1331 se realiza el seguimiento de la documentación adicional.
Uso de un atributo de C# para indicar que una entidad no tiene clave
Ahora se pueden configurar los tipos de entidad para indicar que no tienen clave mediante el nuevo valor
KeylessAttribute . Por ejemplo:

[Keyless]
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public int Zip { get; set; }
}

En el problema n.º 2186 se realiza el seguimiento de la documentación.


Posibilidad de cambiar la conexión o la cadena de conexión en una instancia inicializada de DbContext
Ahora es más fácil crear una instancia de DbContext sin ninguna conexión o cadena de conexión. Además, la
conexión o la cadena de conexión ahora también se pueden mutar en la instancia de contexto. Esto permite que la
misma instancia de contexto se conecte dinámicamente a diferentes bases de datos.
En el problema n.º 2075 se realiza el seguimiento de la documentación.
Proxies de seguimiento de cambios
Ahora EF Core puede generar proxies del entorno de ejecución que implementen automáticamente
INotifyPropertyChanging y INotifyPropertyChanged. A continuación, los cambios de valor en las propiedades de
las entidades se notifican directamente a EF Core, lo cual evita la necesidad de buscar los cambios. Sin embargo, los
proxies vienen con su propio conjunto de limitaciones, por lo que no son para todo el mundo.
En el problema n.º 2076 se realiza el seguimiento de la documentación.
Vistas de depuración mejoradas
Las vistas de depuración son una forma fácil de consultar los aspectos internos de EF Core al depurar problemas.
Hace algún tiempo ya se implementó una vista de depuración para el modelo. En el caso de EF Core 5.0, hemos
facilitado la lectura de la vista de modelo y hemos agregado una nueva vista de depuración para las entidades de
las que se ha realizado un seguimiento en el administrador de estado.
La documentación preliminar se incluye en el estado semanal de EF del 12 de diciembre de 2019.
En el problema n.º 2086 se realiza el seguimiento de la documentación adicional.
Control mejorado de la semántica de valores NULL de base de datos
Normalmente, las bases de datos relacionales tratan NULL como valores desconocidos y, por lo tanto, no son
iguales a otros valores NULL. C#, por otro lado, trata los valores NULL como valores definidos y los compara igual
que con cualquier otro valor NULL. De forma predeterminada, EF Core traduce las consultas para que usen la
semántica de valores NULL de C#. EF Core 5.0 mejora en gran medida la eficacia de dichas traducciones.
En el problema n.º 1612 se realiza el seguimiento de la documentación.
Propiedades del indizador
EF Core 5.0 admite la asignación de propiedades de indizador de C#. Esto permite a las entidades actuar como
bolsas de propiedades en las que las columnas se asignan a las propiedades con nombre en la bolsa.
En el problema n.º 2018 se realiza el seguimiento de la documentación.
Generación de restricciones CHECK para las asignaciones de enumeración
Las migraciones de EF Core 5.0 ahora pueden generar restricciones CHECK para las asignaciones de propiedades
de enumeración. Por ejemplo:

MyEnumColumn VARCHAR(10) NOT NULL CHECK (MyEnumColumn IN('Useful', 'Useless', 'Unknown'))

En el problema n.º 2082 se realiza el seguimiento de la documentación.


IsRelational
Se ha agregado un nuevo método IsRelational , además de los existentes, que son IsSqlServer , IsSqlite y
IsInMemory . Se puede usar para comprobar si DbContext está usando algún proveedor de bases de datos
relacionales. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
if ([Link]())
{
// Do relational-specific model configuration.
}
}

En el problema n.º 2185 se realiza el seguimiento de la documentación.


Simultaneidad optimista de Cosmos con mecanismos ETag
El proveedor de bases de datos de Azure Cosmos DB ya es compatible con la simultaneidad optimista mediante
mecanismos ETag. Utilice el generador de modelos de OnModelCreating para confirmar un mecanismo ETag:
[Link]<Customer>().Property(c => [Link]).IsEtagConcurrency();

Después, SaveChanges generará una excepción DbUpdateConcurrencyException en un conflicto de simultaneidad,


que se podrá manipular, por ejemplo, para implementar reintentos.
En el problema n.º 2099 realiza se el seguimiento de la documentación.
Traducciones de consultas para más construcciones DateTime
Ahora las consultas que contienen la nueva construcción DateTime se traducen.
Además, ahora se asignan las funciones de SQL Server que hay a continuación:
DateDiffWeek
DateFromParts
Por ejemplo:

var count = [Link](c => date > [Link]([Link], 12, 25));

En el problema n.º 2079 realiza se el seguimiento de la documentación.


Traducciones de consultas para más construcciones de matriz de bytes
Las consultas que usan Contains, Length, SequenceEqual, etc. en las propiedades byte[] ahora se traducen a SQL.
La documentación preliminar se incluye en el estado semanal de EF del 5 de diciembre de 2019.
En el problema n.º 2079 se realiza el seguimiento de la documentación adicional.
Traducción de consultas para Reverse
Las consultas que usan Reverse ahora se traducen. Por ejemplo:

[Link](e => [Link]).Reverse()

En el problema n.º 2079 realiza se el seguimiento de la documentación.


Traducción de consultas para operadores bit a bit
Las consultas que usan operadores bit a bit ahora se traducen en más casos, por ejemplo:

[Link](o => ~[Link] == negatedId)

En el problema n.º 2079 realiza se el seguimiento de la documentación.


Traducción de consultas para cadenas en Cosmos
Al emplear el proveedor Azure Cosmos DB, las consultas que usan los métodos de cadena Contains, StartsWith y
EndsWith ahora se traducen.
En el problema n.º 2079 realiza se el seguimiento de la documentación.
Características nuevas de Entity Framework Core 3.0
08/04/2020 • 13 minutes to read • Edit Online

En la lista siguiente se incluyen las principales características nuevas de EF Core 3.0.


Como versión principal, EF Core 3.0 también presenta varios cambios importantes, que son mejoras en la API que
podrían afectar negativamente a las aplicaciones existentes.

Revisión de LINQ
LINQ permite escribir consultas a la base de datos en el lenguaje .NET que prefiera, con lo que se aprovecha la
información de tipo enriquecido para ofrecer la comprobación de IntelliSense y de tipos en tiempo de compilación.
Pero LINQ también permite escribir un número ilimitado de consultas complicadas que contienen expresiones
arbitrarias (llamadas a métodos u operaciones). Cómo controlar todas esas combinaciones es el principal desafío
para los proveedores LINQ.
En EF Core 3,0, hemos rediseñado nuestro proveedor LINQ para habilitar la conversión de más patrones de
consulta en SQL, la generación de consultas eficientes en más casos y la prevención de que las consultas ineficaces
no se detecten. El nuevo proveedor LINQ es la base sobre la que podremos ofrecer nuevas funcionalidades de
consulta y mejoras de rendimiento en futuras versiones, sin interrumpir las aplicaciones y los proveedores de datos
existentes.
Evaluación de cliente restringida
El cambio de diseño más importante tiene que ver con la forma en que manejamos las expresiones LINQ que no se
pueden convertir a parámetros ni traducir a SQL.
En las versiones anteriores, EF Core identificada qué partes de una consulta se podían traducir a SQL y ejecutaba el
resto de la consulta en el cliente. Este tipo de ejecución en el lado cliente es una opción interesante en algunas
situaciones, pero en muchos otros casos puede dar lugar a consultas ineficaces.
Por ejemplo, si EF Core 2.2 no podía traducir un predicado en una llamada a Where() , ejecutaba una instrucción
SQL sin filtro, transfería todas las filas de la base de datos y luego las filtraba en memoria:

var specialCustomers = [Link]


.Where(c => [Link](n) && IsSpecialCustomer(c));

Esta operación puede ser aceptable si la base de datos contiene pocas filas, pero puede dar lugar a problemas de
rendimiento considerables o incluso errores en la aplicación si la base de datos contiene muchas filas.
En EF Core 3.0 hemos restringido la evaluación de cliente para que solo suceda en la proyección de nivel superior
(fundamentalmente, la última llamada a Select() ). Cuando EF Core 3.0 detecta expresiones que no se pueden
traducir en ningún otro lugar de la consulta, produce una excepción en tiempo de ejecución.
Para evaluar una condición de predicado en el cliente como en el ejemplo anterior, los desarrolladores ahora tienen
que cambiar explícitamente la evaluación de la consulta a LINQ to Objects:

var specialCustomers = [Link]


.Where(c => [Link](n))
.AsEnumerable() // switches to LINQ to Objects
.Where(c => IsSpecialCustomer(c));

Consulte la documentación sobre cambios importantes para más detalles sobre cómo esto puede afectar a las
aplicaciones existentes.
Instrucción SQL única por consulta LINQ
Otro aspecto del diseño que cambió significativamente en la versión 3.0 es que ahora siempre se genera una única
instrucción SQL por cada consulta LINQ. En versiones anteriores, se usaba para generar varias instrucciones SQL en
ciertos casos, llamadas Include() traducidas en las propiedades de navegación de la colección y consultas
traducidas que seguían determinados patrones con subconsultas. Aunque en ocasiones este diseño resultaba
práctico y, en el caso de Include() , incluso ayudaba a evitar el envío de datos redundantes a través de la conexión,
la implementación era compleja y se producían algunos comportamientos considerablemente ineficaces (consultas
N+1). Había situaciones en las que los datos devueltos en varias consultas eran incoherentes en potencia.
De forma similar a la evaluación del cliente, si EF Core 3.0 no puede convertir una consulta LINQ en una única
instrucción SQL, se inicia una excepción en tiempo de ejecución. Pero hicimos que EF Core fuera capaz de traducir
muchos de los patrones comunes que solían generar varias consultas en una sola consulta con JOIN.

Compatibilidad con Cosmos DB


Con el proveedor de Cosmos DB para EF Core, los desarrolladores que están familiarizados con el modelo de
programación de EF puedan usar fácilmente Azure Cosmos DB como base de datos de aplicación. El objetivo es
hacer que algunas de las ventajas de Cosmos DB, como la distribución global, la disponibilidad "AlwaysOn", la
escalabilidad elástica y la baja latencia, sean aún más accesibles para los desarrolladores de .NET. El proveedor
habilita la mayoría de las características de EF Core, como el seguimiento automático de cambios, LINQ y
conversiones de valores, en comparación con SQL API de Cosmos DB.
Consulte la documentación del proveedor Cosmos DB para más detalles.

Compatibilidad con C# 8.0


EF Core 3.0 aprovecha varias características nuevas de C# 8.0:
Secuencias asincrónicas
Los resultados de la consulta asincrónica se exponen ahora mediante la nueva interfaz de IAsyncEnumerable<T>
estándar y se pueden usar con await foreach .

var orders =
from o in [Link]
where [Link] == [Link]
select o;

await foreach(var o in [Link]())


{
Process(o);
}

Consulte las transmisiones asíncronas en la documentación de C# para más detalles.


Tipos de referencia que aceptan valores NULL
Cuando esta nueva característica está habilitada en el código, EF Core examina la nulabilidad de las propiedades de
tipo de referencia y la aplica a las columnas y relaciones correspondientes en la base de datos: las propiedades de
los tipos de referencia no anulables se tratan como si tuvieran el atributo de anotación de datos [Required] .
Por ejemplo, en la clase siguiente, las propiedades marcadas como de tipo string? se configurarán como
opcionales, mientras que string se configurará según sea necesario:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string? MiddleName { get; set; }
}

Consulte Trabajar con tipos de referencia que aceptan valores NULL en la documentación de EF Core para más
detalles.

Intercepción de operaciones de bases de datos


La nueva API de intercepción en EF Core 3.0 permite proporcionar una lógica personalizada que se invoca
automáticamente cada vez que se producen operaciones de base de datos de bajo nivel como parte del
funcionamiento normal de EF Core. Por ejemplo, al abrir conexiones, confirmar transacciones o ejecutar comandos.
De manera similar a las características de intercepción que existían en EF 6, los interceptores le permiten interceptar
operaciones antes o después de que sucedan. Cuando las intercepta antes de que sucedan, puede omitir la
ejecución y proporcionar resultados alternativos de la lógica de intercepción.
Por ejemplo, para manipular el texto del comando, puede crear IDbCommandInterceptor :

public class HintCommandInterceptor : DbCommandInterceptor


{
public override InterceptionResult ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult result)
{
// Manipulate the command text, etc. here...
[Link] += " OPTION (OPTIMIZE FOR UNKNOWN)";
return result;
}
}

Y registrarlo con su DbContext :

[Link](b => b
.UseSqlServer(connectionString)
.AddInterceptors(new HintCommandInterceptor()));

Ingeniería inversa de vistas de base de datos


El nombre de los tipos de consulta, que representan datos que pueden leerse de la base de datos pero no
actualizarse, se ha cambiado a tipos de entidad sin clave. Como son una excelente opción para asignar vistas de
bases de datos en la mayoría de los escenarios, EF Core ahora crea automáticamente tipos de entidades sin clave
cuando se invierten las vistas de bases de datos de ingeniería.
Por ejemplo, con la herramienta de línea de comandos dotnet ef, puede escribir:

dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;"


[Link]

Y la herramienta ahora anulará automáticamente los tipos de scaffold para vistas y tablas sin claves:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
[Link]<Names>(entity =>
{
[Link]();
[Link]("Names");
});

[Link]<Things>(entity =>
{
[Link]();
});
}

Ahora, las entidades dependientes que comparten la tabla con la


entidad de seguridad son opcionales
A partir de la versión EF Core 3.0, si OrderDetails pertenece a Order o está asignado a la misma tabla
explícitamente, será posible agregar Order sin OrderDetails ; todas las propiedades OrderDetails , salvo la clave
principal, se asignarán a columnas que aceptan valores NULL.
Al realizar consultas, EF Core establecerá OrderDetails en null si ninguna de las propiedades necesarias tiene un
valor, o bien no tiene las propiedades necesarias más allá de la clave principal y todas las propiedades son null .

public class Order


{
public int Id { get; set; }
public int CustomerId { get; set; }
public OrderDetails Details { get; set; }
}

[Owned]
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}

EF 6.3 en .NET Core


Esta no es realmente una característica de EF Core 3.0, pero creemos que es importante para muchos de nuestros
clientes actuales.
Entendemos que muchas aplicaciones existentes utilizan versiones anteriores de EF y que portarlas a EF Core solo
para aprovechar las ventajas de .NET Core puede requerir un esfuerzo considerable. Por ese motivo, decidimos
portar a la versión más reciente de EF 6 para que se ejecute en .NET Core 3.0.
Para más detalles, consulte Novedades de EF 6.

Características pospuestas
Algunas características planeadas originalmente para EF Core 3.0 se pospusieron para versiones futuras:
Capacidad de omitir partes de un modelo en migraciones, con seguimiento realizado a través del problema nº
2725.
Entidades contenedoras de propiedades, de las que se realiza un seguimiento a través de dos problemas
independientes: nº 9914 sobre las entidades de tipo compartido y nº 13610 sobre la compatibilidad con la
asignación de propiedades indizadas.
Cambios importantes incluidos en EF Core 3.0
08/04/2020 • 88 minutes to read • Edit Online

Es posible que los siguientes cambios de API y comportamiento interrumpan las aplicaciones actuales cuando se
actualicen a la versión 3.0.0. Los cambios que esperamos que solo afecten a proveedores de base de datos se
documentan en Cambios para proveedores.

Resumen
C A M B IO IM P O RTA N T E IM PA C TO

Las consultas LINQ ya no se evalúan en el cliente Alto

EF Core 3.0 tiene como destino .NET Standard 2.1, y no .NET Alto
Standard 2.0

La herramienta de línea de comandos de EF Core, dotnet ef, Alto


ya no forma parte del SDK de .NET Core

DetectChanges respeta los valores de clave generados por el Alto


almacén

FromSql, ExecuteSql y ExecuteSqlAsync han cambiado de Alto


nombre

Los tipos de consulta se consolidan con tipos de entidad Alto

Entity Framework Core ya no forma parte del marco Media


compartido [Link] Core

Las eliminaciones en cascada ahora se realizan Media


inmediatamente de forma predeterminada

La carga diligente de entidades relacionadas ahora se realiza Media


en una sola consulta

[Link] tiene una semántica más limpia Media

La API de configuración para las relaciones de tipo de Media


propiedad ha cambiado

Cada propiedad usa la generación de claves enteras en Media


memoria independiente

Las consultas sin seguimiento ya no realizan la resolución de Media


la identidad

Cambios en la API de metadatos Media

Cambios en la API de metadatos específicos del proveedor Media


C A M B IO IM P O RTA N T E IM PA C TO

Se ha quitado el elemento UseRowNumberForPaging Media

Cuando el método FromSql se usa con un procedimiento Media


almacenado no se puede redactar

Solo se pueden especificar métodos de FromSql en raíces de Bajo


consulta

La ejecución de consultas se registra en el nivel de depuración Bajo


Revertido

Los valores de clave temporal ya no se establecen en Bajo


instancias de entidad

Las entidades dependientes que comparten la tabla con la Bajo


entidad de seguridad son ahora opcionales

Todas las entidades que compartan una tabla con una Bajo
columna de token de simultaneidad tienen que asignarla a
una propiedad

Las entidades en propiedad no se pueden consultar sin el Bajo


propietario mediante una consulta de seguimiento

Las propiedades heredadas de tipos sin asignar se asignan Bajo


ahora a una única columna para todos los tipos derivados

La convención de propiedad de clave externa ya no coincide Bajo


con el mismo nombre que la propiedad de entidad de
seguridad

La conexión de base de datos ahora se cierra si ya no se usa Bajo


antes de que se complete TransactionScope

Los campos de respaldo se usan de forma predeterminada Bajo

Inicio de excepciones si se encuentran varios campos de Bajo


respaldo compatibles

Los nombres de propiedades de solo campo deben coincidir Bajo


con el nombre del campo

AddDbContext/AddDbContextPool ya no llaman a Bajo


AddLogging ni a AddMemoryCache

AddEntityFramework* agrega IMemoryCache con un límite Bajo


de tamaño

[Link] realiza ahora una operación DetectChanges Bajo


local

El cliente no genera las claves de matriz de cadena y byte de Bajo


forma predeterminada
C A M B IO IM P O RTA N T E IM PA C TO

ILoggerFactory es ahora un servicio con ámbito Bajo

En los proxies de carga diferida ya no se supone que las Bajo


propiedades de navegación están totalmente cargadas

La creación excesiva de proveedores de servicios internos Bajo


ahora es un error de forma predeterminada

Comportamiento nuevo de HasOne/HasMany llamado con Bajo


una sola cadena

El tipo de valor devuelto para varios métodos asincrónicos se Bajo


ha cambiado de Task a ValueTask

La anotación Relational:TypeMapping ahora es simplemente Bajo


TypeMapping

ToTable en un tipo derivado produce una excepción Bajo

EF Core ya no envía pragma para el cumplimiento de SQLite Bajo


FK

[Link] ahora depende de Bajo


SQLitePCLRaw.bundle_e_sqlite3

Los valores GUID se almacenan ahora como TEXT en SQLite Bajo

Los valores char se almacenan ahora como TEXT en SQLite Bajo

Los id. de migración ahora se generan usando el calendario Bajo


de la referencia cultural invariable

La información o los metadatos de la extensión se han Bajo


quitado de IDbContextOptionsExtension

LogQueryPossibleExceptionWithAggregateOperator ha Bajo
cambiado de nombre

Clarificación de la API para nombres de restricciones de claves Bajo


externas

[Link]/HasTablesAsync se han Bajo


hecho públicos

[Link] es ahora un paquete Bajo


DevelopmentDependency

[Link] se ha actualizado a la versión 2.0.0 Bajo

NetTopologySuite se actualizó a la versión 2.0.0 Bajo

Se usa [Link] en lugar de Bajo


[Link]
C A M B IO IM P O RTA N T E IM PA C TO

Se deben configurar varias relaciones de referencia Bajo


automática ambiguas

[Link] es NULL o la cadena vacía lo configura Bajo


para estar en el esquema predeterminado del modelo

Las consultas LINQ ya no se evalúan en el cliente


Problema de seguimiento n.° 14935 Consulte también el problema n.° 12795
Compor tamiento anterior
Antes de 3.0, cuando en EF Core no se podía convertir una expresión que formaba parte de una consulta SQL o un
parámetro, la expresión se evaluaba de forma automática en el cliente. De forma predeterminada, la evaluación de
cliente de expresiones potencialmente costosas solo desencadenaba una advertencia.
Compor tamiento nuevo
A partir de 3.0, en EF Core solo se permite que se evalúen en el cliente las expresiones en la proyección de nivel
superior (la última llamada a Select() de la consulta). Cuando las expresiones de otra parte de la consulta no se
pueden convertir en SQL o un parámetro, se inicia una excepción.
Por qué
La evaluación de cliente automática de las consultas permite que se ejecuten muchas consultas incluso si no se
pueden convertir elementos importantes de ellas. Esto puede provocar un comportamiento inesperado y
potencialmente dañino que es posible que solo sea evidente en entornos de producción. Por ejemplo, una
condición en una llamada a Where() que no se puede convertir puede provocar que todas las filas de la tabla se
transfieran desde el servidor de base de datos y que el filtro se aplique en el cliente. Esta situación puede pasar
desapercibida fácilmente si la tabla solo contiene algunas filas en la fase de desarrollo, pero ser más grave cuando
la aplicación pase a producción, donde la tabla puede contener millones de filas. Las advertencias de evaluación
de cliente también se suelen pasar por alto durante el desarrollo.
Además de esto, la evaluación de cliente automática puede causar problemas en los que la mejora de la
traducción de consultas para expresiones específicas provocaba cambios importantes no deseados entre
versiones.
Mitigaciones
Si una consulta no se puede traducir totalmente, vuelva a escribirla en un formato que se pueda traducir, o bien
use AsEnumerable() , ToList() o una función similar para devolver los datos al cliente de forma explícita, donde
después se puedan seguir procesando mediante LINQ to Objects.
EF Core 3.0 tiene como destino .NET Standard 2.1, y no .NET Standard 2.0
Problema de seguimiento n.º 15498

IMPORTANT
EF Core 3.1 vuelve a tener como objetivo a .NET Standard 2.0. Esto reincorpora la compatibilidad con .NET Framework.

Compor tamiento anterior


Antes de la versión 3.0, EF Core tenía como destino .NET Standard 2.0 y se podía ejecutar en todas las plataformas
que admitieran dicho estándar, incluido .NET Framework.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core tiene como destino .NET Standard 2.1 y se puede ejecutar en todas las
plataformas que admitan dicho estándar. Esto no incluye .NET Framework.
Por qué
Esto forma parte de una decisión estratégica para todas las tecnologías de .NET que tiene como objetivo centrar
los esfuerzos en .NET Core y otras plataformas modernas de .NET, como Xamarin.
Mitigaciones
Use EF Core 3.1.
Entity Framework Core ya no forma parte del marco compartido [Link] Core
Anuncios del problema de seguimiento n.º 325
Compor tamiento anterior
Antes de [Link] Core 3.0, cuando se agregaba una referencia de paquete a [Link] o
[Link] , se incluía EF Core y algunos de los proveedores de datos de EF Core, como el de SQL
Server.
Compor tamiento nuevo
A partir de la versión 3.0, el marco compartido [Link] Core no incluye EF Core ni ningún proveedor de datos de
EF Core.
Por qué
Antes de este cambio, para obtener EF Core se necesitaban varios pasos en función de si la aplicación se destinaba
a [Link] Core y SQL Server o no. Además, la actualización de [Link] Core forzaba la de EF Core y el proveedor
de SQL Server, lo que no siempre es deseable.
Con este cambio, la experiencia de obtención de EF Core es la misma en todos los proveedores, implementaciones
admitidas de .NET y tipos de aplicación. Ahora los desarrolladores también pueden controlar exactamente cuándo
se actualizan EF Core y los proveedores de datos de EF Core.
Mitigaciones
Para usar EF Core en una aplicación [Link] Core 3.0 o cualquier otra aplicación compatible, debe agregar de
forma explícita una referencia de paquete al proveedor de base de datos de EF Core que se va a usar en la
aplicación.
La herramienta de línea de comandos de EF Core, dotnet ef, ya no forma parte del SDK de .NET Core
Problema de seguimiento n.º 14016
Compor tamiento anterior
Antes de 3.0, la herramienta dotnet ef se incluía en el SDK de .NET Core y estaba disponible para usarse desde la
línea de comandos de cualquier proyecto sin necesidad de realizar pasos adicionales.
Compor tamiento nuevo
A partir de la versión 3.0, el SDK de .NET no incluye la herramienta dotnet ef , por lo que antes de poder usarla
tendrá que instalarla de forma explícita como una herramienta local o global.
Por qué
Este cambio nos permite distribuir y actualizar dotnet ef como una herramienta convencional de la CLI de .NET
en NuGet, coherente con el hecho de que la versión 3.0 de EF Core también se distribuye siempre como un
paquete NuGet.
Mitigaciones
Para poder administrar las migraciones o aplicar scaffolding a DbContext , instale dotnet-ef como herramienta
global:

$ dotnet tool install --global dotnet-ef

También se puede obtener una herramienta local cuando se restauran las dependencias de un proyecto que la
declara como una dependencia de herramientas mediante un archivo de manifiesto de herramientas.
FromSql, ExecuteSql y ExecuteSqlAsync han cambiado de nombre
Problema de seguimiento n.º 10996
Compor tamiento anterior
Antes de EF Core 3.0, estos nombres de métodos se sobrecargaban para funcionar tanto con una cadena normal
como con una cadena que se debería interpolar en SQL y parámetros.
Compor tamiento nuevo
A partir de la versión EF Core 3.0, use FromSqlRaw , ExecuteSqlRaw y ExecuteSqlRawAsync para crear una consulta
con parámetros donde los parámetros se pasan por separado de la cadena de consulta. Por ejemplo:

[Link](
"SELECT * FROM Products WHERE Name = {0}",
[Link]);

Use FromSqlInterpolated , ExecuteSqlInterpolated y ExecuteSqlInterpolatedAsync para crear una consulta con


parámetros donde los parámetros se pasan como parte de una cadena de consulta interpolada. Por ejemplo:

[Link](
$"SELECT * FROM Products WHERE Name = {[Link]}");

Tenga en cuenta que las dos consultas anteriores producirán el mismo código SQL parametrizado con los mismos
parámetros SQL.
Por qué
Las sobrecargas del método como esta facilitan las llamadas accidentales al método de cadena sin procesar
cuando la intención era llamar al método de cadena interpolada y viceversa. Esto podría resultar en consultas que
no se parametrizan cuando deberían.
Mitigaciones
Haga el cambio para usar los nuevos nombres de métodos.
Cuando el método FromSql se usa con un procedimiento almacenado no se puede redactar
Problema de seguimiento n.° 15392
Compor tamiento anterior
Antes de EF Core 3.0, el método FromSql intentaba detectar si se podía redactar en el código SQL pasado. Cuando
el código SQL no se podía redactar, como un procedimiento almacenado, realizaba la evaluación de cliente. La
consulta siguiente funcionaba al ejecutar el procedimiento almacenado en el servidor y aplicar FirstOrDefault en
el lado cliente.
[Link]("[dbo].[Ten Most Expensive Products]").FirstOrDefault();

Compor tamiento nuevo


A partir de EF Core 3.0, EF Core no intentará analizar el código SQL. Por tanto, si va a redactar después de
FromSqlRaw/FromSqlInterpolated, EF Core redactará el código SQL generando una subconsulta. Por tanto, si usa
un procedimiento almacenado con la redacción, obtendrá una excepción de sintaxis de SQL no válida.
Por qué
EF Core 3.0 no admite la evaluación automática de cliente, ya que era propenso a errores, como se explica aquí.
Mitigación
Si usa un procedimiento almacenado en FromSqlRaw/FromSqlInterpolated, sabe que no se puede redactar, por lo
que puede agregar AsEnumerable/AsAsyncEnumerable justo después de la llamada al método FromSql para
evitar cualquier redacción en el lado servidor.

[Link]("[dbo].[Ten Most Expensive Products]").AsEnumerable().FirstOrDefault();

Solo se pueden especificar métodos de FromSql en raíces de consulta.


Problema de seguimiento n.° 15704
Compor tamiento anterior
Antes de EF Core 3.0, el método FromSql podía especificarse en cualquier lugar en la consulta.
Compor tamiento nuevo
A partir de EF Core 3.0, los nuevos métodos FromSqlRaw y FromSqlInterpolated (que reemplazan a FromSql ) solo
pueden especificarse en las raíces de la consulta, es decir, directamente en DbSet<> . Si intenta especificarlos en
cualquier otro lugar se producirá un error de compilación.
Por qué
La especificación de FromSql en cualquier otro lugar diferente de DbSet no tenía un significado o valor agregado,
y podría causar ambigüedad en ciertos escenarios.
Mitigaciones
Las invocaciones de FromSql se deben mover para que estén directamente en el DbSet al que se aplican.
Las consultas sin seguimiento ya no realizan la resolución de la identidad
Problema de seguimiento n.º 13518
Compor tamiento anterior
Antes de EF Core 3.0, se usaba la misma instancia de la entidad para cada aparición de una entidad con un tipo e
identificador determinados. Este comportamiento coincide con el de las consultas de seguimiento. Por ejemplo,
esta consulta:

var results = [Link](e => [Link]).AsNoTracking().ToList();

Esta consulta devolverá la misma instancia de Category para cada elemento Product asociado con la categoría
determinada.
Compor tamiento nuevo
A partir de EF Core 3.0, se crean distintas instancias de la entidad si se encuentra una entidad con un tipo e
identificador determinados en varias ubicaciones del gráfico devuelto. Por ejemplo, la consulta anterior ahora
devolverá una nueva instancia de Category para cada elemento Product cuando haya dos productos asociados a
la misma categoría.
Por qué
La resolución de las identidades (es decir, el hecho de determinar que una entidad tiene los mismos tipo e
identificador que la entidad encontrada) agrega más rendimiento y sobrecarga de memoria. Este enfoque suele
ser contrario a por qué las consultas sin seguimiento se usan en primer lugar. Además, aunque la resolución de las
identidades a veces puede resultar útil, no es necesaria si las entidades se van a serializar y enviar a un cliente,
algo habitual para las consultas sin seguimiento.
Mitigaciones
Si se requiere la resolución de identidad, use una consulta de seguimiento.
La ejecución de consultas se registra en el nivel de depuración Revertido
Problema de seguimiento n.º 14523
Revertimos este cambio porque la nueva configuración de EF Core 3.0 permite a la aplicación especificar el nivel
de registro para cualquier evento. Por ejemplo, para cambiar el registro de SQL a Debug , configure el nivel de
forma explícita en OnConfiguring o AddDbContext :

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.UseSqlServer(connectionString)
.ConfigureWarnings(c => [Link](([Link], [Link])));

Los valores de clave temporal ya no se establecen en instancias de entidad


Problema de seguimiento n.º 12378
Compor tamiento anterior
Antes de EF Core 3.0, los valores temporales se asignaban a todas las propiedades de clave para las que
posteriormente la base de datos generaba un valor real. Normalmente, estos valores temporales eran números
negativos grandes.
Compor tamiento nuevo
A partir de la versión 3.0, en EF Core se almacena el valor de clave temporal como parte de la información de
seguimiento de la entidad y la propiedad clave en sí no se modifica.
Por qué
Este cambio se ha realizado para evitar que los valores de clave temporales se conviertan erróneamente en
permanentes cuando una entidad de la que previamente una instancia de DbContext ha realizado el seguimiento
se mueve a otra instancia de DbContext .
Mitigaciones
Las aplicaciones que asignan valores de clave principal a claves externas para crear asociaciones entre entidades
pueden depender del comportamiento anterior si las claves principales son generadas por el almacén y
pertenecen a entidades en el estado Added . Esto se puede evitar con las siguientes situaciones:
No se usan claves generadas por el almacén.
Se establecen propiedades de navegación para crear relaciones en lugar de establecer valores de clave externa.
Se obtienen los valores de clave temporal reales de la información de seguimiento de la entidad. Por ejemplo,
[Link](blog).Property(e => [Link]).CurrentValue devolverá el valor temporal aunque no se haya
establecido [Link] .
DetectChanges respeta los valores de clave generados por el almacén
Problema de seguimiento n.º 14616
Compor tamiento anterior
Antes de EF Core 3.0, se realizaba el seguimiento en el estado DetectChanges de las entidades sin seguimiento
detectadas por Added y se insertaban como una fila nueva cuando se llamaba a SaveChanges .
Compor tamiento nuevo
A partir de EF Core 3.0, si una entidad usa valores de clave generados y se establece un valor de clave, se realizará
el seguimiento de la entidad en el estado Modified . Esto significa que se supone que existe una fila para la
entidad y que se actualizará cuando se llame a SaveChanges . Si no se establece el valor de clave, o bien si el tipo
de entidad no usa claves generadas, se seguirá realizando el seguimiento de la entidad nueva como Added al
igual que en las versiones anteriores.
Por qué
Este cambio se ha realizado para que sea más sencillo y coherente trabajar con gráficos de entidades
desconectadas mientras se usan claves generadas por el almacén.
Mitigaciones
Este cambio puede interrumpir una aplicación si se configura un tipo de entidad para usar claves generadas, pero
se establecen de forma explícita valores de clave para las instancias nuevas. La solución consiste en configurar de
forma explícita las propiedades de clave para que no usen valores generados. Por ejemplo, con la API fluida:

modelBuilder
.Entity<Blog>()
.Property(e => [Link])
.ValueGeneratedNever();

O bien con anotaciones de datos:

[DatabaseGenerated([Link])]
public string Id { get; set; }

Las eliminaciones en cascada ahora se realizan inmediatamente de forma predeterminada


Problema de seguimiento n.º 10114
Compor tamiento anterior
Antes de la versión 3.0, en EF Core no se aplicaban acciones en cascada (eliminación de entidades dependientes
cuando se eliminaba una entidad de seguridad obligatoria o cuando se rompía la relación con una entidad de
seguridad obligatoria) hasta que se llamaba a SaveChanges.
Compor tamiento nuevo
A partir de 3.0, en EF Core las acciones en cascada se aplican en cuanto se detecta la condición desencadenadora.
Por ejemplo, como resultado de la llamada a [Link]() para eliminar una entidad de seguridad, todos los
dependientes obligatorios relacionados de los que se realiza el seguimiento también se establecen en Deleted
inmediatamente.
Por qué
Este cambio se ha realizado para mejorar la experiencia en escenarios de auditoría y enlace de datos, donde es
importante comprender qué entidades se van a eliminar antes de llamar a SaveChanges .
Mitigaciones
El comportamiento anterior se puede restaurar mediante opciones de [Link] . Por ejemplo:

[Link] = [Link];
[Link] = [Link];

La carga diligente de entidades relacionadas ahora se realiza en una sola consulta


Problema de seguimiento n.º 18022
Compor tamiento anterior
Antes de la versión 3.0, la carga diligente de navegaciones de colección a través de operadores Include
provocaba la generación de varias consultas en la base de datos relacional, una para cada tipo de entidad
relacionada.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core genera una sola consulta con operadores JOIN en las bases de datos
relacionales.
Por qué
La emisión de varias consultas para implementar una única consulta LINQ provocaba numerosos problemas,
incluido el rendimiento negativo, ya que se necesitaban varios recorridos de ida y vuelta a la base de datos, y
problemas de coherencia de datos, ya que cada consulta podía observar un estado distinto de la base de datos.
Mitigaciones
Aunque técnicamente esto no es un cambio importante, podría tener un efecto considerable en el rendimiento de
la aplicación cuando una sola consulta contiene un gran número de operadores Include en las navegaciones de
la colección. Vea este comentario para obtener más información y para volver a escribir las consultas de una
manera más eficaz.
**
[Link] tiene una semántica más limpia
Problema de seguimiento n.º 12661
Compor tamiento anterior
Antes de la versión 3.0, [Link] creaba claves externas en la base de datos con la semántica
Restrict , pero también realizaba una corrección interna de manera no evidente.

Compor tamiento nuevo


A partir de la versión 3.0, [Link] garantiza que las claves externas se crean con la semántica
Restrict , es decir, sin cascadas y realizando una infracción de restricción, sin llevar a cabo correcciones internas
de EF.
Por qué
Este cambio se realizó para mejorar la experiencia de uso de DeleteBehavior de manera intuitiva sin efectos
secundarios inesperados.
Mitigaciones
El comportamiento anterior se puede restaurar con [Link] .
Los tipos de consulta se consolidan con tipos de entidad
Problema de seguimiento n.º 14194
Compor tamiento anterior
Antes de EF Core 3.0, los tipos de consulta eran un medio para consultar los datos que no definen una clave
principal de una manera estructurada. Es decir, un tipo de consulta se usaba para asignar tipos de entidad sin
claves (más probablemente desde una vista, pero posiblemente desde una tabla), mientras que un tipo de entidad
estándar se usaba cuando había una clave disponible (más probablemente desde una tabla, pero posiblemente
desde una vista).
Compor tamiento nuevo
Ahora un tipo de consulta se convierte en un tipo de entidad sin una clave principal. Los tipos de entidad sin clave
tienen la misma funcionalidad que los tipos de consulta de las versiones anteriores.
Por qué
Este cambio se ha realizado para reducir la confusión en torno a la finalidad de los tipos de consulta. En concreto,
son tipos de entidad sin clave y, por ello, intrínsecamente son de solo lectura, pero no se deben usar solo porque
un tipo de entidad tenga que ser de solo lectura. Del mismo modo, se suelen asignar a vistas, pero solo porque las
vistas no suelen definir claves.
Mitigaciones
Los elementos siguientes de la API ahora están obsoletos:
[Link]<>() : en su lugar es necesario llamar a [Link]<>().HasNoKey() para marcar
un tipo de entidad como sin claves. Esto todavía no se configurará por convención para evitar una
configuración incorrecta cuando se espera una clave principal, pero no coincide con la convención.
DbQuery<> : en su lugar se debe usar DbSet<> .
[Link]<>() : en su lugar se debe usar [Link]<>() .

La API de configuración para las relaciones de tipo de propiedad ha cambiado


Problema de seguimiento n.º 12444 Problema de seguimiento n.º 9148 Problema de seguimiento n.º 14153
Compor tamiento anterior
Antes de EF Core 3.0, la configuración de la relación de propiedad se realizaba directamente después de la
llamada a OwnsOne o OwnsMany .
Compor tamiento nuevo
A partir de EF Core 3.0, ahora hay una API fluida para configurar una propiedad de navegación para el propietario
mediante WithOwner() . Por ejemplo:

[Link]<Order>.OwnsOne(e => [Link]).WithOwner(e => [Link]);

La configuración relacionada con la relación entre el propietario y lo que se posee ahora se debe encadenar
después de WithOwner() , de forma similar a cómo se configuran otras relaciones. Pero la configuración del propio
tipo de propiedad se seguirá encadenando después de OwnsOne()/OwnsMany() . Por ejemplo:
[Link]<Order>.OwnsOne(e => [Link], eb =>
{
[Link]()
.HasForeignKey(e => [Link])
.HasConstraintName("FK_OrderDetails");

[Link]("OrderDetails");
[Link](e => [Link]);
[Link](e => [Link]);

[Link](e => [Link]).WithOne();

[Link](
new OrderDetails
{
AlternateId = 1,
Id = -1
});
});

Además, la llamada a Entity() , HasOne() o Set() con un tipo de propiedad de destino ahora iniciará una
excepción.
Por qué
Este cambio se ha realizado para crear una separación más clara entre la configuración del propio tipo de
propiedad y la relación con el tipo de propiedad. A su vez, esto elimina la ambigüedad y la confusión de métodos
como HasForeignKey .
Mitigaciones
Cambie la configuración de las relaciones de tipo de propiedad para usar la nueva superficie de API, como se
muestra en el ejemplo anterior.
Ahora, las entidades dependientes que comparten la tabla con la entidad de seguridad son opcionales
Problema de seguimiento n.º 9005
Compor tamiento anterior
Considere el modelo siguiente:

public class Order


{
public int Id { get; set; }
public int CustomerId { get; set; }
public OrderDetails Details { get; set; }
}

public class OrderDetails


{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}

Antes de EF Core 3.0, si OrderDetails era propiedad de Order o estaba asignado explícitamente a la misma tabla,
siempre era necesaria una instancia de OrderDetails al agregar un elemento Order nuevo.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core permite agregar Order sin OrderDetails y asigna todas las propiedades
OrderDetails a excepción de la clave principal a columnas que aceptan valores NULL. Al realizar consultas, EF
Core establece OrderDetails en null si ninguna de las propiedades necesarias tiene un valor o si no tiene
propiedades necesarias más allá de la clave principal y todas las propiedades son null .
Mitigaciones
Si el modelo tiene una tabla que comparte dependencias con todas las columnas opcionales, pero la navegación
que apunta a ella no se espera que sea null , la aplicación debería modificarse para controlar los casos en los que
la navegación sea null . Si esto no es posible, debería agregarse una propiedad necesaria al tipo de entidad o, al
menos, una entidad debería tener un valor distinto a null asignado.
Todas las entidades que compartan una tabla con una columna de token de simultaneidad tienen que asignarla
a una propiedad
Problema de seguimiento n.º 14154
Compor tamiento anterior
Considere el modelo siguiente:

public class Order


{
public int Id { get; set; }
public int CustomerId { get; set; }
public byte[] Version { get; set; }
public OrderDetails Details { get; set; }
}

public class OrderDetails


{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Order>()
.Property(o => [Link]).IsRowVersion().HasColumnName("Version");
}

Antes de EF Core 3.0, si OrderDetails era propiedad de Order o estaba asignado explícitamente a la misma tabla,
si solo se actualizaba OrderDetails , no se actualizaba el valor Version en el cliente y se producía un error en la
próxima actualización.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core propaga el nuevo valor Version en Order si posee OrderDetails . En caso
contrario, se produce una excepción durante la validación del modelo.
Por qué
Este cambio se realizó para evitar un valor de token de simultaneidad obsoleto cuando solo se actualiza una de las
entidades asignadas a la misma tabla.
Mitigaciones
Todas las entidades que comparten la tabla deben incluir una propiedad que se asigna a la columna del token de
simultaneidad. Es posible crear una en estado reemplazado:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
[Link]<OrderDetails>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}

Las entidades en propiedad no se pueden consultar sin el propietario mediante una consulta de seguimiento
Problema de seguimiento n.º 18876
Compor tamiento anterior
Antes de EF Core 3.0, las entidades en propiedad se podían consultar como cualquier otra navegación.

[Link](p => [Link]);

Compor tamiento nuevo


A partir de la versión 3.0, EF Core iniciará una excepción si una consulta de seguimiento proyecta una entidad en
propiedad sin el propietario.
Por qué
Las entidades en propiedad no se pueden manipular sin el propietario, por lo que en la mayoría de los casos es un
error consultarlas de esta manera.
Mitigaciones
Si se debe realizar el seguimiento de la entidad en propiedad para modificarla de cualquier manera posterior, el
propietario se debe incluir en la consulta.
De lo contrario, agregue una llamada a AsNoTracking() :

[Link](p => [Link]).AsNoTracking();

Ahora, las propiedades heredadas de tipos sin asignar se asignan a una única columna para todos los tipos
derivados
Problema de seguimiento n.º 13998
Compor tamiento anterior
Considere el modelo siguiente:
public abstract class EntityBase
{
public int Id { get; set; }
}

public abstract class OrderBase : EntityBase


{
public int ShippingAddress { get; set; }
}

public class BulkOrder : OrderBase


{
}

public class Order : OrderBase


{
}

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<OrderBase>();
[Link]<EntityBase>();
[Link]<BulkOrder>();
[Link]<Order>();
}

Antes de EF Core 3.0, la propiedad ShippingAddress se asignaba a columnas distintas para BulkOrder y Order de
forma predeterminada.
Compor tamiento nuevo
A partir de la versión3.0, EF Core solo crea una columna para ShippingAddress .
Por qué
El comportamiento anterior no era el esperado.
Mitigaciones
Todavía se puede asignar explícitamente la propiedad a columnas separadas en los tipos derivados:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<OrderBase>();
[Link]<EntityBase>();
[Link]<BulkOrder>()
.Property(o => [Link]).HasColumnName("BulkShippingAddress");
[Link]<Order>()
.Property(o => [Link]).HasColumnName("ShippingAddress");
}

La convención de propiedad de clave externa ya no coincide con el mismo nombre que la propiedad de
entidad de seguridad
Problema de seguimiento n.º 13274
Compor tamiento anterior
Considere el modelo siguiente:
public class Customer
{
public int CustomerId { get; set; }
public ICollection<Order> Orders { get; set; }
}

public class Order


{
public int Id { get; set; }
public int CustomerId { get; set; }
}

Antes de EF Core 3.0, se podía usar la propiedad CustomerId para la clave externa por convención. Pero si Order
es un tipo de propiedad, entonces esto convertiría también a CustomerId en la clave principal, algo que no suele
ser lo esperado.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core no intenta usar las propiedades de claves externas por convención si tienen el
mismo nombre que la propiedad de entidad de seguridad. Los patrones de nombre de tipo de entidad de
seguridad concatenado con el nombre de propiedad de la entidad de seguridad y de nombre de navegación
concatenado con el nombre de propiedad de la entidad de seguridad todavía se hacen coincidir. Por ejemplo:

public class Customer


{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}

public class Order


{
public int Id { get; set; }
public int CustomerId { get; set; }
}

public class Customer


{
public int Id { get; set; }
public ICollection<Order> Orders { get; set; }
}

public class Order


{
public int Id { get; set; }
public int BuyerId { get; set; }
public Customer Buyer { get; set; }
}

Por qué
Este cambio se ha realizado para evitar definir erróneamente una propiedad de clave principal en el tipo de
propiedad.
Mitigaciones
Si la propiedad se ha diseñado para ser la clave externa y, por tanto, parte de la clave principal, se debe configurar
explícitamente como tal.
Ahora, la conexión de base de datos se cierra si ya no se usa antes de que se complete TransactionScope
Problema de seguimiento n.º 14218
Compor tamiento anterior
Antes de EF Core 3.0, si el contexto abría la conexión dentro de TransactionScope , la conexión permanecía abierta
mientras el ámbito actual TransactionScope estuviese activo.

using (new TransactionScope())


{
using (AdventureWorks context = new AdventureWorks())
{
[Link](new ProductCategory());
[Link]();

// Old behavior: Connection is still open at this point

var categories = [Link]().ToList();


}
}

Compor tamiento nuevo


A partir de la versión 3.0, EF Core cierra la conexión en cuanto se deja de usar.
Por qué
Este cambio permite usar varios contextos en el mismo ámbito TransactionScope . El comportamiento nuevo
también coincide con el de EF6.
Mitigaciones
Si la conexión debe permanecer abierta, una llamada explícita a OpenConnection() asegurará que EF Core no la
cierre de forma prematura:

using (new TransactionScope())


{
using (AdventureWorks context = new AdventureWorks())
{
[Link]();
[Link](new ProductCategory());
[Link]();

var categories = [Link]().ToList();


[Link]();
}
}

Cada propiedad usa la generación de claves enteras en memoria independiente


Problema de seguimiento n.º 6872
Compor tamiento anterior
Antes de EF Core 3.0, se usaba un generador de valores compartidos para todas las propiedades de clave entera
en memoria.
Compor tamiento nuevo
A partir de EF Core 3.0, cada propiedad de clave entera obtiene su propio generador de valores cuando se usa la
base de datos en memoria. Además, si se elimina la base de datos, la generación de claves se restablece para
todas las tablas.
Por qué
Este cambio se ha realizado para alinear la generación de claves en memoria más estrechamente a la generación
de claves de base de datos reales y para mejorar la capacidad para aislar las pruebas entre sí cuando se usa la
base de datos en memoria.
Mitigaciones
Esto puede interrumpir una aplicación que se base en el establecimiento de valores de clave específicos en
memoria. En su lugar, considere la posibilidad de no depender de valores de clave específicos, o bien de actualizar
para que coincida con el comportamiento nuevo.
Los campos de respaldo se usan de forma predeterminada
Problema de seguimiento n.º 12430
Compor tamiento anterior
Antes de la versión 3.0, incluso si se conocía el campo de respaldo de una propiedad, de forma predeterminada en
EF Core se leía y escribía el valor de propiedad mediante los métodos captadores y establecedores de
propiedades. La excepción era la ejecución de consultas, donde el campo de respaldo se establecía directamente si
se conocía.
Compor tamiento nuevo
A partir de EF Core 3.0, si se conoce el campo de respaldo para una propiedad, EF Core siempre la leerá y escribirá
mediante el campo de respaldo. Esto podría provocar una interrupción de la aplicación si depende de un
comportamiento adicional codificado en los métodos captadores o establecedores.
Por qué
Este cambio se ha realizado para evitar que EF Core desencadene erróneamente lógica de negocios de forma
predeterminada al realizar operaciones de base de datos que implican entidades.
Mitigaciones
El comportamiento anterior a la versión 3.0 se puede restaurar mediante la configuración del modo de acceso de
propiedad en ModelBuilder . Por ejemplo:

[Link]([Link]);

Inicio de excepciones si se encuentran varios campos de respaldo compatibles


Problema de seguimiento n.º 12523
Compor tamiento anterior
Antes de EF Core 3.0, si varios campos coincidían con las reglas para buscar el campo de respaldo de una
propiedad, se elegía un campo según un orden de prioridad. Esto podía producir que, en caso de ambigüedad, se
usara el campo incorrecto.
Compor tamiento nuevo
A partir de EF Core 3.0, si varios campos coinciden con la misma propiedad, se inicia una excepción.
Por qué
Este cambio se ha realizado para evitar de forma silenciosa el uso de un campo con respecto a otro cuando solo
uno puede ser correcto.
Mitigaciones
En las propiedades con campos de respaldo ambiguos se debe especificar de forma explícita el campo que se va
usar. Por ejemplo, con la API fluida:

modelBuilder
.Entity<Blog>()
.Property(e => [Link])
.HasField("_id");

Los nombres de propiedades de solo campo deben coincidir con el nombre del campo
Compor tamiento anterior
Antes de EF Core 3.0, una propiedad podía especificarse con un valor de cadena y, si no había ninguna propiedad
con ese nombre en el tipo .NET, EF Core intentaba hacerla coincidir con un campo mediante reglas de convención.

private class Blog


{
private int _id;
public string Name { get; set; }
}

modelBuilder
.Entity<Blog>()
.Property("Id");

Compor tamiento nuevo


A partir de EF Core 3.0, una propiedad de solo campo debe coincidir exactamente con el nombre del campo.

modelBuilder
.Entity<Blog>()
.Property("_id");

Por qué
Este cambio se realizó para evitar el uso del mismo campo para dos propiedades con nombres similares. También
hace que las reglas de coincidencia para propiedades solo de campo sean las mismas que para las propiedades
asignadas a propiedades CLR.
Mitigaciones
Las propiedades solo de campo deberían tener el mismo nombre que el campo al que están asignadas. En una
próxima versión de EF Core 3.0 tenemos planeado volver a habilitar la configuración explícita de un nombre de
campo distinto al nombre de la propiedad (vea el problema n.° 15307):

modelBuilder
.Entity<Blog>()
.Property("Id")
.HasField("_id");

AddDbContext/AddDbContextPool ya no llaman a AddLogging ni a AddMemoryCache


Problema de seguimiento n.° 14756
Compor tamiento anterior
Antes de EF Core 3.0, la llamada a AddDbContext o AddDbContextPool también podría registrar los servicios de
almacenamiento en caché y de registro con inserción de dependencias a través de llamadas a AddLogging y
AddMemoryCache.
Compor tamiento nuevo
A partir de EF Core 3.0, AddDbContext y AddDbContextPool ya no registrarán estos servicios con inserción de
dependencias (DI).
Por qué
EF Core 3.0 no requiere que estos servicios estén en el contenedor de inserción de dependencias de la aplicación.
Pero si ILoggerFactory se registra en el contenedor de DI de la aplicación, EF Core lo empezará a usar de todos
modos.
Mitigaciones
Si la aplicación necesita estos servicios, regístrelos de manera explícita con el contenedor de DI mediante
AddLogging o AddMemoryCache.
AddEntityFramework* agrega IMemoryCache con un límite de tamaño
Problema de seguimiento n.º 12905
Compor tamiento anterior
Antes de EF Core 3.0, la llamada a los métodos AddEntityFramework* también registraba los servicios de
almacenamiento en caché de memoria con inserción de dependencias sin límite de tamaño.
Compor tamiento nuevo
A partir de EF Core 3.0, AddEntityFramework* registrará un servicio IMemoryCache con un límite de tamaño. Si
otros servicios agregados después dependen de IMemoryCache, pueden alcanzar rápidamente el límite
predeterminado y provocar excepciones o un rendimiento degradado.
Por qué
El uso de IMemoryCache sin un límite podría dar lugar a un uso de memoria no controlado si hay un error en la
lógica de almacenamiento en caché de las consultas o las consultas se generan de forma dinámica. Tener un límite
predeterminado mitiga un posible ataque DoS.
Mitigaciones
En la mayoría de los casos, no es necesario llamar a AddEntityFramework* si también se llama a AddDbContext o
AddDbContextPool . Por tanto, la mejor mitigación consiste en quitar la llamada a AddEntityFramework* .

Si la aplicación necesita estos servicios, registre de forma explícita una implementación de IMemoryCache con el
contenedor de DI por anticipado mediante AddMemoryCache.
Ahora [Link] realiza una operación DetectChanges local
Problema de seguimiento n.º 13552
Compor tamiento anterior
Antes de EF Core 3.0, la llamada a [Link] provocaba que se detectaran cambios para todas las
entidades con seguimiento. Esto garantizaba que el estado expuesto en EntityEntry estuviera actualizado.
Compor tamiento nuevo
A partir de EF Core 3.0, ahora la llamada a [Link] solo intenta detectar cambios en la entidad dada y
cualquier entidad de seguridad relacionada con ella de la que se haya realizado el seguimiento. Esto significa que
es posible que la llamada a este método no haya detectado otros cambios, lo que podría tener implicaciones en el
estado de la aplicación.
Observe que si [Link] se establece en false incluso esta detección de cambios
local se deshabilitará.
Otros métodos que provocan la detección de cambios (como [Link] y SaveChanges ) siguen
provocando una acción DetectChanges completa de todas las entidades de las que se realiza el seguimiento.
Por qué
Este cambio se ha realizado para mejorar el rendimiento predeterminado del uso de [Link] .
Mitigaciones
Llame a [Link]() de forma explícita antes de llamar a Entry para garantizar el
comportamiento anterior a la versión 3.0.
El cliente no genera las claves de matriz de cadena y byte de forma predeterminada
Problema de seguimiento n.º 14617
Compor tamiento anterior
Antes de EF Core 3.0, se podían usar las propiedades de clave string y byte[] sin tener que establecer de forma
explícita un valor distinto de NULL. En ese caso, el valor de clave se generaba en el cliente como un GUID, que se
serializaba en bytes para byte[] .
Compor tamiento nuevo
A partir de EF Core 3.0, se iniciará una excepción en la que indica que no se ha establecido ningún valor de clave.
Por qué
Este cambio se ha realizado porque los valores string / byte[] generados por el cliente no suelen ser útiles, y el
comportamiento predeterminado dificultaba razonar sobre los valores de clave generados de una forma habitual.
Mitigaciones
Se puede obtener el comportamiento anterior a la versión 3.0 si se especifica de forma explícita que las
propiedades de clave deben usar los valores generados si no se establece ningún otro valor distinto de NULL. Por
ejemplo, con la API fluida:

modelBuilder
.Entity<Blog>()
.Property(e => [Link])
.ValueGeneratedOnAdd();

O bien con anotaciones de datos:

[DatabaseGenerated([Link])]
public string Id { get; set; }

Ahora ILoggerFactory es un servicio con ámbito


Problema de seguimiento n.º 14698
Compor tamiento anterior
Antes de EF Core 3.0, ILoggerFactory se registraba como un servicio de singleton.
Compor tamiento nuevo
A partir de EF Core 3.0, ILoggerFactory ahora se registra como con ámbito.
Por qué
Este cambio se ha realizado para permitir la asociación de un registrador con una instancia de DbContext , lo que
habilita otras funciones y quita algunos casos de comportamiento patológico como un aumento vertiginoso de
los proveedores de servicios internos.
Mitigaciones
Este cambio no debería afectar al código de la aplicación a menos que registre y use servicios personalizados en
el proveedor de servicios internos de EF Core. Esto no es habitual. En estos casos, la mayoría de los elementos
seguirá funcionando, pero cualquier servicio de singleton que dependiera de ILoggerFactory tendrá que
cambiarse para obtener la interfaz ILoggerFactory de otra forma.
Si experimenta situaciones como esta, registre un problema en el rastreador de problemas de GitHub de EF Core
para hacernos saber cómo usa ILoggerFactory , para que podamos comprender mejor cómo evitar esta
interrupción en el futuro.
En los proxies de carga diferida ya no se supone que las propiedades de navegación están totalmente cargadas
Problema de seguimiento n.º 12780
Compor tamiento anterior
Antes de EF Core 3.0, una vez que se eliminaba DbContext no había ninguna forma de saber si una determinada
propiedad de navegación de una entidad obtenida de ese contexto se había cargado completamente o no. En su
lugar, los proxies asumían que una navegación de referencia se cargaba si tenía un valor distinto de NULL, y que
una navegación de colección se cargaba si no estaba vacía. En estos casos, el intento de carga diferida era no
operativo.
Compor tamiento nuevo
A partir de EF Core 3.0, los proxies realizan el seguimiento de si una propiedad de navegación se carga o no. Esto
significa que el intento de acceder a una propiedad de navegación que se carga después de que se haya eliminado
el contexto siempre será no operativo, incluso cuando la navegación cargada está vacía o es NULL. Por el
contrario, el intento de acceder a una propiedad de navegación que no está cargada iniciará una excepción si el
contexto se ha eliminado, incluso si la propiedad de navegación es una colección no vacía. Si se produce esta
situación, significa que el código de aplicación está intentando usar la carga diferida en un momento no válido y
que se debe cambiar la aplicación para que lo no haga.
Por qué
Este cambio se ha realizado para que el comportamiento sea coherente y correcto cuando se intenta la carga
diferida de una instancia de DbContext eliminada.
Mitigaciones
Actualice el código de la aplicación para que no intente la carga diferida con un contexto eliminado, o bien
configúrelo para que sea no operativo, como se describe en el mensaje de la excepción.
La creación excesiva de proveedores de servicios internos ahora es un error de forma predeterminada
Problema de seguimiento n.º 10236
Compor tamiento anterior
Antes de EF Core 3.0, se registraba una advertencia para una aplicación que creaba un número patológico de
proveedores de servicios internos.
Compor tamiento nuevo
A partir de EF Core 3.0, ahora esta advertencia se considera un error y se inicia una excepción.
Por qué
Este cambio se ha realizado para controlar mejor el código de la aplicación mediante la exposición de este caso
patológico de una forma más explícita.
Mitigaciones
Cuando se produce este error, la acción más adecuada consiste en comprender la causa raíz y detener la creación
de tantos proveedores de servicios internos. Pero el error se puede convertir en una advertencia (u omitirse)
mediante configuración en DbContextOptionsBuilder . Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder
.ConfigureWarnings(w => [Link]([Link]));
}

Comportamiento nuevo de HasOne/HasMany llamado con una sola cadena


Problema de seguimiento n.° 9171
Compor tamiento anterior
Antes de EF Core 3.0, el código para llamar a HasOne o HasMany con una cadena se interpretaba de manera
confusa. Por ejemplo:

[Link]<Samurai>().HasOne("Entrance").WithOne();

El código parece relacionar Samurai con otro tipo de entidad mediante la propiedad de navegación Entrance ,
que puede ser privada.
En realidad, este código intenta crear una relación con algún tipo de entidad denominada Entrance sin ninguna
propiedad de navegación.
Compor tamiento nuevo
A partir de EF Core 3.0, el código anterior ahora hace lo que parecía que debía hacer antes.
Por qué
El comportamiento anterior era muy confuso, especialmente al leer el código de configuración y al buscar errores.
Mitigaciones
Esto solo interrumpirá las aplicaciones que configuran de manera explícita las relaciones con cadenas para
nombres de tipos y sin especificar explícitamente la propiedad de navegación. Esto no es habitual. El
comportamiento anterior se puede obtener al pasar de manera explícita null para el nombre de la propiedad de
navegación. Por ejemplo:

[Link]<Samurai>().HasOne("[Link]", null).WithOne();

El tipo de valor devuelto para varios métodos asincrónicos se ha cambiado de Task a ValueTask
Problema de seguimiento n.º 15184
Compor tamiento anterior
Antes, los siguientes métodos asincrónicos devolvían Task<T> :
[Link]()
[Link]()
[Link]()
[Link]()
[Link]() (y las clases derivadas)
Compor tamiento nuevo
Dichos métodos ahora devuelven ValueTask<T> durante el mismo T que antes.
Por qué
Este cambio reduce el número de asignaciones de montones que se producen al invocar estos métodos, lo que
mejora el rendimiento general.
Mitigaciones
Las aplicaciones que simplemente esperen las API anteriores solo necesitan recompilarse, sin que sea necesario
realizar cambios en el código fuente. Un uso más complejo (p. ej., pasar el valor Task devuelto a [Link]() )
normalmente requiere que el valor ValueTask<T> devuelto se convierta en Task<T> mediante una llamada a
AsTask() en él. Tenga en cuenta que esto niega la reducción de asignación que implica este cambio.

La anotación Relational:TypeMapping ahora es simplemente TypeMapping


Problema de seguimiento n.º 9913
Compor tamiento anterior
El nombre de anotación para las anotaciones de asignación de tipos era "Relational:TypeMapping".
Compor tamiento nuevo
Ahora, el nombre de anotación para las anotaciones de asignación de tipos es "TypeMapping".
Por qué
Ahora, las asignaciones de tipos se usan para algo más que solo para proveedores de bases de datos relacionales.
Mitigaciones
Esto solo interrumpirá a las aplicaciones que acceden directamente a la asignación de tipos como una anotación,
lo que no es habitual. La acción más apropiada para corregir es usar la superficie de API para acceder a las
asignaciones de tipos en lugar de usar directamente la anotación.
ToTable en un tipo derivado inicia una excepción
Problema de seguimiento n.º 11811
Compor tamiento anterior
Antes de EF Core 3.0, la llamada a ToTable() en un tipo derivado se omitía, ya que la única estrategia asignación
de herencia era TPH, lo que no es válido.
Compor tamiento nuevo
A partir de EF Core 3.0, y en preparación para agregar compatibilidad con TPT y TPC en una versión posterior,
ahora la llamada a ToTable() en un tipo derivado iniciará una excepción para evitar un cambio de asignación
inesperado en el futuro.
Por qué
En la actualidad no se considera válido asignar un tipo derivado a otra tabla. Este cambio evita interrupciones en
el futuro, cuando se convierta en una operación válida.
Mitigaciones
Quite todos los intentos de asignar tipos derivados a otras tablas.
ForSqlServerHasIndex se ha reemplazado por HasIndex
Problema de seguimiento n.º 12366
Compor tamiento anterior
Antes de EF Core 3.0, ForSqlServerHasIndex().ForSqlServerInclude() proporcionaba una manera de configurar las
columnas que se usaban con INCLUDE .
Compor tamiento nuevo
A partir de EF Core 3.0, ya se admite el uso de Include en un índice en el nivel relacional. Mediante
HasIndex().ForSqlServerInclude() .

Por qué
Este cambio se ha realizado para consolidar la API para índices con Include en un mismo lugar para todos los
proveedores de base de datos.
Mitigaciones
Use la API nueva, como se ha mostrado anteriormente.
Cambios en la API de metadatos
Problema de seguimiento n.º 214
Compor tamiento nuevo
Las siguientes propiedades se han convertido en métodos de extensión:
[Link] -> GetQueryFilter()
[Link] -> GetDefiningQuery()
[Link] -> IsShadowProperty()
[Link] -> GetBeforeSaveBehavior()
[Link] -> GetAfterSaveBehavior()

Por qué
Este cambio simplifica la implementación de las interfaces mencionadas anteriormente.
Mitigaciones
Use los nuevos métodos de extensión.
Cambios en la API de metadatos específicos del proveedor
Problema de seguimiento n.º 214
Compor tamiento nuevo
Los métodos de extensión específicos del proveedor se simplificarán:
[Link]().ColumnName -> [Link]()
[Link]().IsMemoryOptimized -> [Link]()
[Link]() -> [Link]()

Por qué
Este cambio simplifica la implementación de los métodos de extensión mencionados anteriormente.
Mitigaciones
Use los nuevos métodos de extensión.
EF Core ya no envía pragma para el cumplimiento de SQLite FK
Problema de seguimiento n.º 12151
Compor tamiento anterior
Antes de EF Core 3.0, EF Core enviaba PRAGMA foreign_keys = 1 cuando se abría una conexión con SQLite.
Compor tamiento nuevo
A partir de EF Core 3.0, EF Core ya no envía PRAGMA foreign_keys = 1 cuando se abre una conexión con SQLite.
Por qué
Este cambio se ha realizado porque en EF Core se usa SQLitePCLRaw.bundle_e_sqlite3 de forma predeterminada,
lo que a su vez significa que el cumplimiento de CD está activado de forma predeterminada y no es necesario
habilitarlo explícitamente cada vez que se abra una conexión.
Mitigaciones
Las claves externas se habilitan de forma predeterminada en SQLitePCLRaw.bundle_e_sqlite3, que en EF Core se
usa de forma predeterminada. Para otros casos, las claves externas se pueden habilitar mediante la especificación
de Foreign Keys=True en la cadena de conexión.
[Link] ahora depende de SQLitePCLRaw.bundle_e_sqlite3
Compor tamiento anterior
Antes de EF Core 3.0, en EF Core se usaba SQLitePCLRaw.bundle_green .
Compor tamiento nuevo
A partir de EF Core 3.0, en EF Core se usa SQLitePCLRaw.bundle_e_sqlite3 .
Por qué
Este cambio se ha realizado para que la versión de SQLite que se usa en iOS sea coherente con otras plataformas.
Mitigaciones
Para usar la versión nativa de SQLite en iOS, configure [Link] para usar otra agrupación
SQLitePCLRaw .

Almacenamiento de valores GUID como TEXT en SQLite


Problema de seguimiento n.º 15078
Compor tamiento anterior
Antes, los valores GUID se almacenaban como valores BLOB en SQLite.
Compor tamiento nuevo
Ahora, los valores GUID se almacenan como TEXT.
Por qué
El formato binario de los GUID no está normalizado. El almacenamiento de los valores como TEXT mejora la
compatibilidad de la base de datos con otras tecnologías.
Mitigaciones
Puede migrar las bases de datos existentes al nuevo formato ejecutando SQL de la siguiente forma.

UPDATE MyTable
SET GuidColumn = hex(substr(GuidColumn, 4, 1)) ||
hex(substr(GuidColumn, 3, 1)) ||
hex(substr(GuidColumn, 2, 1)) ||
hex(substr(GuidColumn, 1, 1)) || '-' ||
hex(substr(GuidColumn, 6, 1)) ||
hex(substr(GuidColumn, 5, 1)) || '-' ||
hex(substr(GuidColumn, 8, 1)) ||
hex(substr(GuidColumn, 7, 1)) || '-' ||
hex(substr(GuidColumn, 9, 2)) || '-' ||
hex(substr(GuidColumn, 11, 6))
WHERE typeof(GuidColumn) == 'blob';

En EF Core, también puede seguir usando el comportamiento anterior configurando un convertidor de valores en
estas propiedades.

modelBuilder
.Entity<MyEntity>()
.Property(e => [Link])
.HasConversion(
g => [Link](),
b => new Guid(b));

[Link] sigue siendo capaz de leer valores GUID de ambas columnas BLOB y TEXT. Sin embargo,
dado que el formato predeterminado de los parámetros y las constantes ha cambiado, seguramente deberá
realizar alguna acción en la mayoría de casos que impliquen el uso de valores GUID.
Ahora los valores char se almacenan como TEXT en SQLite
Problema de seguimiento n.º 15020
Compor tamiento anterior
Anteriormente los valores char se almacenaban como valores INTEGER en SQLite. Por ejemplo, un valor char de A
se almacenaba como el valor entero 65.
Compor tamiento nuevo
Ahora, los valores char se almacenan como TEXT.
Por qué
El almacenamiento de valores como TEXT es más natural y mejora la compatibilidad de la base de datos con otras
tecnologías.
Mitigaciones
Puede migrar las bases de datos existentes al nuevo formato ejecutando SQL de la siguiente forma.

UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';

En EF Core, también puede seguir usando el comportamiento anterior configurando un convertidor de valores en
estas propiedades.
modelBuilder
.Entity<MyEntity>()
.Property(e => [Link])
.HasConversion(
c => (long)c,
i => (char)i);

[Link] también puede leer valores de caracteres tanto de columnas INTEGER como de columnas
TEXT, por lo que es posible que no deba hacer nada dependiendo de su caso.
Ahora los id. de migración se generan usando el calendario de la referencia cultural invariable
Problema de seguimiento n.º 12978
Compor tamiento anterior
Los identificadores de migración se generaban de forma involuntaria con el calendario de la referencia cultural
actual.
Compor tamiento nuevo
Ahora los id. de migración siempre se generan usando el calendario de la referencia cultural invariable
(gregoriano).
Por qué
El orden de las migraciones es importante al actualizar la base de datos o al solucionar conflictos de combinación.
Al usar el calendario invariable, se evitan problemas de ordenación que pueden producirse si los miembros del
equipo tienen distintos calendarios del sistema.
Mitigaciones
Esta cambio afecta a todas las personas que usan un calendario no gregoriano en el que el año sea superior al del
calendario gregoriano (como el calendario budista tailandés). Los id. de migración existentes deberán actualizarse
para que las migraciones nuevas se ordenen después de las existentes.
Puede ver el id. de migración en el atributo Migration de los archivos de diseñador de la migración.

[DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
partial class MyMigration
{

También debe actualizarse la tabla de historial de migraciones.

UPDATE __EFMigrationsHistory
SET MigrationId = CONCAT(LEFT(MigrationId, 4) - 543, SUBSTRING(MigrationId, 4, 150))

Se ha quitado el elemento UseRowNumberForPaging


Problema de seguimiento n.º 16400
Compor tamiento anterior
Antes de EF Core 3.0, UseRowNumberForPaging se podía usar para generar SQL para la paginación de forma que
fuera compatible con SQL Server 2008.
Compor tamiento nuevo
A partir de EF Core 3.0, EF solo genera SQL para la paginación que únicamente es compatible con las versiones
posteriores de SQL Server.
Por qué
El motivo de este cambio es que SQL Server 2008 ya no se admite. Además, la actualización de esta característica
para que funcionase con los cambios en las consultas implementados en EF Core 3.0 llevaría mucho trabajo.
Mitigaciones
Se recomienda actualizar a una versión más reciente de SQL Server, o bien utilizar un nivel de compatibilidad
superior, de modo que el SQL que se genere se admita. Dicho esto, si no puede hacerlo, escriba un comentario en
el problema de seguimiento con los detalles al respecto. En función de los comentarios, es posible que volvamos a
valorar esta decisión.
La información o metadatos de la extensión se han quitado de IDbContextOptionsExtension
Problema de seguimiento n.º 16119
Compor tamiento anterior
IDbContextOptionsExtension incluía métodos para proporcionar metadatos sobre la extensión.
Compor tamiento nuevo
Estos métodos se han movido a una nueva clase base abstracta DbContextOptionsExtensionInfo , que se devuelve
desde una nueva propiedad [Link] .
Por qué
Al lanzarse las versiones 2.0 y 3.0, tuvimos que agregar o cambiar estos métodos varias veces. Su división en una
nueva clase base abstracta facilitará la realización de este tipo de cambios sin interrumpir las extensiones
existentes.
Mitigaciones
Actualice las extensiones para seguir el nuevo patrón. Encontrará ejemplos en las muchas implementaciones de
IDbContextOptionsExtension para los diferentes tipos de extensiones en el código fuente de EF Core.

Cambio de nombre de LogQueryPossibleExceptionWithAggregateOperator


Problema de seguimiento n.º 10985
Cambio
Se ha cambiado el nombre de [Link] a
[Link] .
Por qué
Conviene alinear el nombre de este evento de advertencia con el del resto de eventos de advertencia.
Mitigaciones
Use el nuevo nombre. (Tenga en cuenta que el número de id. evento sigue siendo el mismo).
Clarificación de la API para nombres de restricciones de claves externas
Problema de seguimiento n.º 10730
Compor tamiento anterior
Antes de EF Core 3.0, se utilizaba simplemente el término "nombre" para hacer referencia a los nombres de las
restricciones de claves externas. Por ejemplo:
var constraintName = [Link];

Compor tamiento nuevo


A partir de EF Core 3.0, el término con el que se hace referencia a los nombres de las restricciones de claves
externas es "nombre de la restricción". Por ejemplo:

var constraintName = [Link];

Por qué
Este cambio permite mejorar la coherencia relativa a la nomenclatura en este aspecto y aclarar que se trata del
nombre de una restricción de clave externa, y no del de la columna o propiedad en la que está definida la clave
externa.
Mitigaciones
Use el nuevo nombre.
[Link]/HasTablesAsync se han hecho públicos
Problema de seguimiento n.° 15997
Compor tamiento anterior
Antes de EF Core 3.0, estos métodos estaban protegidos.
Compor tamiento nuevo
Desde EF Core 3.0, estos métodos son públicos.
Por qué
EF usa estos métodos para determinar si se ha creado una base de datos, pero está vacía. Esto también puede
resultar útil fuera de EF al determinar si se deben aplicar migraciones o no.
Mitigaciones
Cambie la accesibilidad de cualquier invalidación.
[Link] es ahora un paquete DevelopmentDependency
Problema de seguimiento n.° 11506
Compor tamiento anterior
Antes de EF Core 3.0, [Link] era un paquete NuGet regular con un ensamblado al
que podían hacer referencia los proyectos que dependían de él.
Compor tamiento nuevo
Desde EF Core 3.0, es un paquete DevelopmentDependency. Esto significa que la dependencia no fluirá de manera
transitiva en otros proyectos y que ya no puede, de forma predeterminada, hacer referencia a su ensamblado.
Por qué
Este paquete solo está destinado a usarse en tiempo de diseño. Las aplicaciones implementadas no deben hacer
referencia al mismo. Hacer que el paquete sea DevelopmentDependency refuerza esta recomendación.
Mitigaciones
Si tiene que hacer referencia a este paquete para invalidar el comportamiento en tiempo de diseño de EF Core,
puede actualizar los metadatos de elementos PackageReference del proyecto.

<PackageReference Include="[Link]" Version="3.0.0">


<PrivateAssets>all</PrivateAssets>
<!-- Remove IncludeAssets to allow compiling against the assembly -->
<!--<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>-->
</PackageReference>

Si se hace referencia al paquete de manera transitiva a través de [Link], tendrá que


agregar una PackageReference explícita al paquete para cambiar sus metadatos. Este tipo de referencia explícita
debe agregarse a cualquier proyecto que requiera los tipos de paquete.
[Link] se ha actualizado a la versión 2.0.0
Problema de seguimiento n.° 14824
Compor tamiento anterior
[Link] dependía anteriormente de la versión 1.1.12 de [Link].
Compor tamiento nuevo
Hemos actualizado nuestro paquete para depender de la versión 2.0.0.
Por qué
La versión 2.0.0 de [Link] selecciona .NET Standard 2.0 como destino. Anteriormente seleccionaba .NET
Standard 1.1 como destino, que requería el cierre a gran escala de paquetes transitivos para su funcionamiento.
Mitigaciones
En la versión 2.0.0 de [Link] se incluyen algunos cambios importantes. Consulte las notas de la versión
para obtener detalles.
NetTopologySuite se actualizó a la versión 2.0.0
Problema de seguimiento n.° 14825
Compor tamiento anterior
Los paquetes espaciales anteriormente dependían de la versión 1.15.1 de NetTopologySuite.
Compor tamiento nuevo
Hemos actualizado nuestro paquete para depender de la versión 2.0.0.
Por qué
La versión 2.0.0 de NetTopologySuite pretende resolver varios problemas de usabilidad que encontraron los
usuarios de EF Core.
Mitigaciones
En la versión 2.0.0 de NetTopologySuite se incluyen algunos cambios importantes. Consulte las notas de la
versión para obtener detalles.
Se usa [Link] en lugar de [Link]
Problema de seguimiento n.º 15636
Compor tamiento anterior
[Link] dependía anteriormente de la versión [Link].
Compor tamiento nuevo
Hemos actualizado nuestro paquete para que dependa de [Link].
Por qué
A partir de ahora, [Link] es el controlador de acceso a datos insignia para SQL Server y
[Link] ya no es el centro de desarrollo. Algunas características importantes, como Always
Encrypted, solo están disponibles en [Link].
Mitigaciones
Si el código toma una dependencia directa en [Link], debe cambiarla para que haga referencia a
[Link] en su lugar. Dado que los dos paquetes mantienen un grado muy alto de compatibilidad
con la API, solo debería ser un paquete simple y un cambio de espacio de nombres.
Se deben configurar varias relaciones de referencia automática ambiguas
Problema de seguimiento n.º 13573
Compor tamiento anterior
Un tipo de entidad con varias propiedades de navegación unidireccional de referencia automática y claves
externas coincidentes se configuró incorrectamente como una única relación. Por ejemplo:

public class User


{
public Guid Id { get; set; }
public User CreatedBy { get; set; }
public User UpdatedBy { get; set; }
public Guid CreatedById { get; set; }
public Guid? UpdatedById { get; set; }
}

Compor tamiento nuevo


Este escenario se detecta ahora en la generación del modelo y se produce una excepción que indica que el modelo
es ambiguo.
Por qué
El modelo resultante era ambiguo, y lo más probable es que sea incorrecto en este caso.
Mitigaciones
Utilice la configuración completa de la relación. Por ejemplo:

modelBuilder
.Entity<User>()
.HasOne(e => [Link])
.WithMany();

modelBuilder
.Entity<User>()
.HasOne(e => [Link])
.WithMany();

[Link] es NULL o la cadena vacía lo configura para estar en el esquema predeterminado del
modelo
Problema de seguimiento n.º 12757
Compor tamiento anterior
Una función DbFunction configurada con el esquema como una cadena vacía se trataba como una función
integrada sin un esquema. Por ejemplo, el código siguiente asignará la función CLR DatePart a la función
integrada DATEPART en SqlServer.

[DbFunction("DATEPART", Schema = "")]


public static int? DatePart(string datePartArg, DateTime? date) => throw new Exception();

Compor tamiento nuevo


Todas las asignaciones de DbFunction se consideran asignadas a funciones definidas por el usuario. Por lo tanto, el
valor de cadena vacía colocaría la función dentro del esquema predeterminado del modelo, que podría ser el
esquema configurado de forma explícita mediante [Link]() de la API fluida o dbo en
caso contrario.
Por qué
Anteriormente, el esquema vacío era una manera de indicar que la función estaba integrada, pero esa lógica solo
es aplicable a SqlServer, donde las funciones integradas no pertenecen a ningún esquema.
Mitigaciones
Configure la traslación de DbFunction manualmente para asignarla a una función integrada.

modelBuilder
.HasDbFunction(typeof(MyContext).GetMethod(nameof([Link])))
.HasTranslation(args => [Link]("DatePart", args, typeof(int?), null));
Novedades de EF Core 2.2
08/04/2020 • 4 minutes to read • Edit Online

Compatibilidad con datos espaciales


Los datos espaciales pueden usarse para representar la ubicación física y la forma de los objetos. Muchas bases de
datos pueden almacenar, indexar y consultar datos espaciales de forma nativa. Entre los escenarios habituales se
incluye la consulta de objetos dentro de una distancia determinada y la prueba de si un polígono contiene una
ubicación determinada. EF Core 2.2 ahora admite trabajar con datos espaciales de varias bases de datos utilizando
tipos de la biblioteca NetTopologySuite (NTS).
La compatibilidad con datos espaciales se implementa como una serie de paquetes de extensión específicos del
proveedor. Cada uno de estos paquetes contribuye a las asignaciones de tipos y métodos de NTS y los
correspondientes tipos espaciales y funciones en la base de datos. Estas extensiones de proveedor ahora están
disponibles para SQL Server, SQLite y PostgreSQL (del proyecto Npgsql). Los tipos espaciales pueden usarse
directamente con el proveedor en memoria de EF Core sin extensiones adicionales.
Una vez que se instala la extensión del proveedor, puede agregar propiedades de los tipos admitidos a las
entidades. Por ejemplo:

using [Link];

namespace MyApp
{
public class Friend
{
[Key]
public string Name { get; set; }

[Required]
public Point Location { get; set; }
}
}

Luego puede guardar entidades con datos espaciales:

using (var context = new MyDbContext())


{
[Link](
new Friend
{
Name = "Bill",
Location = new Point(-122.34877, 47.6233355) {SRID = 4326 }
});
[Link]();
}

Y puede ejecutar consultas de base de datos basadas en datos y operaciones espaciales:

var nearestFriends =
(from f in [Link]
orderby [Link](myLocation) descending
select f).Take(5).ToList();
Para obtener más información sobre esta característica, consulte la documentación sobre tipos espaciales.

Colecciones de entidades en propiedad


EF Core 2.0 agregó la capacidad de modelar la propiedad en asociaciones de uno a uno. EF Core 2.2 extiende la
capacidad de expresar la propiedad a asociaciones de uno a varios. La propiedad ayuda a restringir el modo en que
se usan las entidades.
Por ejemplo, las entidades en propiedad:
Solo pueden aparecer en las propiedades de navegación de otros tipos de entidad.
Se cargan automáticamente, y solo se puede hacer su seguimiento por un DbContext junto con su propietario.
En bases de datos relacionales, las colecciones en propiedad se asignan a tablas independientes del propietario, al
igual que las asociaciones regulares de uno a varios. Pero en las bases de datos orientadas a documentos, tenemos
previsto anidar entidades en propiedad (en colecciones o referencias en propiedad) dentro del mismo documento
que el propietario.
Puede usar la característica mediante una llamada a la nueva API OwnsMany():

[Link]<Customer>().OwnsMany(c => [Link]);

Para obtener más información, consulte la documentación actualizada de entidades en propiedad.

Etiquetas de consulta
Esta característica simplifica la correlación de las consultas LINQ en el código con las consultas SQL generadas
capturadas en los registros.
Para aprovechar las ventajas de las etiquetas de consulta, anote una consulta LINQ mediante el nuevo método
TagWith(). Uso de la consulta espacial de un ejemplo anterior:

var nearestFriends =
(from f in [Link](@"This is my spatial query!")
orderby [Link](myLocation) descending
select f).Take(5).ToList();

Esta consulta LINQ producirá la siguiente salida SQL:

-- This is my spatial query!

SELECT TOP(@__p_1) [f].[Name], [f].[Location]


FROM [Friends] AS [f]
ORDER BY [f].[Location].STDistance(@__myLocation_0) DESC

Para obtener más información, vea la documentación de etiquetas de consulta.


Novedades de EF Core 2.1
08/04/2020 • 12 minutes to read • Edit Online

Además de numerosas correcciones de errores y pequeñas mejoras funcionales y de rendimiento, EF Core 2.1
incluye algunas características nuevas muy atractivas:

Carga diferida
EF Core contiene ahora los bloques de creación necesarios para quienes quieran crear clases de entidad que
puedan cargar las propiedades de navegación a petición. También hemos creado otro paquete,
[Link], que aprovecha los bloques de creación para generar clases proxy de carga
diferida basadas en clases de entidad apenas modificadas (por ejemplo, clases con propiedades de navegación
virtual).
Consulte la sección sobre cargas diferidas para obtener más información sobre el tema.

Parámetros en constructores de entidad


Como uno de los bloques de creación necesarios para la carga diferida, se habilita la creación de entidades que
aceptan parámetros en sus constructores. Puede usar parámetros para insertar valores de propiedad, delegados de
carga diferida y servicios.
Consulte la sección sobre constructores de entidad con parámetros para obtener más información sobre el tema.

Conversiones de valores
Hasta ahora, EF Core solo podía asignar propiedades de tipos admitidas de forma nativa por el proveedor de bases
de datos subyacente. Los valores se copiaban de un lado a otro entre las columnas y las propiedades sin ninguna
transformación. A partir de EF Core 2.1, pueden aplicarse conversiones de valores para transformar los valores
obtenidos en las columnas antes de que se apliquen a las propiedades, y viceversa. Tenemos varias conversiones
que pueden aplicarse por convención según sea necesario, así como una API de configuración explícita que permite
registrar conversiones personalizadas entre columnas y propiedades. Algunas de las aplicaciones de esta
característica son:
Almacenamiento de enumeraciones como cadenas
Asignación de enteros sin signo con SQL Server
Cifrado y descifrado automáticos de valores de propiedad
Consulte la sección sobre conversiones de valores para obtener más información sobre el tema.

Traslación de GroupBy de LINQ


Antes de la versión 2.1, el operador GroupBy de LINQ en EF Core siempre se evaluaba en la memoria. Ahora se
admite su traslación a la cláusula GROUP BY de SQL en los casos más comunes.
En este ejemplo se muestra una consulta con GroupBy utilizada para calcular diversas funciones de agregado:
var query = [Link]
.GroupBy(o => new { [Link], [Link] })
.Select(g => new
{
[Link],
[Link],
Sum = [Link](o => [Link]),
Min = [Link](o => [Link]),
Max = [Link](o => [Link]),
Avg = [Link](o => [Link])
});

La traslación correspondiente a SQL tiene este aspecto:

SELECT [o].[CustomerId], [o].[EmployeeId],


SUM([o].[Amount]), MIN([o].[Amount]), MAX([o].[Amount]), AVG([o].[Amount])
FROM [Orders] AS [o]
GROUP BY [o].[CustomerId], [o].[EmployeeId];

Propagación de datos
Con la nueva versión, será posible proporcionar datos iniciales para rellenar una base de datos. A diferencia de en
EF6, la propagación de datos está asociada a un tipo de entidad como parte de la configuración del modelo. Las
migraciones de EF Core pueden luego calcular automáticamente las operaciones de inserción, actualización y
eliminación que hay que aplicar al actualizar la base de datos a una nueva versión del modelo.
Por ejemplo, esto se puede usar para configurar los datos de inicialización de un método POST en OnModelCreating :

[Link]<Post>().HasData(new Post{ Id = 1, Text = "Hello World!" });

Consulte la sección sobre propagación de datos para obtener más información sobre el tema.

Tipos de consulta
Un modelo de EF Core ahora puede incluir tipos de consulta. A diferencia de los tipos de entidad, los tipos de
consulta no tienen claves definidas en ellos y no se pueden insertar, eliminar ni actualizar (es decir, son de solo
lectura), pero se pueden devolver directamente en las consultas. Algunos de los escenarios de uso para los tipos de
consulta son:
Asignar a vistas sin claves principales
Asignar a tablas sin claves principales
Asignar a consultas definidas en el modelo
Actuar como tipo de valor devuelto en consultas FromSql()

Consulte la sección sobre tipos de consulta para obtener más información sobre el tema.

Include en tipos derivados


Ahora será posible especificar propiedades de navegación definidas solo en tipos derivados al escribir expresiones
para el método Include . Para la versión fuertemente tipada de Include , se admite el uso de una conversión
explícita o el operador as . Ahora también se admite hacer referencia a los nombres de propiedad de navegación
definidos en tipos derivados en la versión de cadena de Include :
var option1 = [Link](p => ((Student)p).School);
var option2 = [Link](p => (p as Student).School);
var option3 = [Link]("School");

Consulte la sección sobre Include con tipos derivados para obtener más información sobre el tema.

[Link]
Se ha agregado la posibilidad de trabajar con características de [Link] tales como TransactionScope.
Esto funcionará en .NET Framework y en .NET Core cuando se usen proveedores de bases de datos que lo admitan.
Consulte la sección sobre [Link] para obtener más información sobre el tema.

Mejor ordenación de columnas en la migración inicial


En función de los comentarios de clientes, hemos actualizado las migraciones para que las columnas de tablas se
generen inicialmente en el mismo orden en que se declaran las propiedades en clases. Tenga en cuenta que EF Core
no puede cambiar el orden cuando se agregan nuevos miembros después de la creación de la tabla inicial.

Optimización de subconsultas correlacionadas


Se ha mejorado la traslación de consultas para evitar la ejecución de "N + 1" consultas SQL en muchos escenarios
comunes en los que el uso de una propiedad de navegación en la proyección conduce a unir los datos de la
consulta raíz con los datos de una subconsulta correlacionada. La optimización requiere el almacenamiento en
búfer de los resultados de la subconsulta, y hay que modificar la consulta para que participe en el nuevo
comportamiento.
Por ejemplo, la siguiente consulta normalmente se traslada a una consulta para clientes, más N consultas separadas
para pedidos (donde "N" corresponde al número de clientes devueltos):

var query = [Link](


c => [Link](o => [Link] > 100).Select(o => [Link]));

Al incluir ToList() en el lugar correcto, se indica que el almacenamiento en búfer es adecuado para los pedidos, lo
que permite la optimización:

var query = [Link](


c => [Link](o => [Link] > 100).Select(o => [Link]).ToList());

Tenga en cuenta que esta consulta solo se trasladará a dos consultas SQL: una para clientes y la siguiente para
pedidos.

Atributo [Owned]
Ahora es posible configurar tipos de entidad en propiedad anotando simplemente el tipo con [Owned] y
asegurándose luego de que la entidad de propietario se agrega al modelo:
[Owned]
public class StreetAddress
{
public string Street { get; set; }
public string City { get; set; }
}

public class Order


{
public int Id { get; set; }
public StreetAddress ShippingAddress { get; set; }
}

Herramienta de línea de comandos dotnet-ef incluida en el SDK de .NET


Core
Los comandos de dotnet-ef ahora forman parte del SDK de .NET Core, así que ya no es necesario usar
DotNetCliToolReference en el proyecto para poder usar migraciones o para aplicar la técnica scaffolding a
DbContext desde una base de datos existente.
Vea la sección sobre cómo instalar las herramientas para obtener más información sobre cómo habilitar
herramientas de línea de comandos para diferentes versiones del SDK de .NET Core y EF Core.

Paquete [Link]
El nuevo paquete contiene atributos e interfaces que puede usar en los proyectos para activar características de EF
Core sin depender de EF Core como un todo. Por ejemplo, el atributo [Owned] y la interfaz de ILazyLoader se
encuentran aquí.

Eventos de cambio de estado


Los nuevos eventos Tracked y StateChanged de ChangeTracker se pueden usar para escribir lógica que reaccione a
las entidades que entran en DbContext o que cambian su estado.

Analizador de parámetros de SQL sin formato


Un nuevo analizador de código se incluye en EF Core que detecta los usos potencialmente poco seguros de
nuestras API de SQL sin formato, como FromSql o ExecuteSqlCommand . Por ejemplo, para la consulta siguiente, verá
una advertencia porque minAge no tiene parámetros:

var sql = $"SELECT * FROM People WHERE Age > {minAge}";


var query = [Link](sql);

Compatibilidad del proveedor de bases de datos


Se recomienda usar EF Core 2.1 con proveedores que se hayan actualizado o que al menos se haya comprobado
que funcionan con EF Core 2.1.

TIP
Si encuentra alguna incompatibilidad inesperada o algún problema en las nuevas características o si tiene comentarios sobre
ellas, notifíquelos mediante nuestro rastreador de problemas.
Nuevas características de EF Core 2.0
08/04/2020 • 18 minutes to read • Edit Online

.NET Standard 2.0


EF Core tiene ahora como destino .NET Standard 2.0, lo que significa que puede trabajar con .NET Core 2.0, .NET
Framework 4.6.1 y otras bibliotecas que implementan .NET Standard 2.0. Vea Implementaciones de .NET
compatibles para obtener más detalles sobre lo que se admite.

Modelado
División de tablas
Ahora es posible asignar dos o más tipos de entidad a la misma tabla en la que se van a compartir las columnas de
clave principal y cada fila va a corresponder a dos o más entidades.
Para usar la división de tabla, debe configurarse una relación de identificación (donde las propiedades de clave
externa forman la clave principal) entre todos los tipos de entidad que comparten la tabla:

[Link]<Product>()
.HasOne(e => [Link]).WithOne(e => [Link])
.HasForeignKey<ProductDetails>(e => [Link]);
[Link]<Product>().ToTable("Products");
[Link]<ProductDetails>().ToTable("Products");

Consulte la sección sobre la división de las tablas para obtener más información sobre esta característica.
Tipos de propiedad
Un tipo de entidad en propiedad puede compartir el mismo tipo .NET con otro tipo de entidad en propiedad, pero,
dado que no se puede identificar simplemente por el tipo .NET, debe haber una navegación a él desde otro tipo de
entidad. La entidad que contiene la navegación definitoria es el propietario. Al consultar al propietario, los tipos de
propiedad se incluyen de forma predeterminada.
Por convención, se crea una clave principal paralela para el tipo de propiedad y se asigna a la misma tabla que el
propietario mediante la división de tabla. Esto permite usar tipos de propiedad de forma similar al modo en que se
usan los tipos complejos en EF6:
[Link]<Order>().OwnsOne(p => [Link], cb =>
{
[Link](c => [Link]);
[Link](c => [Link]);
});

public class Order


{
public int Id { get; set; }
public OrderDetails OrderDetails { get; set; }
}

public class OrderDetails


{
public StreetAddress BillingAddress { get; set; }
public StreetAddress ShippingAddress { get; set; }
}

public class StreetAddress


{
public string Street { get; set; }
public string City { get; set; }
}

Consulte la sección sobre tipos de entidad en propiedad para obtener más información sobre esta característica.
Filtros de consulta de nivel de modelo
EF Core 2.0 incluye una nueva característica que se denomina filtros de consulta de nivel de modelo. Esta
característica permite que los predicados de consulta LINQ (una expresión booleana que normalmente se pasa al
operador de consulta Where de LINQ) se definan directamente en tipos de entidad del modelo de metadatos
(normalmente en OnModelCreating). Estos filtros se aplican automáticamente a las consultas LINQ que implican a
esos tipos de entidad, incluidos aquellos a los que se hace referencia de forma indirecta, por ejemplo mediante el
uso de Include o de referencias de propiedad de navegación directas. Algunas aplicaciones comunes de esta
característica son:
Eliminación temporal: un tipo de entidad define una propiedad IsDeleted.
Servicios multiinquilino: un tipo de entidad define una propiedad TenantId.
Este es un ejemplo sencillo que muestra la característica para los dos escenarios mencionados arriba:

public class BloggingContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

public int TenantId { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>().HasQueryFilter(
p => ![Link]
&& [Link] == [Link]);
}
}

Se define un filtro de nivel de modelo que implementa los servicios multiinquilino y la eliminación temporal para
instancias del tipo de entidad Post . Observe el uso de una propiedad de nivel de instancia DbContext : TenantId .
Los filtros de nivel de modelo usan el valor de la instancia de contexto correcta (es decir, la instancia de contexto
que está ejecutando la consulta).
Los filtros se pueden deshabilitar para consultas LINQ individuales mediante el operador IgnoreQueryFilters().
Limitaciones
No se permiten las referencias de navegación. Esta característica se puede agregar en función de los
comentarios.
Solo se pueden definir filtros en el tipo de entidad raíz de una jerarquía.
Asignación de función escalar de base de datos
EF Core 2.0 incluye una importante contribución de Paul Middleton que permite la asignación de funciones
escalares de base de datos a stubs de método para que puedan usarse en consultas LINQ y trasladarse a SQL.
Esta es una breve descripción de cómo se puede usar la característica:
Declare un método estático en DbContext y anótelo con DbFunctionAttribute :

public class BloggingContext : DbContext


{
[DbFunction]
public static int PostReadCount(int blogId)
{
throw new NotImplementedException();
}
}

Los métodos como este se registran automáticamente. Una vez registrados, las llamadas al método de una consulta
LINQ pueden trasladarse a llamadas a funciones de SQL:

var query =
from p in [Link]
where [Link]([Link]) > 5
select p;

Puntos a tener en cuenta:


Por convención, el nombre del método se usa como nombre de una función (en este caso una función definida
por el usuario) al generar el código SQL, pero puede invalidar el nombre y el esquema durante el registro del
método.
Actualmente solo se admiten las funciones escalares.
Debe crear la función asignada en la base de datos. La migraciones de EF Core no se encargarán de crearla.
Configuración de tipo independiente para Code First
En EF6 era posible encapsular la configuración de Code First de un tipo de entidad concreto al derivarlo de
EntityTypeConfiguration. En EF Core 2.0 se vuelve a incluir este patrón:

class CustomerConfiguration : IEntityTypeConfiguration<Customer>


{
public void Configure(EntityTypeBuilder<Customer> builder)
{
[Link](c => [Link]);
[Link](c => [Link]).HasMaxLength(200);
}
}

...
// OnModelCreating
[Link](new CustomerConfiguration());
Alto rendimiento
Agrupación de DbContext
El patrón básico para usar EF Core en una aplicación de [Link] Core normalmente implica el registro de un tipo
de DbContext personalizado en el sistema de inserción de dependencias y la posterior obtención de instancias de
ese tipo a través de los parámetros del constructor de los controladores. Esto significa que se crea una nueva
instancia de DbContext para cada solicitud.
En la versión 2.0 se incorpora una nueva manera de registrar tipos de DbContext personalizados en la inserción de
dependencias que presenta un grupo de instancias de DbContext reutilizables de forma transparente. Para usar la
agrupación de DbContext, use AddDbContextPool en lugar de AddDbContext durante el registro del servicio:

[Link]<BloggingContext>(
options => [Link](connectionString));

Si se usa este método, en el momento en que un controlador solicita una instancia de DbContext, primero se
comprueba si hay una disponible en el grupo. Una vez que termina el procesamiento de la solicitud, se restablece
cualquier estado en la instancia y la propia instancia se devuelve al grupo.
Esto es conceptualmente similar a la forma en que funciona la agrupación de conexiones en los proveedores de
[Link] y tiene la ventaja de ahorrar algunos de los costos de inicialización de la instancia de DbContext.
Limitaciones
El nuevo método presenta algunas limitaciones con respecto a lo que se puede hacer en el método
OnConfiguring() de DbContext.

WARNING
Evite el uso de la agrupación de DbContext si mantiene su propio estado (por ejemplo, campos privados) en la clase derivada
DbContext que no debe compartirse con otras solicitudes. EF Core solo restablece el estado del que es consciente antes de
agregar una instancia de DbContext al grupo.

Consultas compiladas de manera explícita


Esta es la segunda característica de rendimiento opcional diseñada para ofrecer ventajas en escenarios de gran
escala.
Las API de consulta compiladas de forma manual o explícita han estado disponibles en versiones anteriores de EF y
también en LINQ to SQL para permitir que las aplicaciones almacenen en caché la traducción de consultas de modo
que se puedan calcular una sola vez y ejecutarse muchas veces.
Aunque en general EF Core puede compilar y almacenar en caché automáticamente las consultas en función de una
representación con hash de las expresiones de consulta, este mecanismo puede usarse para obtener una pequeña
mejora de rendimiento al omitir el cálculo del hash y la búsqueda en caché, lo que permite que la aplicación use
una consulta ya compilada mediante la invocación de un delegado.
// Create an explicitly compiled query
private static Func<CustomerContext, int, Customer> _customerById =
[Link]((CustomerContext db, int id) =>
[Link]
.Include(c => [Link])
.Single(c => [Link] == id));

// Use the compiled query by invoking it


using (var db = new CustomerContext())
{
var customer = _customerById(db, 147);
}

Seguimiento de cambios
La asociación permite realizar un seguimiento de un gráfico de entidades nuevas y existentes.
EF Core admite la generación automática de valores de clave a través de una serie de mecanismos. Al usar esta
característica, se genera un valor si la propiedad de clave es el valor predeterminado de CLR, normalmente cero o
null. Esto significa que se puede pasar un gráfico de entidades a [Link] o [Link] y que EF Core
marca aquellas entidades que tienen una clave ya establecida como Unchanged , mientras que las que no tienen
establecida una clave se marcan como Added . Esto facilita la tarea de asociar un gráfico de entidades mixtas nuevas
y existentes al usar claves generadas. [Link] y [Link] funcionan de la misma manera, salvo que
las entidades con una clave establecida se marcan como Modified en lugar de Unchanged .

Consultar
Traducción de LINQ mejorada
Permite que más consultas se ejecuten correctamente, con más lógica evaluada en la base de datos (en lugar de en
memoria) y menos datos innecesariamente recuperados de la base de datos.
Mejoras de GroupJoin
Este trabajo mejora el SQL que se genera para las combinaciones agrupadas. Las combinaciones agrupadas suelen
ser un resultado de subconsultas en propiedades de navegación opcionales.
Interpolación de cadenas en FromSql y ExecuteSqlCommand
C# 6 presentó la interpolación de cadenas, una característica que permite insertar expresiones de C# directamente
en literales de cadena, lo que proporciona una forma útil de compilar cadenas en tiempo de ejecución. En EF Core
2.0 se ha agregado compatibilidad especial con las cadenas interpoladas a las dos API principales que aceptan
cadenas SQL sin formato: FromSql y ExecuteSqlCommand . Esta nueva compatibilidad permite que la interpolación de
cadenas de C# se use de forma "segura". Es decir, de una forma que protege frente a errores de inserción de SQL
comunes que pueden producirse al crear SQL de forma dinámica en tiempo de ejecución.
Este es un ejemplo:
var city = "London";
var contactTitle = "Sales Representative";

using (var context = CreateContext())


{
[Link]<Customer>()
.FromSql($@"
SELECT *
FROM ""Customers""
WHERE ""City"" = {city} AND
""ContactTitle"" = {contactTitle}")
.ToArray();
}

En este ejemplo hay dos variables insertadas en la cadena de formato SQL. EF Core genera el SQL siguiente:

@p0='London' (Size = 4000)


@p1='Sales Representative' (Size = 4000)

SELECT *
FROM ""Customers""
WHERE ""City"" = @p0
AND ""ContactTitle"" = @p1

[Link] ()
Se ha agregado la propiedad [Link], que EF Core o los proveedores pueden usar para definir métodos que se
asignen a los operadores o a las funciones de base de datos de forma que se puedan invocar en consultas LINQ. El
primer ejemplo de este método es Like():

var aCustomers =
from c in [Link]
where [Link]([Link], "a%")
select c;

Observe que Like() incluye una implementación en memoria, lo que puede resultar útil al trabajar en una base de
datos en memoria o cuando es necesario evaluar el predicado en el lado cliente.

Administración de bases de datos


Enlace de pluralización para scaffolding de DbContext
EF Core 2.0 presenta un nuevo servicio IPluralizer que se usa para singularizar nombres de tipo de entidad y
pluralizar nombres DbSet. La implementación predeterminada no está operativa, por lo que simplemente se trata
de un enlace en el que los usuarios pueden conectar fácilmente su propio pluralizador.
Este es el aspecto del enlace de un desarrollador de su propio pluralizador:
public class MyDesignTimeServices : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
[Link]<IPluralizer, MyPluralizer>();
}
}

public class MyPluralizer : IPluralizer


{
public string Pluralize(string name)
{
return [Link](name) ?? name;
}

public string Singularize(string name)


{
return [Link](name) ?? name;
}
}

Otros
Traslado del proveedor de SQLite de [Link] a [Link]
Esto proporciona una solución más robusta en [Link] para distribuir archivos binarios nativos de
SQLite en distintas plataformas.
Solo un proveedor por modelo
Mejora considerablemente la forma en que los proveedores pueden interactuar con el modelo y simplifica el
funcionamiento de las convenciones, las anotaciones y las API fluidas con distintos proveedores.
EF Core 2.0 ahora compila un elemento IModel diferente para cada proveedor que se va a usar. Esto suele ser
transparente para la aplicación. Esto ha permitido una simplificación de las API de metadatos de nivel inferior, de
modo que cualquier acceso a conceptos de metadatos relacionales comunes siempre se realiza mediante una
llamada a .Relational en lugar de a .SqlServer , .Sqlite , etc.
Registro y diagnóstico consolidados
Los mecanismos de registro (basados en ILogger) y diagnóstico (basados en DiagnosticSource) ahora comparten
más código.
Los identificadores de evento de los mensajes enviados a un elemento ILogger han cambiado en 2.0. Los
identificadores de evento ahora son únicos en el código de EF Core. Ahora, estos mensajes también siguen el
patrón estándar de registro estructurado que usa, por ejemplo, MVC.
Las categorías de registrador también han cambiado. Ahora hay un conjunto conocido de categorías a las que se
accede a través de DbLoggerCategory.
Los eventos de DiagnosticSource ahora usan los mismos nombres de identificador de evento que los mensajes de
ILogger correspondientes.
Características nuevas en EF Core 1.1
08/04/2020 • 2 minutes to read • Edit Online

Modelado
Asignación de campos
Permite configurar un campo de respaldo para una propiedad. Puede resultar útil en las propiedades de solo
lectura o en los datos que tienen métodos Get/Set en lugar de una propiedad.
Asignación a tablas optimizadas para memoria en SQL Server
Puede especificar que la tabla a la que está asignada una entidad está optimizada para memoria. Cuando use EF
Core para crear y mantener una base de datos basada en el modelo (ya sea con migraciones o
[Link]() ), se creará una tabla optimizada para memoria para estas entidades.

seguimiento de cambios
API adicionales de seguimiento de cambios de EF6
Como Reload , GetModifiedProperties , GetDatabaseValues etc.

Consultar
Carga explícita
Permite desencadenar el rellenado de una propiedad de navegación o una entidad que se cargó anteriormente a
partir de la base de datos.
[Link]
Proporciona una manera sencilla de capturar una entidad en función de su valor de clave principal.

Otros
Resistencia de la conexión
Reintenta automáticamente los comandos de base de datos erróneos. Esto resulta especialmente útil cuando se
realizan conexiones a SQL Azure, donde los errores transitorios son comunes.
Reemplazo de servicio simplificado
Facilita el reemplazo de servicios internos que EF usa.
Características incluidas en EF Core 1.0
08/04/2020 • 8 minutes to read • Edit Online

Plataformas
.NET Framework 4.5.1
Incluye la consola, WPF, WinForms, [Link] 4, etc.
.NET Standard 1.3
Incluye [Link] Core que tiene como destino tanto .NET Framework como .NET Core en Windows, OSX y Linux.

Modelado
Modelado básico
Según las entidades POCO con las propiedades get/set de tipos escalares comunes ( int , string , etc.).
Relaciones y propiedades de navegación
Las relaciones uno a varios y uno a cero se pueden especificar en el modelo en función de una clave externa. Las
propiedades de navegación de tipos de referencia o colección simple se pueden asociar con estas relaciones.
Convenciones integradas
Construyen un modelo inicial en función de la forma de las clases de entidad.
API fluida
Permite reemplazar el método OnModelCreating en el contexto para seguir configurando el modelo que la
convención detectó.
Anotaciones de datos
Son atributos que se pueden agregar a las propiedades o clases de entidad y que influyen en el modelo de EF. Por
ejemplo, al agregar [Required] se indica a EF que una propiedad es obligatoria.
Asignación de tabla relacional
Permite asignar las entidades a tablas o columnas.
Generación de valor de clave
Incluye la generación de bases de datos y la generación del lado cliente.
Valores generados por la base de datos
Permite que la base de datos genere los valores en la inserción (valores predeterminados) o la actualización
(columnas calculadas).
Secuencias en SQL Server
Permite definir los objetos de secuencia en el modelo.
Restricciones únicas
Permite la definición de las claves alternativas y la capacidad de definir las relaciones que se dirigen a esa clave.
Índices
La definición de índices en el modelo introduce automáticamente índices en la base de datos. También se admiten
los índices únicos.
Propiedades de estado reemplazadas
Permite que las propiedades que se definen en el modelo no se declaren ni almacenen en la clase .NET, pero EF
Core sí puede hacer un seguimiento de ellas y actualizarlas. Suele usarse para las propiedades de clave externa
cuando no se desea exponerlas en el objeto.
Patrón de herencia de tabla por jerarquía
Permite que las entidades de una jerarquía de herencia se guarde en una sola tabla a través de una columna de
discriminador para identificar el tipo de entidad de un registro determinado en la base de datos.
Validación de modelos
Detecta los patrones no válidos del modelo y proporciona mensajes de error útiles.

seguimiento de cambios
Seguimiento de cambios de instantánea
Permite detectar automáticamente los cambios en las entidades a través de la comparación del estado actual con
una copia (instantánea) del estado original.
Seguimiento de cambios de notificación
Permite que las entidades notifiquen a la herramienta de seguimiento de cambios cuando se modifiquen los
valores de propiedad.
Acceso al estado con seguimiento
A través de [Link] y [Link] .
Adjuntar grafos o entidades desasociados
La nueva API [Link] ayuda a volver a adjuntar entidades a un contexto para guardar las entidades
nuevas o modificadas.

Guardar datos
Funcionalidad básica de guardado
Permite que los cambios en las instancias de la entidad se conserven en la base de datos.
Simultaneidad optimista
Impide sobrescribir los cambios realizados por otro usuario desde que se capturaron de la base de datos.
Característica SaveChanges asincrónica
Puede liberar el subproceso actual para que procese otras solicitudes mientras la base de datos procesa los
comandos que se emiten desde SaveChanges .
Transacciones de bases de datos
Es decir, SaveChanges siempre es atómica (lo que significa que siempre se completa correctamente o que no se
realiza ningún cambio en la base de datos). También hay API relacionadas con transacciones que permiten
compartir las transacciones entre las instancias de contexto, etc.
Relacional: procesamiento de instrucciones por lotes
Proporciona un mejor rendimiento mediante el procesamiento por lotes de varios comandos
INSERT/UPDATE/DELETE en un solo ciclo de ida y vuelta a la base de datos.

Consultar
Compatibilidad básica con LINQ
Proporciona la capacidad de usar LINQ para recuperar datos de la base de datos.
Evaluación combinada de cliente/servidor
Permite que las consultas contengan una lógica que no se puede evaluar en la base de datos y, por lo tanto, se debe
evaluar después de que los datos se recuperan en la memoria.
NoTracking
Las consultas permiten ejecutar más rápido las consultas cuando el contexto no necesita supervisar los cambios
realizados en las instancias de entidad (esto es útil si los resultados son de solo lectura).
Carga diligente
Proporciona los métodos Include y ThenInclude para identificar los datos relacionados que se deben capturar
cuando se realizan las consultas.
Consulta asincrónica
Puede liberar el subproceso actual (y los recursos asociados) para que procese otras solicitudes mientras la base de
datos procesa la consulta.
Consultas SQL sin formato
Proporciona el método [Link] para usar consultas SQL sin procesar para capturar datos. Estas consultas
también se pueden componer mediante LINQ.

Administración de esquemas de la base de datos


API de creación o eliminación de la base de datos
Diseñadas principalmente para realizar pruebas en las que desea crear o eliminar rápidamente la base de datos sin
usar migraciones.
Migraciones de la base de datos relacional
Permiten que un esquema de la base de datos relacional evolucione en el tiempo a medida que cambia el modelo.
Ingeniería inversa desde la base de datos
Aplica scaffolding a un modelo de EF en función de un esquema de la base de datos relacional.

Proveedores de bases de datos


SQL Server
Se conecta a Microsoft SQL Server 2008 y versiones posteriores.
SQLite
Se conecta a una base de datos SQLite 3.
En memoria
Diseñado para habilitar fácilmente la realización de pruebas sin conectarse a una base de datos real.
Proveedores de terceros
Existen varios proveedores disponibles para otros motores de base de datos. Para una lista completa, consulte
Proveedores de bases de datos.
Actualización de EF Core 1,0 RC1 a 1,0 RC2
11/03/2020 • 9 minutes to read

En este artículo se proporcionan instrucciones para mover una aplicación compilada con paquetes RC1 a RC2.

Nombres y versiones de los paquetes


Entre RC1 y RC2, cambiamos de "Entity Framework 7" a "Entity Framework Core". Puede leer más sobre los
motivos del cambio en esta publicación de Scott Hanselman. Debido a este cambio, los nombres de los paquetes
cambiaron de EntityFramework.* a [Link].* y nuestras versiones de 7.0.0-rc1-final a
1.0.0-rc2-final (o 1.0.0-preview1-final para las herramientas).

Tendrá que quitar por completo los paquetes RC1 y, a continuación, instalar los RC2. Esta es la
asignación para algunos paquetes comunes.

PA Q UET E RC 1 EQ UIVA L EN T ES DE RC 2

EntityFramework. MicrosoftSqlServer 7.0.0-RC1-final Microsoft. EntityFrameworkCore. SqlServer 1.0.0-RC2-final

EntityFramework. SQLite 7.0.0-RC1-final Microsoft. EntityFrameworkCore. SQLite 1.0.0-RC2-final

EntityFramework7. Npgsql 3.1.0-RC1-3 NpgSql. EntityFrameworkCore. Postgres

EntityFramework. SqlServerCompact35 7.0.0-RC1-final EntityFrameworkCore. SqlServerCompact35 1.0.0-RC2-final

EntityFramework. SqlServerCompact40 7.0.0-RC1-final EntityFrameworkCore. SqlServerCompact40 1.0.0-RC2-final

EntityFramework. inmemory 7.0.0-RC1-final Microsoft. EntityFrameworkCore. inmemory 1.0.0-RC2-final

EntityFramework. IBMDataServer 7.0.0-beta1 Todavía no está disponible para RC2

EntityFramework. Commands 7.0.0-RC1-final Microsoft. EntityFrameworkCore. Tools 1.0.0-preview1-final

EntityFramework. MicrosoftSqlServer. Design 7.0.0-RC1-final Microsoft. EntityFrameworkCore. SqlServer. Design 1.0.0-RC2-


final

Espacios de nombres
Junto con los nombres de paquete, los espacios de nombres cambiaron de [Link].* a
[Link].* . Puede controlar este cambio con una búsqueda/reemplazo de
using [Link] con using [Link] .

Cambios en la Convención de nomenclatura de tablas


Un cambio funcional significativo que se llevó a cabo en RC2 era usar el nombre de la propiedad DbSet<TEntity>
de una entidad determinada como el nombre de tabla al que se asigna, en lugar de simplemente el nombre de
clase. Puede leer más sobre este cambio en el problema del anuncio relacionado.
En el caso de las aplicaciones RC1 existentes, se recomienda agregar el código siguiente al principio del método
OnModelCreating para mantener la estrategia de nomenclatura RC1:
foreach (var entity in [Link]())
{
[Link]().TableName = [Link]();
}

Si desea adoptar la nueva estrategia de nomenclatura, se recomienda completar correctamente el resto de los
pasos de actualización y, a continuación, quitar el código y crear una migración para aplicar el cambio de nombre
de la tabla.

AddDbContext/[Link] cambia (solo proyectos de [Link] Core)


En RC1, tenía que agregar Entity Framework servicios al proveedor de servicios de la aplicación
[Link](...) :

[Link]()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
[Link](Configuration["ConnectionStrings:DefaultConnection"]));

En RC2, puede quitar las llamadas a AddEntityFramework() , AddSqlServer() , etc.:

[Link]<ApplicationDbContext>(options =>
[Link](Configuration["ConnectionStrings:DefaultConnection"]));

También debe agregar un constructor, al contexto derivado, que toma las opciones de contexto y las pasa al
constructor base. Esto es necesario porque hemos quitado algunas de las ideas mágicas que snuck en segundo
plano:

public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)


: base(options)
{
}

Pasar un IServiceProvider
Si tiene código RC1 que pasa un IServiceProvider al contexto, ahora se mueve a DbContextOptions , en lugar de ser
un parámetro de constructor independiente. Utilice [Link](...) para
establecer el proveedor de servicios.
Prueba
El escenario más común para hacerlo era controlar el ámbito de una base de datos inmemory al realizar las
pruebas. Vea el artículo sobre las pruebas actualizadas para obtener un ejemplo de cómo hacerlo con RC2.
Resolver servicios internos desde el proveedor de servicios de aplicación (solo proyectos de [Link] Core )
Si tiene una aplicación [Link] Core y desea que EF resuelva los servicios internos del proveedor de servicios de
aplicación, hay una sobrecarga de AddDbContext que le permite configurar lo siguiente:

[Link]()
.AddDbContext<ApplicationDbContext>((serviceProvider, options) =>
[Link](Configuration["ConnectionStrings:DefaultConnection"])
.UseInternalServiceProvider(serviceProvider));
WARNING
Se recomienda permitir que EF administre internamente sus propios servicios, a menos que tenga un motivo para combinar
los servicios de EF internos en el proveedor de servicios de aplicación. La razón principal por la que puede querer hacer esto
es usar el proveedor de servicios de aplicación para reemplazar los servicios que EF usa internamente

Comandos de DNX = CLI de .NET > (solo para proyectos de [Link]


Core)
Si anteriormente usó los comandos dnx ef para proyectos de [Link] 5, ahora se han pasado a dotnet ef
comandos. Todavía se aplica la misma sintaxis de comando. Puede usar dotnet ef --help para obtener
información sobre la sintaxis.
La forma en que se registran los comandos ha cambiado en RC2, debido a que DNX se ha reemplazado por la CLI
de .NET. Los comandos se registran ahora en una sección tools en [Link] :

"tools": {
"[Link]": {
"version": "1.0.0-preview1-final",
"imports": [
"portable-net45+win8+dnxcore50",
"portable-net45+win8"
]
}
}

TIP
Si usa Visual Studio, ahora puede usar la consola del administrador de paquetes para ejecutar comandos EF para proyectos de
[Link] Core (esto no se admitía en RC1). Todavía tiene que registrar los comandos en la sección tools de [Link]
para hacerlo.

Los comandos del administrador de paquetes requieren PowerShell 5


Si usa los comandos Entity Framework en la consola del administrador de paquetes en Visual Studio, tendrá que
asegurarse de que tiene PowerShell 5 instalado. Se trata de un requisito temporal que se quitará en la siguiente
versión (consulte el problema #5327 para obtener más detalles).

Usar "Imports" en Project. JSON


Algunas de las dependencias de EF Core no admiten aún .NET Standard. EF Core en .NET Standard y los proyectos
de .NET Core pueden requerir la adición de "Imports" a Project. JSON como una solución temporal.
Al agregar EF, la restauración de NuGet mostrará este mensaje de error:
Package Ix-Async 1.2.5 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package Ix-Async 1.2.5
supports:
- net40 (.NETFramework,Version=v4.0)
- net45 (.NETFramework,Version=v4.5)
- portable-net45+win8+wp8 (.NETPortable,Version=v0.0,Profile=Profile78)
Package [Link] 2.0.2 is not compatible with netcoreapp1.0 (.NETCoreApp,Version=v1.0). Package
[Link] 2.0.2 supports:
- net35 (.NETFramework,Version=v3.5)
- net40 (.NETFramework,Version=v4.0)
- net45 (.NETFramework,Version=v4.5)
- portable-net45+win8+wp8+wpa81 (.NETPortable,Version=v0.0,Profile=Profile259)

La solución consiste en importar manualmente el perfil portátil "portable-net451 + win8". Esto obliga a NuGet a
tratar estos binarios que coinciden con este proporcionado como un marco compatible con .NET Standard, aunque
no lo sean. Aunque "portable-net451 + win8" no es 100% compatible con .NET Standard, es lo suficientemente
compatible para la transición de PCL a .NET Standard. Las importaciones se pueden quitar cuando las dependencias
de EF finalmente se actualizan a .NET Standard.
Se pueden agregar varios marcos a "Imports" en la sintaxis de la matriz. Otras importaciones pueden ser
necesarias si agrega bibliotecas adicionales al proyecto.

{
"frameworks": {
"netcoreapp1.0": {
"imports": ["dnxcore50", "portable-net451+win8"]
}
}
}

Vea el problema #5176.


Actualización de EF Core 1,0 RC2 a RTM
11/03/2020 • 4 minutes to read

En este artículo se proporcionan instrucciones para mover una aplicación compilada con los paquetes de RC2 a
1.0.0 RTM.

Versiones de paquetes
Los nombres de los paquetes de nivel superior que se instalarían normalmente en una aplicación no cambiaban
entre RC2 y RTM.
Debe actualizar los paquetes instalados a las versiones de RTM:
Los paquetes en tiempo de ejecución (por ejemplo, [Link] ) cambiaron de
1.0.0-rc2-final a 1.0.0 .

El paquete de [Link] ha cambiado de 1.0.0-preview1-final a


1.0.0-preview2-final . Tenga en cuenta que las herramientas siguen siendo versiones preliminares.

Es posible que las migraciones existentes necesiten maxLength


agregadas
En RC2, la definición de columna en una migración se parece a [Link]<string>(nullable: true) y la longitud
de la columna se buscó en algunos metadatos que almacenamos en el código subyacente a la migración. En RTM,
la longitud se incluye ahora en el código con scaffolding [Link]<string>(maxLength: 450, nullable: true) .
Las migraciones existentes con scaffolding antes de usar RTM no tendrán el argumento maxLength especificado.
Esto significa que se utilizará la longitud máxima admitida por la base de datos ( nvarchar(max) en SQL Server).
Esto puede ser adecuado para algunas columnas, pero las columnas que forman parte de una clave, clave externa o
índice deben actualizarse para incluir una longitud máxima. Por Convención, 450 es la longitud máxima utilizada
para las claves, las claves externas y las columnas indizadas. Si ha configurado explícitamente una longitud en el
modelo, debe utilizar esa longitud en su lugar.
[Link] Identity
Este cambio afecta a los proyectos que usan [Link] Identity y que se crearon a partir de una plantilla de proyecto
anterior a RTM. La plantilla de proyecto incluye una migración que se usa para crear la base de datos. Esta
migración se debe editar para especificar una longitud máxima de 256 para las columnas siguientes.
AspNetRoles
Nombre
NormalizedName
AspNetUsers
Email
NormalizedEmail
NormalizedUserName
UserName
Si no se realiza este cambio, se producirá la siguiente excepción cuando la migración inicial se aplique a una base
de datos.
[Link] (0x80131904): Column 'NormalizedName' in table 'AspNetRoles' is of a type
that is invalid for use as a key column in an index.

.NET Core: Quite "Imports" en Project. JSON


Si el destino era .NET Core con RC2, necesitaba agregar imports a Project. JSON como una solución temporal para
algunas de las dependencias de EF Core que no admiten .NET Standard. Ahora se pueden quitar.

{
"frameworks": {
"netcoreapp1.0": {
"imports": ["dnxcore50", "portable-net451+win8"]
}
}
}

NOTE
A partir de la versión 1,0 RTM, el SDK de .net Core ya no admite [Link] ni desarrollar aplicaciones de .net Core con
Visual Studio 2015. Se recomienda migrar de [Link] a csproj. Si usa Visual Studio, se recomienda que actualice a visual
studio 2017.

UWP: agregar redirecciones de enlace


Al intentar ejecutar los comandos EF en los proyectos de Plataforma universal de Windows (UWP) se produce el
siguiente error:

[Link]: Could not load file or assembly '[Link], Version=[Link],


Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The located assembly's manifest
definition does not match the assembly reference.

Debe agregar manualmente redirecciones de enlace al proyecto de UWP. Cree un archivo denominado [Link]
en la carpeta raíz del proyecto y agregue redireccionamientos a las versiones de ensamblado correctas.

<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="[Link]"
publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="[Link]"
newVersion="[Link]"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="[Link]"
publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="[Link]"
newVersion="[Link]"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Actualización de las aplicaciones de las versiones
anteriores a EF Core 2,0
11/03/2020 • 16 minutes to read

Hemos aprovechado la oportunidad de refinar significativamente las API y los comportamientos existentes en 2,0.
Hay algunas mejoras que pueden requerir la modificación del código de aplicación existente, aunque creemos que,
para la mayoría de las aplicaciones, el impacto será bajo, en la mayoría de los casos solo requiere volver a compilar
y cambios guiados mínimos para reemplazar las API obsoletas.
La actualización de una aplicación existente a EF Core 2,0 puede requerir:
1. Actualización de la implementación de .NET de destino de la aplicación a una que admite .NET Standard 2,0.
Vea implementaciones de .net compatibles para obtener más detalles.
2. Identifique un proveedor para la base de datos de destino que sea compatible con EF Core 2,0. Consulte EF
Core 2,0 requiere un proveedor de base de datos 2,0 .
3. Actualizar todos los paquetes de EF Core (tiempo de ejecución y herramientas) a 2,0. Consulte instalación de
EF Core para obtener más detalles.
4. Realice los cambios de código necesarios para compensar los cambios importantes descritos en el resto de
este documento.

[Link] Core ahora incluye EF Core


Las aplicaciones para [Link] Core 2.0 pueden usar EF Core 2.0 sin dependencias adicionales además de
proveedores de bases de datos de terceros. Sin embargo, las aplicaciones que tienen como destino versiones
anteriores de [Link] Core deben actualizar a [Link] Core 2,0 para poder usar EF Core 2,0. Para obtener más
información sobre la actualización de aplicaciones [Link] Core a 2,0, consulte la documentación de [Link] Core
sobre el tema.

Nueva forma de obtener servicios de aplicación en [Link] Core


El patrón recomendado para las aplicaciones Web de [Link] Core se ha actualizado para 2,0 de forma que se
interrumpió la lógica en tiempo de diseño EF Core utiliza en 1. x. Anteriormente, en tiempo de diseño, EF Core
intentaría invocar [Link] directamente para tener acceso al proveedor de servicios de la
aplicación. En [Link] Core 2,0, la configuración se inicializa fuera de la clase Startup . Las aplicaciones que usan
EF Core normalmente acceden a su cadena de conexión desde la configuración, por lo que Startup por sí misma
ya no es suficiente. Si actualiza una aplicación [Link] Core 1. x, es posible que reciba el siguiente error al usar las
herramientas de EF Core.

No se encontró ningún constructor sin parámetros en ' ApplicationContext '. Agregue un constructor sin
parámetros a ' ApplicationContext ' o agregue una implementación de '
IDesignTimeDbContextFactory<ApplicationContext>' en el mismo ensamblado que ' ApplicationContext '

Se ha agregado un nuevo enlace en tiempo de diseño a la plantilla predeterminada de [Link] Core 2.0. El método
[Link] estático permite a EF Core tener acceso al proveedor de servicios de la aplicación en tiempo
de diseño. Si está actualizando una aplicación [Link] Core 1. x, deberá actualizar la clase Program para que se
parezca a lo siguiente.
using [Link];
using [Link];

namespace AspNetCoreDotNetCore2._0App
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>


[Link](args)
.UseStartup<Startup>()
.Build();
}
}

La adopción de este nuevo patrón al actualizar las aplicaciones a 2,0 es muy recomendable y es necesaria para que
funcionen las características del producto, como Entity Framework Core las migraciones. La otra alternativa común
es implementar IDesignTimeDbContextFactory<TContext > .

IDbContextFactory cambiado
Con el fin de admitir diversos patrones de aplicación y proporcionar a los usuarios un mayor control sobre cómo se
utiliza su DbContext en tiempo de diseño, en el pasado, siempre se ofrecía la interfaz IDbContextFactory<TContext> .
En tiempo de diseño, las herramientas de EF Core detectarán las implementaciones de esta interfaz en el proyecto y
la usarán para crear objetos DbContext .
Esta interfaz tenía un nombre muy general que engaña a algunos usuarios para intentar volver a usarlo para otros
escenarios de creación de DbContext . No estaban protegidos cuando las herramientas de EF intentaban usar su
implementación en tiempo de diseño y provocaban que se produjera un error en los comandos como
Update-Database o dotnet ef database update .

Con el fin de comunicar la semántica sólida en tiempo de diseño de esta interfaz, se le ha cambiado el nombre a
IDesignTimeDbContextFactory<TContext> .

En la versión 2,0, el IDbContextFactory<TContext> todavía existe pero está marcado como obsoleto.

DbContextFactoryOptions quitado
Debido a los cambios [Link] Core 2,0 descritos anteriormente, encontramos que DbContextFactoryOptions ya no
era necesario en la nueva interfaz de IDesignTimeDbContextFactory<TContext> . Estas son las alternativas que debe
usar en su lugar.

DB C O N T EXT FA C TO RY O P T IO N S A LT ERN AT IVA

ApplicationBasePath AppContext. BaseDirectory

ContentRootPath Directorio. GetCurrentDirectory ()

EnvironmentName Environment. GetEnvironmentVariable


("ASPNETCORE_ENVIRONMENT")

Directorio de trabajo en tiempo de diseño cambiado


Los cambios [Link] Core 2,0 también requerían el directorio de trabajo que usa dotnet ef para alinearse con el
directorio de trabajo que usa Visual Studio cuando se ejecuta la aplicación. Un efecto lateral observable de esto es
que los nombres de archivo de SQLite ahora son relativos al directorio del proyecto y no al directorio de salida
como se solían usar.

EF Core 2,0 requiere un proveedor de base de datos 2,0


Por EF Core 2,0 hemos realizado muchas simplificaciones y mejoras en la forma en que funcionan los proveedores
de bases de datos. Esto significa que los proveedores 1.0. x y 1.1. x no funcionarán con EF Core 2,0.
El equipo de EF envía los proveedores de SQL Server y SQLite, y las versiones 2,0 estarán disponibles como parte
de la versión 2,0. Los proveedores de terceros de código abierto para SQL Compact, PostgreSQLy MySQL se están
actualizando para 2,0. Para todos los demás proveedores, póngase en contacto con el escritor del proveedor.

Los eventos de diagnóstico y registro han cambiado


Nota: estos cambios no deben afectar a la mayoría del código de aplicación.
Los identificadores de eventos de los mensajes enviados a un ILogger han cambiado en 2,0. Los identificadores de
evento ahora son únicos en el código de EF Core. Ahora, estos mensajes también siguen el patrón estándar de
registro estructurado que usa, por ejemplo, MVC.
Las categorías de registrador también han cambiado. Ahora hay un conjunto conocido de categorías a las que se
accede a través de DbLoggerCategory.
Los eventos de DiagnosticSource ahora usan los mismos nombres de ID. de evento que los mensajes de ILogger
correspondientes. Las cargas de evento son tipos nominales derivados de EventData.
Los identificadores de eventos, tipos de carga y categorías se documentan en las clases CoreEventId y
RelationalEventId .
Los identificadores también se han pasado de Microsoft. EntityFrameworkCore. Infrastructure al nuevo espacio de
nombres Microsoft. EntityFrameworkCore. Diagnostics.

Cambios de la API de metadatos relacionales EF Core


EF Core 2.0 ahora compila un elemento IModel diferente para cada proveedor que se va a usar. Esto suele ser
transparente para la aplicación. Esto ha facilitado la simplificación de las API de metadatos de nivel inferior, de
modo que cualquier acceso a conceptos de metadatos relacionales comunes siempre se realiza a través de una
llamada a .Relational en lugar de .SqlServer , .Sqlite , etc. Por ejemplo, 1.1. x código similar al siguiente:

var tableName = [Link](typeof(User)).SqlServer().TableName;

Ahora debe escribirse de la siguiente manera:

var tableName = [Link](typeof(User)).Relational().TableName;

En lugar de utilizar métodos como ForSqlServerToTable , los métodos de extensión ahora están disponibles para
escribir código condicional basado en el proveedor actual en uso. Por ejemplo:

[Link]<User>().ToTable(
[Link]() ? "SqlServerName" : "OtherName");

Tenga en cuenta que este cambio solo se aplica a las API/metadatos que se definen para todos los proveedores
relacionales. La API y los metadatos siguen siendo los mismos cuando son específicos de un solo proveedor. Por
ejemplo, los índices clúster son específicos de SQL Server, por lo que se deben seguir ForSqlServerIsClustered y
.SqlServer().IsClustered() .

No tomar el control del proveedor de servicios de EF


EF Core usa una IServiceProvider interna (un contenedor de inserción de dependencias) para su implementación
interna. Las aplicaciones deben permitir que EF Core crear y administrar este proveedor, excepto en casos
especiales. Considere la posibilidad de quitar todas las llamadas a UseInternalServiceProvider . Si una aplicación
necesita llamar a UseInternalServiceProvider , considere la posibilidad de presentar un problema para que
podamos investigar otras maneras de controlar su escenario.
El código de aplicación no requiere la llamada a AddEntityFramework , AddEntityFrameworkSqlServer , etc. a menos
que se llame también a UseInternalServiceProvider . Quite todas las llamadas existentes a AddEntityFramework o
AddEntityFrameworkSqlServer , etc. AddDbContext debe seguir utilizándose de la misma manera que antes.

Las bases de datos en memoria deben tener nombre


La base de datos en memoria global sin nombre se ha quitado y, en su lugar, todas las bases de datos en memoria
deben tener nombre. Por ejemplo:

[Link]("MyDatabase");

Esto crea o usa una base de datos con el nombre "base de datos". Si se llama de nuevo a UseInMemoryDatabase con
el mismo nombre, se usará la misma base de datos en memoria, lo que permite que varias instancias de contexto lo
compartan.

Cambios de la API de solo lectura


IsReadOnlyBeforeSave , IsReadOnlyAfterSave y IsStoreGeneratedAlways han quedado obsoletos y se han
reemplazado por BeforeSaveBehavior y AfterSaveBehavior. Estos comportamientos se aplican a cualquier
propiedad (no solo a las propiedades generadas por el almacén) y determinan cómo se debe usar el valor de la
propiedad al insertar en una fila de base de datos ( BeforeSaveBehavior ) o al actualizar una fila de base de datos
existente ( AfterSaveBehavior ).
Las propiedades marcadas como ValueGenerated. OnAddOrUpdate (por ejemplo, para las columnas calculadas)
omitirán de forma predeterminada cualquier valor establecido actualmente en la propiedad. Esto significa que
siempre se obtendrá un valor generado por el almacén independientemente de si se ha establecido o modificado
algún valor en la entidad a la que se realiza el seguimiento. Esto se puede cambiar estableciendo un
Before\AfterSaveBehavior diferente.

Nuevo comportamiento de eliminación de ClientSetNull


En versiones anteriores, DeleteBehavior. Restrict tenía un comportamiento para las entidades a las que realiza un
seguimiento el contexto que mejoró la semántica de SetNull coincidentes. En EF Core 2,0, se ha introducido un
nuevo comportamiento de ClientSetNull como valor predeterminado para las relaciones opcionales. Este
comportamiento tiene SetNull semántica para las entidades sometidas a seguimiento y el comportamiento de
Restrict para las bases de datos creadas mediante EF Core. En nuestra experiencia, estos son los
comportamientos más previstos y útiles para las entidades de las que se realiza un seguimiento y la base de datos.
ahora se respeta [Link] para las entidades de las que se ha realizado un seguimiento cuando se
establece para relaciones opcionales.
Paquetes en tiempo de diseño del proveedor quitados
Se ha quitado el paquete de [Link]. Su contenido se consolida en
[Link] y [Link] .
Esto se propaga a los paquetes en tiempo de diseño del proveedor. Los paquetes (
[Link] , [Link] , etc.) se han quitado
y su contenido se ha consolidado en los paquetes principales del proveedor.
Para habilitar Scaffold-DbContext o dotnet ef dbcontext scaffold en EF Core 2,0, solo tiene que hacer referencia al
paquete de proveedor único:

<PackageReference Include="[Link]"
Version="2.0.0" />
<PackageReference Include="[Link]"
Version="2.0.0"
PrivateAssets="All" />
<DotNetCliToolReference Include="[Link]"
Version="2.0.0" />
Introducción a EF Core
08/04/2020 • 7 minutes to read • Edit Online

En este tutorial se crea una aplicación de consola de .NET Core que realiza el acceso a datos en una base de datos
SQLite mediante Entity Framework Core.
Puede seguir el tutorial con Visual Studio en Windows o mediante la CLI de .NET Core en Windows, macOS o
Linux.
Vea un ejemplo de este artículo en GitHub.

Requisitos previos
Instale el software siguiente:
CLI de .NET Core
Visual Studio
SDK de .NET Core.

Crear un proyecto nuevo


CLI de .NET Core
Visual Studio

dotnet new console -o EFGetStarted


cd EFGetStarted

Instalación de Entity Framework Core


Para instalar EF Core, instale el paquete de los proveedores de bases de datos de EF Core que quiera establecer
como destino. Este tutorial usa SQLite porque se ejecuta en todas las plataformas compatibles con .NET Core. Para
obtener una lista de proveedores disponibles, vea Proveedores de bases de datos.
CLI de .NET Core
Visual Studio

dotnet add package [Link]

Creación del modelo


Defina una clase de contexto y clases de entidad que conformen el modelo.
CLI de .NET Core
Visual Studio
En el directorio del proyecto, cree [Link] con el código siguiente.
using [Link];
using [Link];

namespace EFGetStarted
{
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder options)


=> [Link]("Data Source=[Link]");
}

public class Blog


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

public List<Post> Posts { get; } = new List<Post>();


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}
}

EF Core también puede aplicar ingeniería inversa en un modelo desde una base de datos existente.
Sugerencia: En una aplicación real, lo habitual sería colocar cada clase en un archivo independiente y la cadena de
conexión, en un archivo de configuración o una variable de entorno. Para que el tutorial sea sencillo, todo está
incluido en un archivo.

Creación de la base de datos


Los pasos siguientes usan migraciones para crear una base de datos.
CLI de .NET Core
Visual Studio
Ejecute los comandos siguientes:

dotnet tool install --global dotnet-ef


dotnet add package [Link]
dotnet ef migrations add InitialCreate
dotnet ef database update

Esto instala dotnet ef y el paquete de diseño necesario para ejecutar el comando en un proyecto. El
comando migrations aplica la técnica scaffolding a una migración para crear el conjunto inicial de tablas
para el modelo. El comando database update crea la base de datos y le aplica la nueva migración.

Creación, lectura, actualización y eliminación


Abra [Link] y reemplace el contenido por el código siguiente:
using System;
using [Link];

namespace EFGetStarted
{
class Program
{
static void Main()
{
using (var db = new BloggingContext())
{
// Create
[Link]("Inserting a new blog");
[Link](new Blog { Url = "[Link] });
[Link]();

// Read
[Link]("Querying for a blog");
var blog = [Link]
.OrderBy(b => [Link])
.First();

// Update
[Link]("Updating the blog and adding a post");
[Link] = "[Link]
[Link](
new Post
{
Title = "Hello World",
Content = "I wrote an app using EF Core!"
});
[Link]();

// Delete
[Link]("Delete the blog");
[Link](blog);
[Link]();
}
}
}
}

Ejecutar la aplicación
CLI de .NET Core
Visual Studio

dotnet run

Pasos siguientes
Siga el tutorial de [Link] Core para usar EF Core en una aplicación web.
Obtenga más información sobre las expresiones de consulta LINQ.
Configure su modelo para especificar aspectos como requerido y longitud máxima.
Use Migraciones para actualizar el esquema de la base de datos después de cambiar el modelo.
Instalación de Entity Framework Core
08/04/2020 • 10 minutes to read • Edit Online

Prerequisites
EF Core es una biblioteca de .NET Standard 2.0. Por este motivo, EF Core requiere una implementación de
.NET que admita .NET Standard 2.0 para poder ejecutarse. Otras bibliotecas de .NET Standard 2.0 también
pueden hacer referencia a EF Core.
Por ejemplo, puede usar EF Core para desarrollar aplicaciones que tengan como destino .NET Core. La
compilación de aplicaciones de .NET Core requiere el SDK de .NET Core. También puede usar un entorno de
desarrollo como Visual Studio, Visual Studio para Mac o Visual Studio Code. Para obtener más información,
vea Introducción a .NET Core.
Puede usar EF Core para desarrollar aplicaciones en Windows con Visual Studio. Se recomienda usar la
última versión de Visual Studio.
EF Core puede ejecutarse en otras implementaciones de .NET, como Xamarin y .NET Native. Pero en la
práctica, estas implementaciones tienen limitaciones de runtime que podrían afectar el rendimiento de EF
Core en su aplicación. Para obtener más información, vea Implementaciones de .NET compatibles con EF
Core.
Por último, los diferentes proveedores de bases de datos pueden requerir versiones de motores de bases de
datos, implementaciones de .NET o sistemas operativos específicas. Asegúrese de que esté disponible un
proveedor de bases de datos de EF Core que admita el entorno adecuado para su aplicación.

Obtención del runtime de Entity Framework Core


Para agregar EF Core a una aplicación, instale el paquete NuGet para el proveedor de bases de datos que quiera
usar.
Si está desarrollando una aplicación de [Link] Core, no tendrá que instalar los proveedores en memoria ni de
SQL Server. Estos proveedores están incluidos en las versiones actuales de [Link] Core, junto al runtime de EF
Core.
Para instalar o actualizar paquetes NuGet, puede usar la interfaz de la línea de comandos (CLI) de .NET Core, o bien
el cuadro de diálogo o la consola del Administrador de paquetes de Visual Studio.
CLI de .NET Core
Use el comando de la CLI de .NET Core en la línea de comandos del sistema operativo para instalar o
actualizar el proveedor de SQL Server de EF Core:

dotnet add package [Link]

Puede indicar una versión específica en el comando dotnet add package usando el modificador -v . Por
ejemplo, para instalar paquetes de EF Core 2.2.0, anexe -v 2.2.0 al comando.
Para obtener más información, vea Herramientas de la interfaz de la línea de comandos (CLI) de .NET Core.
Cuadro de diálogo Administrador de paquetes NuGet en Visual Studio
En el menú de Visual Studio, seleccione Proyecto > Administrar paquetes NuGet
Haga clic en la pestaña Examinar o Actualizaciones
Para instalar o actualizar el proveedor de SQL Server, seleccione el paquete
[Link] y confirme la acción.

Para obtener más información, vea Diálogo del Administrador de paquetes NuGet.
Consola del Administrador de paquetes NuGet de Visual Studio
En el menú de Visual Studio, seleccione Herramientas > Administrador de paquetes NuGet >
Consola del Administrador de paquetes .
Para instalar el proveedor de SQL Server, ejecute el comando siguiente en la consola del Administrador de
paquetes:

Install-Package [Link]

Para actualizar el proveedor, use el comando Update-Package .


Para especificar una versión, use el modificador -Version . Por ejemplo, para instalar paquetes de EF Core
2.2.0, anexe -Version 2.2.0 a los comandos.
Para obtener más información, vea Consola del Administrador de paquetes.

Obtención de las herramientas de Entity Framework Core


Puede instalar herramientas para llevar a cabo tareas relacionadas con EF Core en el proyecto, como crear y aplicar
las migraciones de bases de datos o crear un modelo de EF Core basado en una base de datos existente.
Existen dos conjuntos de herramientas:
Las herramientas de la interfaz de la línea de comandos (CLI) de .NET Core pueden usarse en Windows,
Linux y macOS. Estos comandos comienzan por dotnet ef .
Las herramientas de la consola del Administrador de paquetes (PMC) se ejecutan en Visual Studio
(Windows). Estos comandos empiezan por un verbo. Por ejemplo: Add-Migration , Update-Database .

Aunque puede usar los comandos de dotnet ef desde la consola del Administrador de paquetes, le
recomendamos que use las herramientas de la consola del Administrador de paquetes en Visual Studio:
Trabajan automáticamente con el proyecto actual seleccionado en la PMC de Visual Studio sin necesidad de
cambiar manualmente entre directorios.
Abren automáticamente los archivos generados por los comandos de Visual Studio una vez completado el
comando.
Obtención de las herramientas de la CLI de .NET Core
Las herramientas de la CLI de .NET Core requieren el SDK de .NET Core, tal como se indica en Requisitos previos.
Los comandos de dotnet ef están incluidos en las versiones actuales del SDK de .NET Core, pero es necesario
instalar el paquete [Link] para habilitarlos en un proyecto específico:

dotnet add package [Link]


IMPORTANT
Use siempre la versión del paquete de herramientas que coincida con la versión principal de los paquetes en tiempo de
ejecución.

Obtención de las herramientas de la consola del Administrador de paquetes


Para obtener las herramientas de la consola del Administrador de paquetes para EF Core, instale el paquete
[Link] . Por ejemplo, en Visual Studio:

Install-Package [Link]

Para las aplicaciones de [Link] Core, este paquete se incluye automáticamente.

Actualización a la versión más reciente de EF Core


Cuando publicamos una nueva versión de EF Core, también publicamos una nueva versión de los
proveedores que forman parte del proyecto de EF Core, como, por ejemplo:
[Link], [Link] y
[Link]. Para obtener todas las mejoras, solo tiene que actualizar a la
nueva versión del proveedor.
EF Core y los proveedores de SQL Server y en memoria están incluidos en las versiones actuales de [Link]
Core. Para actualizar una aplicación de [Link] Core existente a una versión más reciente de EF Core,
actualice siempre la versión de [Link] Core.
Si necesita actualizar una aplicación que usa un proveedor de base de datos de terceros, busque siempre
una actualización del proveedor que sea compatible con la versión de EF Core que quiere usar. Por ejemplo,
los proveedores de bases de datos de las versiones anteriores no son compatibles con la versión 2.0 del
runtime de EF Core.
Los proveedores de terceros de EF Core no suelen publicar versiones de revisión junto al runtime de EF
Core. Para actualizar una aplicación que use un proveedor de terceros a una versión de revisión de EF Core,
puede que deba agregar una referencia directa a determinados componentes de runtime de EF Core, como
[Link] o [Link].
Si va a actualizar una aplicación existente a la última versión de EF Core, es posible que algunas referencias
a los paquetes más antiguos de EF Core tengan que quitarse manualmente:
Los paquetes en tiempo de diseño del proveedor de base de datos, como
[Link] , ya no son necesarios ni se admiten en EF Core 2.0 y
versiones posteriores, pero no se quitan automáticamente al actualizar los demás paquetes.
Las herramientas de la CLI de .NET están incluidas en el SDK de .NET desde la versión 2.1, por lo que
se puede quitar la referencia a ese paquete desde el archivo del proyecto:

<DotNetCliToolReference Include="[Link]" Version="2.0.0" />


Cadenas de conexión
11/03/2020 • 4 minutes to read

La mayoría de los proveedores de bases de datos requieren algún tipo de cadena de conexión para conectarse a la
base de datos. A veces, esta cadena de conexión contiene información confidencial que debe protegerse. También
es posible que necesite cambiar la cadena de conexión a medida que mueva la aplicación entre entornos, como
desarrollo, pruebas y producción.

Aplicaciones de WinForms & WPF


Las aplicaciones WinForms, WPF y [Link] 4 tienen un patrón de cadena de conexión probado y probado. La
cadena de conexión debe agregarse al archivo app. config de la aplicación (Web. config si usa [Link]). Si la
cadena de conexión contiene información confidencial, como el nombre de usuario y la contraseña, puede
proteger el contenido del archivo de configuración mediante la herramienta Administrador de secretos.

<?xml version="1.0" encoding="utf-8"?>


<configuration>

<connectionStrings>
<add name="BloggingDatabase"
connectionString="Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" />
</connectionStrings>
</configuration>

TIP
El valor providerName no es necesario EF Core en las cadenas de conexión almacenadas en el archivo app. config porque el
proveedor de base de datos se configura mediante código.

Después, puede leer la cadena de conexión mediante el ConfigurationManager API en el método de OnConfiguring
del contexto. Es posible que tenga que agregar una referencia al ensamblado de [Link] Framework
para poder usar esta API.

public class BloggingContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{

[Link]([Link]["BloggingDatabase"].ConnectionString);
}
}

Plataforma universal de Windows (UWP)


Las cadenas de conexión en una aplicación de UWP suelen ser una conexión de SQLite que solo especifica un
nombre de archivo local. Normalmente no contienen información confidencial y no es necesario cambiarla a
medida que se implementa una aplicación. Por lo tanto, estas cadenas de conexión suelen funcionar correctamente
al dejarse en el código, como se muestra a continuación. Si quiere moverlos fuera del código, UWP admite el
concepto de configuración, consulte la sección configuración de la aplicación de la documentación de UWP para
obtener más información.

public class BloggingContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
[Link]("Data Source=[Link]");
}
}

[Link] Core
En [Link] Core el sistema de configuración es muy flexible y la cadena de conexión puede almacenarse en
[Link] , una variable de entorno, el almacén de secretos de usuario u otro origen de configuración.
Consulte la sección configuración de la documentación de [Link] Core para obtener más detalles. En el ejemplo
siguiente se muestra la cadena de conexión almacenada en [Link] .

{
"ConnectionStrings": {
"BloggingDatabase": "Server=
(localdb)\\mssqllocaldb;Database=[Link];Trusted_Connection=True;"
},
}

Normalmente, el contexto se configura en [Link] con la cadena de conexión que se lee de la configuración.
Tenga en cuenta que el método GetConnectionString() busca un valor de configuración cuya clave sea
ConnectionStrings:<connection string name> . Debe importar el espacio de nombres Microsoft. Extensions.
Configuration para usar este método de extensión.

public void ConfigureServices(IServiceCollection services)


{
[Link]<BloggingContext>(options =>
[Link]([Link]("BloggingDatabase")));
}
Registro
11/03/2020 • 4 minutes to read

TIP
Puede ver un ejemplo de este artículo en GitHub.

Aplicaciones de [Link] Core


EF Core se integra automáticamente con los mecanismos de registro de [Link] Core cada vez que se utiliza
AddDbContext o AddDbContextPool . Por lo tanto, al usar [Link] Core, el registro debe configurarse tal y como se
describe en la documentación de [Link] Core.

Otras aplicaciones
El registro de EF Core requiere un ILoggerFactory que se configura a su vez con uno o más proveedores de
registro. Los proveedores comunes se incluyen en los siguientes paquetes:
Microsoft. Extensions. Logging. Console: un registrador de consola simple.
Microsoft. Extensions. Logging. AzureAppServices: admite las características ' registros de diagnóstico ' y ' flujo
de registro ' de los servicios de App de Azure.
Microsoft. Extensions. Logging. Debug: inicia sesión en un monitor de depurador con System. Diagnostics.
Debug. WriteLine ().
Microsoft. Extensions. Logging. EventLog: registra en el registro de eventos de Windows.
Microsoft. Extensions. Logging. EventSource: admite EventSource/EventListener.
Microsoft. Extensions. Logging. TraceSource: registra en un agente de escucha de seguimiento mediante
[Link]() .

Después de instalar los paquetes adecuados, la aplicación debe crear una instancia singleton/global de un
LoggerFactory. Por ejemplo, mediante el registrador de consola:
Versión 3.x
Versión 2.x

public static readonly ILoggerFactory MyLoggerFactory


= [Link](builder => { [Link](); });

Esta instancia singleton/global se debe registrar con EF Core en el DbContextOptionsBuilder . Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.UseLoggerFactory(MyLoggerFactory) // Warning: Do not create a new ILoggerFactory instance each time
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFLogging;Trusted_Connection=True;ConnectRetryCount=0");
WARNING
Es muy importante que las aplicaciones no creen una nueva instancia de ILoggerFactory para cada instancia de contexto. Si lo
hace, se producirá una fuga de memoria y un rendimiento deficiente.

Filtrar lo que se registra


La aplicación puede controlar lo que se registra mediante la configuración de un filtro en el ILoggerProvider. Por
ejemplo:
Versión 3.x
Versión 2.x

public static readonly ILoggerFactory MyLoggerFactory


= [Link](builder =>
{
builder
.AddFilter((category, level) =>
category == [Link]
&& level == [Link])
.AddConsole();
});

En este ejemplo, el registro se filtra para que solo se devuelvan mensajes:


en la categoría "Microsoft. EntityFrameworkCore. Database. Command"
en el nivel de "información"
Por EF Core, las categorías del registrador se definen en la clase DbLoggerCategory para que sea más fácil encontrar
categorías, pero estas se resuelven en cadenas simples.
Puede encontrar más detalles sobre la infraestructura de registro subyacente en la documentación de registro de
[Link] Core.
Resistencia de conexión
11/03/2020 • 9 minutes to read

La resistencia de conexión reintenta automáticamente los comandos de base de datos con errores. La característica
se puede usar con cualquier base de datos proporcionando una "estrategia de ejecución", que encapsula la lógica
necesaria para detectar errores y volver a ejecutar los comandos. Los proveedores de EF Core pueden proporcionar
estrategias de ejecución adaptadas a sus condiciones de error de base de datos específica y las directivas de
reintento óptima.
Como ejemplo, el proveedor de SQL Server incluye una estrategia de ejecución que se adapta específicamente a
SQL Server (incluido SQL Azure). Es consciente de los tipos de excepción que se pueden reintentar y tienen valores
predeterminados razonables para el número máximo de reintentos, el retraso entre reintentos, etc.
Cuando se configuran las opciones para el contexto, se especifica una estrategia de ejecución. Normalmente, se
encuentra en el método OnConfiguring del contexto derivado:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder
.UseSqlServer(
@"Server=
(localdb)\mssqllocaldb;Database=[Link];Trusted_Connection=True;ConnectRetryCount=0
",
options => [Link]());
}

o en [Link] para una aplicación [Link] Core:

public void ConfigureServices(IServiceCollection services)


{
[Link]<PicnicContext>(
options => [Link](
"<connection string>",
providerOptions => [Link]()));
}

Estrategia de ejecución personalizada


Existe un mecanismo para registrar una estrategia de ejecución personalizada propia si desea cambiar cualquiera
de los valores predeterminados.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder
.UseMyProvider(
"<connection string>",
options => [Link](...));
}

Estrategias de ejecución y transacciones


Una estrategia de ejecución que reintenta automáticamente los errores debe ser capaz de reproducir cada
operación en un bloque de reintentos en el que se produce un error. Cuando se habilitan los reintentos, cada
operación que se realiza a través de EF Core se convierte en su propia operación admite reintentos. Es decir, cada
consulta y cada llamada a SaveChanges() se reintentará como una unidad si se produce un error transitorio.
Sin embargo, si el código inicia una transacción mediante BeginTransaction() está definiendo su propio grupo de
operaciones que deben tratarse como una unidad, y todo lo que haya dentro de la transacción tendría que volver a
reproducirse en caso de que se produzca un error. Recibirá una excepción similar a la siguiente si intenta hacerlo al
usar una estrategia de ejecución:

InvalidOperationException: la estrategia de ejecución configurada ' SqlServerRetryingExecutionStrategy ' no


admite transacciones iniciadas por el usuario. Use la estrategia de ejecución que devuelve
"[Link]()" para ejecutar todas las operaciones en la transacción como
una unidad que se puede reintentar.

La solución consiste en invocar manualmente la estrategia de ejecución con un delegado que representa todo lo
que se debe ejecutar. Si se produce un error transitorio, la estrategia de ejecución invoca al delegado de nuevo.

using (var db = new BloggingContext())


{
var strategy = [Link]();

[Link](() =>
{
using (var context = new BloggingContext())
{
using (var transaction = [Link]())
{
[Link](new Blog {Url = "[Link]
[Link]();

[Link](new Blog {Url = "[Link]


[Link]();

[Link]();
}
}
});
}

Este enfoque también se puede usar con transacciones de ambiente.


using (var context1 = new BloggingContext())
{
[Link](new Blog { Url = "[Link] });

var strategy = [Link]();

[Link](() =>
{
using (var context2 = new BloggingContext())
{
using (var transaction = new TransactionScope())
{
[Link](new Blog { Url = "[Link] });
[Link]();

[Link]();

[Link]();
}
}
});
}

Error de confirmación de la transacción y el problema Idempotencia


En general, cuando se produce un error de conexión, se revierte la transacción actual. Sin embargo, si se quita la
conexión mientras se confirma la transacción, se desconoce el estado resultante de la transacción.
De forma predeterminada, la estrategia de ejecución reintentará la operación como si la transacción se revirtió,
pero si no es así, se producirá una excepción si el nuevo estado de la base de datos es incompatible o podría
provocar daños en los datos si la operación no se basa en un estado determinado, por ejemplo, al insertar una
nueva fila con valores de clave generados automáticamente.
Hay varias maneras de solucionar este error.
Opción 1: hacer (casi) nada
La probabilidad de que se produzca un error de conexión durante la confirmación de la transacción es baja, por lo
que puede ser aceptable que la aplicación no funcione correctamente si esta condición se produce realmente.
Sin embargo, debe evitar el uso de claves generadas por el almacén para asegurarse de que se produce una
excepción en lugar de agregar una fila duplicada. Considere la posibilidad de usar un valor GUID generado por el
cliente o un generador de valores del lado cliente.
Opción 2: recompilar el estado de la aplicación
1. Descartar el DbContext actual.
2. Cree una nueva DbContext y restaure el estado de la aplicación desde la base de datos.
3. Informe al usuario de que es posible que la última operación no se haya completado correctamente.
Opción 3: agregar comprobación de estado
Para la mayoría de las operaciones que cambian el estado de la base de datos es posible agregar código que
comprueba si se ha realizado correctamente. EF proporciona un método de extensión para facilitar esta
[Link] .

Este método inicia y confirma una transacción y también acepta una función en el parámetro verifySucceeded que
se invoca cuando se produce un error transitorio durante la confirmación de la transacción.
using (var db = new BloggingContext())
{
var strategy = [Link]();

var blogToAdd = new Blog {Url = "[Link]


[Link](blogToAdd);

[Link](db,
operation: context =>
{
[Link](acceptAllChangesOnSuccess: false);
},
verifySucceeded: context => [Link]().Any(b => [Link] == [Link]));

[Link]();
}

NOTE
Aquí SaveChanges se invoca con acceptAllChangesOnSuccess establecido en false para evitar cambiar el estado de la
entidad Blog a Unchanged si SaveChanges se realiza correctamente. Esto permite volver a intentar la misma operación si
se produce un error en la confirmación y se revierte la transacción.

Opción 4: realizar un seguimiento manual de la transacción


Si necesita usar claves generadas por el almacén o necesita una manera genérica de controlar los errores de
confirmación que no dependen de la operación realizada, se podría asignar a cada transacción un identificador que
se comprueba cuando se produce un error en la confirmación.
1. Agregue una tabla a la base de datos utilizada para realizar el seguimiento del estado de las transacciones.
2. Inserte una fila en la tabla al principio de cada transacción.
3. Si se produce un error en la conexión durante la confirmación, Compruebe la presencia de la fila
correspondiente en la base de datos.
4. Si la confirmación se realiza correctamente, elimine la fila correspondiente para evitar el crecimiento de la tabla.

using (var db = new BloggingContext())


{
var strategy = [Link]();

[Link](new Blog { Url = "[Link] });

var transaction = new TransactionRow {Id = [Link]()};


[Link](transaction);

[Link](db,
operation: context =>
{
[Link](acceptAllChangesOnSuccess: false);
},
verifySucceeded: context => [Link]().Any(t => [Link] == [Link]));

[Link]();
[Link](transaction);
[Link]();
}
NOTE
Asegúrese de que el contexto utilizado para la comprobación tenga definida una estrategia de ejecución, ya que es probable
que se produzca un error en la conexión durante la comprobación si se produjo un error durante la confirmación de la
transacción.
Pruebas de código que usa EF Core
09/04/2020 • 10 minutes to read

Para probar el código que accede a una base de datos, es necesario:


Ejecutar consultas y actualizaciones en el mismo sistema de base de datos que se usa en producción.
Ejecutar consultas y actualizaciones en algún otro sistema de base de datos más fácil de administrar.
Usar dobles de prueba o algún otro mecanismo para evitar por completo el uso de una base de datos.
En este documento se describen las ventajas e inconvenientes de cada una de estas opciones y se muestra cómo
usar EF Core con cada método.

Todos los proveedores de bases de datos no son iguales


Es muy importante entender que EF Core no está diseñado para extraer cada aspecto del sistema de base de datos
subyacente. EF Core es un conjunto común de patrones y conceptos que se pueden usar con cualquier sistema de
base de datos. Así, los proveedores de bases de datos de EF Core basan el comportamiento y la funcionalidad
específicos de base de datos en este marco común. Esto permite a cada sistema de base de datos hacer lo que
mejor se le da, a la vez que mantiene la homogeneidad, si fuera necesario, con otros sistemas de base de datos.
Básicamente, esto significa que al cambiar de proveedor de base de datos, cambia el comportamiento de EF Core y
no se puede esperar que la aplicación funcione correctamente a menos que tenga en cuenta de forma explícita
todas las diferencias de comportamiento. Dicho esto, en muchos casos esto funciona, ya que hay un alto grado de
homogeneidad entre bases de datos relacionales. Esto es bueno y malo. Es bueno porque el cambio entre bases de
datos puede ser relativamente fácil. Es malo porque puede dar una falsa sensación de seguridad si la aplicación no
se prueba por completo en el nuevo sistema de base de datos.

Enfoque 1: Sistema de base de datos de producción


Como se ha explicado en la sección anterior, la única manera de asegurarse de que se está probando lo que se
ejecuta en producción es usar el mismo sistema de base de datos. Por ejemplo, si la aplicación implementada usa
SQL Azure, las pruebas también deben realizarse en SQL Azure.
Pero que cada desarrollador ejecute pruebas en SQL Azure mientras trabaja activamente en el código es lento y
costoso. Esto muestra la principal contrapartida de estos métodos: ¿cuándo resulta adecuado desviarse del
sistema de base de datos de producción para mejorar la eficacia de las pruebas?
Afortunadamente, en este caso, la respuesta es bastante fácil: use la instancia local de SQL Server para las pruebas
de desarrollador. SQL Azure y SQL Server son muy similares, por lo que realizar las pruebas en SQL Server suele
ser una contrapartida razonable. Dicho esto, sigue siendo aconsejable ejecutar las pruebas en SQL Azure antes de
pasar a producción.
LocalDb
Todos los principales sistemas de base de datos tienen alguna forma de "edición para desarrolladores" para las
pruebas locales. SQL Server también tiene una característica denominada LocalDb. La principal ventaja de LocalDb
es que inicia la instancia de base de datos a petición. Esto evita que haya un servicio de base de datos
ejecutándose en el equipo aunque no se estén ejecutando pruebas.
Pero LocalDb también plantea problemas:
No admite todo lo que SQL Server Developer Edition.
No está disponible en Linux.
Puede producir un retraso en la primera serie de pruebas cuando se inicia el servicio.
Personalmente, nunca me ha parecido un problema que haya un servicio de base de datos ejecutándose en el
equipo de desarrollo y, en general, recomendaría usar Developer Edition. Pero puede ser adecuado para algunas
personas, especialmente en equipos de desarrollo menos potentes.

Enfoque 2: SQLite
EF Core prueba el proveedor de SQL Server principalmente mediante su ejecución en una instancia local de SQL
Server. Estas pruebas ejecutan decenas de miles de consultas en un par de minutos en un equipo rápido. Esto
muestra que el uso del sistema de base de datos real puede ser una solución eficiente. Es un mito que el uso de
una base de datos más ligera sea la única manera de ejecutar pruebas rápidamente.
Dicho esto, ¿qué ocurre si, por cualquier motivo, no se pueden ejecutar pruebas en algo cercano al sistema de base
de datos de producción? La siguiente mejor opción es usar algo con funcionalidad similar. Esto suele significar otra
base de datos relacional, para lo que SQLite es la opción obvia.
SQLite es una buena opción porque:
Se ejecuta en proceso con la aplicación y, por tanto, tiene poca sobrecarga.
Usa archivos simples creados automáticamente para bases de datos, por lo que no requiere administración de
bases de datos.
Tiene un modo en memoria que evita incluso la creación de archivos.
Pero recuerde que:
SQLite inevitablemente no admite todo lo que el sistema de base de datos de producción.
SQLite se comporta de forma diferente al sistema de base de datos de producción para algunas consultas.
Por lo tanto, si usa SQLite para algunas pruebas, asegúrese de probar también en el sistema de base de datos real.
Vea Pruebas con SQLite para obtener instrucciones específicas de EF Core.

Método 3: Base de datos en memoria de EF Core


EF Core incluye una base de datos en memoria que se usa para las pruebas internas del propio EF Core. Esta base
de datos en general no es adecuada como sustituto para probar las aplicaciones que usan EF Core . De
manera específica:
No es una base de datos relacional
No admite transacciones
No está optimizada para el rendimiento
Nada de esto es muy importante a la hora de probar elementos internos de EF Core, ya que se usa
específicamente donde la base de datos es irrelevante para la prueba. Por otro lado, estos aspectos tienden a ser
muy importantes al probar una aplicación que usa EF Core.

Pruebas unitarias
Imagine que va a probar una parte de la lógica de negocio que pueda necesitar usar algunos datos de una base de
datos, pero que no supone probar propiamente las interacciones de la base de datos. Una opción es usar un doble
de prueba como simulacro o imitación.
Los dobles de prueba se usan para las pruebas internas de EF Core. Pero nunca se intentan simular DbContext o
IQueryable. Hacerlo es difícil, engorroso y delicado. No lo haga.
En su lugar, se usa la base de datos en memoria siempre que se realizan pruebas unitarias de algo que usa
DbContext. En este caso, el uso de la base de datos en memoria es adecuado porque la prueba no depende del
comportamiento de la base de datos. Pero no lo haga para probar consultas o actualizaciones reales de la base de
datos.
Vea Pruebas con InMemory para obtener instrucciones específicas de EF Core sobre el uso de la base de datos en
memoria para las pruebas unitarias.
Pruebas con SQLite
11/03/2020 • 5 minutes to read

SQLite tiene un modo en memoria que le permite usar SQLite para escribir pruebas en una base de datos
relacional, sin la sobrecarga de las operaciones de base de datos reales.

TIP
Puede ver el ejemplo de este artículo en github

Escenario de prueba de ejemplo


Considere el siguiente servicio que permite al código de la aplicación realizar algunas operaciones relacionadas
con blogs. Utiliza internamente un DbContext que se conecta a una base de datos SQL Server. Sería útil
intercambiar este contexto para conectarse a una base de datos de SQLite en memoria, de modo que podamos
escribir pruebas eficaces para este servicio sin tener que modificar el código o hacer mucho trabajo para crear un
doble de prueba del contexto.

using [Link];
using [Link];

namespace BusinessLogic
{
public class BlogService
{
private BloggingContext _context;

public BlogService(BloggingContext context)


{
_context = context;
}

public void Add(string url)


{
var blog = new Blog { Url = url };
_context.[Link](blog);
_context.SaveChanges();
}

public IEnumerable<Blog> Find(string term)


{
return _context.Blogs
.Where(b => [Link](term))
.OrderBy(b => [Link])
.ToList();
}
}
}

Preparar el contexto
Evite configurar dos proveedores de bases de datos
En las pruebas, va a configurar externamente el contexto para usar el proveedor de inmemory. Si está
configurando un proveedor de base de datos invalidando OnConfiguring en el contexto, deberá agregar código
condicional para asegurarse de que solo se configura el proveedor de base de datos si aún no se ha configurado
ninguno.

TIP
Si usa [Link] Core, no necesitará este código, ya que el proveedor de la base de datos se configura fuera del contexto (en
[Link]).

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
if (![Link])
{
[Link](@"Server=
(localdb)\mssqllocaldb;Database=[Link];Trusted_Connection=True;ConnectRetryCount=0");
}
}

Agregar un constructor para las pruebas


La manera más sencilla de habilitar las pruebas en una base de datos diferente es modificar el contexto para
exponer un constructor que acepta un DbContextOptions<TContext> .

public class BloggingContext : DbContext


{
public BloggingContext()
{ }

public BloggingContext(DbContextOptions<BloggingContext> options)


: base(options)
{ }

TIP
DbContextOptions<TContext> indica al contexto toda su configuración, como la base de datos a la que se va a conectar.
Este es el mismo objeto que se genera al ejecutar el método de configuración en el contexto.

Escribir pruebas
La clave para realizar pruebas con este proveedor es la posibilidad de indicar al contexto que use SQLite y
controlar el ámbito de la base de datos en memoria. El ámbito de la base de datos se controla mediante la
apertura y el cierre de la conexión. La base de datos está en el ámbito de la duración de la conexión abierta.
Normalmente, desea una base de datos limpia para cada método de prueba.

TIP
Para usar SqliteConnection() y el método de extensión .UseSqlite() , haga referencia al paquete NuGet Microsoft.
EntityFrameworkCore. SQLite.

using BusinessLogic;
using [Link];
using [Link];
using [Link];
using Xunit;

namespace [Link]
{
{
public class BlogServiceTests
{
[Fact]
public void Add_writes_to_database()
{
// In-memory database only exists while the connection is open
var connection = new SqliteConnection("DataSource=:memory:");
[Link]();

try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlite(connection)
.Options;

// Create the schema in the database


using (var context = new BloggingContext(options))
{
[Link]();
}

// Run the test against one instance of the context


using (var context = new BloggingContext(options))
{
var service = new BlogService(context);
[Link]("[Link]
[Link]();
}

// Use a separate instance of the context to verify correct data was saved to database
using (var context = new BloggingContext(options))
{
[Link](1, [Link]());
[Link]("[Link] [Link]().Url);
}
}
finally
{
[Link]();
}
}

[Fact]
public void Find_searches_url()
{
// In-memory database only exists while the connection is open
var connection = new SqliteConnection("DataSource=:memory:");
[Link]();

try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlite(connection)
.Options;

// Create the schema in the database


using (var context = new BloggingContext(options))
{
[Link]();
}

// Insert seed data into the database using one instance of the context
using (var context = new BloggingContext(options))
{
[Link](new Blog { Url = "[Link] });
[Link](new Blog { Url = "[Link] });
[Link](new Blog { Url = "[Link] });
[Link]();
}

// Use a clean instance of the context to run the test


using (var context = new BloggingContext(options))
{
var service = new BlogService(context);
var result = [Link]("cat");
[Link](2, [Link]());
}
}
finally
{
[Link]();
}
}
}
}
Pruebas con InMemory
11/03/2020 • 6 minutes to read

El proveedor de inmemory es útil cuando se desea probar los componentes mediante algo que se aproxima a la
conexión a la base de datos real, sin la sobrecarga de las operaciones de base de datos reales.

TIP
Puede ver un ejemplo de este artículo en GitHub.

La inmemory no es una base de datos relacional


EF Core proveedores de bases de datos no tienen que ser bases de datos relacionales. La inmemory está diseñada
para ser una base de datos de uso general para las pruebas y no está diseñada para imitar una base de datos
relacional.
Algunos ejemplos de esto son:
La inmemory le permitirá guardar datos que infrinjan las restricciones de integridad referencial en una base de
datos relacional.
Si usa DefaultValueSql (String) para una propiedad en el modelo, se trata de una API de base de datos
relacional y no tendrá ningún efecto cuando se ejecute en inmemory.
No se admite la simultaneidad mediante la versión de marca de tiempo o fila ( [Timestamp] o IsRowVersion ).
No se producirá DbUpdateConcurrencyException si se realiza una actualización mediante un token de
simultaneidad antiguo.

TIP
Para muchos propósitos de prueba, estas diferencias no serán importantes. Sin embargo, si desea probar algo que se
comporta más como una verdadera base de datos relacional, considere el uso del modo en memoria de SQLite.

Escenario de prueba de ejemplo


Considere el siguiente servicio que permite al código de la aplicación realizar algunas operaciones relacionadas
con blogs. Utiliza internamente un DbContext que se conecta a una base de datos SQL Server. Sería útil
intercambiar este contexto para conectarse a una base de datos inmemory de modo que podamos escribir
pruebas eficaces para este servicio sin tener que modificar el código, o hacer mucho trabajo para crear un doble
de prueba del contexto.
using [Link];
using [Link];

namespace BusinessLogic
{
public class BlogService
{
private BloggingContext _context;

public BlogService(BloggingContext context)


{
_context = context;
}

public void Add(string url)


{
var blog = new Blog { Url = url };
_context.[Link](blog);
_context.SaveChanges();
}

public IEnumerable<Blog> Find(string term)


{
return _context.Blogs
.Where(b => [Link](term))
.OrderBy(b => [Link])
.ToList();
}
}
}

Preparar el contexto
Evite configurar dos proveedores de bases de datos
En las pruebas, va a configurar externamente el contexto para usar el proveedor de inmemory. Si está
configurando un proveedor de base de datos invalidando OnConfiguring en el contexto, deberá agregar código
condicional para asegurarse de que solo se configura el proveedor de base de datos si aún no se ha configurado
ninguno.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
if (![Link])
{
[Link](@"Server=
(localdb)\mssqllocaldb;Database=[Link];Trusted_Connection=True;ConnectRetryCount=0");
}
}

TIP
Si usa [Link] Core, no necesitará este código ya que el proveedor de base de datos ya está configurado fuera del contexto
(en [Link]).

Agregar un constructor para las pruebas


La manera más sencilla de habilitar las pruebas en una base de datos diferente es modificar el contexto para
exponer un constructor que acepta un DbContextOptions<TContext> .
public class BloggingContext : DbContext
{
public BloggingContext()
{ }

public BloggingContext(DbContextOptions<BloggingContext> options)


: base(options)
{ }

TIP
DbContextOptions<TContext> indica al contexto toda su configuración, como la base de datos a la que se va a conectar.
Este es el mismo objeto que se genera al ejecutar el método de configuración en el contexto.

Escribir pruebas
La clave para probar con este proveedor es la capacidad de indicar al contexto que use el proveedor de inmemory
y controlar el ámbito de la base de datos en memoria. Normalmente, desea una base de datos limpia para cada
método de prueba.
Este es un ejemplo de una clase de prueba que usa la base de datos inmemory. Cada método de prueba especifica
un nombre de base de datos único, lo que significa que cada método tiene su propia base de datos inmemory.

TIP
Para usar el método de extensión .UseInMemoryDatabase() , haga referencia al paquete NuGet Microsoft.
EntityFrameworkCore. inmemory.
using BusinessLogic;
using [Link];
using [Link];
using Xunit;

namespace [Link]
{
public class BlogServiceTests
{
[Fact]
public void Add_writes_to_database()
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "Add_writes_to_database")
.Options;

// Run the test against one instance of the context


using (var context = new BloggingContext(options))
{
var service = new BlogService(context);
[Link]("[Link]
[Link]();
}

// Use a separate instance of the context to verify correct data was saved to database
using (var context = new BloggingContext(options))
{
[Link](1, [Link]());
[Link]("[Link] [Link]().Url);
}
}

[Fact]
public void Find_searches_url()
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "Find_searches_url")
.Options;

// Insert seed data into the database using one instance of the context
using (var context = new BloggingContext(options))
{
[Link](new Blog { Url = "[Link] });
[Link](new Blog { Url = "[Link] });
[Link](new Blog { Url = "[Link] });
[Link]();
}

// Use a clean instance of the context to run the test


using (var context = new BloggingContext(options))
{
var service = new BlogService(context);
var result = [Link]("cat");
[Link](2, [Link]());
}
}
}
}
Configuración de un DbContext
25/03/2020 • 11 minutes to read

En este artículo se muestran los patrones básicos para configurar un DbContext a través de un DbContextOptions
para conectarse a una base de datos mediante un proveedor de EF Core específico y comportamientos opcionales.

Configuración de DbContext en tiempo de diseño


EF Core herramientas en tiempo de diseño como las migraciones deben ser capaces de detectar y crear una
instancia de trabajo de un tipo de DbContext para recopilar detalles sobre los tipos de entidad de la aplicación y
cómo se asignan a un esquema de base de datos. Este proceso puede ser automático siempre y cuando la
herramienta pueda crear fácilmente el DbContext de manera que se configurará de forma similar a como se
configuraría en tiempo de ejecución.
Aunque cualquier patrón que proporcione la información de configuración necesaria para el DbContext puede
funcionar en tiempo de ejecución, las herramientas que requieren el uso de un DbContext en tiempo de diseño
solo pueden funcionar con un número limitado de patrones. Estos se describen con más detalle en la sección
creación de contexto en tiempo de diseño .

Configuración de DbContextOptions
DbContext debe tener una instancia de DbContextOptions para poder realizar cualquier trabajo. La instancia de
DbContextOptions contiene información de configuración como:

Proveedor de base de datos que se va a usar, normalmente seleccionado mediante la invocación de un método
como UseSqlServer o UseSqlite . Estos métodos de extensión requieren el paquete de proveedor
correspondiente, como [Link] o [Link] . Los
métodos se definen en el espacio de nombres [Link] .
Cualquier cadena de conexión o identificador de la instancia de base de datos que sea necesario, normalmente
se pasa como argumento al método de selección de proveedor mencionado anteriormente
Cualquier selector de comportamiento opcional de nivel de proveedor, normalmente también encadenado
dentro de la llamada al método de selección de proveedor.
Los selectores de comportamiento de EF Core general, normalmente encadenados después o antes del método
de selector de proveedor
En el ejemplo siguiente se configura el DbContextOptions para utilizar el proveedor de SQL Server, una conexión
contenida en la variable connectionString , un tiempo de espera de comando de nivel de proveedor y un selector
de comportamiento de EF Core que hace que todas las consultas ejecutadas en el DbContext sin seguimiento de
forma predeterminada:

optionsBuilder
.UseSqlServer(connectionString, providerOptions=>[Link](60))
.UseQueryTrackingBehavior([Link]);
NOTE
Los métodos de selector de proveedor y otros métodos de selector de comportamiento mencionados anteriormente son
métodos de extensión en DbContextOptions o clases de opciones específicas del proveedor. Para tener acceso a estos
métodos de extensión, puede que necesite tener un espacio de nombres (normalmente [Link] )
en el ámbito e incluir dependencias de paquetes adicionales en el proyecto.

El DbContextOptions se puede proporcionar al DbContext invalidando el método de OnConfiguring o externamente


a través de un argumento de constructor.
Si se usan ambos, OnConfiguring se aplica en último lugar y puede sobrescribir las opciones proporcionadas al
argumento del constructor.
Argumento de constructor
El constructor simplemente puede aceptar un DbContextOptions como se indica a continuación:

public class BloggingContext : DbContext


{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }

public DbSet<Blog> Blogs { get; set; }


}

TIP
El constructor base de DbContext también acepta la versión no genérica de DbContextOptions , pero no se recomienda usar
la versión no genérica para las aplicaciones con varios tipos de contexto.

Ahora la aplicación puede pasar el DbContextOptions al crear una instancia de un contexto, como se indica a
continuación:

var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();


[Link]("Data Source=[Link]");

using (var context = new BloggingContext([Link]))


{
// do stuff
}

Configuración
También puede inicializar la DbContextOptions dentro del propio contexto. Aunque puede usar esta técnica para la
configuración básica, normalmente tendrá que obtener determinados detalles de configuración del exterior, por
ejemplo, una cadena de conexión de base de datos. Esto puede hacerse con una API de configuración o con
cualquier otro medio.
Para inicializar DbContextOptions dentro del contexto, invalide el método OnConfiguring y llame a los métodos en
el DbContextOptionsBuilder proporcionado:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
[Link]("Data Source=[Link]");
}
}

Una aplicación simplemente puede crear una instancia de este tipo de contexto sin pasar nada a su constructor:

using (var context = new BloggingContext())


{
// do stuff
}

TIP
Este enfoque no se presta a las pruebas, a menos que las pruebas tengan como destino la base de datos completa.

Usar DbContext con la inserción de dependencias


EF Core admite el uso de DbContext con un contenedor de inserción de dependencias. El tipo DbContext se puede
Agregar al contenedor de servicio mediante el método AddDbContext<TContext> .
AddDbContext<TContext> hará que el tipo DbContext, el TContext y el DbContextOptions<TContext> correspondiente
estén disponibles para la inserción desde el contenedor de servicios.
Vea más información a continuación para obtener más información sobre la inserción de dependencias.
Agregar el DbContext a la inserción de dependencias:

public void ConfigureServices(IServiceCollection services)


{
[Link]<BloggingContext>(options => [Link]("Data Source=[Link]"));
}

Esto requiere agregar un argumento de constructor al tipo DbContext que acepte DbContextOptions<TContext> .
Código de contexto:

public class BloggingContext : DbContext


{
public BloggingContext(DbContextOptions<BloggingContext> options)
:base(options)
{ }

public DbSet<Blog> Blogs { get; set; }


}

Código de la aplicación (en [Link] Core):


public class MyController
{
private readonly BloggingContext _context;

public MyController(BloggingContext context)


{
_context = context;
}

...
}

Código de aplicación (mediante ServiceProvider directamente, menos común):

using (var context = [Link]<BloggingContext>())


{
// do stuff
}

var options = [Link]<DbContextOptions<BloggingContext>>();

Evitar problemas de subprocesos DbContext


Entity Framework Core no admite varias operaciones paralelas que se ejecutan en la misma instancia de
DbContext . Esto incluye la ejecución paralela de consultas asincrónicas y cualquier uso simultáneo explícito de
varios subprocesos. Por lo tanto, siempre await llamadas asincrónicas inmediatamente, o bien usar instancias de
DbContext independientes para las operaciones que se ejecutan en paralelo.

Cuando EF Core detecta un intento de usar una instancia de DbContext simultáneamente, verá un
InvalidOperationException con un mensaje similar al siguiente:

Se inició una segunda operación en este contexto antes de que se completara una operación anterior. Esto se
debe normalmente a distintos subprocesos que usan la misma instancia de DbContext; sin embargo, no se
garantiza que los miembros de instancia sean seguros para subprocesos.

Cuando el acceso simultáneo no se detecta, puede dar lugar a un comportamiento indefinido, a bloqueos de la
aplicación y a daños en los datos.
Hay errores comunes que pueden provocar involuntariamente el acceso simultáneo en la misma instancia de
DbContext :

Se olvida esperar la finalización de una operación asincrónica antes de iniciar cualquier otra operación en el
mismo DbContext
Los métodos asincrónicos permiten a EF Core iniciar las operaciones que tienen acceso a la base de datos sin
bloqueos. Pero si el autor de la llamada no espera la finalización de uno de estos métodos y continúa realizando
otras operaciones en el DbContext , el estado de la DbContext puede ser (y probablemente estará) dañado.
Esperar siempre EF Core métodos asincrónicos inmediatamente.
Compartir instancias de DbContext implícitamente entre varios subprocesos mediante la inserción de
dependencias
El método de extensión AddDbContext registra los tipos de DbContext con una duración de ámbito de forma
predeterminada.
Esto es seguro frente a problemas de acceso simultáneos en la mayoría de las aplicaciones [Link] Core porque
solo hay un subproceso que ejecuta cada solicitud de cliente en un momento dado, y dado que cada solicitud
obtiene un ámbito de inyección de dependencia independiente (y, por tanto, una instancia de DbContext
independiente). Para el modelo de hospedaje de servidor increíble, se usa una solicitud lógica para mantener el
circuito de usuario increíble y, por lo tanto, solo hay una instancia de DbContext con ámbito disponible por circuito
de usuario si se usa el ámbito de inyección predeterminado.
Cualquier código que ejecute explícitamente varios subprocesos en paralelo debe asegurarse de que no se tiene
acceso a las instancias de DbContext simultáneamente.
Mediante la inserción de dependencias, esto se puede lograr registrando el contexto como ámbito y creando
ámbitos (mediante IServiceScopeFactory ) para cada subproceso, o registrando el DbContext como transitorio
(mediante la sobrecarga de AddDbContext que toma un parámetro ServiceLifetime ).

Más lecturas
Lea inserción de dependencias para obtener más información sobre el uso de di.
Lea pruebas para obtener más información.
Trabajar con tipos de referencia que aceptan valores
NULL
11/03/2020 • 10 minutes to read

C#8 presentó una nueva característica denominada tipos de referencia que aceptan valores NULL, lo que permite
anotar tipos de referencia, lo que indica si es válido que contengan null o not. Si no está familiarizado con esta
característica, se recomienda que se familiarice con ella leyendo los C# documentos.
En esta página se presenta la compatibilidad de EF Core con tipos de referencia que aceptan valores NULL y se
describen los procedimientos recomendados para trabajar con ellos.

Propiedades obligatorias y opcionales


La documentación principal sobre las propiedades obligatorias y opcionales y su interacción con tipos de
referencia que aceptan valores NULL es la página de propiedades obligatoria y opcional . Se recomienda comenzar
leyendo primero esa página.

NOTE
Tenga cuidado al habilitar los tipos de referencia que aceptan valores NULL en un proyecto existente: las propiedades de tipo
de referencia que se configuraron anteriormente como opcional ahora se configurarán según sea necesario, a menos que se
anoten explícitamente para que acepten valores NULL. Al administrar un esquema de base de datos relacional, esto puede
provocar que se generen migraciones que modifiquen la nulabilidad de la columna de la base de datos.

DbContext y DbSet
Cuando se habilitan los tipos de referencia que C# aceptan valores NULL, el compilador emite advertencias para
cualquier propiedad no inicializada que no acepte valores NULL, ya que estos contendrían el valor null. Como
resultado, la práctica común de definir un DbSet que no acepta valores NULL en un contexto generará ahora una
advertencia. Sin embargo, EF Core siempre inicializa todas las propiedades DbSet en tipos derivados de
DbContext, por lo que se garantiza que nunca serán null, incluso si el compilador no es consciente de esto. Por lo
tanto, se recomienda mantener las propiedades de DbSet que no admitan valores NULL, lo que le permite tener
acceso a ellas sin comprobaciones nulas, y para silenciar las advertencias del compilador estableciéndolo
explícitamente en NULL con la ayuda del operador null-permisivo (!):

public class NullableReferenceTypesContext : DbContext


{
public DbSet<Customer> Customers { get; set; } = null!;
public DbSet<Order> Orders { get; set; } = null!;

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.UseSqlServer(@"Server=
(localdb)\mssqllocaldb;Database=EFNullableReferenceTypes;Trusted_Connection=True;ConnectRetryCount=0");
}

Propiedades y inicialización que no aceptan valores NULL


Las advertencias del compilador para los tipos de referencia no inicializados que no aceptan valores null también
son un problema para las propiedades normales en los tipos de entidad. En el ejemplo anterior, se han evitado
estas advertencias mediante el enlace de constructor, una característica que funciona perfectamente con
propiedades que no aceptan valores NULL, lo que garantiza que siempre se inicializan. Sin embargo, en algunos
escenarios, el enlace del constructor no es una opción: las propiedades de navegación, por ejemplo, no se pueden
inicializar de esta manera.
Las propiedades de navegación requeridas presentan una dificultad adicional: aunque un dependiente siempre
existirá para una entidad de seguridad determinada, se puede cargar o no mediante una consulta determinada,
dependiendo de las necesidades en ese punto del programa (vea los distintos patrones para cargar datos). Al
mismo tiempo, no es deseable que estas propiedades acepten valores NULL, ya que esto obligaría a tener acceso a
ellas para comprobar si son NULL, incluso aunque se requieran.
Una manera de tratar estos escenarios consiste en tener una propiedad que no acepte valores NULL con un campo
de respaldoque acepte valores NULL:

private Address? _shippingAddress;

public Address ShippingAddress


{
set => _shippingAddress = value;
get => _shippingAddress
?? throw new InvalidOperationException("Uninitialized property: " + nameof(ShippingAddress));
}

Dado que la propiedad de navegación no admite valores NULL, se configura una navegación necesaria. y, siempre
y cuando la navegación se haya cargado correctamente, se podrá acceder al dependiente a través de la propiedad.
Sin embargo, si se tiene acceso a la propiedad sin cargar primero la entidad relacionada correctamente, se inicia
una excepción InvalidOperationException, ya que el contrato de la API se ha utilizado incorrectamente. Tenga en
cuenta que EF debe configurarse para tener acceso siempre al campo de respaldo y no a la propiedad, ya que se
basa en poder leer el valor aunque no se haya establecido. Consulte la documentación sobre los campos de
respaldo para ver cómo hacerlo y considere la posibilidad de especificar [Link] para
asegurarse de que la configuración es correcta.
Como alternativa de terser, es posible simplemente inicializar la propiedad en NULL con la ayuda del operador
null-permisivo (!):

public Product Product { get; set; } = null!;

Nunca se observará un valor nulo real excepto como resultado de un error de programación, por ejemplo, el
acceso a la propiedad de navegación sin cargar correctamente la entidad relacionada.

NOTE
Las navegaciones de colección, que contienen referencias a varias entidades relacionadas, siempre deben ser no NULL. Una
colección vacía significa que no existe ninguna entidad relacionada, pero la propia lista nunca debe ser null.

Navegar y incluir relaciones que aceptan valores NULL


Cuando se trabaja con relaciones opcionales, es posible encontrar advertencias del compilador en las que una
excepción de referencia nula real sería imposible. Cuando se traducen y ejecutan las consultas LINQ, EF Core
garantiza que si no existe una entidad relacionada opcional, cualquier navegación a ella simplemente se omitirá,
en lugar de producirse. Sin embargo, el compilador no es consciente de esta garantía EF Core y genera
advertencias como si la consulta LINQ se ejecutara en memoria, con LINQ to Objects. Como resultado, es
necesario usar el operador null-permisivo (!) para notificar al compilador que no es posible un valor null real:
[Link]([Link]!.ExtraAdditionalInfo!.SomeExtraAdditionalInfo);

Se produce un problema similar al incluir varios niveles de relaciones entre las navegaciones opcionales:

var order = [Link]


.Include(o => [Link]!)
.ThenInclude(op => [Link])
.Single();

Si tiene que hacer esto mucho y los tipos de entidad en cuestión son principalmente (o exclusivamente) usados en
consultas de EF Core, considere la posibilidad de hacer que las propiedades de navegación no acepten valores
NULL y configurarlas como opcionales a través de la API fluida o las anotaciones de datos. Esto quitará todas las
advertencias del compilador manteniendo la relación opcional; sin embargo, si las entidades se recorren fuera de
EF Core, puede observar valores NULL, aunque las propiedades se anotan como que no aceptan valores NULL.

Limitaciones
La ingeniería inversa no admite C# C# actualmente 8 tipos de referencia que aceptan valores NULL (NRTs): EF
Core siempre genera código que supone que la característica está desactivada. Por ejemplo, las columnas de
texto que aceptan valores NULL se scaffolding como una propiedad con el tipo string , no string? , con la API
fluida o las anotaciones de datos que se usan para configurar si una propiedad es obligatoria o no. Puede editar
el código con scaffolding y reemplazarlo con anotaciones de C# nulabilidad. El seguimiento de la
compatibilidad con scaffolding para tipos de referencia que aceptan valores NULL se realiza mediante el
problema #15520.
La superficie de la API pública de EF Core todavía no se ha anotado para la nulabilidad (la API pública es "null-
desconocen"), lo que a veces es difícil de usar cuando la característica NRT está activada. Esto incluye
principalmente los operadores Async LINQ expuestos por EF Core, como FirstOrDefaultAsync. Tenemos
previsto abordar esto para la versión 5,0.
Creación y configuración de un modelo
08/04/2020 • 2 minutes to read

Entity Framework usa un conjunto de convenciones para compilar un modelo basado en la forma de las clases de
entidad. Puede especificar una configuración adicional para complementar o reemplazar lo que se ha detectado
por convención.
Este artículo trata de la configuración que se puede aplicar a un modelo para cualquier almacén de datos y que se
puede aplicar al elegir como destino cualquier base de datos relacional. Los proveedores también pueden habilitar
la configuración específica de un almacén de datos determinado. Para obtener documentación sobre la
configuración específica del proveedor, vea la sección Proveedores de bases de datos .

TIP
Puede ver un ejemplo 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 ModelBuilder API 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.

using [Link];

namespace [Link]
{
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }

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

public class Blog


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

Uso de anotaciones de datos para configurar un modelo


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

namespace [Link]
{
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}

#region Required
public class Blog
{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}
#endregion
}
Tipos de entidad
11/03/2020 • 4 minutes to read

La inclusión de un DbSet de un tipo en el contexto significa que se incluye en el modelo de EF Core. normalmente
hacemos referencia a este tipo como una entidad. EF Core puede leer y escribir instancias de entidad desde y hacia
la base de datos, y si está utilizando una base de datos relacional, EF Core puede crear tablas para las entidades a
través de migraciones.

Incluir tipos en el modelo


Por Convención, los tipos que se exponen en las propiedades de DbSet en el contexto se incluyen en el modelo
como entidades. También se incluyen los tipos de entidad que se especifican en el método OnModelCreating , al igual
que los tipos que se encuentran al explorar de forma recursiva las propiedades de navegación de otros tipos de
entidades detectadas.
En el ejemplo de código siguiente, se incluyen todos los tipos:
Blog se incluye porque se expone en una propiedad DbSet en el contexto.
Post se incluye porque se detecta a través de la propiedad de navegación [Link] .
AuditEntry porque se especifica en OnModelCreating .

class MyContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<AuditEntry>();
}
}

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public Blog Blog { get; set; }


}

public class AuditEntry


{
public int AuditEntryId { get; set; }
public string Username { get; set; }
public string Action { get; set; }
}
Excluir tipos del modelo
Si no desea incluir un tipo en el modelo, puede excluirlo:
Anotaciones de datos
API fluida

[NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}

Nombre de la tabla
Por Convención, cada tipo de entidad se configurará para asignarse a una tabla de base de datos con el mismo
nombre que la propiedad DbSet que expone la entidad. Si no existe ningún DbSet para la entidad especificada, se
utiliza el nombre de clase.
Puede configurar manualmente el nombre de la tabla:
Anotaciones de datos
API fluida

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

Esquema de tabla
Al utilizar una base de datos relacional, las tablas se crean por Convención en el esquema predeterminado de la
base de datos. Por ejemplo, Microsoft SQL Server usará el esquema de dbo (SQLite no admite esquemas).
Puede configurar las tablas que se van a crear en un esquema específico de la siguiente manera:
Anotaciones de datos
API fluida

[Table("blogs", Schema = "blogging")]


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

En lugar de especificar el esquema de cada tabla, también puede definir el esquema predeterminado en el nivel de
modelo con la API fluida:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]("blogging");
}
Tenga en cuenta que al establecer el esquema predeterminado también se verán afectados otros objetos de base de
datos, como las secuencias.
Propiedades de entidad
11/03/2020 • 9 minutes to read

Cada tipo de entidad del modelo tiene un conjunto de propiedades, que EF Core leerán y escribirán en la base de
datos. Si utiliza una base de datos relacional, las propiedades de entidad se asignan a las columnas de la tabla.

Propiedades incluidas y excluidas


Por Convención, todas las propiedades públicas con un captador y un establecedor se incluirán en el modelo.
Las propiedades específicas se pueden excluir de la manera siguiente:
Anotaciones de datos
API fluida

public class Blog


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

[NotMapped]
public DateTime LoadedFromDatabase { get; set; }
}

Nombres de columna
Por Convención, cuando se utiliza una base de datos relacional, las propiedades de entidad se asignan a las
columnas de la tabla que tienen el mismo nombre que la propiedad.
Si prefiere configurar las columnas con nombres diferentes, puede hacerlo de la siguiente manera:
Anotaciones de datos
API fluida

public class Blog


{
[Column("blog_id")]
public int BlogId { get; set; }
public string Url { get; set; }
}

Tipos de datos de columna


Al utilizar una base de datos relacional, el proveedor de base de datos selecciona un tipo de datos basado en el tipo
.NET de la propiedad. También tiene en cuenta otros metadatos, como la longitud máximaconfigurada, si la
propiedad forma parte de una clave principal, etc.
Por ejemplo, SQL Server asigna propiedades de DateTime a columnas de datetime2(7) y string propiedades a
nvarchar(max) columnas (o a nvarchar(450) para propiedades que se utilizan como clave).

También puede configurar las columnas para especificar un tipo de datos exacto para una columna. Por ejemplo, el
código siguiente configura Url como una cadena no Unicode con una longitud máxima de 200 y Rating como
decimal con precisión de 5 y la escala de 2 :
Anotaciones de datos
API fluida

public class Blog


{
public int BlogId { get; set; }
[Column(TypeName = "varchar(200)")]
public string Url { get; set; }
[Column(TypeName = "decimal(5, 2)")]
public decimal Rating { get; set; }
}

Longitud máxima
La configuración de una longitud máxima proporciona una sugerencia al proveedor de base de datos sobre el tipo
de datos de columna adecuado que se debe elegir para una propiedad determinada. La longitud máxima solo se
aplica a los tipos de datos de matriz, como string y byte[] .

NOTE
Entity Framework no realiza ninguna validación de la longitud máxima antes de pasar datos al proveedor. Depende del
proveedor o del almacén de datos que se valide si es necesario. Por ejemplo, cuando el destino es SQL Server, si se supera la
longitud máxima, se producirá una excepción, ya que el tipo de datos de la columna subyacente no permitirá que se
almacenen los datos sobrantes.

En el ejemplo siguiente, la configuración de una longitud máxima de 500 hará que se cree una columna de tipo
nvarchar(500) en SQL Server:

Anotaciones de datos
API fluida

public class Blog


{
public int BlogId { get; set; }
[MaxLength(500)]
public string Url { get; set; }
}

Propiedades obligatorias y opcionales


Una propiedad se considera opcional si es válida para que contenga null . Si null no es un valor válido que se va
a asignar a una propiedad, se considera que es una propiedad obligatoria. Al asignar a un esquema de base de
datos relacional, las propiedades requeridas se crean como columnas que no aceptan valores NULL y las
propiedades opcionales se crean como columnas que aceptan valores NULL.
Convenciones
Por Convención, una propiedad cuyo tipo .NET pueda contener NULL se configurará como opcional, mientras que
las propiedades cuyo tipo .NET no puede contener valores NULL se configurarán según sea necesario. Por ejemplo,
todas las propiedades con tipos de valor .NET ( int , decimal , bool , etc.) se configuran según sea necesario y
todas las propiedades con tipos de valor .NET que aceptan valores NULL ( int? , decimal? , bool? , etc.) se
configuran como opcionales.
C#8 presentó una nueva característica denominada tipos de referencia que aceptan valores NULL, que permite
anotar tipos de referencia, lo que indica si es válido para que contengan null o not. Esta característica está
deshabilitada de forma predeterminada y, si está habilitada, modifica el comportamiento de EF Core de la siguiente
manera:
Si los tipos de referencia que aceptan valores NULL están deshabilitados (el valor predeterminado), todas las
propiedades con tipos de referencia de .NET se configuran como opcionales por Convención (por ejemplo,
string ).
Si los tipos de referencia que aceptan valores NULL están habilitados, las propiedades C# se configurarán en
función de la nulabilidad de su tipo .net: string? se configurarán como opcionales, mientras que string se
configurarán según sea necesario.
En el ejemplo siguiente se muestra un tipo de entidad con propiedades obligatorias y opcionales, con la
característica de referencia que acepta valores NULL deshabilitada (valor predeterminado) y habilitada:
Sin tipos de referencia que aceptan valores NULL (valor predeterminado)
Con tipos de referencia que aceptan valores NULL

public class CustomerWithoutNullableReferenceTypes


{
public int Id { get; set; }
[Required] // Data annotations needed to configure as required
public string FirstName { get; set; }
[Required]
public string LastName { get; set; } // Data annotations needed to configure as required
public string MiddleName { get; set; } // Optional by convention
}

Se recomienda el uso de tipos de referencia que aceptan valores NULL, ya que C# fluye la nulabilidad expresada en
el código para EF Core modelo y en la base de datos, e obvia el uso de las anotaciones de datos o la API fluida para
expresar el mismo concepto dos veces.

NOTE
Tenga cuidado al habilitar los tipos de referencia que aceptan valores NULL en un proyecto existente: las propiedades de tipo
de referencia que se configuraron anteriormente como opcional ahora se configurarán según sea necesario, a menos que se
anoten explícitamente para que acepten valores NULL. Al administrar un esquema de base de datos relacional, esto puede
provocar que se generen migraciones que modifiquen la nulabilidad de la columna de la base de datos.

Para obtener más información sobre los tipos de referencia que aceptan valores NULL y cómo usarlos con EF Core,
consulte la página de documentación dedicada para esta característica.
Configuración explícita
Una propiedad que sería opcional por Convención se puede configurar para que sea necesaria de la siguiente
manera:
Anotaciones de datos
API fluida

public class Blog


{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}
Claves
11/03/2020 • 7 minutes to read

Una clave actúa como identificador único para cada instancia de la entidad. La mayoría de las entidades de EF
tienen una sola clave, que se asigna al concepto de una clave principal en las bases de datos relacionales (para
entidades sin claves, vea entidadessin clave). Las entidades pueden tener claves adicionales más allá de la clave
principal (consulte claves alternativas para obtener más información).
Por Convención, una propiedad denominada Id o <type name>Id se configurará como la clave principal de una
entidad.

class Car
{
public string Id { get; set; }

public string Make { get; set; }


public string Model { get; set; }
}

class Truck
{
public string TruckId { get; set; }

public string Make { get; set; }


public string Model { get; set; }
}

NOTE
Los tipos de entidad de propiedad usan reglas diferentes para definir claves.

Puede configurar una única propiedad para que sea la clave principal de una entidad, como se indica a
continuación:
Anotaciones de datos
API fluida

class Car
{
[Key]
public string LicensePlate { get; set; }

public string Make { get; set; }


public string Model { get; set; }
}

También puede configurar varias propiedades para que sean la clave de una entidad, lo que se conoce como clave
compuesta. Las claves compuestas solo se pueden configurar mediante la API fluida; las convenciones nunca
configurarán una clave compuesta y no se pueden usar anotaciones de datos para configurar una.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
[Link]<Car>()
.HasKey(c => new { [Link], [Link] });
}

Nombre de clave principal


Por Convención, en las bases de datos relacionales se crean claves principales con el nombre PK_<type name> .
Puede configurar el nombre de la restricción Primary Key de la manera siguiente:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasKey(b => [Link])
.HasName("PrimaryKey_BlogId");
}

Tipos de clave y valores


Aunque EF Core admite el uso de propiedades de cualquier tipo primitivo como clave principal, incluidos string ,
Guid , byte[] y otros, no todas las bases de datos admiten todos los tipos como claves. En algunos casos, los
valores de clave se pueden convertir automáticamente a un tipo compatible, de lo contrario, la conversión se debe
especificar manualmente.
Las propiedades de clave deben tener siempre un valor no predeterminado al agregar una nueva entidad al
contexto, pero la base de datos generaráalgunos tipos. En ese caso, EF intentará generar un valor temporal cuando
la entidad se agregue con fines de seguimiento. Después de llamar a SaveChanges , el valor temporal se
reemplazará por el valor generado por la base de datos.

IMPORTANT
Si una propiedad de clave tiene su valor generado por la base de datos y se especifica un valor no predeterminado al agregar
una entidad, EF asumirá que la entidad ya existe en la base de datos e intentará actualizarla en lugar de insertar una nueva.
Para evitar esto, desactive la generación de valores o vea Cómo especificar valores explícitos para las propiedades generadas.

Claves alternativas
Una clave alternativa actúa como identificador único alternativo para cada instancia de entidad además de la clave
principal; se puede usar como destino de una relación. Al utilizar una base de datos relacional, se asigna al concepto
de un índice o una restricción únicos en las columnas de clave alternativas y una o varias restricciones de clave
externa que hacen referencia a las columnas.

TIP
Si solo desea exigir la unicidad en una columna, defina un índice único en lugar de una clave alternativa (consulte índices). En
EF, las claves alternativas son de solo lectura y proporcionan una semántica adicional sobre índices únicos, ya que se pueden
usar como destino de una clave externa.

Normalmente, se introducen claves alternativas cuando sea necesario y no es necesario configurarlas


manualmente. Por Convención, se introduce una clave alternativa cuando se identifica una propiedad que no es la
clave principal como destino de una relación.
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasOne(p => [Link])
.WithMany(b => [Link])
.HasForeignKey(p => [Link])
.HasPrincipalKey(b => [Link]);
}
}

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public string BlogUrl { get; set; }


public Blog Blog { get; set; }
}

También puede configurar una sola propiedad para que sea una clave alternativa:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Car>()
.HasAlternateKey(c => [Link]);
}

También puede configurar varias propiedades para que sean una clave alternativa (conocida como clave alternativa
compuesta):

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Car>()
.HasAlternateKey(c => new { [Link], [Link] });
}

Por último, por Convención, el índice y la restricción que se introducen para una clave alternativa se denominarán
AK_<type name>_<property name> (para las claves alternativas compuestas <property name> se convierte en una lista
de nombres de propiedad separados por un carácter de subrayado). Puede configurar el nombre del índice de la
clave alternativa y la restricción UNIQUE:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
[Link]<Car>()
.HasAlternateKey(c => [Link])
.HasName("AlternateKey_LicensePlate");
}
Valores generados
11/03/2020 • 10 minutes to read

Patrones de generación de valores


Hay tres patrones de generación de valores que se pueden usar para las propiedades:
Sin generación de valores
Valor generado al agregar
Valor generado al agregar o actualizar
Sin generación de valores
Ninguna generación de valores significa que siempre se proporcionará un valor válido que se va a guardar en la
base de datos. Este valor válido se debe asignar a las nuevas entidades antes de que se agreguen al contexto.
Valor generado al agregar
El valor generado al agregar significa que se genera un valor para las nuevas entidades.
Dependiendo del proveedor de base de datos que se use, es posible que se generen valores de cliente en EF o en
la base de datos. Si la base de datos genera el valor, EF puede asignar un valor temporal al agregar la entidad al
contexto. Este valor temporal se reemplazará por el valor generado por la base de datos durante la SaveChanges() .
Si agrega una entidad al contexto que tiene un valor asignado a la propiedad, EF intentará insertar ese valor en
lugar de generar uno nuevo. Se considera que una propiedad tiene un valor asignado si no se le asigna el valor
predeterminado de CLR ( null para string , 0 para int , [Link] para Guid , etc.). Para obtener más
información, vea valores explícitos para las propiedades generadas.

WARNING
La forma en que se genera el valor para las entidades agregadas dependerá del proveedor de base de datos que se use. Los
proveedores de bases de datos pueden configurar automáticamente la generación de valores para algunos tipos de
propiedad, pero otros pueden requerir que se configure manualmente cómo se genera el valor.
Por ejemplo, al usar SQL Server, se generarán automáticamente valores para las propiedades de GUID (mediante el
algoritmo GUID secuencial SQL Server). Sin embargo, si especifica que se genere una propiedad DateTime en Add, debe
configurar una manera de que se generen los valores. Una manera de hacerlo es configurar un valor predeterminado de
GETDATE() , vea valores predeterminados.

Valor generado al agregar o actualizar


El valor generado al agregar o actualizar significa que se genera un nuevo valor cada vez que se guarda el registro
(Insert o Update).
Como value generated on add , si especifica un valor para la propiedad en una instancia recién agregada de una
entidad, se insertará ese valor en lugar de un valor que se va a generar. También es posible establecer un valor
explícito al actualizar. Para obtener más información, vea valores explícitos para las propiedades generadas.
WARNING
La forma en que se genera el valor para las entidades agregadas y actualizadas dependerá del proveedor de base de datos
que se use. Los proveedores de bases de datos pueden configurar automáticamente la generación de valores para algunos
tipos de propiedades, mientras que otros requerirán que se configure manualmente cómo se genera el valor.
Por ejemplo, al usar SQL Server, byte[] propiedades que se establecen como se generan al agregar o actualizar y
marcadas como tokens de simultaneidad, se configurarán con el tipo de datos rowversion , de modo que los valores se
generarán en la base de datos. Sin embargo, si especifica que se genere una propiedad DateTime en Add o Update, debe
configurar una manera de que se generen los valores. Una forma de hacerlo consiste en configurar un valor predeterminado
de GETDATE() (vea valores predeterminados) para generar valores para las nuevas filas. Después, puede usar un
desencadenador de base de datos para generar valores durante las actualizaciones (como el siguiente desencadenador de
ejemplo).

CREATE TRIGGER [dbo].[Blogs_UPDATE] ON [dbo].[Blogs]


AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;

IF ((SELECT TRIGGER_NESTLEVEL()) > 1) RETURN;

DECLARE @Id INT

SELECT @Id = [Link]


FROM INSERTED

UPDATE [Link]
SET LastUpdated = GETDATE()
WHERE BlogId = @Id
END

Valor generado al agregar


Por Convención, las claves principales no compuestas de tipo Short, int, Long o GUID están configuradas para que
tengan valores generados para las entidades insertadas, si la aplicación no proporciona un valor. Normalmente, el
proveedor de base de datos se encarga de la configuración necesaria. por ejemplo, una clave principal numérica
en SQL Server se configura automáticamente para que sea una columna de identidad.
Puede configurar cualquier propiedad para que se genere su valor para las entidades insertadas como se indica a
continuación:
Anotaciones de datos
API fluida

public class Blog


{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated([Link])]
public DateTime Inserted { get; set; }
}

WARNING
Esto solo permite que EF sepa que los valores se generan para las entidades agregadas, no garantiza que EF configurará el
mecanismo real para generar valores. Vea la sección valor generado al agregar para obtener más detalles.
Valores predeterminados
En las bases de datos relacionales, una columna se puede configurar con un valor predeterminado. Si se inserta
una fila sin un valor para esa columna, se usará el valor predeterminado.
Puede configurar un valor predeterminado en una propiedad:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property(b => [Link])
.HasDefaultValue(3);
}

También puede especificar un fragmento de SQL que se usa para calcular el valor predeterminado:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property(b => [Link])
.HasDefaultValueSql("getdate()");
}

Si se especifica un valor predeterminado, se configurará implícitamente la propiedad como valor generado al


agregar.

Valor generado al agregar o actualizar


Anotaciones de datos
API fluida

public class Blog


{
public int BlogId { get; set; }
public string Url { get; set; }
[DatabaseGenerated([Link])]
public DateTime LastUpdated { get; set; }
}

WARNING
Esto solo permite que EF sepa que se generan valores para entidades agregadas o actualizadas, no garantiza que EF
configure el mecanismo real para generar valores. Vea la sección valor generado al agregar o actualizar para obtener más
detalles.

Columnas calculadas
En algunas bases de datos relacionales, una columna se puede configurar para que se calcule su valor en la base
de datos, normalmente con una expresión que haga referencia a otras columnas:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Person>()
.Property(p => [Link])
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");
}
NOTE
En algunos casos, el valor de la columna se calcula cada vez que se captura (a veces denominado columnas virtuales ) y en
otros se calcula en cada actualización de la fila y se almacena (a veces denominada columnas almacenadas o persistentes ).
Esto varía en los proveedores de bases de datos.

Sin generación de valores


Normalmente, es necesario deshabilitar la generación de valores en una propiedad si una Convención la configura
para la generación de valores. Por ejemplo, si tiene una clave principal de tipo int, se establecerá implícitamente
configurada como valor generado al agregar; puede deshabilitarlo a través de lo siguiente:
Anotaciones de datos
API fluida

public class Blog


{
[DatabaseGenerated([Link])]
public int BlogId { get; set; }
public string Url { get; set; }
}
Tokens de simultaneidad
11/03/2020 • 2 minutes to read

NOTE
En esta página se documenta cómo configurar los tokens de simultaneidad. Vea controlar los conflictos de simultaneidad
para obtener una explicación detallada de cómo funciona el control de simultaneidad en EF Core y ejemplos de cómo
controlar los conflictos de simultaneidad en la aplicación.

Las propiedades configuradas como tokens de simultaneidad se usan para implementar el control de
simultaneidad optimista.

Configuración
Anotaciones de datos
API fluida

public class Person


{
public int PersonId { get; set; }

[ConcurrencyCheck]
public string LastName { get; set; }

public string FirstName { get; set; }


}

Marca de tiempo/rowversion
Timestamp/rowversion es una propiedad para la cual la base de datos genera automáticamente un nuevo valor
cada vez que se inserta o se actualiza una fila. La propiedad también se trata como un token de simultaneidad, lo
que garantiza que se obtiene una excepción si una fila que se está actualizando ha cambiado desde que se realizó
la consulta. Los detalles precisos dependen del proveedor de base de datos utilizado; por SQL Server, normalmente
se utiliza una propiedad Byte [] , que se configurará como una columna ROWVERSION en la base de datos.
Puede configurar una propiedad para que sea una marca de tiempo o rowversion como se indica a continuación:
Anotaciones de datos
API fluida

public class Blog


{
public int BlogId { get; set; }

public string Url { get; set; }

[Timestamp]
public byte[] Timestamp { get; set; }
}
Propiedades reemplazadas
11/03/2020 • 4 minutes to read

Las propiedades de sombra son propiedades que no están definidas en la clase de entidad de .NET pero que se
definen para ese tipo de entidad en el modelo de EF Core. El valor y el estado de estas propiedades se mantienen
únicamente en el seguimiento de cambios. Las propiedades Shadow son útiles cuando hay datos en la base de
datos que no se deben exponer en los tipos de entidad asignados.

Propiedades de sombra de clave externa


Las propiedades de sombra se utilizan con más frecuencia para las propiedades de clave externa, donde la
relación entre dos entidades se representa mediante un valor de clave externa en la base de datos, pero la
relación se administra en los tipos de entidad mediante las propiedades de navegación entre la entidad. distintos.
Por Convención, EF introducirá una propiedad Shadow cuando se detecte una relación, pero no se encuentra
ninguna propiedad de clave externa en la clase de entidad dependiente.
La propiedad se denominará <navigation property name><principal key property name> (la navegación en la
entidad dependiente, que apunta a la entidad principal, se usa para la nomenclatura). Si el nombre de la
propiedad de clave principal incluye el nombre de la propiedad de navegación, el nombre simplemente se
<principal key property name> . Si no hay ninguna propiedad de navegación en la entidad dependiente, el
nombre del tipo de entidad de seguridad se usa en su lugar.
Por ejemplo, la siguiente lista de código dará como resultado la inclusión de una BlogId propiedad Shadow en la
entidad Post :

class MyContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

// Since there is no CLR property which holds the foreign


// key for this relationship, a shadow property is created.
public Blog Blog { get; set; }
}

Configurar propiedades de instantáneas


Puede usar la API fluida para configurar las propiedades de las instantáneas. Una vez que haya llamado a la
sobrecarga de la cadena de Property , puede encadenar cualquiera de las llamadas de configuración que desee
para otras propiedades. En el ejemplo siguiente, puesto que Blog no tiene ninguna propiedad CLR denominada
LastUpdated , se crea una propiedad Shadow:

class MyContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property<DateTime>("LastUpdated");
}
}

public class Blog


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

Si el nombre proporcionado al método Property coincide con el nombre de una propiedad existente (una
propiedad Shadow o una definida en la clase de entidad), el código configurará esa propiedad existente en lugar
de introducir una nueva propiedad Shadow.

Obtener acceso a las propiedades de sombra


Los valores de las propiedades Shadow se pueden obtener y cambiar a través de la API de ChangeTracker :

[Link](myBlog).Property("LastUpdated").CurrentValue = [Link];

Se puede hacer referencia a las propiedades Shadow en consultas LINQ a través del método estático
[Link] :

var blogs = [Link]


.OrderBy(b => [Link]<DateTime>(b, "LastUpdated"));
Relaciones
16/03/2020 • 26 minutes to read

Una relación define el modo en que dos entidades se relacionan entre sí. En una base de datos relacional, se
representa mediante una restricción FOREIGN KEY.

NOTE
La mayoría de los ejemplos de este artículo usan una relación de uno a varios para demostrar los conceptos. Para obtener
ejemplos de relaciones de uno a uno y de varios a varios, consulte la sección otros patrones de relación al final del artículo.

Definición de términos
Hay una serie de términos que se usan para describir las relaciones
Entidad dependiente: Esta es la entidad que contiene las propiedades de clave externa. A veces se conoce
como "secundario" de la relación.
Entidad de entidad de seguridad: Esta es la entidad que contiene las propiedades de clave
principal/alternativa. A veces se denomina "primario" de la relación.
Clave principal: Propiedades que identifican de forma única la entidad principal. Puede ser la clave
principal o una clave alternativa.
Clave externa: Propiedades de la entidad dependiente que se usan para almacenar los valores de clave
principal para la entidad relacionada.
Propiedad de navegación: Propiedad definida en la entidad principal o dependiente que hace referencia a
la entidad relacionada.
Propiedad de navegación de colección: Propiedad de navegación que contiene referencias a
muchas entidades relacionadas.
Propiedad de navegación de referencia: Propiedad de navegación que contiene una referencia a
una sola entidad relacionada.
Propiedad de navegación inversa: Al discutir una propiedad de navegación determinada, este
término hace referencia a la propiedad de navegación en el otro extremo de la relación.
Relación que hace referencia a sí misma: Una relación en la que los tipos de entidad dependiente y
principal son iguales.
En el código siguiente se muestra una relación de uno a varios entre Blog y Post
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}

Post es la entidad dependiente


Blog es la entidad principal
[Link] es la clave principal (en este caso, es una clave principal en lugar de una clave alternativa)
[Link] es la clave externa
[Link] es una propiedad de navegación de referencia
[Link] es una propiedad de navegación de colección
[Link] es la propiedad de navegación inversa de [Link] (y viceversa)

Convenciones
De forma predeterminada, se creará una relación cuando se detecte una propiedad de navegación en un tipo. Una
propiedad se considera una propiedad de navegación si el tipo al que señala no se puede asignar como un tipo
escalar por el proveedor de base de datos actual.

NOTE
Las relaciones detectadas por la Convención siempre tendrán como destino la clave principal de la entidad principal. Para
elegir como destino una clave alternativa, se debe realizar una configuración adicional mediante la API fluida.

Relaciones totalmente definidas


El patrón más común para las relaciones es tener propiedades de navegación definidas en ambos extremos de la
relación y una propiedad de clave externa definida en la clase de entidad dependiente.
Si se encuentra un par de propiedades de navegación entre dos tipos, se configurarán como propiedades de
navegación inversa de la misma relación.
Si la entidad dependiente contiene una propiedad con un nombre que coincide con uno de estos patrones,
se configurará como la clave externa:
<navigation property name><principal key property name>
<navigation property name>Id
<principal entity name><principal key property name>
<principal entity name>Id
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}

En este ejemplo, las propiedades resaltadas se usarán para configurar la relación.

NOTE
Si la propiedad es la clave principal o es de un tipo no compatible con la clave principal, no se configurará como clave externa.

NOTE
Antes de EF Core 3,0 la propiedad denominada exactamente igual que la propiedad de clave principal también coincidía con la
clave externa .

No hay propiedad de clave externa


Aunque se recomienda tener una propiedad de clave externa definida en la clase de entidad dependiente, no es
necesario. Si no se encuentra ninguna propiedad de clave externa, se introducirá una propiedad de clave externa de
sombra con el nombre <navigation property name><principal key property name> o
<principal entity name><principal key property name> si no hay ninguna navegación presente en el tipo
dependiente.

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public Blog Blog { get; set; }


}

En este ejemplo, la clave externa de la sombra se BlogId porque, si se antepone el nombre de navegación, sería
redundante.
NOTE
Si ya existe una propiedad con el mismo nombre, el nombre de la propiedad Shadow tendrá como sufijo un número.

Propiedad de navegación única


Incluir solo una propiedad de navegación (sin navegación inversa y sin propiedad de clave externa) es suficiente
para tener una relación definida por Convención. También puede tener una propiedad de navegación única y una
propiedad de clave externa.

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}

Limitaciones
Cuando hay varias propiedades de navegación definidas entre dos tipos (es decir, más de un par de navegaciones
que apuntan entre sí), las relaciones representadas por las propiedades de navegación son ambiguas. Tendrá que
configurarlos manualmente para resolver la ambigüedad.
Eliminación en cascada
Por Convención, la eliminación en cascada se establecerá en Cascade para las relaciones necesarias y ClientSetNull
para las relaciones opcionales. Cascade significa que las entidades dependientes también se eliminan. ClientSetNull
significa que las entidades dependientes que no se cargan en la memoria permanecerán sin cambios y deben
eliminarse manualmente o actualizarse para que apunten a una entidad principal válida. En el caso de las entidades
que se cargan en memoria, EF Core intentará establecer las propiedades de clave externa en NULL.
Vea la sección relaciones obligatorias y opcionales para ver la diferencia entre las relaciones obligatorias y
opcionales.
Consulte eliminación en cascada para obtener más detalles sobre los distintos comportamientos de eliminación y
los valores predeterminados que usa la Convención.

Configuración manual
API fluida
Anotaciones de datos
Para configurar una relación en la API fluida, empiece por identificar las propiedades de navegación que componen
la relación. HasOne o HasMany identifica la propiedad de navegación en el tipo de entidad en el que se está
iniciando la configuración. A continuación, encadenar una llamada a WithOne o WithMany para identificar la
navegación inversa. HasOne / WithOne se utilizan para las propiedades de navegación de referencia y HasMany /
WithMany se utilizan para las propiedades de navegación de la colección.
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasOne(p => [Link])
.WithMany(b => [Link]);
}
}

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public Blog Blog { get; set; }


}

Propiedad de navegación única


Si solo tiene una propiedad de navegación, hay sobrecargas sin parámetros de WithOne y WithMany . Esto indica
que hay conceptualmente una referencia o una colección en el otro extremo de la relación, pero no hay ninguna
propiedad de navegación incluida en la clase de entidad.

class MyContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasMany(b => [Link])
.WithOne();
}
}

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
}
Clave externa
API fluida (clave simple)
API fluida (clave compuesta)
Anotaciones de datos (clave simple)
Puede usar la API fluida para configurar qué propiedad se debe usar como la propiedad de clave externa para una
relación determinada:

class MyContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasOne(p => [Link])
.WithMany(b => [Link])
.HasForeignKey(p => [Link]);
}
}

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public int BlogForeignKey { get; set; }


public Blog Blog { get; set; }
}

Clave externa de sombra


Puede usar la sobrecarga de cadena de HasForeignKey(...) para configurar una propiedad Shadow como clave
externa (consulte propiedades de sombra para obtener más información). Se recomienda agregar explícitamente la
propiedad Shadow al modelo antes de usarla como clave externa (como se muestra a continuación).
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
// Add the shadow property to the model
[Link]<Post>()
.Property<int>("BlogForeignKey");

// Use the shadow property as a foreign key


[Link]<Post>()
.HasOne(p => [Link])
.WithMany(b => [Link])
.HasForeignKey("BlogForeignKey");
}
}

public class Blog


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

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public Blog Blog { get; set; }


}

Nombre de restricción de clave externa


Por Convención, cuando el destino es una base de datos relacional, las restricciones Foreign Key se denominan FK_ .
En el caso de las claves externas compuestas se convierte en una lista de nombres de propiedades de clave externa
separadas por guiones bajos.
También puede configurar el nombre de la restricción de la siguiente manera:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasOne(p => [Link])
.WithMany(b => [Link])
.HasForeignKey(p => [Link])
.HasConstraintName("ForeignKey_Post_Blog");
}

Sin propiedad de navegación


No es necesario proporcionar una propiedad de navegación. Simplemente puede proporcionar una clave externa
en un lado de la relación.
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasOne<Blog>()
.WithMany()
.HasForeignKey(p => [Link]);
}
}

public class Blog


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

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; }


}

Clave principal
Si desea que la clave externa haga referencia a una propiedad que no sea la clave principal, puede usar la API fluida
para configurar la propiedad de clave principal de la relación. La propiedad que se configura como clave principal
se configurará automáticamente como una clave alternativa.
Clave simple
Clave compuesta
class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<RecordOfSale>()
.HasOne(s => [Link])
.WithMany(c => [Link])
.HasForeignKey(s => [Link])
.HasPrincipalKey(c => [Link]);
}
}

public class Car


{
public int CarId { get; set; }
public string LicensePlate { get; set; }
public string Make { get; set; }
public string Model { get; set; }

public List<RecordOfSale> SaleHistory { get; set; }


}

public class RecordOfSale


{
public int RecordOfSaleId { get; set; }
public DateTime DateSold { get; set; }
public decimal Price { get; set; }

public string CarLicensePlate { get; set; }


public Car Car { get; set; }
}

Relaciones obligatorias y opcionales


Puede usar la API fluida para configurar si la relación es obligatoria u opcional. En última instancia, controla si la
propiedad de clave externa es obligatoria u opcional. Esto es muy útil cuando se usa una clave externa de estado de
sombra. Si tiene una propiedad de clave externa en la clase de entidad, la necesidad de la relación se determina en
función de si la propiedad de clave externa es necesaria u opcional (vea propiedades obligatorias y opcionales para
obtener más información).

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasOne(p => [Link])
.WithMany(b => [Link])
.IsRequired();
}

NOTE
La llamada a IsRequired(false) también hace que la propiedad de clave externa sea opcional a menos que esté
configurada de otro modo.

Eliminación en cascada
Puede usar la API fluida para configurar explícitamente el comportamiento de eliminación en cascada para una
relación determinada.
Consulte la eliminación en cascada para obtener una explicación detallada de cada opción.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
[Link]<Post>()
.HasOne(p => [Link])
.WithMany(b => [Link])
.OnDelete([Link]);
}

Otros patrones de relación


Uno a uno
Una relación de uno a uno tiene una propiedad de navegación de referencia en ambos lados. Siguen las mismas
convenciones que las relaciones uno a varios, pero se incluye un índice único en la propiedad de clave externa para
asegurarse de que solo un dependiente esté relacionado con cada entidad de seguridad.

public class Blog


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

public BlogImage BlogImage { get; set; }


}

public class BlogImage


{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}

NOTE
EF elegirá una de las entidades como dependiente en función de su capacidad para detectar una propiedad de clave externa.
Si se elige la entidad equivocada como dependiente, puede usar la API fluida para corregir este problema.

Al configurar la relación con la API fluida, se usan los métodos HasOne y WithOne .
Al configurar la clave externa, debe especificar el tipo de entidad dependiente: Observe que el parámetro genérico
proporcionado a HasForeignKey en la siguiente lista. En una relación uno a varios, es evidente que la entidad con la
navegación de referencia es el dependiente y el que tiene la colección es la entidad de seguridad. Pero esto no es
así en una relación uno a uno; por lo tanto, la necesidad de definirla explícitamente.
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<BlogImage> BlogImages { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasOne(b => [Link])
.WithOne(i => [Link])
.HasForeignKey<BlogImage>(b => [Link]);
}
}

public class Blog


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

public BlogImage BlogImage { get; set; }


}

public class BlogImage


{
public int BlogImageId { get; set; }
public byte[] Image { get; set; }
public string Caption { get; set; }

public int BlogForeignKey { get; set; }


public Blog Blog { get; set; }
}

Varios a varios
Todavía no se admiten las relaciones de varios a varios sin una clase de entidad para representar la tabla de
combinación. Sin embargo, puede representar una relación de varios a varios incluyendo una clase de entidad para
la tabla de combinación y asignando dos relaciones uno a varios independientes.
class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<PostTag>()
.HasKey(t => new { [Link], [Link] });

[Link]<PostTag>()
.HasOne(pt => [Link])
.WithMany(p => [Link])
.HasForeignKey(pt => [Link]);

[Link]<PostTag>()
.HasOne(pt => [Link])
.WithMany(t => [Link])
.HasForeignKey(pt => [Link]);
}
}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public List<PostTag> PostTags { get; set; }


}

public class Tag


{
public string TagId { get; set; }

public List<PostTag> PostTags { get; set; }


}

public class PostTag


{
public int PostId { get; set; }
public Post Post { get; set; }

public string TagId { get; set; }


public Tag Tag { get; set; }
}
Índices
11/03/2020 • 5 minutes to read

Los índices son un concepto común en muchos almacenes de datos. Aunque su implementación en el almacén de
datos puede variar, se usan para realizar búsquedas basadas en una columna (o conjunto de columnas) más
eficaces.
Los índices no se pueden crear con anotaciones de datos. Puede usar la API fluida para especificar un índice en una
sola columna de la siguiente manera:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasIndex(b => [Link]);
}

También puede especificar un índice en más de una columna:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Person>()
.HasIndex(p => new { [Link], [Link] });
}

NOTE
Por Convención, se crea un índice en cada propiedad (o conjunto de propiedades) que se usa como clave externa.
EF Core solo admite un índice por conjunto de propiedades distinto. Si usa la API fluida para configurar un índice en un
conjunto de propiedades que ya tiene definido un índice, ya sea por Convención o por configuración anterior, cambiará la
definición de ese índice. Esto resulta útil si desea seguir configurando un índice creado por la Convención.

Unicidad del índice


De forma predeterminada, los índices no son únicos: se permite que varias filas tengan los mismos valores para el
conjunto de columnas del índice. Puede crear un índice único como se indica a continuación:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasIndex(b => [Link])
.IsUnique();
}

Si se intenta insertar más de una entidad con los mismos valores para el conjunto de columnas del índice, se
producirá una excepción.

Nombre del índice


Por Convención, los índices creados en una base de datos relacional se denominan
IX_<type name>_<property name> . En el caso de los índices compuestos, <property name> se convierte en una lista
de nombres de propiedad separados por guiones bajos.
Puede usar la API fluida para establecer el nombre del índice creado en la base de datos:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasIndex(b => [Link])
.HasName("Index_Url");
}

Filtro de índice
Algunas bases de datos relacionales permiten especificar un índice parcial o filtrado. Esto permite indizar solo un
subconjunto de los valores de una columna, reduciendo el tamaño del índice y mejorando el rendimiento y el uso
del espacio en disco. Para obtener más información sobre SQL Server los índices filtrados, vea la
documentaciónde.
Puede usar la API fluida para especificar un filtro en un índice, proporcionado como una expresión SQL:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasIndex(b => [Link])
.HasFilter("[Url] IS NOT NULL");
}

Al usar el proveedor de SQL Server EF agrega un filtro de 'IS NOT NULL' para todas las columnas que aceptan
valores NULL que forman parte de un índice único. Para invalidar esta Convención, puede proporcionar un valor
null .

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasIndex(b => [Link])
.IsUnique()
.HasFilter(null);
}

Columnas incluidas
Algunas bases de datos relacionales permiten configurar un conjunto de columnas que se incluyen en el índice,
pero que no forman parte de su "clave". Esto puede mejorar significativamente el rendimiento de las consultas
cuando todas las columnas de la consulta se incluyen en el índice como columnas de clave o sin clave, ya que no es
necesario tener acceso a la tabla en sí. Para obtener más información sobre SQL Server columnas incluidas, vea la
documentaciónde.
En el ejemplo siguiente, la columna Url forma parte de la clave de índice, por lo que cualquier filtrado de
consultas en esa columna puede utilizar el índice. Pero además, las consultas que solo tienen acceso a las columnas
Title y PublishedOn no tendrán que acceder a la tabla y se ejecutarán de forma más eficaz:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
[Link]<Post>()
.HasIndex(p => [Link])
.IncludeProperties(p => new
{
[Link],
[Link]
});
}
Herencia
11/03/2020 • 5 minutes to read

EF puede asignar una jerarquía de tipos .NET a una base de datos. Esto le permite escribir las entidades .NET en el
código como de costumbre, con los tipos base y derivados, y hacer que EF cree sin problemas el esquema de base
de datos adecuado, las consultas de problemas, etc. Los detalles reales de cómo se asigna una jerarquía de tipos
dependen del proveedor; en esta página se describe la compatibilidad de herencia en el contexto de una base de
datos relacional.
En este momento, EF Core solo admite el patrón de tabla por jerarquía (TPH). TPH usa una sola tabla para
almacenar los datos de todos los tipos de la jerarquía y se usa una columna de discriminador para identificar qué
tipo representa cada fila.

NOTE
Los EF Core no admiten la tabla por tipo (TPT) y la tabla por tipo específico (TPC), que son compatibles con EF6. TPT es una
característica importante planeada para EF Core 5,0.

Asignación de jerarquía de tipos de entidad


Por Convención, EF solo configurará la herencia si dos o más tipos heredados se incluyen explícitamente en el
modelo. EF no buscará automáticamente tipos base o derivados que no se incluyan en el modelo de otra forma.
Puede incluir tipos en el modelo exponiendo un DbSet para cada tipo en la jerarquía de herencia:

class MyContext : DbContext


{
public DbSet<Blog> Blogs { get; set; }
public DbSet<RssBlog> RssBlogs { get; set; }
}

public class Blog


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

public class RssBlog : Blog


{
public string RssUrl { get; set; }
}

Este modelo se asignará al siguiente esquema de la base de datos (tenga en cuenta la columna discriminada
creada implícitamente, que identifica qué tipo de blog se almacena en cada fila):
NOTE
Las columnas de la base de datos se convierten en NULL automáticamente según sea necesario al usar la asignación TPH.
Por ejemplo, la columna RssUrl admite valores NULL porque las instancias de blog normales no tienen esa propiedad.

Si no desea exponer un DbSet para una o varias entidades de la jerarquía, también puede usar la API fluida para
asegurarse de que se incluyen en el modelo.

TIP
Si no se basa en las convenciones, puede especificar explícitamente el tipo base mediante HasBaseType . También puede usar
.HasBaseType((Type)null) para quitar un tipo de entidad de la jerarquía.

Configuración de discriminador
Puede configurar el nombre y el tipo de la columna discriminadora y los valores que se usan para identificar cada
tipo en la jerarquía:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasDiscriminator<string>("blog_type")
.HasValue<Blog>("blog_base")
.HasValue<RssBlog>("blog_rss");
}

En los ejemplos anteriores, EF agregó el discriminador implícitamente como una propiedad Shadow en la entidad
base de la jerarquía. Esta propiedad se puede configurar como cualquier otra:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property("Discriminator")
.HasMaxLength(200);
}

Por último, el discriminador también se puede asignar a una propiedad .NET normal en la entidad:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasDiscriminator(b => [Link]);

[Link]<Blog>()
.Property(e => [Link])
.HasMaxLength(200)
.HasColumnName("blog_type");
}

Columnas compartidas
De forma predeterminada, cuando dos tipos de entidad del mismo nivel en la jerarquía tienen una propiedad con
el mismo nombre, se asignarán a dos columnas independientes. Sin embargo, si su tipo es idéntico, se pueden
asignar a la misma columna de base de datos:
public class MyContext : DbContext
{
public DbSet<BlogBase> Blogs { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property(b => [Link])
.HasColumnName("Url");

[Link]<RssBlog>()
.Property(b => [Link])
.HasColumnName("Url");
}
}

public abstract class BlogBase


{
public int BlogId { get; set; }
}

public class Blog : BlogBase


{
public string Url { get; set; }
}

public class RssBlog : BlogBase


{
public string Url { get; set; }
}
Secuencias
11/03/2020 • 2 minutes to read

NOTE
Las secuencias son una característica que normalmente solo admiten las bases de datos relacionales. Si utiliza una base de
datos no relacional como Cosmos, consulte la documentación de la base de datos para generar valores únicos.

Una secuencia genera valores numéricos únicos y secuenciales en la base de datos. Las secuencias no están
asociadas a una tabla específica y se pueden configurar varias tablas para que dibujen valores de la misma
secuencia.

Uso básico
Puede configurar una secuencia en el modelo y, a continuación, utilizarla para generar valores para las propiedades:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<int>("OrderNumbers");

[Link]<Order>()
.Property(o => [Link])
.HasDefaultValueSql("NEXT VALUE FOR [Link]");
}

Tenga en cuenta que el SQL específico que se usa para generar un valor a partir de una secuencia es específico de
la base de datos; el ejemplo anterior funciona en SQL Server pero producirá un error en otras bases de datos.
Consulte la documentación específica de su base de datos para obtener más información.

Configuración de las opciones de secuencia


También puede configurar aspectos adicionales de la secuencia, como su esquema, valor inicial, incremento, etc.:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<int>("OrderNumbers", schema: "shared")
.StartsAt(1000)
.IncrementsBy(5);
}
Campos de respaldo
11/03/2020 • 5 minutes to read

Los campos de respaldo permiten a EF leer o escribir en un campo en lugar de una propiedad. Esto puede ser útil
cuando se usa la encapsulación en la clase para restringir el uso de y/o mejorar la semántica en torno al acceso a
los datos por código de aplicación, pero el valor debe leerse o escribirse en la base de datos sin usar esas
restricciones o mejoras.

Configuración básica
Por Convención, se detectarán los campos siguientes como campos de respaldo para una propiedad determinada
(que se muestra en orden de prioridad).
_<camel-cased property name>
_<property name>
m_<camel-cased property name>
m_<property name>

En el ejemplo siguiente, la propiedad Url está configurada para tener _url como campo de respaldo:

public class Blog


{
private string _url;

public int BlogId { get; set; }

public string Url


{
get { return _url; }
set { _url = value; }
}
}

Tenga en cuenta que los campos de respaldo solo se detectan para las propiedades que se incluyen en el modelo.
Para obtener más información sobre las propiedades que se incluyen en el modelo, vea incluir & excluyendo las
propiedades.
También puede configurar los campos de respaldo explícitamente, por ejemplo, si el nombre del campo no se
corresponde con las convenciones anteriores:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property(b => [Link])
.HasField("_validatedUrl");
}

Acceso a campos y propiedades


De forma predeterminada, EF siempre leerá y escribirá en el campo de respaldo, suponiendo que se haya
configurado correctamente, y que nunca usará la propiedad. Sin embargo, EF también admite otros patrones de
acceso. Por ejemplo, en el ejemplo siguiente se indica a EF que escriba en el campo de respaldo solo mientras
materializa y use la propiedad en todos los demás casos:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property(b => [Link])
.HasField("_validatedUrl")
.UsePropertyAccessMode([Link]);
}

Vea la enumeración PropertyAccessMode para obtener el conjunto completo de opciones admitidas.

NOTE
Con EF Core 3,0, el modo de acceso de propiedad predeterminado cambió de PreferFieldDuringConstruction a
PreferField .

Propiedades de solo campo


También puede crear una propiedad conceptual en el modelo que no tiene una propiedad de CLR correspondiente
en la clase de entidad, sino que usa un campo para almacenar los datos en la entidad. Esto es diferente de las
propiedades de las instantáneas, donde los datos se almacenan en el seguimiento de cambios, en lugar de en el
tipo CLR de la entidad. Las propiedades de solo campo se utilizan normalmente cuando la clase de entidad usa
métodos en lugar de propiedades para obtener o establecer valores, o en casos en los que los campos no se
deben exponer en absoluto en el modelo de dominio (por ejemplo, las claves principales).
Puede configurar una propiedad de solo campo proporcionando un nombre en la API de Property(...) :
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property("_validatedUrl");
}
}

public class Blog


{
private string _validatedUrl;

public int BlogId { get; set; }

public string GetUrl()


{
return _validatedUrl;
}

public void SetUrl(string url)


{
using (var client = new HttpClient())
{
var response = [Link](url).Result;
[Link]();
}

_validatedUrl = url;
}
}

EF intentará buscar una propiedad CLR con el nombre especificado o un campo si no se encuentra una propiedad.
Si no se encuentra ninguna propiedad ni un campo, se configurará una propiedad Shadow en su lugar.
Es posible que tenga que hacer referencia a una propiedad de solo campo desde las consultas LINQ, pero estos
campos suelen ser privados. Puede usar el método [Link](...) en una consulta LINQ para hacer referencia
al campo:

var blogs = [Link](b => [Link]<string>(b, "_validatedUrl"));


Conversiones de valores
11/03/2020 • 8 minutes to read

NOTE
Esta característica es nueva en EF Core 2.1.

Los convertidores de valores permiten convertir los valores de propiedad al leer o escribir en la base de datos.
Esta conversión puede ser de un valor a otro del mismo tipo (por ejemplo, cifrar cadenas) o de un valor de un
tipo a un valor de otro tipo (por ejemplo, convertir valores de enumeración en cadenas en la base de datos y
desde ellas).

Aspectos básicos
Los convertidores de valores se especifican en términos de un ModelClrType y un ProviderClrType . El tipo de
modelo es el tipo .NET de la propiedad en el tipo de entidad. El tipo de proveedor es el tipo .NET que entiende el
proveedor de base de datos. Por ejemplo, para guardar las enumeraciones como cadenas en la base de datos, el
tipo de modelo es el tipo de la enumeración y el tipo de proveedor es String . Estos dos tipos pueden ser iguales.
Las conversiones se definen utilizando dos Func árboles de expresión: uno de ModelClrType a ProviderClrType
y otro de ProviderClrType a ModelClrType . Los árboles de expresión se usan para que se puedan compilar en el
código de acceso a la base de datos para conversiones eficientes. En las conversiones complejas, el árbol de
expresión puede ser una llamada simple a un método que realiza la conversión.

Configurar un convertidor de valores


Las conversiones de valores se definen en las propiedades del OnModelCreating de la DbContext . Por ejemplo,
considere una enumeración y un tipo de entidad definidos como:

public class Rider


{
public int Id { get; set; }
public EquineBeast Mount { get; set; }
}

public enum EquineBeast


{
Donkey,
Mule,
Horse,
Unicorn
}

A continuación, se pueden definir conversiones en OnModelCreating para almacenar los valores de enumeración
como cadenas (por ejemplo, "Donkey", "Mule",...) en la base de datos:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion(
v => [Link](),
v => (EquineBeast)[Link](typeof(EquineBeast), v));
}

NOTE
Un valor null nunca se pasará a un convertidor de valores. Esto hace que la implementación de conversiones sea más
sencilla y permite que se compartan entre propiedades que aceptan valores NULL y que no aceptan valores NULL.

La clase ValueConverter
Al llamar a HasConversion como se muestra anteriormente, se creará una instancia de ValueConverter y se
establecerá en la propiedad. En su lugar, se puede crear el ValueConverter explícitamente. Por ejemplo:

var converter = new ValueConverter<EquineBeast, string>(


v => [Link](),
v => (EquineBeast)[Link](typeof(EquineBeast), v));

modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion(converter);

Esto puede ser útil cuando varias propiedades usan la misma conversión.

NOTE
Actualmente no hay ninguna manera de especificar en un lugar que cada propiedad de un tipo determinado debe usar el
mismo convertidor de valores. Esta característica se considerará para futuras versiones.

Convertidores integrados
EF Core incluye un conjunto de clases de ValueConverter predefinidas, que se encuentran en el espacio de
nombres [Link] . Dichos componentes son:
BoolToZeroOneConverter -bool a cero y uno
BoolToStringConverter -bool a cadenas como "Y" y "N"
BoolToTwoValuesConverter -bool a dos valores cualesquiera
matriz de bytes de BytesToStringConverter a una cadena codificada en Base64
conversiones de CastingConverter que requieren solo una conversión de tipos
CharToStringConverter -char a una cadena de un solo carácter
DateTimeOffsetToBinaryConverter -DateTimeOffset a un valor codificado en binario 64 bits
DateTimeOffsetToBytesConverter -DateTimeOffset a la matriz de bytes
DateTimeOffsetToStringConverter -DateTimeOffset a cadena
DateTimeToBinaryConverter : DateTime al valor de 64 bits, incluido DateTimeKind
DateTimeToStringConverter : fecha y hora en cadena
DateTimeToTicksConverter : fecha y hora en pasos
EnumToNumberConverter -enum al número subyacente
EnumToStringConverter -enum a cadena
GuidToBytesConverter -GUID a una matriz de bytes
GuidToStringConverter -GUID a cadena
NumberToBytesConverter : cualquier valor numérico en una matriz de bytes
NumberToStringConverter : cualquier valor numérico a cadena
StringToBytesConverter : cadena en bytes UTF8
TimeSpanToStringConverter -TimeSpan a String
TimeSpanToTicksConverter -TimeSpan a ticks

Observe que EnumToStringConverter se incluye en esta lista. Esto significa que no es necesario especificar la
conversión explícitamente, como se muestra anteriormente. En su lugar, use simplemente el convertidor
integrado:

var converter = new EnumToStringConverter<EquineBeast>();

modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion(converter);

Tenga en cuenta que todos los convertidores integrados no tienen estado y, por tanto, una sola instancia puede
compartirse de forma segura con varias propiedades.

Conversiones predefinidas
En el caso de las conversiones comunes para las que existe un convertidor integrado, no es necesario especificar
el convertidor explícitamente. En su lugar, solo tiene que configurar el tipo de proveedor que se debe usar y EF
usará automáticamente el convertidor integrado adecuado. La enumeración de las conversiones de cadenas se
usa como ejemplo anterior, pero EF lo hará automáticamente si se configura el tipo de proveedor:

modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion<string>();

Lo mismo se puede lograr especificando explícitamente el tipo de columna. Por ejemplo, si el tipo de entidad se
define de la manera siguiente:

public class Rider


{
public int Id { get; set; }

[Column(TypeName = "nvarchar(24)")]
public EquineBeast Mount { get; set; }
}

A continuación, los valores de enumeración se guardarán como cadenas en la base de datos sin ninguna otra
configuración en OnModelCreating .

Limitaciones
Hay algunas limitaciones actuales conocidas del sistema de conversión de valores:
Como se indicó anteriormente, no se puede convertir null .
Actualmente no hay ninguna manera de distribuir una conversión de una propiedad a varias columnas o
viceversa.
El uso de conversiones de valores puede afectar a la capacidad de EF Core para traducir expresiones a SQL. Se
registrará una advertencia para dichos casos. La eliminación de estas limitaciones se está considerando en una
versión futura.
Propagación de datos
11/03/2020 • 7 minutes to read

La propagación de datos es el proceso de rellenar una base de datos con un conjunto inicial de datos.
Hay varias maneras de lograrlo en EF Core:
Datos de inicialización del modelo
Personalización de la migración manual
Lógica de inicialización personalizada

Datos de inicialización del modelo


NOTE
Esta característica es nueva en EF Core 2.1.

A diferencia de EF6, en EF Core, la propagación de datos se puede asociar a un tipo de entidad como parte de la
configuración del modelo. A continuación, las migraciones de EF Core pueden calcular automáticamente las
operaciones de inserción, actualización o eliminación que se deben aplicar al actualizar la base de datos a una
nueva versión del modelo.

NOTE
Las migraciones solo tienen en cuenta los cambios del modelo al determinar qué operación se debe realizar para obtener los
datos de inicialización en el estado deseado. Por lo tanto, es posible que se pierdan los cambios realizados en los datos fuera
de las migraciones o se produzca un error.

Por ejemplo, se configurarán los datos de inicialización de una Blog en OnModelCreating :

[Link]<Blog>().HasData(new Blog {BlogId = 1, Url = "[Link]

Para agregar entidades que tienen una relación, es necesario especificar los valores de clave externa:

[Link]<Post>().HasData(
new Post() { BlogId = 1, PostId = 1, Title = "First post", Content = "Test 1" });

Si el tipo de entidad tiene propiedades en el estado de sombra, se puede usar una clase anónima para
proporcionar los valores:

[Link]<Post>().HasData(
new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });

Los tipos de entidad de propiedad se pueden inicializar de una manera similar:


[Link]<Post>().OwnsOne(p => [Link]).HasData(
new { PostId = 1, First = "Andriy", Last = "Svyryd" },
new { PostId = 2, First = "Diego", Last = "Vega" });

Vea el proyecto de ejemplo completo para obtener más contexto.


Una vez que se han agregado los datos al modelo, se deben usar las migraciones para aplicar los cambios.

TIP
Si necesita aplicar las migraciones como parte de una implementación automatizada, puede crear un script SQL que se
pueda obtener como vista previa antes de la ejecución.

Como alternativa, puede usar [Link]() para crear una nueva base de datos que contenga
los datos de inicialización, por ejemplo, para una base de datos de prueba o cuando se usa el proveedor en
memoria o cualquier base de datos que no sea de relación. Tenga en cuenta que si la base de datos ya existe,
EnsureCreated() no actualizará el esquema ni los datos de inicialización en la base de datos. En el caso de las
bases de datos relacionales, no debe llamar a EnsureCreated() si tiene previsto usar las migraciones.
Limitaciones de los datos de inicialización del modelo
Este tipo de datos de inicialización se administra mediante migraciones y el script para actualizar los datos que ya
están en la base de datos debe generarse sin necesidad de conectarse a la base de datos. Esto impone algunas
restricciones:
El valor de clave principal debe especificarse incluso si la base de datos lo genera normalmente. Se usará para
detectar los cambios de datos entre las migraciones.
Los datos previamente inicializados se quitarán si se cambia la clave principal de cualquier manera.
Por lo tanto, esta característica es muy útil para los datos estáticos que no se espera que cambien fuera de las
migraciones y no depende de nada más en la base de datos, por ejemplo códigos postales.
Si el escenario incluye alguno de los siguientes, se recomienda usar la lógica de inicialización personalizada que se
describe en la última sección:
Datos temporales para pruebas
Datos que dependen del estado de la base de datos
Datos que necesitan que la base de datos genere valores clave, incluidas las entidades que usan claves
alternativas como identidad.
Datos que requieren una transformación personalizada (que no se controlan mediante conversiones de
valores), como algunas operaciones hash de contraseñas.
Datos que requieren llamadas a la API externa, como [Link] Core roles de identidad y la creación de usuarios

Personalización de la migración manual


Cuando se agrega una migración, los cambios en los datos especificados con HasData se transforman en llamadas
a InsertData() , UpdateData() y DeleteData() . Una manera de resolver algunas de las limitaciones de HasData es
agregar manualmente estas llamadas o operaciones personalizadas a la migración.

[Link](
table: "Blogs",
columns: new[] { "Url" },
values: new object[] { "[Link] });
Lógica de inicialización personalizada
Una manera sencilla y eficaz de realizar la propagación de datos es usar [Link]() antes de que la
lógica de la aplicación principal comience la ejecución.

using (var context = new DataSeedingContext())


{
[Link]();

var testBlog = [Link](b => [Link] == "[Link]


if (testBlog == null)
{
[Link](new Blog { Url = "[Link] });
}
[Link]();
}

WARNING
El código de propagación no debe formar parte de la ejecución normal de la aplicación, ya que esto puede provocar
problemas de simultaneidad cuando se ejecutan varias instancias y también requeriría que la aplicación tuviera permiso para
modificar el esquema de la base de datos.

En función de las restricciones de la implementación, el código de inicialización se puede ejecutar de diferentes


maneras:
Ejecución local de la aplicación de inicialización
Implementar la aplicación de inicialización con la aplicación principal, invocar la rutina de inicialización y
deshabilitar o quitar la aplicación de inicialización.
Normalmente, esto se puede automatizar mediante el uso de perfiles de publicación.
Tipos de entidad con constructores
11/03/2020 • 11 minutes to read

NOTE
Esta característica es nueva en EF Core 2.1.

A partir de EF Core 2,1, ahora es posible definir un constructor con parámetros y EF Core llamar a este constructor
al crear una instancia de la entidad. Los parámetros de constructor se pueden enlazar a propiedades asignadas o a
varios tipos de servicios para facilitar comportamientos como la carga diferida.

NOTE
A partir de EF Core 2,1, todos los enlaces de constructor son por Convención. La configuración de constructores específicos
que se va a usar está planeada para una versión futura.

Enlazar a propiedades asignadas


Considere un modelo típico de blog o post:

public class Blog


{
public int Id { get; set; }

public string Name { get; set; }


public string Author { get; set; }

public ICollection<Post> Posts { get; } = new List<Post>();


}

public class Post


{
public int Id { get; set; }

public string Title { get; set; }


public string Content { get; set; }
public DateTime PostedOn { get; set; }

public Blog Blog { get; set; }


}

Cuando EF Core crea instancias de estos tipos, como los resultados de una consulta, primero llamará al
constructor sin parámetros predeterminado y, a continuación, establecerá cada propiedad en el valor de la base de
datos. Sin embargo, si EF Core encuentra un constructor con parámetros con nombres de parámetros y tipos que
coinciden con los de propiedades asignadas, se llamará en su lugar al constructor con parámetros con valores
para esas propiedades y no establecerá cada propiedad explícitamente. Por ejemplo:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}

public int Id { get; set; }

public string Name { get; set; }


public string Author { get; set; }

public ICollection<Post> Posts { get; } = new List<Post>();


}

public class Post


{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}

public int Id { get; set; }

public string Title { get; set; }


public string Content { get; set; }
public DateTime PostedOn { get; set; }

public Blog Blog { get; set; }


}

Cosas que tener en cuenta:


No todas las propiedades deben tener parámetros de constructor. Por ejemplo, la propiedad post. Content no
está establecida por ningún parámetro de constructor, por lo que EF Core la establecerá después de llamar al
constructor de la manera normal.
Los nombres y tipos de parámetros deben coincidir con los nombres y tipos de propiedad, con la excepción de
que las propiedades pueden tener mayúsculas y minúsculas Pascal, mientras que los parámetros tienen
mayúsculas y minúsculas Camel.
EF Core no pueden establecer las propiedades de navegación (como blog o publicaciones anteriores) mediante
un constructor.
El constructor puede ser público, privado o tener cualquier otra accesibilidad. Sin embargo, los proxies de
carga diferida requieren que el constructor sea accesible desde la clase de proxy heredada. Normalmente esto
significa que se hace público o protegido.
Propiedades de solo lectura
Una vez que las propiedades se establecen mediante el constructor, puede tener sentido que algunas de ellas sean
de solo lectura. EF Core es compatible con esto, pero hay algunas cosas que debe buscar:
Las propiedades sin establecedores no se asignan por Convención. (Esto tiende a asignar propiedades que no
deben asignarse, como las propiedades calculadas).
El uso de valores de clave generados automáticamente requiere una propiedad de clave que sea de lectura y
escritura, ya que el valor de clave debe establecerse mediante el generador de claves al insertar nuevas
entidades.
Una manera sencilla de evitar estos aspectos es usar establecedores privados. Por ejemplo:
public class Blog
{
public Blog(int id, string name, string author)
{
Id = id;
Name = name;
Author = author;
}

public int Id { get; private set; }

public string Name { get; private set; }


public string Author { get; private set; }

public ICollection<Post> Posts { get; } = new List<Post>();


}

public class Post


{
public Post(int id, string title, DateTime postedOn)
{
Id = id;
Title = title;
PostedOn = postedOn;
}

public int Id { get; private set; }

public string Title { get; private set; }


public string Content { get; set; }
public DateTime PostedOn { get; private set; }

public Blog Blog { get; set; }


}

EF Core ve una propiedad con un establecedor privado como de lectura y escritura, lo que significa que todas las
propiedades se asignan como antes y la clave todavía se puede generar en el almacén.
Una alternativa al uso de establecedores privados es hacer que las propiedades sean realmente de solo lectura y
agregar una asignación más explícita en OnModelCreating. Del mismo modo, algunas propiedades se pueden
quitar por completo y reemplazar solo por campos. Por ejemplo, considere estos tipos de entidad:
public class Blog
{
private int _id;

public Blog(string name, string author)


{
Name = name;
Author = author;
}

public string Name { get; }


public string Author { get; }

public ICollection<Post> Posts { get; } = new List<Post>();


}

public class Post


{
private int _id;

public Post(string title, DateTime postedOn)


{
Title = title;
PostedOn = postedOn;
}

public string Title { get; }


public string Content { get; set; }
public DateTime PostedOn { get; }

public Blog Blog { get; set; }


}

Y esta configuración en OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>(
b =>
{
[Link]("_id");
[Link](e => [Link]);
[Link](e => [Link]);
});

[Link]<Post>(
b =>
{
[Link]("_id");
[Link](e => [Link]);
[Link](e => [Link]);
});
}

Cosas que hay que tener en cuenta:


La clave "propiedad" es ahora un campo. No se trata de un campo de readonly para que se puedan usar las
claves generadas por el almacén.
Las demás propiedades son propiedades de solo lectura establecidas solo en el constructor.
Si el valor de la clave principal solo se establece en EF o se lee de la base de datos, no es necesario incluirlo en
el constructor. Esto deja la clave "Property" como un campo simple y deja claro que no se debe establecer
explícitamente al crear nuevos blogs o publicaciones.
NOTE
Este código producirá una advertencia del compilador ' 169 ' que indica que el campo nunca se utiliza. Esto puede pasarse
por alto, ya que en realidad EF Core está utilizando el campo de forma extralingüística.

Insertar servicios
EF Core también puede insertar "servicios" en el constructor de un tipo de entidad. Por ejemplo, se puede insertar
lo siguiente:
DbContext : la instancia de contexto actual, que también se puede escribir como el tipo de DbContext derivado.
ILazyLoader -el servicio de carga diferida, consulte la documentación sobre la carga diferida para obtener más
detalles.
Action<object, string> : un delegado de carga diferida; consulte la documentación sobre la carga diferida para
obtener más detalles.
IEntityType : los metadatos de EF Core asociados a este tipo de entidad

NOTE
A partir de EF Core 2,1, solo se pueden insertar los servicios conocidos por EF Core. Se está considerando la compatibilidad
con la inserción de servicios de aplicación en una versión futura.

Por ejemplo, un DbContext insertado se puede usar para tener acceso de forma selectiva a la base de datos para
obtener información sobre las entidades relacionadas sin cargarlas todas. En el ejemplo siguiente, se usa para
obtener el número de publicaciones en un blog sin cargar las entradas:
public class Blog
{
public Blog()
{
}

private Blog(BloggingContext context)


{
Context = context;
}

private BloggingContext Context { get; set; }

public int Id { get; set; }


public string Name { get; set; }
public string Author { get; set; }

public ICollection<Post> Posts { get; set; }

public int PostsCount


=> Posts?.Count
?? Context?.Set<Post>().Count(p => Id == [Link]<int?>(p, "BlogId"))
?? 0;
}

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime PostedOn { get; set; }

public Blog Blog { get; set; }


}

Algunos aspectos que se deben tener en cuenta:


El constructor es privado, ya que nunca lo llama EF Core, y hay otro constructor público para uso general.
El código que usa el servicio inyectado (es decir, el contexto) está defensivo con respecto a que se null
controlar los casos en los que EF Core no crea la instancia.
Dado que el servicio se almacena en una propiedad de lectura y escritura, se restablecerá cuando la entidad se
adjunte a una nueva instancia de contexto.

WARNING
Inyectar DbContext como esto se suele considerar un anti-patrón, ya que une los tipos de entidad directamente a EF Core.
Tenga en cuenta todas las opciones antes de usar la inserción de servicios como esta.
División de tablas
11/03/2020 • 3 minutes to read

EF Core permite asignar dos o más entidades a una sola fila. Esto se denomina División de tablas o uso
compartido de tablas.

Configuración
Para usar la división de tablas, los tipos de entidad deben asignarse a la misma tabla, tener las claves principales
asignadas a las mismas columnas y al menos una relación configurada entre la clave principal de un tipo de
entidad y otra en la misma tabla.
Un escenario común para la división de tablas es usar solo un subconjunto de las columnas de la tabla para un
mayor rendimiento o encapsulación.
En este ejemplo Order representa un subconjunto de DetailedOrder .

public class Order


{
public int Id { get; set; }
public OrderStatus? Status { get; set; }
public DetailedOrder DetailedOrder { get; set; }
}

public class DetailedOrder


{
public int Id { get; set; }
public OrderStatus? Status { get; set; }
public string BillingAddress { get; set; }
public string ShippingAddress { get; set; }
public byte[] Version { get; set; }
}

Además de la configuración necesaria, llamamos Property(o => [Link]).HasColumnName("Status") para asignar


[Link] a la misma columna que [Link] .

[Link]<DetailedOrder>(dob =>
{
[Link]("Orders");
[Link](o => [Link]).HasColumnName("Status");
});

[Link]<Order>(ob =>
{
[Link]("Orders");
[Link](o => [Link]).HasColumnName("Status");
[Link](o => [Link]).WithOne()
.HasForeignKey<DetailedOrder>(o => [Link]);
});
TIP
Vea el proyecto de ejemplo completo para obtener más contexto.

Uso
Guardar y consultar entidades mediante la división de tablas se realiza de la misma manera que otras entidades:

using (var context = new TableSplittingContext())


{
[Link]();
[Link]();

[Link](new Order
{
Status = [Link],
DetailedOrder = new DetailedOrder
{
Status = [Link],
ShippingAddress = "221 B Baker St, London",
BillingAddress = "11 Wall Street, New York"
}
});

[Link]();
}

using (var context = new TableSplittingContext())


{
var pendingCount = [Link](o => [Link] == [Link]);
[Link]($"Current number of pending orders: {pendingCount}");
}

using (var context = new TableSplittingContext())


{
var order = [Link](o => [Link] == [Link]);
[Link]($"First pending order will ship to: {[Link]}");
}

Entidad dependiente opcional


NOTE
Esta característica se presentó en EF Core 3,0.

Si todas las columnas utilizadas por una entidad dependiente están NULL en la base de datos, no se creará
ninguna instancia para ella cuando se realice la consulta. Esto permite el modelado de una entidad dependiente
opcional, donde la propiedad Relationship de la entidad de seguridad sería null. Tenga en cuenta que esto también
ocurrirá si todas las propiedades del dependiente son opcionales y se establecen en null , lo que podría no ser el
esperado.

Tokens de simultaneidad
Si alguno de los tipos de entidad que comparten una tabla tiene un token de simultaneidad, también debe
incluirse en todos los demás tipos de entidad. Esto es necesario para evitar un valor de token de simultaneidad
obsoleto cuando solo se actualiza una de las entidades asignadas a la misma tabla.
Para evitar exponer el token de simultaneidad al código utilizado, es posible crear uno como una propiedad de
sombra:

[Link]<Order>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");

[Link]<DetailedOrder>()
.Property(o => [Link]).IsRowVersion().HasColumnName("Version");
Tipos de entidad en propiedad
11/03/2020 • 16 minutes to read

EF Core permite a tipos de entidad del modelo que sólo pueden aparecer en las propiedades de navegación de
otros tipos de entidad. Se denominan tipos de entidad de propiedad. La entidad que contiene un tipo de entidad
propiedad es su propietario.
Las entidades propiedad son esencialmente parte del propietario y no pueden existir sin ella, son
conceptualmente similares a los agregados. Esto significa que la entidad propiedad es por definición en el lado
dependiente de la relación con el propietario.

Configuración explícita
Propiedad de entidad tipos nunca se incluyen por EF Core en el modelo por convención. Puede usar el método
OwnsOne en OnModelCreating o anotar el tipo con OwnedAttribute (novedad en EF Core 2,1) para configurar el
tipo como un tipo de propiedad.
En este ejemplo, StreetAddress es un tipo sin propiedad de identidad. Se usa como propiedad del tipo Order
para especificar la dirección de envío de un pedido en concreto.
Podemos usar el OwnedAttribute para tratarlo como una entidad propiedad cuando se hace referencia a él desde
otro tipo de entidad:

[Owned]
public class StreetAddress
{
public string Street { get; set; }
public string City { get; set; }
}

public class Order


{
public int Id { get; set; }
public StreetAddress ShippingAddress { get; set; }
}

También es posible usar el método OwnsOne de OnModelCreating para especificar que la propiedad
ShippingAddress es una entidad propiedad del tipo de entidad Order y para configurar aspectos adicionales si
es necesario.

[Link]<Order>().OwnsOne(p => [Link]);

Si la propiedad ShippingAddress es privada en el tipo de Order , puede usar la versión de cadena del método
OwnsOne :

[Link]<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");

Vea el proyecto de ejemplo completo para obtener más contexto.


Claves IMPLÍCITAS
Los tipos de propiedad configurados con OwnsOne o detectados a través de una navegación de referencia
siempre tienen una relación uno a uno con el propietario, por lo que no necesitan sus propios valores de clave, ya
que los valores de clave externa son únicos. En el ejemplo anterior, el tipo de StreetAddress no necesita definir
una propiedad de clave.
Para entender cómo EF Core realiza el seguimiento de estos objetos, es útil saber que se crea una clave principal
como una propiedad de sombra para el tipo de propiedad. El valor de la clave de una instancia del tipo de
propiedad será el mismo que el valor de la clave de la instancia de propietario.

Colecciones de tipos de propiedad


NOTE
Esta característica es nueva en EF Core 2.2.

Para configurar una colección de tipos de propiedad, use OwnsMany en OnModelCreating .


Los tipos de propiedad necesitan una clave principal. Si no hay buenas propiedades candidatas en el tipo .NET, EF
Core puede intentar crear una. Sin embargo, cuando los tipos de propiedad se definen a través de una colección,
no basta con crear simplemente una propiedad Shadow que actúe como clave externa en el propietario y la clave
principal de la instancia de propiedad, como hacemos para OwnsOne : puede haber varias instancias de tipo de
propiedad para cada propietario y, por lo tanto, la clave del propietario no es suficiente para proporcionar una
identidad única para cada instancia de
Las dos soluciones más directas son:
Definir una clave principal suplente en una nueva propiedad independiente de la clave externa que señala al
propietario. Los valores contenidos deben ser únicos en todos los propietarios (por ejemplo, si el {1} primario
tiene {1}secundarios, el {2} primario no puede tener {1}secundarios), por lo que el valor no tiene ningún
significado inherente. Dado que la clave externa no forma parte de la clave principal, sus valores se pueden
cambiar, por lo que puede trasladar un elemento secundario de un elemento primario a otro; sin embargo,
esto suele pasar por la semántica de agregado.
Usar la clave externa y una propiedad adicional como clave compuesta. El valor de propiedad adicional ahora
solo debe ser único para un elemento primario determinado (por lo que si el {1} primario tiene {1,1}
secundarios, el {2} principal todavía puede tener {2,1}secundarios). Al convertir la parte de clave externa de la
clave principal en la relación entre el propietario y la entidad propiedad, se convierte en inmutable y se refleja
mejor la semántica de agregado. Esto es lo que EF Core hace de forma predeterminada.
En este ejemplo, usaremos la clase Distributor :

public class Distributor


{
public int Id { get; set; }
public ICollection<StreetAddress> ShippingCenters { get; set; }
}

De forma predeterminada, la clave principal que se usa para el tipo de propiedad al que se hace referencia a
través de la propiedad de navegación de ShippingCenters se ("DistributorId", "Id") donde "DistributorId"
es el FK y "Id" es un valor de int único.
Para configurar una llamada PK diferente HasKey :
[Link]<Distributor>().OwnsMany(p => [Link], a =>
{
[Link]().HasForeignKey("OwnerId");
[Link]<int>("Id");
[Link]("Id");
});

NOTE
Antes de que el método EF Core 3,0 WithOwner() no existiera, se debe quitar esta llamada. Además, la clave principal no
se detectó automáticamente, por lo que siempre se especificó.

Asignar tipos de propiedad con división de tabla


Cuando se usan bases de datos relacionales, de forma predeterminada, los tipos de propiedad de propiedad se
asignan a la misma tabla que el propietario. Esto requiere dividir la tabla en dos: se usarán algunas columnas
para almacenar los datos del propietario, y algunas columnas se utilizarán para almacenar los datos de la entidad
propiedad. Se trata de una característica común conocida como División de tablas.
De forma predeterminada, EF Core asignará el nombre a las columnas de la base de datos para las propiedades
del tipo de entidad propiedad que sigue al patrón Navigation_OwnedEntityProperty. Por lo tanto, las propiedades
de StreetAddress aparecerán en la tabla ' Orders ' con los nombres ' ShippingAddress_Street ' y '
ShippingAddress_City '.
Puede usar el método HasColumnName para cambiar el nombre de esas columnas:

[Link]<Order>().OwnsOne(
o => [Link],
sa =>
{
[Link](p => [Link]).HasColumnName("ShipsToStreet");
[Link](p => [Link]).HasColumnName("ShipsToCity");
});

NOTE
La mayoría de los métodos de configuración de tipo de entidad normales, como Ignore , se pueden llamar de la misma
manera.

Compartir el mismo tipo .NET entre varios tipos de propiedad


Un tipo de entidad de propiedad puede ser del mismo tipo de .NET que otro tipo de entidad de propiedad, por lo
que es posible que el tipo .NET no sea suficiente para identificar un tipo de propiedad.
En esos casos, la propiedad que apunta del propietario a la entidad propiedad se convierte en la navegación de
definición del tipo de entidad de propiedad. Desde la perspectiva del EF Core, la definición de la navegación es
parte de la identidad del tipo junto con el tipo de .NET.
Por ejemplo, en la siguiente clase ShippingAddress y BillingAddress son ambos del mismo tipo .NET,
StreetAddress :
public class OrderDetails
{
public DetailedOrder Order { get; set; }
public StreetAddress BillingAddress { get; set; }
public StreetAddress ShippingAddress { get; set; }
}

Para entender cómo EF Core distinguirá las instancias de de estos objetos de las que se ha realizado un
seguimiento, puede ser útil pensar que la navegación que define se ha convertido en parte de la clave de la
instancia junto con el valor de la clave del propietario y el tipo .NET del tipo de propiedad.

Tipos de propiedad anidados


En este ejemplo OrderDetails posee BillingAddress y ShippingAddress , que son tipos de StreetAddress . Luego,
OrderDetails es propiedad del tipo DetailedOrder .

public class DetailedOrder


{
public int Id { get; set; }
public OrderDetails OrderDetails { get; set; }
public OrderStatus Status { get; set; }
}

public enum OrderStatus


{
Pending,
Shipped
}

Cada navegación a un tipo de propiedad define un tipo de entidad independiente con una configuración
completamente independiente.
Además de los tipos de propiedad anidados, un tipo de propiedad puede hacer referencia a una entidad normal,
puede ser el propietario o una entidad distinta, siempre y cuando la entidad propiedad esté en el lado
dependiente. Esta funcionalidad establece tipos de entidad de propiedad además de tipos complejos en EF6.

public class OrderDetails


{
public DetailedOrder Order { get; set; }
public StreetAddress BillingAddress { get; set; }
public StreetAddress ShippingAddress { get; set; }
}

Es posible encadenar el método OwnsOne en una llamada fluida para configurar este modelo:

[Link]<DetailedOrder>().OwnsOne(p => [Link], od =>


{
[Link](d => [Link]);
[Link](c => [Link]);
[Link](c => [Link]);
});

Observe la llamada WithOwner utilizada para configurar la propiedad de navegación que señala hacia atrás en el
propietario. Para configurar una navegación al tipo de entidad Owner que no forma parte de la relación de
propiedad WithOwner() se debe llamar a sin ningún argumento.
Es posible lograr el resultado mediante OwnedAttribute en OrderDetails y StreetAddress .

Almacenar tipos de propiedad en tablas independientes


Además, a diferencia de los tipos complejos de EF6, los tipos de propiedad se pueden almacenar en una tabla
independiente del propietario. Para invalidar la Convención que asigna un tipo de propiedad a la misma tabla
que el propietario, puede llamar simplemente a ToTable y proporcionar un nombre de tabla diferente. En el
ejemplo siguiente se asignará OrderDetails y sus dos direcciones a una tabla independiente de DetailedOrder :

[Link]<DetailedOrder>().OwnsOne(p => [Link], od =>


{
[Link]("OrderDetails");
});

También es posible usar el TableAttribute para lograr esto, pero tenga en cuenta que esto produciría un error si
hay varias navegaciones al tipo de propiedad, ya que en ese caso se asignarían varios tipos de entidad a la misma
tabla.

Consultar tipos de propiedad


Al consultar al propietario, los tipos de propiedad se incluyen de forma predeterminada. No es necesario utilizar
el método Include , incluso si los tipos de propiedad se almacenan en una tabla independiente. En función del
modelo descrito anteriormente, la siguiente consulta obtendrá Order , OrderDetails y los dos StreetAddresses
de propiedad de la base de datos:

var order = [Link](o => [Link] == [Link]);


[Link]($"First pending order will ship to: {[Link]}");

Limitaciones
Algunas de estas limitaciones son fundamentales para el funcionamiento de los tipos de entidad de propiedad,
pero otras son restricciones que podríamos ser capaces de quitar en versiones futuras:
Restricciones por diseño
No se puede crear un DbSet<T> para un tipo de propiedad
No se puede llamar a Entity<T>() con un tipo de propiedad en ModelBuilder

Deficiencias actuales
Los tipos de entidad de propiedad no pueden tener jerarquías de herencia
Las navegaciones de referencia a tipos de entidad de propiedad no pueden ser null a menos que se asignen
explícitamente a una tabla independiente del propietario.
Los distintos propietarios no pueden compartir instancias de tipos de entidad con propiedad (este es un
escenario conocido para objetos de valor que no se pueden implementar mediante tipos de entidad de
propiedad)
Deficiencias en versiones anteriores
En EF Core 2,0, las navegaciones a tipos de entidad de propiedad no se pueden declarar en tipos de entidad
derivadas a menos que las entidades de propiedad se asignen explícitamente a una tabla independiente de la
jerarquía de propietarios. Esta limitación se ha eliminado en EF Core 2,1
En EF Core 2,0 y 2,1 solo se admiten las navegaciones de referencia a los tipos de propiedad. Esta limitación se
ha eliminado en EF Core 2,2
Tipos de entidad sin llave
11/03/2020 • 7 minutes to read

NOTE
Esta característica se agregó en EF Core 2,1 bajo el nombre de los tipos de consulta. En EF Core 3,0 se cambió el nombre
del concepto a tipos de entidad sin entrada.

Además de los tipos de entidad normales, un modelo de EF Core puede contener _tipos de entidad_sin clave, que
se pueden usar para realizar consultas de base de datos con datos que no contengan valores de clave.

Características de tipos de entidad sin llave


Los tipos de entidad sin llave admiten muchas de las mismas capacidades de asignación que los tipos de entidad
normales, como las propiedades de navegación y asignación de herencia. En almacenes relacionales, pueden
configurar los objetos de base de datos de destino y las columnas a través de métodos de la API fluidos o las
anotaciones de datos.
Sin embargo, son diferentes de los tipos de entidad normales en que:
No se puede definir una clave.
Nunca se realiza un seguimiento de los cambios en DbContext y, por lo tanto, nunca se insertan, actualizan o
eliminan en la base de datos.
Nunca se detectan por convención.
Solo admite un subconjunto de capacidades de asignación de navegación, en concreto:
Nunca pueden actuar como el extremo principal de una relación.
Puede que no tengan navegaciones a entidades propiedad
Solo pueden contener propiedades de navegación de referencia que apunten a entidades normales.
Las entidades no pueden contener propiedades de navegación a tipos de entidad sin llave.
Debe configurarse con .HasNoKey() llamada al método.
Se puede asignar a una consulta de definición. Una consulta de definición es una consulta declarada en el
modelo que actúa como origen de datos para un tipo de entidad sin entrada.

Escenarios de uso
Algunos de los escenarios de uso principales de los tipos de entidad sin llave son:
Actúa como el tipo de valor devuelto para las consultas SQL sin procesar.
Asignación a vistas de base de datos que no contienen una clave principal.
Asignación de tablas que no tiene definida una clave principal.
Asignación de las consultas definidas en el modelo.

Asignación de objetos de base de datos


La asignación de un tipo de entidad sin llave a un objeto de base de datos se consigue mediante el ToTable o
ToView API fluida. Desde la perspectiva de EF Core, el objeto de base de datos especificado en este método es
una vista, lo que significa que se trata como un origen de consulta de solo lectura y no puede ser el destino de
las operaciones de actualización, inserción o eliminación. Sin embargo, esto no significa que el objeto de base de
datos sea realmente necesario para ser una vista de base de datos. Como alternativa, puede tratarse de una tabla
de base de datos que se tratará como de solo lectura. Por el contrario, en el caso de los tipos de entidad
normales, EF Core supone que un objeto de base de datos especificado en el método ToTable se puede tratar
como una tabla, lo que significa que se puede usar como origen de la consulta, pero también como destino de
las operaciones de actualización, eliminación e inserción. De hecho, puede especificar el nombre de una vista de
base de datos en ToTable y todo debería funcionar bien siempre que la vista esté configurada para ser
actualizable en la base de datos.

NOTE
ToView supone que el objeto ya existe en la base de datos y que no lo crearán las migraciones.

Ejemplo
En el ejemplo siguiente se muestra cómo utilizar los tipos de entidad sin entrada para consultar una vista de
base de datos.

TIP
Puede ver un ejemplo de este artículo en GitHub.

En primer lugar, definimos un modelo sencillo de Blog y Post:

public class Blog


{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public ICollection<Post> Posts { get; set; }
}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
}

A continuación, definimos una vista de base de datos simple que nos permitirá consultar el número de entradas
vinculadas a cada blog:

[Link](
@"CREATE VIEW View_BlogPostCounts AS
SELECT [Link], Count([Link]) as PostCount
FROM Blogs b
JOIN Posts p on [Link] = [Link]
GROUP BY [Link]");

A continuación, definimos una clase para contener el resultado de la vista de base de datos:
public class BlogPostsCount
{
public string BlogName { get; set; }
public int PostCount { get; set; }
}

A continuación, configuraremos el tipo de entidad sin llave en OnModelCreating con la API de HasNoKey .
Usamos la API de configuración fluida para configurar la asignación para el tipo de entidad sin llave:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<BlogPostsCount>(eb =>
{
[Link]();
[Link]("View_BlogPostCounts");
[Link](v => [Link]).HasColumnName("Name");
});
}

A continuación, configuramos el DbContext para incluir el DbSet<T> :

public DbSet<BlogPostsCount> BlogPostCounts { get; set; }

Por último, podemos consultar la vista de base de datos de la manera estándar:

var postCounts = [Link]();

foreach (var postCount in postCounts)


{
[Link]($"{[Link]} has {[Link]} posts.");
[Link]();
}

TIP
Nota también hemos definido una propiedad de consulta de nivel de contexto (DbSet) para que actúe como raíz para las
consultas en este tipo.
Alternar entre varios modelos con el mismo tipo
DbContext
11/03/2020 • 2 minutes to read

El modelo integrado OnModelCreating puede utilizar una propiedad en el contexto para cambiar la forma en que se
compila el modelo. Por ejemplo, supongamos que desea configurar una entidad de forma diferente en función de
alguna propiedad:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
if (UseIntProperty)
{
[Link]<ConfigurableEntity>().Ignore(e => [Link]);
}
else
{
[Link]<ConfigurableEntity>().Ignore(e => [Link]);
}
}

Desafortunadamente, este código no funcionaría tal cual, ya que EF crea el modelo y se ejecuta OnModelCreating
una sola vez, con lo que se almacena en caché el resultado por motivos de rendimiento. Sin embargo, puede
enlazar con el mecanismo de almacenamiento en caché del modelo para que EF tenga en cuenta la propiedad que
genera distintos modelos.

IModelCacheKeyFactory
EF utiliza el IModelCacheKeyFactory para generar claves de caché para los modelos; de forma predeterminada, EF
supone que para un tipo de contexto dado, el modelo será el mismo, por lo que la implementación predeterminada
de este servicio devuelve una clave que solo contiene el tipo de contexto. Para generar diferentes modelos a partir
del mismo tipo de contexto, debe reemplazar el servicio IModelCacheKeyFactory con la implementación correcta. la
clave generada se comparará con otras claves del modelo mediante el método Equals , teniendo en cuenta todas
las variables que afectan al modelo:
La siguiente implementación tiene en cuenta el IgnoreIntProperty al generar una clave de caché del modelo:

public class DynamicModelCacheKeyFactory : IModelCacheKeyFactory


{
public object Create(DbContext context)
=> context is DynamicContext dynamicContext
? ([Link](), [Link])
: (object)[Link]();
}

Por último, registre la nueva IModelCacheKeyFactory en el OnConfiguring de contexto:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.UseInMemoryDatabase("DynamicContext")
.ReplaceService<IModelCacheKeyFactory, DynamicModelCacheKeyFactory>();
Vea el proyecto de ejemplo completo para obtener más contexto.
Datos espaciales
11/03/2020 • 14 minutes to read

NOTE
Esta característica se agregó en EF Core 2,2.

Los datos espaciales representan la ubicación física y la forma de los objetos. Muchas bases de datos proporcionan
compatibilidad con este tipo de datos, por lo que se puede indizar y consultar junto con otros datos. Entre los
escenarios comunes se incluyen las consultas de objetos dentro de una distancia determinada desde una ubicación
o la selección del objeto cuyo borde contiene una ubicación determinada. EF Core admite la asignación a tipos de
datos espaciales mediante la biblioteca espacial NetTopologySuite .

Instalación
Para usar los datos espaciales con EF Core, debe instalar el paquete NuGet de soporte adecuado. El paquete que
necesita instalar depende del proveedor que esté usando.

P RO VEEDO R DE EF C O RE PA Q UET E DE N UGET ESPA C IA L

[Link] Microsoft. EntityFrameworkCore. SqlServer. NetTopologySuite

[Link] Microsoft. EntityFrameworkCore. SQLite. NetTopologySuite

[Link] NetTopologySuite

[Link] Npgsql. EntityFrameworkCore. PostgreSQL. NetTopologySuite

Ingeniería inversa
Los paquetes de NuGet espaciales también habilitan los modelos de ingeniería inversa con propiedades espaciales,
pero debe instalar el paquete antes de ejecutar Scaffold-DbContext o dotnet ef dbcontext scaffold . Si no lo hace,
recibirá advertencias sobre cómo no encontrar las asignaciones de tipos para las columnas y se omitirán las
columnas.

NetTopologySuite (NTS)
NetTopologySuite es una biblioteca espacial para .NET. EF Core permite la asignación a tipos de datos espaciales en
la base de datos mediante el uso de tipos NTS en el modelo.
Para habilitar la asignación a tipos espaciales a través de NTS, llame al método UseNetTopologySuite en el
generador de opciones DbContext del proveedor. Por ejemplo, con SQL Server le llamaría como esto.

[Link](
@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters",
x => [Link]());

Hay varios tipos de datos espaciales. El tipo que use dependerá de los tipos de formas que desee permitir. Esta es la
jerarquía de tipos NTS que puede usar para las propiedades del modelo. Están ubicados en el espacio de nombres
[Link] .
Geometría
Punto
LineString
Polygon
GeometryCollection
MultiPoint
MultiLineString
MultiPolygon

WARNING
La CircularString, CompoundCurve y CurePolygon no son compatibles con NTS.

El uso del tipo de geometría base permite que la propiedad especifique cualquier tipo de forma.
Las siguientes clases de entidad se pueden usar para asignar tablas en la base de datos de ejemplo Wide World
Importers.

[Table("Cities", Schema = "Application"))]


class City
{
public int CityID { get; set; }

public string CityName { get; set; }

public Point Location { get; set; }


}

[Table("Countries", Schema = "Application"))]


class Country
{
public int CountryID { get; set; }

public string CountryName { get; set; }

// Database includes both Polygon and MultiPolygon values


public Geometry Border { get; set; }
}

Crear valores
Puede usar constructores para crear objetos Geometry; sin embargo, NTS recomienda el uso de un generador de
geometría en su lugar. Esto le permite especificar un valor predeterminado de SRID (el sistema de referencia
espacial que usan las coordenadas) y le proporciona el control sobre aspectos más avanzados, como el modelo de
precisión (usado durante los cálculos) y la secuencia de coordenadas (determina las coordenadas: dimensiones). y
las medidas--están disponibles).

var geometryFactory = [Link](srid: 4326);


var currentLocation = [Link](-122.121512, 47.6739882);

NOTE
4326 hace referencia a WGS 84, un estándar que se usa en GPS y en otros sistemas geográficos.
Longitud y latitud
Las coordenadas en NTS están en términos de valores X e y. Para representar la longitud y la latitud, use X para
longitud e y para latitud. Tenga en cuenta que esto es hacia atrás desde el formato de latitude, longitude en el
que normalmente se ven estos valores.
SRID omitido durante las operaciones de cliente
NTS omite los valores de SRID durante las operaciones. Supone un sistema de coordenadas plano. Esto significa
que si especifica coordenadas en términos de longitud y latitud, algunos valores evaluados por el cliente como la
distancia, la longitud y el área estarán en grados, no en metros. Para valores más significativos, primero debe
proyectar las coordenadas en otro sistema de coordenadas mediante una biblioteca como ProjNet4GeoAPI antes
de calcular estos valores.
Si una operación es evaluada por el servidor mediante EF Core a través de SQL, la unidad del resultado se
determinará por la base de datos.
Este es un ejemplo del uso de ProjNet4GeoAPI para calcular la distancia entre dos ciudades.

static class GeometryExtensions


{
static readonly CoordinateSystemServices _coordinateSystemServices
= new CoordinateSystemServices(
new CoordinateSystemFactory(),
new CoordinateTransformationFactory(),
new Dictionary<int, string>
{
// Coordinate systems:

[4326] = [Link],

// This coordinate system covers the area of our data.


// Different data requires a different coordinate system.
[2855] =
@"
PROJCS[""NAD83(HARN) / Washington North"",
GEOGCS[""NAD83(HARN)"",
DATUM[""NAD83_High_Accuracy_Regional_Network"",
SPHEROID[""GRS 1980"",6378137,298.257222101,
AUTHORITY[""EPSG"",""7019""]],
AUTHORITY[""EPSG"",""6152""]],
PRIMEM[""Greenwich"",0,
AUTHORITY[""EPSG"",""8901""]],
UNIT[""degree"",0.01745329251994328,
AUTHORITY[""EPSG"",""9122""]],
AUTHORITY[""EPSG"",""4152""]],
PROJECTION[""Lambert_Conformal_Conic_2SP""],
PARAMETER[""standard_parallel_1"",48.73333333333333],
PARAMETER[""standard_parallel_2"",47.5],
PARAMETER[""latitude_of_origin"",47],
PARAMETER[""central_meridian"",-120.8333333333333],
PARAMETER[""false_easting"",500000],
PARAMETER[""false_northing"",0],
UNIT[""metre"",1,
AUTHORITY[""EPSG"",""9001""]],
AUTHORITY[""EPSG"",""2855""]]
"
});

public static Geometry ProjectTo(this Geometry geometry, int srid)


{
var transformation = _coordinateSystemServices.CreateTransformation([Link], srid);

var result = [Link]();


[Link](new MathTransformFilter([Link]));

return result;
return result;
}

class MathTransformFilter : ICoordinateSequenceFilter


{
readonly MathTransform _transform;

public MathTransformFilter(MathTransform transform)


=> _transform = transform;

public bool Done => false;


public bool GeometryChanged => true;

public void Filter(CoordinateSequence seq, int i)


{
var result = _transform.Transform(
new[]
{
[Link](i, Ordinate.X),
[Link](i, Ordinate.Y)
});
[Link](i, Ordinate.X, result[0]);
[Link](i, Ordinate.Y, result[1]);
}
}
}

var seattle = new Point(-122.333056, 47.609722) { SRID = 4326 };


var redmond = new Point(-122.123889, 47.669444) { SRID = 4326 };

var distance = [Link](2855).Distance([Link](2855));

Consulta de datos
En LINQ, los métodos y las propiedades NTS disponibles como funciones de base de datos se traducirán a SQL. Por
ejemplo, los métodos Distance y Contains se traducen en las siguientes consultas. En la tabla al final de este
artículo se muestran los miembros que son compatibles con varios proveedores de EF Core.

var nearestCity = [Link]


.OrderBy(c => [Link](currentLocation))
.FirstOrDefault();

var currentCountry = [Link]


.FirstOrDefault(c => [Link](currentLocation));

SQL Server
Si usa SQL Server, hay algunos aspectos adicionales que debe tener en cuenta.
Geografía o geometría
De forma predeterminada, las propiedades espaciales se asignan a geography columnas en SQL Server. Para usar
geometry , Configure el tipo de columna en el modelo.

Anillos de polígono de geografía


Cuando se usa el tipo de columna geography , SQL Server impone requisitos adicionales en el anillo exterior (o
shell) y los anillos interiores (o agujeros). El anillo exterior debe estar orientado en sentido contrario a las agujas
del reloj y los anillos interiores hacia la derecha. NTS valida esto antes de enviar los valores a la base de datos.
FullGlobe
SQL Server tiene un tipo de geometría no estándar para representar todo el globo terráqueo cuando se usa el tipo
de columna geography . También tiene una forma de representar polígonos basados en el globo completo (sin un
anillo exterior). Ninguno de ellos es compatible con NTS.

WARNING
Los FullGlobe y los polígonos basados en ellos no son compatibles con NTS.

SQLite
A continuación se muestra información adicional para los usuarios que usan SQLite.
Instalación de SpatiaLite
En Windows, la biblioteca de mod_spatialite nativa se distribuye como una dependencia del paquete NuGet. Otras
plataformas deben instalarse por separado. Esto se suele hacer mediante un administrador de paquetes de
software. Por ejemplo, puede usar APT en Ubuntu y homebrew en MacOS.

# Ubuntu
apt-get install libsqlite3-mod-spatialite

# macOS
brew install libspatialite

Desafortunadamente, las versiones más recientes de PROJ (una dependencia de SpatiaLite) son incompatibles con
el paquete SQLitePCLRawpredeterminado de EF. Para solucionar este fin, puede crear un proveedor de
SQLitePCLRaw personalizado que use la biblioteca de SQLite del sistema, o bien puede instalar una compilación
personalizada de SpatiaLite deshabilitar la compatibilidad con proj.

curl [Link] | tar -xz


cd libspatialite-4.3.0a

if [[ `uname -s` == Darwin* ]]; then


# Mac OS requires some minor patching
sed -i "" "s/shrext_cmds='\`test \\.\$module = .yes && echo .so \\|\\| echo
\\.dylib\`'/shrext_cmds='.dylib'/g" configure
fi

./configure --disable-proj
make
make install

Configuración de SRID
En SpatiaLite, las columnas deben especificar un SRID por columna. El valor predeterminado de SRID es 0 .
Especifique otro SRID con el método ForSqliteHasSrid.

[Link]<City>().Property(c => [Link])


.ForSqliteHasSrid(4326);

Dimensión
De forma similar a SRID, la dimensión de una columna (o las ordenadas) también se especifica como parte de la
columna. Las ordenadas predeterminadas son X e y. Habilite las ordenadas adicionales (Z y M) mediante el método
ForSqliteHasDimension.
[Link]<City>().Property(c => [Link])
.ForSqliteHasDimension([Link]);

Operaciones traducidas
En esta tabla se muestran los miembros de NTS que cada proveedor de EF Core traduce en SQL.

SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L

Geometry. Area ✔ ✔ ✔ ✔

Geometry. AsBinary () ✔ ✔ ✔ ✔

Geometry. astext () ✔ ✔ ✔ ✔

Geometry. Boundary ✔ ✔ ✔

Geometry. Buffer ✔ ✔ ✔ ✔
(Double)

Geometry. Buffer ✔ ✔
(Double, int)

Geometry. centroide ✔ ✔ ✔

Geometry. Contains ✔ ✔ ✔ ✔
(Geometry)

Geometry. ✔ ✔ ✔ ✔
ConvexHull ()

Geometry. CoveredBy ✔ ✔
(Geometry)

Geometry. cubiertas ✔ ✔
(Geometry)

Geometry. Crosses ✔ ✔ ✔
(Geometry)

Geometry. Difference ✔ ✔ ✔ ✔
(Geometry)

Geometry. Dimension ✔ ✔ ✔ ✔

Geometry. disunion ✔ ✔ ✔ ✔
(Geometry)

Geometry. Distance ✔ ✔ ✔ ✔
(Geometry)

Geometría. sobre ✔ ✔ ✔
SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L

Geometry. ✔
EqualsExact
(Geometry)

Geometry. ✔ ✔ ✔ ✔
EqualsTopologically
(Geometry)

Geometry. ✔ ✔ ✔ ✔
GeometryType

Geometry. ✔ ✔ ✔
GetGeometryN (int)

Geometry. ✔ ✔ ✔
InteriorPoint

Geometry. ✔ ✔ ✔ ✔
Intersection
(Geometry)

Geometry. Intersects ✔ ✔ ✔ ✔
(Geometry)

Geometry. IsEmpty ✔ ✔ ✔ ✔

Geometry. IsSimple ✔ ✔ ✔

Geometry. IsValid ✔ ✔ ✔ ✔

Geometry. ✔ ✔ ✔
IsWithinDistance
(Geometry, Double)

Geometry. length ✔ ✔ ✔ ✔

Geometry. ✔ ✔ ✔ ✔
NumGeometries

Geometry. NumPoints ✔ ✔ ✔ ✔

Geometry. ✔ ✔ ✔ ✔
OgcGeometryType

Geometry. superpone ✔ ✔ ✔ ✔
(Geometry)

Geometry. ✔ ✔ ✔
PointOnSurface

Geometry. Relate ✔ ✔ ✔
(Geometry, String)
SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L

Geometry. Reverse () ✔ ✔

Geometry. SRID ✔ ✔ ✔ ✔

Geometry. ✔ ✔ ✔ ✔
SymmetricDifference
(Geometry)

Geometry. ToBinary () ✔ ✔ ✔ ✔

Geometry. ToText () ✔ ✔ ✔ ✔

Geometry. toques ✔ ✔ ✔
(Geometry)

Geometry. Union () ✔ ✔

Geometry. Union ✔ ✔ ✔ ✔
(Geometry)

Geometry. Within ✔ ✔ ✔ ✔
(Geometry)

GeometryCollection. ✔ ✔ ✔ ✔
Count

GeometryCollection ✔ ✔ ✔ ✔
[int]

LineString. Count ✔ ✔ ✔ ✔

LineString. EndPoint ✔ ✔ ✔ ✔

LineString. GetPointN ✔ ✔ ✔ ✔
(int)

LineString. IsClosed ✔ ✔ ✔ ✔

LineString. IsRing ✔ ✔ ✔

LineString. StartPoint ✔ ✔ ✔ ✔

MultiLineString. ✔ ✔ ✔ ✔
IsClosed

Punto. M ✔ ✔ ✔ ✔

Point. X ✔ ✔ ✔ ✔

Punto. Y ✔ ✔ ✔ ✔
SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L

Punto. Z ✔ ✔ ✔ ✔

Polygon. ExteriorRing ✔ ✔ ✔ ✔

Polygon. ✔ ✔ ✔ ✔
GetInteriorRingN (int)

Polygon. ✔ ✔ ✔ ✔
NumInteriorRings

Recursos adicionales
Datos espaciales en SQL Server
Página principal de SpatiaLite
Documentación espacial Npgsql
Documentación de PostGIS
Administración de esquemas de base de datos
08/04/2020 • 2 minutes to read

EF Core proporciona dos métodos principales para mantener sincronizados el esquema de la base de datos y el
modelo de EF Core. Para elegir entre los dos, decida si es el modelo de EF Core o el esquema de la base de datos el
origen verdadero.
Si quiere que el modelo de EF Core sea el origen verdadero, use Migraciones. Al realizar cambios en el modelo de
EF Core, este método aplica de forma incremental los cambios de esquema correspondientes a la base de datos
para que siga siendo compatible con el modelo de EF Core.
Si quiere que el esquema de la base de datos sea el origen verdadero, use Ingeniería inversa. Este método permite
aplicar la técnica de scaffolding a un elemento DbContext y a las clases de tipo de entidad mediante la aplicación de
ingeniería inversa al esquema de la base de datos de un modelo de EF Core.

NOTE
Las API de creación y eliminación también pueden crear el esquema de la base de datos a partir del modelo de EF Core. Pero
son principalmente para pruebas, creación de prototipos y otros escenarios donde la eliminación de la base de datos es
aceptable.
Migraciones
08/04/2020 • 12 minutes to read

Un modelo de datos cambia durante el desarrollo y deja de estar sincronizado con la base de datos. Puede
quitar la base de datos y dejar que EF cree una que coincida con el modelo, pero este procedimiento provoca
la pérdida de datos. La característica de migraciones de EF Core proporciona una manera de actualizar
incrementalmente el esquema de la base de datos para mantenerla sincronizada con el modelo de datos de la
aplicación al tiempo que se conservan los datos existentes en la base de datos.
Las migraciones incluyen herramientas de línea de comandos y API que facilitan las siguientes tareas:
Crear una migración. Generar código que puede actualizar la base de datos para sincronizarla con un
conjunto de cambios en el modelo.
Actualizar la base de datos. Aplicar las migraciones pendientes para actualizar el esquema de la base de
datos.
Personalizar el código de migración. A veces el código generado debe modificarse o complementarse.
Quitar una migración. Eliminar el código generado.
Revertir una migración. Deshacer los cambios de la base de datos.
Generar scripts SQL. Puede que necesite un script para actualizar una base de datos de producción o para
solucionar problemas con el código de migración.
Aplicar migraciones en tiempo de ejecución. Si las actualizaciones en tiempo de diseño y la ejecución de
scripts no son las mejores opciones, llame al método Migrate() .

TIP
Si DbContext está en un ensamblado diferente al del proyecto de inicio, puede especificar de manera explícita los
proyectos de destino e inicio tanto en las herramientas de la Consola del Administrador de paquetes como en las
herramientas de la CLI de .NET Core.

Instalar las herramientas


Instale las herramientas de línea de comandos:
Para Visual Studio, se recomiendan las herramientas de la Consola del Administrador de paquetes.
Para otros entornos de desarrollo, elija las herramientas de la CLI de .NET Core.

Crear una migración


Una vez definido el modelo inicial, es el momento de crear la base de datos. Para agregar una migración
inicial, ejecute el siguiente comando.
CLI de .NET Core
Visual Studio

dotnet ef migrations add InitialCreate

Se agregan tres archivos al proyecto en el directorio Migraciones :


XXXXXXXXXXXXXX_InitialCreate.cs : archivo principal de las migraciones. Contiene las operaciones
necesarias para aplicar la migración (en Up() ) y para revertirla (en Down() ).
XXXXXXXXXXXXXX_InitialCreate.[Link] : archivo de metadatos de las migraciones. Contiene
información que usa EF.
[Link] : instantánea del modelo actual. Se usa para determinar qué ha cambiado
al agregar la siguiente migración.
La marca de tiempo del nombre del archivo ayuda a mantenerlos ordenados cronológicamente para que se
pueda ver la progresión de cambios.

TIP
Puede mover los archivos de Migraciones y cambiar su espacio de nombres. Se crean nuevas migraciones como
elementos del mismo nivel de la última migración.

Actualizar la base de datos


Luego aplique la migración a la base de datos para crear el esquema.
CLI de .NET Core
Visual Studio

dotnet ef database update

Personalizar el código de migración


Después de realizar cambios en el modelo de EF Core, puede que el esquema de la base de datos no esté
sincronizado. Para ponerlo al día, agregue otra migración. El nombre de la migración se puede usar como
mensaje de confirmación en un sistema de control de versiones. Por ejemplo, podría elegir un nombre como
AddProductReviews si el cambio es una nueva clase de entidad para las revisiones.
CLI de .NET Core
Visual Studio

dotnet ef migrations add AddProductReviews

Tras aplicar scaffolding a la migración (código generado para ella), revise el código para mayor precisión y
agregue, quite o modifique todas las operaciones necesarias para aplicarla correctamente.
Por ejemplo, una migración podría contener las siguientes operaciones:

[Link](
name: "FirstName",
table: "Customer");

[Link](
name: "LastName",
table: "Customer");

[Link]<string>(
name: "Name",
table: "Customer",
nullable: true);

Aunque estas operaciones hacen que el esquema de la base de datos sea compatible, no conservan los
nombres de cliente existentes. Para que sea mejor, vuelva a escribirla como se indica a continuación.

[Link]<string>(
name: "Name",
table: "Customer",
nullable: true);

[Link](
@"
UPDATE Customer
SET Name = FirstName + ' ' + LastName;
");

[Link](
name: "FirstName",
table: "Customer");

[Link](
name: "LastName",
table: "Customer");

TIP
El proceso de scaffolding de la migración advierte si una operación puede ocasionar una pérdida de datos (como el
borrado de una columna). Si aparece dicha advertencia, asegúrese especialmente de revisar el código de las migraciones
para mayor precisión.

Aplique la migración a la base de datos con el comando apropiado.


CLI de .NET Core
Visual Studio

dotnet ef database update

Migraciones vacías
A veces resulta útil agregar una migración sin realizar ningún cambio de modelo. En este caso, agregar una
nueva migración crea archivos de código con clases vacías. Puede personalizar esta migración para llevar a
cabo operaciones que no estén directamente relacionadas con el modelo de EF Core. Algunos aspectos que
podría querer administrar de esta manera son:
Búsqueda de texto completo
Funciones
Procedimientos almacenados
Desencadenadores
Vistas

Quitar una migración


A veces uno agrega una migración y se da cuenta de que debe realizar cambios adicionales en el modelo de
EF Core antes de aplicarla. Para quitar la última migración, use este comando.
CLI de .NET Core
Visual Studio
dotnet ef migrations remove

Después de quitar la migración, puede realizar los cambios de modelo adicionales y volver a agregarla.

Revertir una migración


Si ya ha aplicado una migración (o varias migraciones) a la base de datos pero necesita revertirla, puede usar
el mismo comando que para aplicar migraciones, aunque debe especificar el nombre de la migración que
quiere revertir.
CLI de .NET Core
Visual Studio

dotnet ef database update LastGoodMigration

Generar scripts SQL


Al depurar las migraciones o implementarlas en una base de datos de producción, es útil generar un script
SQL. El script luego se puede revisar y ajustar a las necesidades de una base de datos de producción. El script
también puede usarse junto con una tecnología de implementación. El comando básico es el siguiente.
CLI de .NET Core
Visual Studio
Uso básico

dotnet ef migrations script

Con From (To implícito)


Se generará un script SQL desde esta migración a la migración más reciente.

dotnet ef migrations script 20190725054716_Add_new_tables

Con From y To
Se generará un script SQL de la migración de from a la migración de to especificada.

dotnet ef migrations script 20190725054716_Add_new_tables 20190829031257_Add_audit_table

Puede usar un valor from que sea más reciente que el valor to para generar un script de reversión. Tome
nota de los posibles escenarios de pérdida de datos.
Hay varias opciones para este comando.
La migración from debe ser la última migración aplicada a la base de datos antes de ejecutar el script. Si no se
han aplicado migraciones, especifique 0 (es el valor predeterminado).
La migración to debe ser la última migración que se va a aplicar a la base de datos después de ejecutar el
script. El valor predeterminado es la última migración del proyecto.
Se puede generar un script idempotent de forma opcional. Este script solo aplica migraciones si aún no se
han aplicado a la base de datos. Es útil si no sabe exactamente cuál ha sido la última migración aplicada a la
base de datos o si va a implementar en varias bases de datos que pueden encontrarse en migraciones
diferentes.
Aplicar migraciones en tiempo de ejecución
Algunas aplicaciones pueden querer aplicar migraciones en tiempo de ejecución durante el inicio o la primera
ejecución. Para ello, se usa el método Migrate() .
Este método se basa en el servicio , que se puede usar para escenarios más avanzados. Use
IMigrator
[Link]().GetService<IMigrator>() para acceder a él.

[Link]();

WARNING
Este método no es para todos. Aunque es excelente para las aplicaciones con una base de datos local, la mayoría de
las aplicaciones necesitan estrategias de implementación más sólidas, como la generación de scripts SQL.
No llame a EnsureCreated() antes de Migrate() . EnsureCreated() omite las migraciones para crear el
esquema, lo cual provoca un error de Migrate() .

Pasos siguientes
Para obtener más información, vea Referencia sobre las herramientas de Entity Framework Core (EF Core).
Migraciones en entornos de equipo
11/03/2020 • 3 minutes to read

Al trabajar con migraciones en entornos de equipo, preste especial atención al archivo de instantáneas del modelo.
Este archivo puede indicarle si la migración de su compañero de equipo se combina correctamente con la suya o si
necesita resolver un conflicto volviendo a crear la migración antes de compartirla.

Combinación
Al fusionar mediante combinación las migraciones de sus compañeros de equipo, puede obtener conflictos en el
archivo de instantánea del modelo. Si los dos cambios no están relacionados, la combinación es trivial y las dos
migraciones pueden coexistir. Por ejemplo, puede obtener un conflicto de fusión mediante combinación en la
configuración del tipo de entidad Customer, que tiene el siguiente aspecto:

<<<<<<< Mine
[Link]<bool>("Deactivated");
=======
[Link]<int>("LoyaltyPoints");
>>>>>>> Theirs

Puesto que ambas propiedades deben existir en el modelo final, complete la combinación agregando ambas
propiedades. En muchos casos, es posible que el sistema de control de versiones combine automáticamente estos
cambios.

[Link]<bool>("Deactivated");
[Link]<int>("LoyaltyPoints");

En estos casos, la migración y la migración de su compañero son independientes entre sí. Dado que cualquiera de
ellas se podría aplicar en primer lugar, no es necesario realizar ningún cambio adicional en la migración antes de
compartirla con el equipo.

Resolución de conflictos
A veces se produce un conflicto real al combinar el modelo de instantánea de modelo. Por ejemplo, usted y su
compañero de equipo pueden cambiar el nombre de la misma propiedad.

<<<<<<< Mine
[Link]<string>("Username");
=======
[Link]<string>("Alias");
>>>>>>> Theirs

Si encuentra este tipo de conflicto, resuélvalos volviendo a crear la migración. Siga estos pasos:
1. Anular la combinación y revertir al directorio de trabajo antes de la fusión mediante combinación
2. Quitar la migración (pero mantener los cambios del modelo)
3. Combinar los cambios de su compañero en el directorio de trabajo
4. Volver a agregar la migración
Después de hacer esto, las dos migraciones se pueden aplicar en el orden correcto. En primer lugar, se aplica su
migración, cambiando el nombre de la columna a aliasy, a partir de ese momento, la migración lo cambia por
nombre de usuario.
La migración puede compartirse de forma segura con el resto del equipo.
Operaciones de migración personalizadas
11/03/2020 • 3 minutes to read

La API de MigrationBuilder permite realizar muchos tipos diferentes de operaciones durante una migración, pero
está lejos de ser exhaustiva. Sin embargo, la API también es extensible, lo que le permite definir sus propias
operaciones. Hay dos maneras de extender la API: mediante el método Sql() o mediante la definición de objetos
de MigrationOperation personalizados.
Para ilustrar, echemos un vistazo a la implementación de una operación que crea un usuario de base de datos
mediante cada enfoque. En nuestras migraciones, queremos habilitar la escritura del código siguiente:

[Link]("SQLUser1", "Password");

Usar MigrationBuilder. SQL ()


La forma más fácil de implementar una operación personalizada es definir un método de extensión que llame a
[Link]() . Este es un ejemplo que genera el correspondiente Transact-SQL.

static MigrationBuilder CreateUser(


this MigrationBuilder migrationBuilder,
string name,
string password)
=> [Link]($"CREATE USER {name} WITH PASSWORD '{password}';");

Si las migraciones necesitan admitir varios proveedores de bases de datos, puede usar la propiedad
[Link] . Este es un ejemplo que admite tanto Microsoft SQL Server como PostgreSQL.

static MigrationBuilder CreateUser(


this MigrationBuilder migrationBuilder,
string name,
string password)
{
switch ([Link])
{
case "[Link]":
return migrationBuilder
.Sql($"CREATE USER {name} WITH PASSWORD '{password}';");

case "[Link]":
return migrationBuilder
.Sql($"CREATE USER {name} WITH PASSWORD = '{password}';");
}

return migrationBuilder;
}

Este enfoque solo funciona si conoce todos los proveedores en los que se va a aplicar la operación personalizada.

Uso de un MigrationOperation
Para desacoplar la operación personalizada de SQL, puede definir su propia MigrationOperation para
representarla. A continuación, la operación se pasa al proveedor para que pueda determinar el SQL adecuado que
se va a generar.

class CreateUserOperation : MigrationOperation


{
public string Name { get; set; }
public string Password { get; set; }
}

Con este enfoque, el método de extensión solo tiene que agregar una de estas operaciones a
[Link] .

static MigrationBuilder CreateUser(


this MigrationBuilder migrationBuilder,
string name,
string password)
{
[Link](
new CreateUserOperation
{
Name = name,
Password = password
});

return migrationBuilder;
}

Este enfoque requiere que cada proveedor sepa cómo generar SQL para esta operación en su servicio
IMigrationsSqlGenerator . Este es un ejemplo invalidando el generador del SQL Server para administrar la nueva
operación.
class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
public MyMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations)
{
}

protected override void Generate(


MigrationOperation operation,
IModel model,
MigrationCommandListBuilder builder)
{
if (operation is CreateUserOperation createUserOperation)
{
Generate(createUserOperation, builder);
}
else
{
[Link](operation, model, builder);
}
}

private void Generate(


CreateUserOperation operation,
MigrationCommandListBuilder builder)
{
var sqlHelper = [Link];
var stringMapping = [Link](typeof(string));

builder
.Append("CREATE USER ")
.Append([Link]([Link]))
.Append(" WITH PASSWORD = ")
.Append([Link]([Link]))
.AppendLine([Link])
.EndCommand();
}
}

Reemplace el servicio de generador de SQL de migraciones predeterminado por el actualizado.

protected override void OnConfiguring(DbContextOptionsBuilder options)


=> options
.UseSqlServer(connectionString)
.ReplaceService<IMigrationsSqlGenerator, MyMigrationsSqlGenerator>();
Uso de un proyecto de migración independiente
11/03/2020 • 2 minutes to read

Es posible que desee almacenar las migraciones en un ensamblado diferente del que contiene el DbContext .
También puede usar esta estrategia para mantener varios conjuntos de migraciones, por ejemplo, una para el
desarrollo y otra para las actualizaciones de lanzamiento a lanzamiento.
Para hacer esto...
1. Cree una nueva biblioteca de clases.
2. Agregue una referencia al ensamblado DbContext.
3. Mueva las migraciones y los archivos de instantáneas de modelo a la biblioteca de clases.

TIP
Si no tiene ninguna migración existente, genere una en el proyecto que contiene el DbContext y muévala. Esto es
importante porque si el ensamblado de migraciones no contiene una migración existente, el comando Add-
Migration no podrá encontrar DbContext.

4. Configure el ensamblado de migraciones:

[Link](
connectionString,
x => [Link]("[Link]"));

5. Agregue una referencia al ensamblado de migraciones desde el ensamblado de inicio.


Si esto produce una dependencia circular, actualice la ruta de acceso de salida de la biblioteca de
clases:

<PropertyGroup>
<OutputPath>..\MyStartupProject\bin\$(Configuration)\</OutputPath>
</PropertyGroup>

Si lo hizo todo correctamente, debería poder agregar nuevas migraciones al proyecto.


CLI de .NET Core
Visual Studio

dotnet ef migrations add NewMigration --project [Link]


Migraciones con varios proveedores
11/03/2020 • 3 minutes to read

Las herramientas de EF Core solo las migraciones de scaffolding para el proveedor activo. Sin embargo, a veces es
posible que desee usar más de un proveedor (por ejemplo Microsoft SQL Server y SQLite) con DbContext. Hay dos
formas de controlar esto con las migraciones. Puede mantener dos conjuntos de migraciones, uno para cada
proveedor, o combinarlos en un único conjunto que pueda funcionar en ambos.

Dos conjuntos de migración


En el primer enfoque, se generan dos migraciones para cada cambio de modelo.
Una manera de hacerlo es colocar cada conjunto de migración en un ensamblado independiente y cambiar
manualmente el proveedor activo (y el ensamblado de migraciones) entre agregar las dos migraciones.
Otro enfoque que facilita el trabajo con las herramientas es crear un nuevo tipo que derive de su DbContext e
invalide el proveedor activo. Este tipo se utiliza en tiempo de diseño al agregar o aplicar migraciones.

class MySqliteDbContext : MyDbContext


{
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> [Link]("Data Source=[Link]");
}

NOTE
Dado que cada conjunto de migración usa sus propios tipos DbContext, este enfoque no requiere el uso de un ensamblado
de migración independiente.

Al agregar una nueva migración, especifique los tipos de contexto.


CLI de .NET Core
Visual Studio

dotnet ef migrations add InitialCreate --context MyDbContext --output-dir Migrations/SqlServerMigrations


dotnet ef migrations add InitialCreate --context MySqliteDbContext --output-dir Migrations/SqliteMigrations

TIP
No es necesario especificar el directorio de salida para las migraciones posteriores, ya que se crean como elementos del
mismo nivel que el último.

Un conjunto de migración
Si no le gusta tener dos conjuntos de migraciones, puede combinarlas manualmente en un único conjunto que se
puede aplicar a ambos proveedores.
Las anotaciones pueden coexistir ya que un proveedor omite cualquier anotación que no comprenda. Por ejemplo,
una columna de clave principal que funciona con Microsoft SQL Server y SQLite podría tener este aspecto.
Id = [Link]<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
[Link])
.Annotation("Sqlite:Autoincrement", true),

Si las operaciones solo se pueden aplicar en un proveedor (o son diferentes entre proveedores), use la propiedad
ActiveProvider para indicar qué proveedor está activo.

if ([Link] == "[Link]")
{
[Link](
name: "EntityFrameworkHiLoSequence");
}
Tabla de historial de migraciones personalizadas
11/03/2020 • 2 minutes to read

De forma predeterminada, EF Core realiza un seguimiento de las migraciones que se han aplicado a la base de
datos mediante su grabación en una tabla denominada __EFMigrationsHistory . Por varias razones, puede que
desee personalizar esta tabla para satisfacer mejor sus necesidades.

IMPORTANT
Si personaliza la tabla de historial de migraciones después de aplicar las migraciones, es responsable de actualizar la tabla
existente en la base de datos.

Esquema y nombre de tabla


Puede cambiar el esquema y el nombre de la tabla mediante el método MigrationsHistoryTable() en
OnConfiguring() (o ConfigureServices() en [Link] Core). Este es un ejemplo del uso del proveedor de EF Core de
SQL Server.

protected override void OnConfiguring(DbContextOptionsBuilder options)


=> [Link](
connectionString,
x => [Link]("__MyMigrationsHistory", "mySchema"));

Otros cambios
Para configurar aspectos adicionales de la tabla, invalide y reemplace el servicio de IHistoryRepository específico
del proveedor. Este es un ejemplo de cómo cambiar el nombre de la columna MigrationId a ID en SQL Server.

protected override void OnConfiguring(DbContextOptionsBuilder options)


=> options
.UseSqlServer(connectionString)
.ReplaceService<IHistoryRepository, MyHistoryRepository>();

WARNING
SqlServerHistoryRepository está dentro de un espacio de nombres interno y puede cambiar en futuras versiones.
class MyHistoryRepository : SqlServerHistoryRepository
{
public MyHistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}

protected override void ConfigureTable(EntityTypeBuilder<HistoryRow> history)


{
[Link](history);

[Link](h => [Link]).HasColumnName("Id");


}
}
Crear y quitar API
11/03/2020 • 2 minutes to read

Los métodos EnsureCreated y EnsureDeleted proporcionan una alternativa ligera a las migraciones para
administrar el esquema de la base de datos. Estos métodos son útiles en escenarios en los que los datos son
transitorios y se pueden quitar cuando cambia el esquema. Por ejemplo, durante el prototipo, en las pruebas o en
las memorias caché locales.
Algunos proveedores (especialmente los no relacionales) no admiten las migraciones. Para estos proveedores,
EnsureCreated suele ser la manera más fácil de inicializar el esquema de la base de datos.

WARNING
EnsureCreated y las migraciones no funcionan bien juntos. Si utiliza migraciones, no use EnsureCreated para inicializar el
esquema.

La transición de EnsureCreated a migraciones no es una experiencia sin problemas. La manera más sencilla de
hacerlo es quitar la base de datos y volver a crearla con las migraciones. Si prevé usar migraciones en el futuro, es
mejor empezar con las migraciones en lugar de usar EnsureCreated.

EnsureDeleted
El método EnsureDeleted quitará la base de datos si existe. Si no tiene los permisos adecuados, se produce una
excepción.

// Drop the database if it exists


[Link]();

EnsureCreated
EnsureCreated creará la base de datos si no existe e inicializará el esquema de la base de datos. Si existe alguna
tabla (incluidas las tablas de otra clase DbContext), el esquema no se inicializará.

// Create the database if it doesn't exist


[Link]();

TIP
También hay disponibles versiones asincrónicas de estos métodos.

Secuencia de comandos de SQL


Para obtener el SQL que usa EnsureCreated, puede utilizar el método GenerateCreateScript.

var sql = [Link]();


Varias clases DbContext
EnsureCreated solo funciona cuando no hay ninguna tabla presente en la base de datos. Si es necesario, puede
escribir su propia comprobación para ver si es necesario inicializar el esquema y usar el servicio
IRelationalDatabaseCreator subyacente para inicializar el esquema.

// TODO: Check whether the schema needs to be initialized

// Initialize the schema for this DbContext


var databaseCreator = [Link]<IRelationalDatabaseCreator>();
[Link]();
Ingeniería inversa
11/03/2020 • 12 minutes to read

La ingeniería inversa es el proceso de scaffolding de las clases de tipo de entidad y una clase DbContext basada
en un esquema de base de datos. Puede realizarse mediante el comando Scaffold-DbContext de las herramientas
de la consola del administrador de paquetes EF Core (PMC) o el comando dotnet ef dbcontext scaffold de las
herramientas de la interfaz de la línea de comandos (CLI) de .NET.

Instalación
Antes de la ingeniería inversa, deberá instalar las herramientas de PMC (solo en Visual Studio) o las
herramientasde la CLI. Vea los vínculos para obtener más información.
También necesitará instalar un proveedor de base de datos adecuado para el esquema de la base de datos al que
desea aplicar ingeniería inversa.

Cadena de conexión
El primer argumento del comando es una cadena de conexión a la base de datos. Las herramientas usarán esta
cadena de conexión para leer el esquema de la base de datos.
La forma de citar y escapar de la cadena de conexión depende del shell que use para ejecutar el comando.
Consulte la documentación de su shell para obtener información específica. Por ejemplo, PowerShell requiere que
se escape el carácter $ , pero no \ .

Scaffold-DbContext 'Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook'


[Link]

dotnet ef dbcontext scaffold "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Chinook"


[Link]

Configuración y secretos de usuario


Si tiene un proyecto de [Link] Core, puede usar la sintaxis de Name=<connection-string> para leer la cadena de
conexión de la configuración.
Esto funciona bien con la herramienta de administración de secretos para mantener la contraseña de la base de
datos separada del código base.

dotnet user-secrets set [Link] "Data Source=(localdb)\MSSQLLocalDB;Initial


Catalog=Chinook"
dotnet ef dbcontext scaffold Name=Chinook [Link]

Nombre de proveedor
El segundo argumento es el nombre del proveedor. El nombre del proveedor suele ser el mismo que el nombre
del paquete NuGet del proveedor.

Especificar tablas
De forma predeterminada, se aplica ingeniería inversa a todas las tablas del esquema de la base de datos en tipos
de entidad. Puede limitar las tablas a las que se aplica ingeniería inversa mediante la especificación de esquemas
y tablas.
El parámetro -Schemas en PMC y la opción --schema de la CLI se pueden usar para incluir todas las tablas de un
esquema.
-Tables (PMC) y --table (CLI) se pueden usar para incluir tablas específicas.
Para incluir varias tablas en PMC, use una matriz.

Scaffold-DbContext ... -Tables Artist, Album

Para incluir varias tablas en la CLI, especifique la opción varias veces.

dotnet ef dbcontext scaffold ... --table Artist --table Album

Conservar nombres
Los nombres de tablas y columnas se han corregido para que coincidan mejor con las convenciones de
nomenclatura de .NET para tipos y propiedades de forma predeterminada. Si se especifica el modificador
-UseDatabaseNames en PMC o la opción --use-database-names de la CLI, se deshabilitará este comportamiento
para conservar los nombres de las bases de datos originales lo máximo posible. Los identificadores de .NET no
válidos seguirán siendo fijos y los nombres sintetizados, como las propiedades de navegación, seguirán
conforme a las convenciones de nomenclatura de .NET.

Anotaciones de datos o API fluidas


Los tipos de entidad se configuran mediante la API fluida de forma predeterminada. Especifique
-DataAnnotations (PMC) o --data-annotations (CLI) para usar las anotaciones de datos siempre que sea posible.

Por ejemplo, el uso de la API fluida le aplicará esta técnica:

[Link](e => [Link])


.IsRequired()
.HasMaxLength(160);

Aunque el uso de anotaciones de datos es scaffolding:

[Required]
[StringLength(160)]
public string Title { get; set; }

Nombre de DbContext
El nombre de la clase DbContext con scaffolding será el nombre de la base de datos con sufijo de contexto de
forma predeterminada. Para especificar otro, use -Context en PMC y --context en la CLI.

Directorios y espacios de nombres


Las clases de entidad y una clase DbContext se scaffolding en el directorio raíz del proyecto y usan el espacio de
nombres predeterminado del proyecto. Puede especificar el directorio en el que se van a aplicar las scaffolding
mediante -OutputDir (PMC) o --output-dir (CLI). El espacio de nombres será el espacio de nombres raíz más
los nombres de los subdirectorios del directorio raíz del proyecto.
También puede usar -ContextDir (PMC) y --context-dir (CLI) para aplicar scaffolding a la clase DbContext en
un directorio independiente de las clases de tipo de entidad.

Scaffold-DbContext ... -ContextDir Data -OutputDir Models

dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models

Funcionamiento
La ingeniería inversa comienza leyendo el esquema de la base de datos. Lee información acerca de las tablas,
columnas, restricciones e índices.
A continuación, usa la información de esquema para crear un modelo de EF Core. Las tablas se usan para crear
tipos de entidad. las columnas se usan para crear propiedades; y las claves externas se utilizan para crear
relaciones.
Por último, el modelo se usa para generar código. Las clases de tipo de entidad, la API fluida y las anotaciones de
datos correspondientes son scaffolding para volver a crear el mismo modelo desde la aplicación.

Limitaciones
No todo lo relacionado con un modelo se puede representar mediante un esquema de la base de datos. Por
ejemplo, la información sobre las jerarquías de herencia , los tipos de propiedad y la División de tablas
no están presentes en el esquema de la base de datos. Por este motivo, estas construcciones nunca se
aplicarán a ingeniería inversa.
Además, es posible que algunos tipos de columna no sean compatibles con el proveedor de EF Core. Estas
columnas no se incluirán en el modelo.
Puede definir tokens de simultaneidad en un modelo de EF Core para evitar que dos usuarios actualicen la
misma entidad al mismo tiempo. Algunas bases de datos tienen un tipo especial para representar este tipo de
columna (por ejemplo, rowversion en SQL Server), en cuyo caso se puede aplicar ingeniería inversa a esta
información; sin embargo, no se aplicarán ingeniería inversa a otros tokens de simultaneidad.
La C# característica 8 tipos de referencia que aceptan valores NULL no se admite actualmente en ingeniería
inversa: EF Core C# siempre genera código que supone que la característica está deshabilitada. Por ejemplo,
las columnas de texto que aceptan valores NULL se scaffolding como una propiedad con el tipo string , no
string? , con la API fluida o las anotaciones de datos que se usan para configurar si una propiedad es
obligatoria o no. Puede editar el código con scaffolding y reemplazarlo con anotaciones de C# nulabilidad. El
seguimiento de la compatibilidad con scaffolding para tipos de referencia que aceptan valores NULL se realiza
mediante el problema #15520.

Personalización del modelo


El código generado por EF Core es el código. No dude en cambiarlo. Solo se regenerará si vuelve a aplicar
ingeniería inversa al mismo modelo. El código con scaffolding representa un modelo que se puede utilizar para
tener acceso a la base de datos, pero ciertamente no es el único modelo que se puede usar.
Personalice las clases de tipo de entidad y la clase DbContext para que se adapte a sus necesidades. Por ejemplo,
puede elegir cambiar el nombre de tipos y propiedades, introducir jerarquías de herencia o dividir una tabla en
varias entidades. También puede quitar índices no únicos, secuencias sin usar y propiedades de navegación,
propiedades escalares opcionales y nombres de restricción del modelo.
También puede Agregar constructores, métodos, propiedades, etc. adicionales. usar otra clase parcial en un
archivo independiente. Este enfoque funciona incluso cuando se desea volver a aplicar ingeniería inversa al
modelo.

Actualizar el modelo
Después de realizar cambios en la base de datos, puede que tenga que actualizar el modelo de EF Core para
reflejar los cambios. Si los cambios en la base de datos son sencillos, puede que sea más fácil realizar los cambios
manualmente en el modelo de EF Core. Por ejemplo, cambiar el nombre de una tabla o columna, quitar una
columna o actualizar el tipo de una columna son cambios triviales que se deben realizar en el código.
Sin embargo, los cambios más significativos no son tan sencillos como los que se realizan de forma manual. Un
flujo de trabajo común consiste en volver a aplicar ingeniería inversa del modelo de la base de datos mediante
-Force (PMC) o --force (CLI) para sobrescribir el modelo existente con uno actualizado.

Otra característica solicitada comúnmente es la posibilidad de actualizar el modelo de la base de datos a la vez
que se conserva la personalización, como cambiar el nombre, las jerarquías de tipos, etc. Use el #831 de
problemas para realizar el seguimiento del progreso de esta característica.

WARNING
Si vuelve a aplicar ingeniería inversa al modelo desde la base de datos, se perderán los cambios realizados en los archivos.
Consulta de datos
08/04/2020 • 2 minutes to read • Edit Online

Entity Framework Core usa Language Integrated Query (LINQ) para consultar datos de la base de datos. LINQ
permite usar C# (o el lenguaje .NET que prefiera) para escribir consultas fuertemente tipadas. Usa el contexto
derivado y las clases de entidad para hacer referencia a los objetos de base de datos. EF Core pasa una
representación de la consulta LINQ al proveedor de la base de datos. A su vez, los proveedores de la base de datos
la traducen al lenguaje de la consulta específico para la base de datos (por ejemplo, SQL para una base de datos
relacional).

TIP
Puede ver un ejemplo de este artículo en GitHub.

Los fragmentos de código siguientes muestran algunos ejemplos de cómo realizar tareas comunes con Entity
Framework Core.

Carga de todos los datos


using (var context = new BloggingContext())
{
var blogs = [Link]();
}

Carga de una sola entidad


using (var context = new BloggingContext())
{
var blog = [Link]
.Single(b => [Link] == 1);
}

Filtros
using (var context = new BloggingContext())
{
var blogs = [Link]
.Where(b => [Link]("dotnet"))
.ToList();
}

Lecturas adicionales
Obtenga más información sobre las expresiones de consulta LINQ.
Para más información sobre cómo se procesa una consulta en EF Core, vea Cómo funcionan las consultas.
Evaluación de cliente frente a servidor
08/04/2020 • 10 minutes to read • Edit Online

Como norma general, Entity Framework Core intenta evaluar una consulta en el servidor lo máximo posible. EF
Core convierte partes de la consulta en parámetros, que se pueden evaluar en el lado cliente. El resto de la consulta
( junto con los parámetros generados) se proporciona al proveedor de base de datos para determinar la consulta
de base de datos equivalente que se va a evaluar en el servidor. EF Core admite la evaluación de cliente parcial en
la proyección de nivel superior (fundamentalmente, la última llamada a Select() ). Si la proyección de nivel
superior de la consulta no se puede traducir en el servidor, EF Core capturará los datos necesarios del servidor y
evaluará las partes restantes de la consulta en el cliente. Si EF Core detecta una expresión, en cualquier lugar que
no sea la proyección de nivel superior, que no se puede traducir en el servidor, inicia una excepción en tiempo de
ejecución. Vea Funcionamiento de la consulta para saber cómo determina EF Core lo que no se puede traducir al
servidor.

NOTE
Antes de la versión 3.0, Entity Framework Core admitía la evaluación de cliente en cualquier parte de la consulta. Para
obtener más información, vea la sección sobre versiones anteriores.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Evaluación de cliente en la proyección de nivel superior


En el ejemplo siguiente, se usa un método auxiliar para estandarizar las direcciones URL para blogs, que se
devuelven desde una base de datos de SQL Server. Como el proveedor de SQL Server no tiene información de
cómo se implementa este método, no es posible traducirlo a código SQL. Todos los demás aspectos de la consulta
se evalúan en la base de datos, pero es el cliente quien pasa la URL devuelta mediante este método.

var blogs = [Link]


.OrderByDescending(blog => [Link])
.Select(blog => new
{
Id = [Link],
Url = StandardizeUrl([Link])
})
.ToList();

public static string StandardizeUrl(string url)


{
url = [Link]();

if (![Link]("[Link]
{
url = [Link]("[Link] url);
}

return url;
}
Evaluación de cliente no admitida
Aunque la evaluación de cliente es útil, en ocasiones puede generar un rendimiento bajo. Considere la consulta
siguiente, en la que ahora el método auxiliar se usa en un filtro WHERE. Como el filtro no se puede aplicar en la
base de datos, se deben extraer todos los datos de la memoria para aplicar el filtro en el cliente. Según el filtro y la
cantidad de datos en el servidor, la evaluación de cliente podría dar lugar a un rendimiento deficiente. Por tanto,
Entity Framework Core bloquea esa evaluación de cliente e inicia una excepción en tiempo de ejecución.

var blogs = [Link]


.Where(blog => StandardizeUrl([Link]).Contains("dotnet"))
.ToList();

Evaluación explícita de cliente


Es posible que tenga que forzar la evaluación de cliente de forma explícita en ciertos casos como los siguientes:
La cantidad de datos es pequeña, por lo que la evaluación en el cliente no incurre en una gran penalización del
rendimiento.
El operador de LINQ que se usa carece de traducción del lado servidor.
En esos casos, puede participar de forma explícita en la evaluación de cliente si llamada a métodos como
AsEnumerable o ToList ( AsAsyncEnumerable o ToListAsync para async). Al usar AsEnumerable se haría streaming
de los resultados, pero al usar ToList se almacenarían en búfer mediante la creación de una lista, que también
consume memoria adicional. Como si se realizara la enumeración varias veces, el almacenamiento de los
resultados en una lista es más útil, ya que solo hay una consulta a la base de datos. En función del uso
determinado, debe evaluar qué método es más útil para cada caso.

var blogs = [Link]


.AsEnumerable()
.Where(blog => StandardizeUrl([Link]).Contains("dotnet"))
.ToList();

Posible fuga de memoria en la evaluación de cliente


Como la traducción y compilación de consultas son costosas, EF Core almacena en caché el plan de consulta
compilado. El delegado en caché puede usar el código de cliente mientras se realiza la evaluación de cliente de la
proyección de nivel superior. EF Core genera parámetros para las partes evaluadas por el cliente del árbol y
reutiliza el plan de consulta en el que reemplaza los valores de parámetro. Pero determinadas constantes del árbol
de expresión no se pueden convertir en parámetros. Si el delegado en caché contiene ese tipo de constantes, esos
objetos no se pueden recolectar como elementos no utilizados porque todavía se hace referencia a ellos. Si este
tipo de objeto contiene un elemento DbContext u otros servicios, podría hacer que el uso de memoria de la
aplicación crezca con el tiempo. Este comportamiento suele ser un signo de fuga de memoria. EF Core inicia una
excepción cada vez que detecta constantes de un tipo que no se puede asignar mediante el proveedor de base de
datos actual. Las causas comunes y sus soluciones son las siguientes:
Uso de un método de instancia : cuando se usan métodos de instancia en una proyección de cliente, el árbol
de expresión contiene una constante de la instancia. Si el método no usa ningún dato de la instancia, considere
la posibilidad de convertirlo en estático. Si necesita datos de la instancia en el cuerpo del método, pase los datos
específicos como argumento al método.
Paso de argumentos constantes al método : este caso surge generalmente al usar this en un argumento
para el método de cliente. Puede dividir el argumento en varios argumentos escalares, que podrá asignar el
proveedor de base de datos.
Otras constantes : si se detecta una constante en cualquier otro caso, puede evaluar si es necesaria para el
procesamiento. Si es necesario tener la constante, o bien si no puede usar una solución de los casos anteriores,
cree una variable local para almacenar el valor y use la variable local en la consulta. EF Core convertirá la
variable local en un parámetro.

Versiones anteriores
La sección siguiente se aplica a las versiones de EF Core anteriores a la 3.0.
En las versiones anteriores de EF Core se admitía la evaluación de cliente en cualquier parte de la consulta, no solo
en la proyección de nivel superior. Por ese motivo las consultas similares a la publicada en la sección Evaluación de
cliente no admitida funcionaban correctamente. Como este comportamiento podría provocar problemas de
rendimiento inadvertidos, EF Core registró una advertencia de evaluación de cliente. Para obtener más información
sobre cómo ver la salida de registro, vea Registro.
Opcionalmente, en EF Core se permitía cambiar el comportamiento predeterminado para iniciar una excepción o
no hacer nada al realizar la evaluación de cliente (excepto para la proyección). El comportamiento de inicio de
excepción haría que fuese similar al de la versión 3.0. Para cambiar el comportamiento, debe configurar las
advertencias al establecer las opciones del contexto, normalmente en [Link] , o bien en
[Link] si usa [Link] Core.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFQuerying;Trusted_Connection=True;")
.ConfigureWarnings(warnings => [Link]([Link]));
}
Consultas de seguimiento frente a consultas de no
seguimiento
08/04/2020 • 8 minutes to read • Edit Online

El comportamiento de seguimiento controla si Entity Framework Core mantendrá información sobre una instancia
de entidad en su herramienta de seguimiento de cambios. Si se hace seguimiento de una entidad, cualquier cambio
detectado en ella persistirá hasta la base de datos durante SaveChanges() . EF Core también corregirá las
propiedades de navegación entre las entidades de un resultado de consulta de seguimiento y las entidades que se
encuentran en la herramienta de seguimiento de cambios.

NOTE
No se realiza el seguimiento de los tipos de entidad sin clave. Siempre que en este artículo se mencionen los tipos de entidad,
se refiere a aquellos con una clave definida.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Consultas de seguimiento
De manera predeterminada, las consultas que devuelven tipos de entidad son consultas de seguimiento. Esto
significa que puede hacer cambios en esas instancias de entidad y que esos cambios se conservan mediante
SaveChanges() . En el ejemplo siguiente, se detectará el cambio en la clasificación de los blogs y persistirá hasta la
base de datos durante SaveChanges() .

var blog = [Link](b => [Link] == 1);


[Link] = 5;
[Link]();

Consultas de no seguimiento
Las consultas de no seguimiento son útiles cuando los resultados se usan en un escenario de solo lectura. Su
ejecución es más rápida porque no es necesario configurar la información de seguimiento de cambios. Si no
necesita actualizar las entidades recuperadas de la base de datos, se debe usar una consulta de no seguimiento.
Puede cambiar una consulta individual para que sea una consulta de no seguimiento.

var blogs = [Link]


.AsNoTracking()
.ToList();

También puede cambiar el comportamiento de seguimiento predeterminado en el nivel de instancia de contexto:

[Link] = [Link];

var blogs = [Link]();


Resolución de identidad
Dado que una consulta de seguimiento usa la herramienta de seguimiento de cambios, EF Core realizará la
resolución de identidades en una consulta de este tipo. Al materializar una entidad, EF Core devolverá la misma
instancia de entidad de la herramienta de seguimiento de cambios si ya está en proceso de seguimiento. Si el
resultado contiene la misma entidad varias veces, se devuelve la misma instancia con cada repetición. Las consultas
de no seguimiento no usan la herramienta de seguimiento de cambios y no realizan la resolución de identidades.
Así que, se devuelve una nueva instancia de la entidad incluso cuando la misma entidad está contenida en el
resultado varias veces. Este comportamiento era diferente en las versiones anteriores a EF Core 3.0; consulte las
versiones anteriores.

Seguimiento y proyecciones personalizadas


Incluso si el tipo de resultado de la consulta no es un tipo de entidad, EF Core seguirá realizando el seguimiento de
los tipos de entidad contenidos en el resultado de forma predeterminada. En la consulta siguiente, que devuelve un
tipo anónimo, se hará seguimiento de las instancias de Blog en el conjunto de resultados.

var blog = [Link]


.Select(b =>
new
{
Blog = b,
PostCount = [Link]()
});

Si el conjunto de resultados contiene tipos de entidad que proceden de la composición LINQ, EF Core realizará un
seguimiento de ellos.

var blog = [Link]


.Select(b =>
new
{
Blog = b,
Post = [Link](p => [Link]).LastOrDefault()
});

Si el conjunto de resultados no contiene ningún tipo de entidad, no se realiza ningún seguimiento. En la consulta
siguiente, se devuelve un tipo anónimo con algunos de los valores de la entidad (pero sin instancias del tipo de
entidad real). No hay entidades con seguimiento que procedan de la consulta.

var blog = [Link]


.Select(b =>
new
{
Id = [Link],
Url = [Link]
});

EF Core admite la evaluación del cliente en la proyección de nivel superior. Si EF Core materializa una instancia de
entidad para la evaluación del cliente, se realizará un seguimiento de esta. Aquí, como se pasan entidades de blog
al método cliente StandardizeURL , EF Core también realizará un seguimiento de las instancias del blog.
var blogs = [Link]
.OrderByDescending(blog => [Link])
.Select(blog => new
{
Id = [Link],
Url = StandardizeUrl(blog)
})
.ToList();

public static string StandardizeUrl(Blog blog)


{
var url = [Link]();

if (![Link]("[Link]
{
url = [Link]("[Link] url);
}

return url;
}

EF Core no realiza un seguimiento de las instancias de entidad sin clave contenidas en el resultado. Sin embargo, sí
lo hace de todas las demás instancias de tipos de entidad con clave según las reglas anteriores.
Algunas de las reglas anteriores funcionaban de forma diferente antes de EF Core 3.0. Para más información,
consulte las versiones anteriores.

Versiones anteriores
Antes de la versión 3.0, EF Core presentaba algunas diferencias en el modo en que se realizaba el seguimiento. Las
diferencias destacables son las siguientes:
Como se explica en la página Evaluación de cliente frente a servidor, EF Core admitía la evaluación de
clientes admitidos en cualquier parte de la consulta anterior antes de la versión 3.0. La evaluación de clientes
provocaba la materialización de entidades, las cuales no formaban parte del resultado. Por lo tanto, EF Core
analizaba el resultado para detectar de qué realizar el seguimiento. Este diseño tenía algunas diferencias,
como se indica a continuación:
No se realizaba el seguimiento de la evaluación de clientes en la proyección, lo que provocaba la
materialización pero no se devolvía la instancia de la entidad materializada. En el ejemplo siguiente
no se realizaba un seguimiento de entidades blog .

var blogs = [Link]


.OrderByDescending(blog => [Link])
.Select(blog => new
{
Id = [Link],
Url = StandardizeUrl(blog)
})
.ToList();

En algunos casos, EF Core no realizaba un seguimiento de los objetos que procedían de la


composición LINQ. En el ejemplo siguiente no se realizaba un seguimiento de Post .
var blog = [Link]
.Select(b =>
new
{
Blog = b,
Post = [Link](p => [Link]).LastOrDefault()
});

Siempre que los resultados de consulta contenían tipos de entidad sin clave, significaba que no se hacía un
seguimiento de la consulta completa. Esto quiere decir que tampoco se realizaba un seguimiento de los tipos
de entidad con claves que estaban en el resultado.
EF Coreó realizaba la resolución de identidades en consultas de no seguimiento. Se usaban referencias
débiles para mantener el seguimiento de entidades que ya se habían devuelto. Por lo tanto, si un conjunto de
resultados contenía la misma entidad varias veces, obtenía la misma instancia para cada caso. Sin embargo,
si un resultado anterior con la misma identidad se salía del ámbito y generaba un elemento no utilizado, EF
Core devolvía una nueva instancia.
Operadores de consulta complejos
08/04/2020 • 13 minutes to read • Edit Online

Language Integrated Query (LINQ) contiene muchos operadores complejos, que combinan varios orígenes de
datos o realizan procesamientos complejos. No todos los operadores de LINQ tienen traducciones adecuadas en el
lado servidor. En ocasiones, una consulta en un formato se traduce en el servidor, pero si se escribe en otro
formato, no se traduce aunque el resultado sea el mismo. En esta página se describen algunos de los operadores
complejos y sus variaciones admitidas. En futuras versiones, es posible que se reconozcan más patrones y se
agreguen sus correspondientes traducciones. También es importante tener en cuenta que la compatibilidad con la
traducción varía entre proveedores. Es posible que una consulta determinada, que se traduzca en SqlServer, no
funcione para bases de datos SQLite.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Join
El operador Join de LINQ permite conectar dos orígenes de datos en función del selector de claves de cada origen,
lo que genera una tupla de valores cuando la clave coincide. Se traduce de forma natural a INNER JOIN en las bases
de datos relacionales. Aunque Join de LINQ tiene selectores de clave externa e interna, la base de datos requiere
una única condición de combinación. Por tanto, EF Core genera una condición de combinación que compara el
selector de clave externa con el selector de clave interna para determinar si son iguales. Además, si los selectores de
clave son tipos anónimos, EF Core genera una condición de combinación para comparar los componentes de
igualdad.

var query = from photo in [Link]<PersonPhoto>()


join person in [Link]<Person>()
on [Link] equals [Link]
select new { person, photo };

SELECT [p].[PersonId], [p].[Name], [p].[PhotoId], [p0].[PersonPhotoId], [p0].[Caption], [p0].[Photo]


FROM [PersonPhoto] AS [p0]
INNER JOIN [Person] AS [p] ON [p0].[PersonPhotoId] = [p].[PhotoId]

GroupJoin
El operador GroupJoin de LINQ permite conectar dos orígenes de datos de forma similar a Join, pero crea un grupo
de valores internos para los elementos externos coincidentes. Al ejecutar una consulta como la del ejemplo
siguiente se genera un resultado de Blog & IEnumerable<Post> . Como las bases de datos (especialmente las
relacionales) no tienen una manera de representar una colección de objetos del lado cliente, en muchos casos
GroupJoin no se traduce en el servidor. Requiere que se obtengan todos los datos del servidor para ejecutar
GroupJoin sin un selector especial (la primera de las consultas siguientes). Pero si el selector limita los datos que se
seleccionan, la captura de todos los datos del servidor puede causar problemas de rendimiento (la segunda de las
consultas siguientes). Por eso EF Core no traduce GroupJoin.
var query = from b in [Link]<Blog>()
join p in [Link]<Post>()
on [Link] equals [Link] into grouping
select new { b, grouping };

var query = from b in [Link]<Blog>()


join p in [Link]<Post>()
on [Link] equals [Link] into grouping
select new { b, Posts = [Link](p => [Link]("EF")).ToList() };

SelectMany
El operador SelectMany de LINQ permite enumerar un selector de colecciones para cada elemento externo y
generar tuplas de valores de cada origen de datos. En cierto modo es una combinación, pero sin ninguna condición,
por lo que todos los elementos externos se conectan con un elemento del origen de la colección. En función de
cómo se relacione el selector de colecciones con el origen de datos externo, SelectMany puede traducirse en varias
consultas diferentes en el lado servidor.
El selector de colecciones no hace referencia al elemento externo
Cuando el selector de colecciones no hace referencia a nada del origen externo, el resultado es un producto
cartesiano de ambos orígenes de datos. Se traduce a CROSS JOIN en las bases de datos relacionales.

var query = from b in [Link]<Blog>()


from p in [Link]<Post>()
select new { b, p };

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].


[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
CROSS JOIN [Posts] AS [p]

El selector de colecciones hace referencia al elemento externo en una cláusula WHERE


Cuando el selector de colecciones tiene una cláusula WHERE, que hace referencia al elemento exterior, EF Core lo
traduce a una combinación de base de datos y usa el predicado como condición de combinación. Normalmente,
este caso se produce cuando se usa la navegación de colección en el elemento exterior como selector de
colecciones. Si la colección está vacía para un elemento externo, no se generarán resultados para ese elemento
externo. Pero si se aplica DefaultIfEmpty en el selector de colecciones, el elemento exterior se conectará con un
valor predeterminado del elemento interno. Debido a esta distinción, este tipo de consultas se traduce a
INNER JOIN en ausencia de DefaultIfEmpty y LEFT JOIN cuando se aplica DefaultIfEmpty .

var query = from b in [Link]<Blog>()


from p in [Link]<Post>().Where(p => [Link] == [Link])
select new { b, p };

var query2 = from b in [Link]<Blog>()


from p in [Link]<Post>().Where(p => [Link] == [Link]).DefaultIfEmpty()
select new { b, p };
SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].
[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
INNER JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].


[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]

El selector de colecciones hace referencia al elemento externo en una cláusula distinta de WHERE
Cuando el selector de colecciones hace referencia al elemento exterior, que no está en una cláusula WHERE (como
en el caso anterior), no se traduce a una combinación de base de datos. Por este motivo es necesario evaluar el
selector de colecciones para cada elemento exterior. Se traduce a operaciones APPLY en muchas bases de datos
relacionales. Si la colección está vacía para un elemento externo, no se generarán resultados para ese elemento
externo. Pero si se aplica DefaultIfEmpty en el selector de colecciones, el elemento exterior se conectará con un
valor predeterminado del elemento interno. Debido a esta distinción, este tipo de consultas se traduce a
CROSS APPLY en ausencia de DefaultIfEmpty y OUTER APPLY cuando se aplica DefaultIfEmpty . Algunas bases de
datos como SQLite no admiten los operadores APPLY , por lo que este tipo de consulta no se puede traducir.

var query = from b in [Link]<Blog>()


from p in [Link]<Post>().Select(p => [Link] + "=>" + [Link])
select new { b, p };

var query2 = from b in [Link]<Blog>()


from p in [Link]<Post>().Select(p => [Link] + "=>" + [Link]).DefaultIfEmpty()
select new { b, p };

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]


FROM [Blogs] AS [b]
CROSS APPLY [Posts] AS [p]

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], ([b].[Url] + N'=>') + [p].[Title] AS [p]


FROM [Blogs] AS [b]
OUTER APPLY [Posts] AS [p]

GROUP BY
Los operadores GroupBy de LINQ crean un resultado de tipo IGrouping<TKey, TElement> , donde TKey y TElement
podrían ser cualquier tipo arbitrario. Además, IGrouping implementa IEnumerable<TElement> , lo que significa que
se puede redactar sobre este elemento con cualquier operador de LINQ después de la agrupación. Como ninguna
estructura de base de datos puede representar una instancia de IGrouping , en la mayoría de los casos los
operadores GroupBy no tienen ninguna traducción. Cuando se aplica un operador de agregado a cada grupo, lo
que devuelve un valor escalar, se puede traducir a GROUP BY de SQL en las bases de datos relacionales. GROUP BY
de SQL también es restrictivo. Requiere que se agrupe solo por valores escalares. La proyección solo puede
contener columnas de clave de agrupación o cualquier agregado aplicado en una columna. EF Core identifica este
patrón y lo traduce al servidor, como en el ejemplo siguiente:
var query = from p in [Link]<Post>()
group p by [Link] into g
select new
{
[Link],
Count = [Link]()
};

SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]


FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]

EF Core también traduce las consultas en las que un operador de agregado en la agrupación aparece en un
operador Where o OrderBy (u otro orden) de LINQ. Usa la cláusula HAVING en SQL para la cláusula WHERE. La
parte de la consulta antes de aplicar el operador GroupBy puede ser cualquier consulta compleja, siempre que se
pueda traducir al servidor. Además, una vez que se aplican operadores de agregado en una consulta de agrupación
para quitar agrupaciones del origen resultante, se puede redactar sobre ella como cualquier otra consulta.

var query = from p in [Link]<Post>()


group p by [Link] into g
where [Link]() > 0
orderby [Link]
select new
{
[Link],
Count = [Link]()
};

SELECT [p].[AuthorId] AS [Key], COUNT(*) AS [Count]


FROM [Posts] AS [p]
GROUP BY [p].[AuthorId]
HAVING COUNT(*) > 0
ORDER BY [p].[AuthorId]

Los operadores de agregado que admite EF Core son los siguientes


Average
Count
LongCount
Max
Min
Sum

Left Join
Aunque Left Join no es un operador de LINQ, las bases de datos relacionales tienen el concepto de combinación
izquierda que se usa con frecuencia en las consultas. Un patrón determinado en las consultas LINQ proporciona el
mismo resultado que LEFT JOIN en el servidor. EF Core identifica estos patrones y genera la operación LEFT JOIN
equivalente en el lado servidor. El patrón implica la creación de GroupJoin entre los dos orígenes de datos y,
después, la reducción de la agrupación mediante el operador SelectMany con DefaultIfEmpty en el origen de
agrupación para que coincida con NULL cuando el elemento interior no tiene un elemento relacionado. En el
ejemplo siguiente se muestra el aspecto de este patrón y lo que genera.
var query = from b in [Link]<Blog>()
join p in [Link]<Post>()
on [Link] equals [Link] into grouping
from p in [Link]()
select new { b, p };

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url], [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].


[Content], [p].[Rating], [p].[Title]
FROM [Blogs] AS [b]
LEFT JOIN [Posts] AS [p] ON [b].[BlogId] = [p].[BlogId]

En el patrón anterior se crea una estructura compleja en el árbol de expresión. Por eso, EF Core requiere que se
reduzcan los resultados de agrupación del operador GroupJoin en un paso inmediatamente después del operador.
Aunque se use GroupJoin-DefaultIfEmpty-SelectMany, pero en otro patrón, es posible que no se identifique como
una combinación izquierda.
Carga de datos relacionados
08/04/2020 • 13 minutes to read • Edit Online

Entity Framework Core permite usar las propiedades de navegación del modelo para cargar las entidades
relacionados. Existen tres patrones de O/RM comunes que se usan para cargar los datos relacionados.
Carga diligente significa que los datos relacionados se cargan desde la base de datos como parte de la
consulta inicial.
Carga explícita significa que los datos relacionados se cargan de manera explícita desde la base de datos
más adelante.
Carga diferida significa que los datos relacionados se cargan de manera transparente desde la base de datos
cuando se accede a la propiedad de navegación.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Carga diligente
Puede usar el método Include para especificar los datos relacionados que se incluirán en los resultados de la
consulta. En el ejemplo siguiente, las entradas relacionadas rellenarán la propiedad Posts de los blogs que se
devuelvan en los resultados.

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.ToList();
}

TIP
Entity Framework Core corregirá automáticamente las propiedades de navegación para todas las entidades que se cargaron
previamente en la instancia del contexto. Por tanto, incluso si los datos de una propiedad de navegación no se incluyen
explícitamente, es posible que la propiedad se siga rellenando si algunas o todas las entidades relacionadas se cargaron
previamente.

Puede incluir los datos relacionados de varias relaciones en una sola consulta.

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.Include(blog => [Link])
.ToList();
}

Inclusión de varios niveles


Puede explorar en profundidad las relaciones para incluir varios niveles de datos relacionados con el método
ThenInclude . En el ejemplo siguiente se cargan todos los blogs, las entradas relacionadas y el creador de cada
entrada.

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.ThenInclude(post => [Link])
.ToList();
}

Puede encadenar varias llamadas en ThenInclude para continuar incluyendo más niveles de datos relacionados.

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.ThenInclude(post => [Link])
.ThenInclude(author => [Link])
.ToList();
}

Puede combinar todo esto para incluir datos relacionados provenientes de varios niveles y varias raíces en la
misma consulta.

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.ThenInclude(post => [Link])
.ThenInclude(author => [Link])
.Include(blog => [Link])
.ThenInclude(owner => [Link])
.ToList();
}

Es posible que quiera incluir varias entidades relacionadas para una de las entidades que se está incluyendo. Por
ejemplo, cuando consulte Blogs , incluye Posts y luego quiere incluir tanto Author como Tags de las Posts .
Para hacerlo, debe especificar cada inicio de ruta de acceso de inclusión en la raíz. Por ejemplo,
Blog -> Posts -> Author y Blog -> Posts -> Tags . Esto no significa que vaya a obtener combinaciones
redundantes; en la mayoría de los casos, EF consolidará las combinaciones al generar código SQL.

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.ThenInclude(post => [Link])
.Include(blog => [Link])
.ThenInclude(post => [Link])
.ToList();
}

Cau t i on

Desde la versión 3.0.0, todas las instancias de Include producirán que se agregue una combinación JOIN
adicional a las consultas SQL generadas por los proveedores relacionales, mientras que las versiones anteriores
generaban consultas SQL adicionales. Esto puede cambiar significativamente el rendimiento de las consultas,
tanto para bien como para mal. En concreto, es posible que las consultas LINQ con un número excesivamente alto
de operadores Include deban dividirse en varias consultas LINQ independientes con el fin de evitar el problema
de explosión cartesiana.
Inclusión en tipos derivados
Puede incluir datos relacionados provenientes de las navegaciones que se definen solo en un tipo derivado con
Include y ThenInclude .

Dado el modelo siguiente:

public class SchoolContext : DbContext


{
public DbSet<Person> People { get; set; }
public DbSet<School> Schools { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<School>().HasMany(s => [Link]).WithOne(s => [Link]);
}
}

public class Person


{
public int Id { get; set; }
public string Name { get; set; }
}

public class Student : Person


{
public School School { get; set; }
}

public class School


{
public int Id { get; set; }
public string Name { get; set; }

public List<Student> Students { get; set; }


}

El contenido de la navegación School de todas las personas que son estudiantes se puede cargar de manera
diligente mediante el uso de diversos patrones:
con conversión

[Link](person => ((Student)person).School).ToList()

con el operador as

[Link](person => (person as Student).School).ToList()

con la sobrecarga de Include que toma el parámetro del tipo string

[Link]("School").ToList()

Carga explícita
Puede cargar de manera explícita una propiedad de navegación a través de la API [Link](...) .
using (var context = new BloggingContext())
{
var blog = [Link]
.Single(b => [Link] == 1);

[Link](blog)
.Collection(b => [Link])
.Load();

[Link](blog)
.Reference(b => [Link])
.Load();
}

También puede cargar de manera explícita una propiedad de navegación si ejecuta una consulta independiente
que devuelve las entidades relacionadas. Si está habilitado el seguimiento de cambios, cuando se cargue una
entidad, EF Core establecerá automáticamente las propiedades de navegación de la entidad recién cargada para
hacer referencia a cualquier entidad ya cargada y establecerá las propiedades de navegación de las entidades ya
cargadas para hacer referencia a la entidad recién cargada.
Consulta de las entidades relacionadas
También puede obtener una consulta LINQ que represente el contenido de una propiedad de navegación.
Esto permite, entre otras acciones, ejecutar un operador de agregado en las entidades relacionadas sin cargarlas
en la memoria.

using (var context = new BloggingContext())


{
var blog = [Link]
.Single(b => [Link] == 1);

var postCount = [Link](blog)


.Collection(b => [Link])
.Query()
.Count();
}

También puede filtrar las entidades relacionadas que se cargan en la memoria.

using (var context = new BloggingContext())


{
var blog = [Link]
.Single(b => [Link] == 1);

var goodPosts = [Link](blog)


.Collection(b => [Link])
.Query()
.Where(p => [Link] > 3)
.ToList();
}

Carga diferida
La manera más simple de usar la carga diferida es instalar el paquete [Link] y
habilitarlo con una llamada a UseLazyLoadingProxies . Por ejemplo:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseLazyLoadingProxies()
.UseSqlServer(myConnectionString);

O al usar AddDbContext:

.AddDbContext<BloggingContext>(
b => [Link]()
.UseSqlServer(myConnectionString));

EF Core habilitará la carga diferida de cualquier propiedad de navegación que se pueda invalidar, es decir, debe
ser virtual y debe estar en una clase desde la que se pueda heredar. Por ejemplo, en las entidades siguientes, las
propiedades de navegación [Link] y [Link] serán de carga diferida.

public class Blog


{
public int Id { get; set; }
public string Name { get; set; }

public virtual ICollection<Post> Posts { get; set; }


}

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public virtual Blog Blog { get; set; }


}

Carga diferida sin servidores proxy


Los servidores proxy de carga diferida funcionan inyectando el servicio ILazyLoader en una entidad, tal como se
describe en Entity Type Constructors (Constructores de tipo de entidad). Por ejemplo:
public class Blog
{
private ICollection<Post> _posts;

public Blog()
{
}

private Blog(ILazyLoader lazyLoader)


{
LazyLoader = lazyLoader;
}

private ILazyLoader LazyLoader { get; set; }

public int Id { get; set; }


public string Name { get; set; }

public ICollection<Post> Posts


{
get => [Link](this, ref _posts);
set => _posts = value;
}
}

public class Post


{
private Blog _blog;

public Post()
{
}

private Post(ILazyLoader lazyLoader)


{
LazyLoader = lazyLoader;
}

private ILazyLoader LazyLoader { get; set; }

public int Id { get; set; }


public string Title { get; set; }
public string Content { get; set; }

public Blog Blog


{
get => [Link](this, ref _blog);
set => _blog = value;
}
}

Esto no requiere tipos de entidad de los cuales heredar ni propiedades de navegación para ser virtual y permite
que las instancias de entidad creadas con new se carguen de manera diferida una vez que se asocian a un
contexto. Sin embargo, requiere una referencia al servicio ILazyLoader , que está definido en el paquete
[Link]. Este paquete contiene un conjunto mínimo de tipos, por lo que es
muy poco el impacto al depender de él. Sin embargo, para evitar por completo depender de cualquier paquete de
EF Core en los tipos de entidad, es posible insertar el método [Link] como delegado. Por ejemplo:
public class Blog
{
private ICollection<Post> _posts;

public Blog()
{
}

private Blog(Action<object, string> lazyLoader)


{
LazyLoader = lazyLoader;
}

private Action<object, string> LazyLoader { get; set; }

public int Id { get; set; }


public string Name { get; set; }

public ICollection<Post> Posts


{
get => [Link](this, ref _posts);
set => _posts = value;
}
}

public class Post


{
private Blog _blog;

public Post()
{
}

private Post(Action<object, string> lazyLoader)


{
LazyLoader = lazyLoader;
}

private Action<object, string> LazyLoader { get; set; }

public int Id { get; set; }


public string Title { get; set; }
public string Content { get; set; }

public Blog Blog


{
get => [Link](this, ref _blog);
set => _blog = value;
}
}

El código anterior usa un método de extensión Load para que el uso del delegado sea un poco más limpio:
public static class PocoLoadingExtensions
{
public static TRelated Load<TRelated>(
this Action<object, string> loader,
object entity,
ref TRelated navigationField,
[CallerMemberName] string navigationName = null)
where TRelated : class
{
loader?.Invoke(entity, navigationName);

return navigationField;
}
}

NOTE
El parámetro de constructor del delegado de carga diferida se debe denominar "lazyLoader". La configuración para usar otro
nombre está planificada para una versión futura.

Datos relacionados y serialización


Como EF Core corregirá automáticamente las propiedades de navegación, puede terminar con ciclos en el grafo
de objetos. Por ejemplo, cargar un blog y sus entradas relacionadas dará lugar a un objeto de blog que hará
referencia a una colección de entradas. Cada una de esas entradas tendrá una referencia de vuelta al blog.
Algunos marcos de serialización no permiten ese tipo de ciclos. Por ejemplo, [Link] generará la excepción
siguiente si se encuentra un ciclo.

[Link]: Self referencing loop detected for property 'Blog' with type
'[Link]'. ([Link]: se detectó un bucle con
autorreferencia para la propiedad "Blog" con el tipo "[Link]").

Si usa [Link] Core, puede configurar [Link] para que omita los ciclos que encuentre en el grafo del objeto.
Esto se hace en el método ConfigureServices(...) en [Link] .

public void ConfigureServices(IServiceCollection services)


{
...

[Link]()
.AddJsonOptions(
options => [Link] =
[Link]
);

...
}

Otra alternativa consiste en decorar una de las propiedades de navegación con el atributo [JsonIgnore] , que
indica a [Link] que no recorra esa propiedad de navegación mientras se serializa.
Consultas asincrónicas
08/04/2020 • 2 minutes to read • Edit Online

Las consultas asincrónicas evitan bloquear un subproceso mientras la consulta se ejecuta en la base de datos. Las
consultas asincrónicas son importantes para mantener una interfaz de usuario dinámica en las aplicaciones cliente
de gran tamaño. También pueden aumentar el rendimiento de las aplicaciones web donde liberan el subproceso
para atender otras solicitudes de las aplicaciones web. Para más información, consulte Programación asincrónica en
C#.

WARNING
EF Core no admite que varias operaciones en paralelo se ejecuten en la misma instancia de contexto. Siempre debe esperar
que se complete una operación antes de iniciar la siguiente. Habitualmente, para esto se usa la palabra clave await en cada
una de las operaciones asincrónicas.

Entity Framework Core proporciona un conjunto de métodos de extensión asincrónicos similares a los de LINQ, que
ejecutan una consulta y devuelven resultados. Entre los ejemplos se incluyen ToListAsync() , ToArrayAsync() ,
SingleAsync() . No hay versiones asincrónicas de algunos operadores de LINQ como Where(...) o OrderBy(...)
porque estos métodos solo generan el árbol de la expresión de LINQ y no hacen que la consulta se ejecute en la
base de datos.

IMPORTANT
Los métodos de extensión asincrónicos de EF Core se define en el espacio de nombres [Link] . Es
necesario importar este espacio de nombres para que los métodos estén disponibles.

public async Task<List<Blog>> GetBlogsAsync()


{
using (var context = new BloggingContext())
{
return await [Link]();
}
}
Consultas SQL sin formato
08/04/2020 • 9 minutes to read • Edit Online

Entity Framework Core le permite descender hasta las consultas SQL sin formato cuando trabaja con una base de
datos relacional. Las consultas SQL sin formato son útiles si la consulta que quiere no se puede expresar mediante
LINQ. Las consultas SQL sin formato también se utilizan si el uso de una consulta LINQ genera una consulta SQL
ineficaz. Las consultas SQL sin formato pueden devolver tipos de entidad normales o tipos de entidad sin clave que
forman parte del modelo.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Consultas SQL básicas sin formato


Puede usar el método de extensión FromSqlRaw para empezar una consulta LINQ basada en una consulta SQL sin
formato. FromSqlRaw solo se puede usar en raíces de consulta, es decir, directamente en DbSet<> .

var blogs = [Link]


.FromSqlRaw("SELECT * FROM [Link]")
.ToList();

Las consultas SQL sin formato se pueden usar para ejecutar un procedimiento almacenado.

var blogs = [Link]


.FromSqlRaw("EXECUTE [Link]")
.ToList();

Paso de parámetros
WARNING
Use siempre la parametrización para las consultas SQL sin formato
Al indicar cualquier valor proporcionado por el usuario en una consulta SQL sin formato, debe tener cuidado para evitar
ataques por inyección de código SQL. Además de validar que dichos valores no contienen caracteres no válidos, use siempre
la parametrización que envía los valores separados del texto SQL.
En concreto, no pase nunca a $"" o FromSqlRaw una cadena concatenada o interpolada ( ExecuteSqlRaw ) con valores
proporcionados por el usuario sin validar. Los métodos FromSqlInterpolated y ExecuteSqlInterpolated permiten usar
la sintaxis de interpolación de cadenas de manera que se protege frente a los ataques por inyección de código SQL.

En el ejemplo siguiente se pasa un parámetro único a un procedimiento almacenado; para ello, se incluye un
marcador de posición de parámetro en la cadena de consulta SQL y se proporciona un argumento adicional.
Aunque esta sintaxis se pueda parecer a la de [Link] , el valor suministrado se encapsula en un elemento
DbParameter y el nombre del parámetro generado se inserta donde se haya especificado el marcador de posición
{0} .
var user = "johndoe";

var blogs = [Link]


.FromSqlRaw("EXECUTE [Link] {0}", user)
.ToList();

FromSqlInterpolated es similar a FromSqlRaw , pero permite usar la sintaxis de interpolación de cadenas. Al igual
que FromSqlRaw , FromSqlInterpolated solo se puede usar en raíces de consulta. Como en el ejemplo anterior, el
valor se convierte a DbParameter y no es vulnerable a la inyección de código SQL.

NOTE
Antes de la versión 3.0, FromSqlRaw y FromSqlInterpolated eran dos sobrecargas denominadas FromSql . Para obtener
más información, vea la sección sobre versiones anteriores.

var user = "johndoe";

var blogs = [Link]


.FromSqlInterpolated($"EXECUTE [Link] {user}")
.ToList();

También puede construir un elemento DbParameter y suministrarlo como un valor de parámetro. Dado que se usa
un marcador de posición de parámetro SQL normal, en lugar de un marcador de posición de cadena, FromSqlRaw
se puede usar de forma segura:

var user = new SqlParameter("user", "johndoe");

var blogs = [Link]


.FromSqlRaw("EXECUTE [Link] @user", user)
.ToList();

FromSqlRaw permite usar parámetros con nombre en la cadena de consulta SQL, lo que resulta útil cuando un
procedimiento almacenado tiene parámetros opcionales:

var user = new SqlParameter("user", "johndoe");

var blogs = [Link]


.FromSqlRaw("EXECUTE [Link] @filterByUser=@user", user)
.ToList();

Redacción con LINQ


Puede redactar sobre la consulta SQL sin formato inicial mediante operadores de LINQ. EF Core la tratará como
una subconsulta y redactará sobre ella en la base de datos. En el ejemplo siguiente se usa una consulta SQL sin
formato que realiza una selección en una función con valores de tabla (TVF). Y después se redacta sobre ella con
LINQ para realizar el filtrado y la ordenación.
var searchTerm = ".NET";

var blogs = [Link]


.FromSqlInterpolated($"SELECT * FROM [Link]({searchTerm})")
.Where(b => [Link] > 3)
.OrderByDescending(b => [Link])
.ToList();

La consulta anterior genera el código SQL siguiente:

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url]


FROM (
SELECT * FROM [Link](@p0)
) AS [b]
WHERE [b].[Rating] > 3
ORDER BY [b].[Rating] DESC

Inclusión de datos relacionados


El método Include puede usarse para incluir datos relacionados, igual que cualquier otra consulta LINQ:

var searchTerm = ".NET";

var blogs = [Link]


.FromSqlInterpolated($"SELECT * FROM [Link]({searchTerm})")
.Include(b => [Link])
.ToList();

La redacción con LINQ requiere que la consulta SQL sin procesar se pueda redactar, ya que EF Core tratará el
código SQL proporcionado como una subconsulta. Las consultas SQL que se pueden redactar empiezan con la
palabra clave SELECT . Es más, el código SQL que se pasa no debe contener ningún carácter ni opción que no sea
válido en una subconsulta, como los siguientes:
Un punto y coma final
En SQL Server, una sugerencia en el nivel de consulta final (por ejemplo, OPTION (HASH JOIN) )
En SQL Server, una cláusula ORDER BY que no se usa con OFFSET 0 O BIEN TOP 100 PERCENT en la cláusula
SELECT

SQL Server no permite la redacción sobre llamadas a procedimientos almacenados, por lo que cualquier intento
de aplicar operadores de consulta adicionales a ese tipo de llamada producirá código SQL no válido. Use el
método AsEnumerable o AsAsyncEnumerable justo después de los métodos FromSqlRaw o FromSqlInterpolated
para asegurarse de que EF Core no intente redactar sobre un procedimiento almacenado.

Seguimiento de cambios
Las consultas que usan los métodos FromSqlRaw o FromSqlInterpolated siguen las mismas reglas de seguimiento
de cambios que las demás consultas LINQ en EF Core. Por ejemplo, si la consulta proyecta tipos de entidad, se
realizará un seguimiento de los resultados de forma predeterminada.
En el ejemplo siguiente se usa una consulta SQL sin formato que realiza una selección en una función con valores
de tabla (TVF) y después deshabilita el seguimiento de cambios con la llamada a AsNoTracking :
var searchTerm = ".NET";

var blogs = [Link]


.FromSqlInterpolated($"SELECT * FROM [Link]({searchTerm})")
.AsNoTracking()
.ToList();

Limitaciones
Existen algunas limitaciones que debe considerar al usar las consultas SQL sin formato:
La consulta SQL debe devolver datos para todas las propiedades del tipo de entidad.
Los nombres de las columnas del conjunto de resultados deben coincidir con los nombres de las columnas a
los que se asignan las propiedades. Tenga en cuenta que este comportamiento es diferente al de EF6. En EF6 se
omitía la asignación de propiedades y columnas para las consultas SQL sin formato, y los nombres de las
columnas del conjunto de resultados tenían que coincidir con los nombres de las propiedades.
La consulta SQL no puede contener datos relacionados. Sin embargo, en muchos casos puede redactar sobre la
consulta si usa el operador Include para devolver datos relacionados (consulte Inclusión de datos
relacionados).

Versiones anteriores
EF Core 2.2 y las versiones anteriores tenían dos sobrecargas de método denominadas FromSql , que se
comportaban de la misma manera que las sobrecargas FromSqlRaw y FromSqlInterpolated más recientes.
Resultaba sencillo llamar de forma accidental al método de cadena sin formato cuando la intención era llamar al
método de cadena interpolada y viceversa. La llamada accidental a la sobrecarga incorrecta podría generar como
resultado consultas que no se parametrizaban cuando debían.
Filtros de consulta global
08/04/2020 • 4 minutes to read • Edit Online

NOTE
Esta característica se incluyó por primera vez en EF Core 2.0.

Los filtros de consulta global son predicados de consulta LINQ (una expresión booleana que habitualmente se pasa
al operador de consulta LINQ Where) aplicados a los tipos de entidad del modelo de metadatos (habitualmente en
OnModelCreating). Estos filtros se aplican automáticamente a las consultas LINQ que implican a esos tipos de
entidad, incluidos aquellos a los que se hace referencia de forma indirecta, por ejemplo mediante el uso de Include
o de referencias de propiedad de navegación directas. Algunas aplicaciones comunes de esta característica son:
Eliminación temporal : un tipo de entidad define una propiedad IsDeleted.
Ser vicios multiinquilino : un tipo de entidad define una propiedad TenantId.

Ejemplo
En el ejemplo siguiente se muestra cómo usar los filtros de consulta global para implementar los comportamientos
de consulta de multiinquilino y de eliminación temporal en un simple modelo de creación de blogs.

TIP
Puede ver un ejemplo de este artículo en GitHub.

En primer lugar, defina las entidades:

public class Blog


{
private string _tenantId;

public int BlogId { get; set; }


public string Name { get; set; }
public string Url { get; set; }

public List<Post> Posts { get; set; }


}

public class Post


{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public bool IsDeleted { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}

Tenga en cuenta la declaración de un campo tenantId en la entidad Blog. Se usará para asociar cada instancia de
blog con un inquilino específico. También hay definida una propiedad IsDeleted en el tipo de entidad Post. Se usa
para llevar un seguimiento de si una instancia Post se eliminó de manera temporal. Es decir, la instancia se marca
como eliminada sin quitar físicamente los datos subyacentes.
A continuación, configure los filtros de consulta en OnModelCreating con la API HasQueryFilter .

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>().Property<string>("_tenantId").HasColumnName("TenantId");

// Configure entity filters


[Link]<Blog>().HasQueryFilter(b => [Link]<string>(b, "_tenantId") == _tenantId);
[Link]<Post>().HasQueryFilter(p => ![Link]);
}

Las expresiones de predicado pasadas a las llamadas de HasQueryFilter ahora se aplicarán automáticamente a
cualquier consulta LINQ para esos tipos.

TIP
Tenga en cuenta el uso de un campo en el nivel de instancia de DbContext: _tenantId se usa para establecer el inquilino
actual. Los filtros de nivel de modelo usan el valor de la instancia de contexto correcta (es decir, la instancia que está
ejecutando la consulta).

NOTE
Actualmente no es posible definir varios filtros de consulta en la misma entidad. Solo se aplicará el último. Sin embargo,
puede definir un único filtro con varias condiciones mediante el operador lógico AND ( && en C#).

Deshabilitación de filtros
Los filtros se pueden deshabilitar para consultas LINQ individuales mediante el operador IgnoreQueryFilters() .

blogs = [Link]
.Include(b => [Link])
.IgnoreQueryFilters()
.ToList();

Limitaciones
Los filtros de consulta global tienen las limitaciones siguientes:
Solo se pueden definir filtros para el tipo de entidad raíz de una jerarquía de herencia.
Etiquetas de consulta
08/04/2020 • 2 minutes to read • Edit Online

NOTE
Esta característica es nueva en EF Core 2.2.

Esta característica ayuda a establecer la correlación de las consultas LINQ en el código con las consultas SQL
generadas capturadas en los registros. El usuario anota una consulta LINQ con el nuevo método TagWith() :

var nearestFriends =
(from f in [Link]("This is my spatial query!")
orderby [Link](myLocation) descending
select f).Take(5).ToList();

Esta consulta LINQ se traduce a la siguiente instrucción SQL:

-- This is my spatial query!

SELECT TOP(@__p_1) [f].[Name], [f].[Location]


FROM [Friends] AS [f]
ORDER BY [f].[Location].STDistance(@__myLocation_0) DESC

Es posible llamar a TagWith() muchas veces en la misma consulta. Las etiquetas de consulta son acumulativas. Por
ejemplo, si tenemos los siguientes métodos:

IQueryable<Friend> GetNearestFriends(Point myLocation) =>


from f in [Link]("GetNearestFriends")
orderby [Link](myLocation) descending
select f;

IQueryable<T> Limit<T>(IQueryable<T> source, int limit) =>


[Link]("Limit").Take(limit);

La siguiente consulta:

var results = Limit(GetNearestFriends(myLocation), 25).ToList();

Se traduce en:

-- GetNearestFriends

-- Limit

SELECT TOP(@__p_1) [f].[Name], [f].[Location]


FROM [Friends] AS [f]
ORDER BY [f].[Location].STDistance(@__myLocation_0) DESC

También es posible utilizar cadenas de varias líneas como etiquetas de consulta. Por ejemplo:
var results = Limit(GetNearestFriends(myLocation), 25).TagWith(
@"This is a multi-line
string").ToList();

Produce el siguiente SQL:

-- GetNearestFriends

-- Limit

-- This is a multi-line
-- string

SELECT TOP(@__p_1) [f].[Name], [f].[Location]


FROM [Friends] AS [f]
ORDER BY [f].[Location].STDistance(@__myLocation_0) DESC

Restricciones conocidas
Las etiquetas de consulta no se pueden parametrizar : EF Core siempre trata las etiquetas de consulta de la
consulta LINQ como literales de cadena que se incluyen en el código SQL generado. Las consultas compiladas que
toman las etiquetas de consulta como parámetros no están permitidas.
Funcionamiento de las consultas
08/04/2020 • 4 minutes to read • Edit Online

Entity Framework Core usa Language Integrated Query (LINQ) para consultar datos de la base de datos. LINQ
permite usar C# (o el lenguaje .NET que prefiera) para escribir consultas fuertemente tipadas basadas en el
contexto derivado y las clases de entidad.

La duración de una consulta


A continuación se muestra información general de alto nivel del proceso por el que pasa toda consulta.
1. Entity Framework Core procesa la consulta LINQ para crear una representación que está lista para que el
proveedor de base de datos la procese.
a. El resultado se almacena en caché para que no sea necesario hacer este procesamiento cada vez que se
ejecuta la consulta.
2. El resultado se pasa al proveedor de base de datos.
a. El proveedor de base de datos identifica qué partes de la consulta se pueden evaluar en la base de datos.
b. Estas partes de la consulta se traducen al lenguaje de la consulta específico para la base de datos (por
ejemplo, SQL para una base de datos relacional).
c. Se envía una consulta a la base de datos y se devuelve el conjunto de resultados (los resultados son
valores de la base de datos y no instancias de entidad).
3. Para cada elemento del conjunto de resultados
a. Si se trata de una consulta con seguimiento, EF comprueba si los datos representan una entidad que ya
existe en la herramienta de seguimiento de cambios para la instancia de contexto.
Si es así, se devuelve la entidad existente.
En caso contrario, se crea una entidad nueva, se configura el seguimiento de cambios y se
devuelve la entidad nueva.
b. Si se trata de una consulta que no es de seguimiento, siempre se crea y devuelve una entidad nueva.

Cuándo se ejecutan las consultas


Cuando llama a los operadores LINQ, simplemente crea una representación de la consulta en memoria. La
consulta solo se envía a la base de datos cuando se usan los resultados.
Las operaciones más comunes que generan que la consulta se envíe a la base de datos son:
La iteración de los resultados en un bucle for
El uso de operadores como ToList , ToArray , Single y Count o las sobrecargas asincrónicas equivalentes

WARNING
Valide siempre la entrada del usuario: aunque EF Core protege contra los ataques por inyección de código SQL con el
uso de parámetros y el escape de cadenas literales en consultas, no valida las entradas. Se debe realizar una validación
apropiada, según los requisitos de la aplicación, antes de que los valores de los orígenes que no son de confianza se usen en
consultas LINQ, se asignen a las propiedades de una entidad o se pasen a otras API de EF Core. Esto incluye cualquier
intervención del usuario que se use para construir consultas de manera dinámica. Incluso al usar LINQ, si acepta la
intervención del usuario para crear expresiones, necesita garantizar que solo se pueden construir las expresiones previstas.
Guardado de datos
08/04/2020 • 2 minutes to read • Edit Online

Cada instancia de contexto tiene un elemento ChangeTracker que es responsable de realizar el seguimiento de los
cambios que deben escribirse en la base de datos. Al realizar cambios en instancias de las clases de entidad, estos
cambios se registran en ChangeTracker y luego se escriben en la base de datos cuando se llama a SaveChanges . El
proveedor de base de datos es responsable de convertir los cambios en operaciones específicas de la base de
datos (por ejemplo, los comandos INSERT , UPDATE y DELETE de una base de datos relacional).
Guardado básico
08/04/2020 • 3 minutes to read • Edit Online

Obtenga información sobre cómo agregar, modificar y quitar datos mediante las clases de entidad y contexto.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Agregar datos
Use el método [Link] para agregar instancias nuevas de las clases de entidad. Los datos se insertarán en la
base de datos cuando llame a SaveChanges.

using (var context = new BloggingContext())


{
var blog = new Blog { Url = "[Link] };
[Link](blog);
[Link]();
}

TIP
Los métodos Add, Attach y Update funcionan en todo el grafo de entidades que se pasaron a ellos, tal como se describe en
la sección de datos relacionados. También puede usar la propiedad [Link] para establecer el estado de una sola
unidad. Por ejemplo, [Link](blog).State = [Link] .

Actualización de datos
EF detectará automáticamente los cambios hechos en una entidad existente de la que hace seguimiento el contexto.
Esto incluye entidades que se cargan o consultan desde la base de datos y las entidades que se agregaron y
guardaron anteriormente en la base de datos.
Solo debe modificar los valores asignados a las propiedades y llamar a SaveChanges.

using (var context = new BloggingContext())


{
var blog = [Link]();
[Link] = "[Link]
[Link]();
}

Eliminar datos
Use el método [Link] para eliminar las instancias de las clases de entidad.
Si la entidad ya existe en la base de datos, se eliminará durante SaveChanges. Si la entidad todavía no se guarda en
la base de datos (es decir, si se hace seguimiento cuando se agrega), se quitará del contexto y ya no se insertará
cuando se llame a SaveChanges.
using (var context = new BloggingContext())
{
var blog = [Link]();
[Link](blog);
[Link]();
}

Varias operaciones en una única llamada a SaveChanges


Puede combinar varias operaciones Add, Update y Remove en una sola llamada a SaveChanges.

NOTE
Para la mayoría de los proveedores de base de datos, SaveChanges es transaccional. Esto significa que todas las operaciones
se realizarán correctamente o presentarán un error y que nunca se aplicarán de manera parcial.

using (var context = new BloggingContext())


{
// seeding database
[Link](new Blog { Url = "[Link] });
[Link](new Blog { Url = "[Link] });
[Link]();
}

using (var context = new BloggingContext())


{
// add
[Link](new Blog { Url = "[Link] });
[Link](new Blog { Url = "[Link] });

// update
var firstBlog = [Link]();
[Link] = "";

// remove
var lastBlog = [Link]();
[Link](lastBlog);

[Link]();
}
Guardado de datos relacionados
08/04/2020 • 4 minutes to read • Edit Online

Además de las entidades aisladas, también puede usar las relaciones definidas en el modelo.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Incorporación de un grafo de entidades nuevas


Si crea varias entidades relacionadas, agregar una de ellas al contexto hará que las otras también se agreguen.
En el ejemplo siguiente, el blog y tres entradas relacionadas se insertan en la base de datos. Las entradas se
buscan y agregan, porque son accesibles a través de la propiedad de navegación [Link] .

using (var context = new BloggingContext())


{
var blog = new Blog
{
Url = "[Link]
Posts = new List<Post>
{
new Post { Title = "Intro to C#" },
new Post { Title = "Intro to [Link]" },
new Post { Title = "Intro to F#" }
}
};

[Link](blog);
[Link]();
}

TIP
Use la propiedad [Link] para establecer el estado de una sola unidad. Por ejemplo,
[Link](blog).State = [Link] .

Incorporación de una entidad relacionada


Si hace referencia a una entidad nueva desde la propiedad de navegación de una entidad a la que el contexto ya
hace seguimiento, se detectará la entidad y se insertará en la base de datos.
En el ejemplo siguiente, la entidad post se inserta porque se agrega a la propiedad Posts de la entidad blog
que se capturó de la base de datos.
using (var context = new BloggingContext())
{
var blog = [Link](b => [Link]).First();
var post = new Post { Title = "Intro to EF Core" };

[Link](post);
[Link]();
}

Cambio de las relaciones


Si cambia la propiedad de navegación de una entidad, los cambios correspondientes se harán en la columna de
clave externa de la base de datos.
En el ejemplo siguiente, la entidad post se actualiza para que pertenezca a la entidad blog nueva, porque su
propiedad de navegación Blog está establecida para que apunte a blog . Tenga en cuenta que blog también se
insertará en la base de datos porque se trata de una entidad nueva a la que hace referencia la propiedad de
navegación de una entidad de la que el contexto ( post ) ya hace seguimiento.

using (var context = new BloggingContext())


{
var blog = new Blog { Url = "[Link] };
var post = [Link]();

[Link] = blog;
[Link]();
}

Eliminación de relaciones
Para quitar una relación, establezca una navegación de referencia en null o quite la entidad relacionada de una
navegación de colección.
Quitar una relación puede tener efectos secundarios en la entidad dependiente, según el comportamiento de
eliminación en cascada que esté configurado en la relación.
De manera predeterminada, en el caso de las relaciones obligatorias, hay configurado un comportamiento de
eliminación en cascada y la entidad secundaria o dependiente se eliminará de la base de datos. En el caso de las
relaciones opcionales, no hay configurada una eliminación en cascada de manera predeterminada, pero la
propiedad de clave externa se establecerá en NULL.
Consulte la sección sobre las relaciones obligatorias y opcionales para más información sobre cómo se puede
configurar la obligatoriedad de las relaciones.
Consulte el artículo sobre la eliminación en cascada para más detalles sobre el funcionamiento de los
comportamientos de eliminación en cascada, cómo se pueden configurar de manera explícita y cómo se
seleccionan por convención.
En el ejemplo siguiente, se configura una eliminación en cascada en la relación entre Blog y Post , por lo que la
entidad post se elimina de la base de datos.
using (var context = new BloggingContext())
{
var blog = [Link](b => [Link]).First();
var post = [Link]();

[Link](post);
[Link]();
}
Eliminación en cascada
08/04/2020 • 23 minutes to read • Edit Online

En la terminología de las bases de datos, la eliminación en cascada se usa habitualmente para describir una
característica que permite eliminar una fila para desencadenar de manera automática la eliminación de las filas
relacionadas. Un concepto estrechamente relacionado que también abarcan los comportamientos de eliminación
de EF Core es la eliminación automática de una entidad secundaria cuando se interrumpe su relación con una
entidad primaria. Esto normalmente se conoce como la "eliminación de entidades huérfanas".
EF Core implementa varios comportamientos de eliminación distintos y permite configurar estos
comportamientos de las relaciones individuales. EF Core también implementa convenciones que configuran
automáticamente útiles comportamientos de eliminación predeterminados para cada relación en función de la
obligatoriedad de la relación.

Comportamientos de eliminación
Los comportamientos de eliminación se define en el tipo de enumerador DeleteBehavior y se pueden pasar a la
API fluida OnDelete para controlar si la eliminación de una entidad principal o primaria o la interrupción de la
relación con entidades dependientes o secundarias debería tener un efecto secundario en estas últimas.
Hay tres acciones que EF puede llevar a cabo cuando se elimina una entidad principal o primaria o cuando se
interrumpe la relación con una entidad secundaria:
Se puede eliminar la entidad secundaria o dependiente.
Los valores de clave externa de la entidad secundaria se pueden establecer en NULL.
La entidad secundaria se mantiene sin cambios.

NOTE
El comportamiento de eliminación configurado en el modelo EF Core solo se aplica cuando la entidad principal se elimina
mediante EF Core y las entidades dependientes se cargan en la memoria (es decir, en el caso de las entidades dependientes
con seguimiento). Es necesario configurar un comportamiento en cascada correspondiente en la base de datos para
garantizar que la acción necesaria se aplique a los datos a los que el contexto no hace seguimiento. Si usa EF Core para
crear la base de datos, este comportamiento en cascada se configurará automáticamente.

La segunda acción mencionada, establecer un valor de clave externa en NULL, no es válida si la clave externa no
admite un valor NULL. (Una clave externa que no admite un valor NULL equivale a una relación obligatoria). En
estos casos, EF Core hace seguimiento de que la propiedad de la clave externa se haya marcado como NULL hasta
que se llama a SaveChanges, momento en que se genera una excepción porque el cambio no puede durar hasta
la base de datos. Esto es similar a obtener una infracción de restricción de la base de datos.
Existen cuatro comportamientos de eliminación, los que se indican en las tablas siguientes.
Relaciones opcionales
En el caso de las relaciones opcionales (clave externa que admite un valor NULL) es posible guardar un valor de
clave externa NULL, lo que tiene como resultado los efectos siguientes:
EF EC TO EN L A EN T IDA D DEP EN DIEN T E EF EC TO EN L A EN T IDA D DEP EN DIEN T E
N O M B RE DEL C O M P O RTA M IEN TO O SEC UN DA RIA EN L A M EM O RIA O SEC UN DA RIA EN L A B A SE DE DATO S

Cascade Las entidades se eliminan Las entidades se eliminan

ClientSetNull (valor predeterminado) Las propiedades de clave externa se None


establecen en NULL

SetNull Las propiedades de clave externa se Las propiedades de clave externa se


establecen en NULL establecen en NULL

Restrict None None

Relaciones obligatorias
En el caso de las relaciones obligatorias (clave externa que no admite un valor NULL) no es posible guardar un
valor de clave externa NULL, lo que tiene como resultado los efectos siguientes:

EF EC TO EN L A EN T IDA D DEP EN DIEN T E EF EC TO EN L A EN T IDA D DEP EN DIEN T E


N O M B RE DEL C O M P O RTA M IEN TO O SEC UN DA RIA EN L A M EM O RIA O SEC UN DA RIA EN L A B A SE DE DATO S

Cascade (valor predeterminado) Las entidades se eliminan Las entidades se eliminan

ClientSetNull SaveChanges genera una excepción None

SetNull SaveChanges genera una excepción SaveChanges genera una excepción

Restrict None None

En las tablas anteriores, None puede dar lugar a una infracción de restricción. Por ejemplo, si se elimina una
entidad principal o secundaria pero no se hace ninguna acción para cambiar la clave externa de una entidad
dependiente o secundaria, es probable que la base de datos genere una excepción en SaveChanges debido a una
infracción de restricción externa.
En un nivel superior:
Si tiene entidades que no pueden existir sin una entidad primaria y quiere que EF se encargue de eliminar
automáticamente las entidades secundarias, use Cascade.
Habitualmente, las entidades que no pueden existir sin una entidad primaria usan las relaciones
obligatorias, en las que el valor predeterminado es Cascade.
Si tiene entidades que pueden tener o no una entidad primaria y quiere que EF se encargue de anular
automáticamente la clave externa, use ClientSetNull.
Habitualmente, las entidades que pueden existir sin una entidad primaria usan las relaciones
opcionales, en las que el valor predeterminado es ClientSetNull.
Si quiere que la base de datos también intente propagar los valores NULL a las claves externas
secundarias incluso si no se cargó la entidad secundaria, use SetNull. Sin embargo, tenga en cuenta que
la base de datos debe admitir esta opción y que configurar de este modo la base de datos puede dar
lugar a otras restricciones, por lo que, en la práctica, a menudo esta opción no es factible. Es por este
motivo que SetNull no es el valor predeterminado.
Si no quiere que EF Core elimine una entidad o anule la clave externa automáticamente, use Restrict. Tenga en
cuenta que esta acción requiere que el código mantenga las entidades secundarias y sus valores de clave
externa sincronizados de manera manual. Si no es así, se generarán excepciones de restricción.
NOTE
En EF Core, a diferencia de lo que ocurre en EF6, los efectos en cascada no se producen de inmediato, sino que solo cuando
se llama a SaveChanges.

NOTE
Cambios en EF Core 2.0: en versiones anteriores, Restrict haría que las propiedades de claves externas opcionales de las
entidades dependientes con seguimiento se establecieran en NULL y que este fuera el comportamiento de eliminación
predeterminado de las relaciones opcionales. En EF Core 2.0, se introdujo ClientSetNull para representar ese
comportamiento y se transformó en el valor predeterminado de las relaciones opcionales. El comportamiento de Restrict se
ajustó para que nunca haya efectos secundarios en las entidades dependientes.

Ejemplos de eliminación de entidades


El código siguiente forma parte de un ejemplo que se puede descargar y ejecutar. El ejemplo muestra qué pasa
con cada comportamiento de eliminación, tanto en las relaciones opcionales como en las obligatorias, cuando se
elimina una entidad primaria.

var blog = [Link](b => [Link]).First();


var posts = [Link]();

DumpEntities(" After loading entities:", context, blog, posts);

[Link](blog);

DumpEntities($" After deleting blog '{[Link]}':", context, blog, posts);

try
{
[Link]();
[Link](" Saving changes:");

[Link]();

DumpSql();

DumpEntities(" After SaveChanges:", context, blog, posts);


}
catch (Exception e)
{
DumpSql();

[Link]();
[Link]($" SaveChanges threw {[Link]().Name}: {(e is DbUpdateException ?
[Link] : [Link])}");
}

Analicemos cada variación para comprender lo que sucede.


[Link] con relación obligatoria u opcional
After loading entities:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After deleting blog '1':


Blog '1' is in state Deleted with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

Saving changes:
DELETE FROM [Posts] WHERE [PostId] = 1
DELETE FROM [Posts] WHERE [PostId] = 2
DELETE FROM [Blogs] WHERE [BlogId] = 1

After SaveChanges:
Blog '1' is in state Detached with 2 posts referenced.
Post '1' is in state Detached with FK '1' and no reference to a blog.
Post '2' is in state Detached with FK '1' and no reference to a blog.

El blog se marca como eliminado


Inicialmente, las entradas no se modifican porque las cascadas no se producen hasta que se genera
SaveChanges
SaveChanges envía eliminaciones para las entidades dependientes o secundarias (entradas) y para la entidad
principal o primaria (blog)
Después de guardar, todas las entidades se desasocian porque se eliminaron de la base de datos
[Link] o [Link] con relación obligatoria

After loading entities:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After deleting blog '1':


Blog '1' is in state Deleted with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1

SaveChanges threw DbUpdateException: Cannot insert the value NULL into column 'BlogId', table
'[Link]'; column does not allow nulls. UPDATE fails. The statement has been
terminated.

El blog se marca como eliminado


Inicialmente, las entradas no se modifican porque las cascadas no se producen hasta que se genera
SaveChanges
SaveChanges intenta establecer la clave externa de la entrada en NULL, pero se produce un error porque la
clave externa no admite un valor NULL
[Link] o [Link] con relación opcional
After loading entities:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After deleting blog '1':


Blog '1' is in state Deleted with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 2
DELETE FROM [Blogs] WHERE [BlogId] = 1

After SaveChanges:
Blog '1' is in state Detached with 2 posts referenced.
Post '1' is in state Unchanged with FK 'null' and no reference to a blog.
Post '2' is in state Unchanged with FK 'null' and no reference to a blog.

El blog se marca como eliminado


Inicialmente, las entradas no se modifican porque las cascadas no se producen hasta que se genera
SaveChanges
SaveChanges intenta establecer la clave externa de las entidades dependientes o secundarias (entradas) en
NULL antes de eliminar la entidad principal o primaria (blog)
Después de guardar, se elimina la entidad principal o primaria (blog), pero todavía se hace seguimiento de las
entidades dependientes o secundarias (entradas)
Las entidades dependientes o secundarias (entradas) con seguimiento ahora tienen valores de clave externa
NULL y se quitó la referencia a la entidad principal o primaria (blog) que se eliminó
[Link] con relación obligatoria u opcional

After loading entities:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After deleting blog '1':


Blog '1' is in state Deleted with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

Saving changes:
SaveChanges threw InvalidOperationException: The association between entity types 'Blog' and 'Post' has
been severed but the foreign key for this relationship cannot be set to null. If the dependent entity should
be deleted, then setup the relationship to use cascade deletes.

El blog se marca como eliminado


Inicialmente, las entradas no se modifican porque las cascadas no se producen hasta que se genera
SaveChanges
Como Restrict le indica a EF que no establezca automáticamente la clave externa en NULL, no se anula y
SaveChanges genera una excepción sin guardar

Ejemplos de eliminación de entidades huérfanas


El código siguiente forma parte de un ejemplo que se puede descargar y ejecutar. El ejemplo muestra qué sucede
en cada comportamiento de eliminación, tanto para las relaciones opcionales como para las obligatorias, cuando
se interrumpe la relación entre una entidad primaria o principal y sus entidades secundarias o dependientes. En
este ejemplo, la relación se interrumpe al quitar las entidades dependientes o secundarias (entradas) de la
propiedad de navegación de la colección en la entidad principal o primaria (blog). Sin embargo, el
comportamiento es el mismo si se anula la referencia de la entidad secundaria o dependiente respecto de la
entidad principal o primaria.

var blog = [Link](b => [Link]).First();


var posts = [Link]();

DumpEntities(" After loading entities:", context, blog, posts);

[Link]();

DumpEntities(" After making posts orphans:", context, blog, posts);

try
{
[Link]();
[Link](" Saving changes:");

[Link]();

DumpSql();

DumpEntities(" After SaveChanges:", context, blog, posts);


}
catch (Exception e)
{
DumpSql();

[Link]();
[Link]($" SaveChanges threw {[Link]().Name}: {(e is DbUpdateException ?
[Link] : [Link])}");
}

Analicemos cada variación para comprender lo que sucede.


[Link] con relación obligatoria u opcional

After loading entities:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After making posts orphans:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Modified with FK '1' and no reference to a blog.
Post '2' is in state Modified with FK '1' and no reference to a blog.

Saving changes:
DELETE FROM [Posts] WHERE [PostId] = 1
DELETE FROM [Posts] WHERE [PostId] = 2

After SaveChanges:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Detached with FK '1' and no reference to a blog.
Post '2' is in state Detached with FK '1' and no reference to a blog.

Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
SaveChanges envía eliminaciones para las entidades dependientes o secundarias (entradas)
Después de guardar, las entidades dependientes o secundarias (entradas) se desasocian porque se eliminaron
de la base de datos
[Link] o [Link] con relación obligatoria

After loading entities:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After making posts orphans:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Modified with FK 'null' and no reference to a blog.
Post '2' is in state Modified with FK 'null' and no reference to a blog.

Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1

SaveChanges threw DbUpdateException: Cannot insert the value NULL into column 'BlogId', table
'[Link]'; column does not allow nulls. UPDATE fails. The statement has been
terminated.

Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
SaveChanges intenta establecer la clave externa de la entrada en NULL, pero se produce un error porque la
clave externa no admite un valor NULL
[Link] o [Link] con relación opcional

After loading entities:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After making posts orphans:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Modified with FK 'null' and no reference to a blog.
Post '2' is in state Modified with FK 'null' and no reference to a blog.

Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 2

After SaveChanges:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK 'null' and no reference to a blog.
Post '2' is in state Unchanged with FK 'null' and no reference to a blog.

Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
SaveChanges establece la clave externa de las entidades dependientes o secundarias (entradas) en NULL
Después de guardar, las entidades dependientes o secundarias (entradas) ahora tienen valores NULL de clave
externa y se quitó la referencia a la entidad principal o primaria (blog) que se eliminó
[Link] con relación obligatoria u opcional
After loading entities:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.

After making posts orphans:


Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Modified with FK '1' and no reference to a blog.
Post '2' is in state Modified with FK '1' and no reference to a blog.

Saving changes:
SaveChanges threw InvalidOperationException: The association between entity types 'Blog' and 'Post' has
been severed but the foreign key for this relationship cannot be set to null. If the dependent entity should
be deleted, then setup the relationship to use cascade deletes.

Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
Como Restrict le indica a EF que no establezca automáticamente la clave externa en NULL, no se anula y
SaveChanges genera una excepción sin guardar

Aplicación en cascada a las entidades sin seguimiento


Cuando llama a SaveChanges, se aplicarán las reglas de la aplicación en cascada a cualquier entidad de la que el
contexto hace el seguimiento. Esto es lo que ocurre en todos los ejemplos anteriores, que es la razón por la cual
se generó código SQL para eliminar tanto la entidad principal o primaria (blog) como todas las entidades
dependientes o secundarias (entradas):

DELETE FROM [Posts] WHERE [PostId] = 1


DELETE FROM [Posts] WHERE [PostId] = 2
DELETE FROM [Blogs] WHERE [BlogId] = 1

Si solo se carga la entidad principal (por ejemplo, cuando se hace una solicitud para un blog sin
Include(b => [Link]) para que también incluya las entradas), SaveChanges solo generará código SQL para
eliminar la entidad principal o primaria:

DELETE FROM [Blogs] WHERE [BlogId] = 1

Las entidades dependientes o secundarias (entradas) solo se eliminarán si la base de datos tiene configurado un
comportamiento en cascada correspondiente. Si usa EF para crear la base de datos, este comportamiento en
cascada se configurará automáticamente.
Administrar los conflictos de simultaneidad
08/04/2020 • 7 minutes to read • Edit Online

NOTE
En esta página se documenta cómo funciona la simultaneidad en EF Core y cómo administrar los conflictos de simultaneidad
en la aplicación. Consulte Concurrency Tokens (Tokens de simultaneidad) para detalles sobre cómo configurar los tokens de
simultaneidad en el modelo.

TIP
Puede ver un ejemplo de este artículo en GitHub.

La simultaneidad de base de datos se refiere a las situaciones en las que varios procesos o usuarios acceden o
cambian los mismos datos de una base de datos al mismo tiempo. El control de simultaneidad se refiere a los
mecanismos específicos que se usan para garantizar la coherencia de los datos en presencia de cambios
simultáneos.
EF Core implementa el control de simultaneidad optimista, lo que significa que permitirá que varios procesos o
usuarios hagan cambios de manera independiente sin la sobrecarga que implica la sincronización o el bloqueo. En
la situación ideal, estos cambios no interferirán entre sí y, por tanto, se realizarán correctamente. En el peor
escenario, dos o más procesos intentarán hacer cambios conflictivos y solo uno de ellos se completará
correctamente.

Funcionamiento del control de simultaneidad en EF Core


Las propiedades configuradas como tokens de simultaneidad se usan para implementar el control de
simultaneidad optimista: cada vez que se realiza una operación de actualización o eliminación durante
SaveChanges , el valor del token de simultaneidad en la base de datos se compara con el valor original leído por EF
Core.
Si los valores coinciden, la operación se puede completar.
Si no coinciden, EF Core supone que otro usuario realizó una operación en conflicto y anula la transacción
actual.
La situación en que otro usuario realizó una operación que entra en conflicto con la operación actual se conoce
como conflicto de simultaneidad.
Los proveedores de base de datos son responsable de implementar la comparación de los valores de los tokens de
simultaneidad.
En las bases de datos relacionales, EF Core incluye una comprobación del valor del token de simultaneidad en la
cláusula WHERE de cualquier instrucción UPDATE o DELETE . Después de ejecutar las instrucciones, EF Core lee el
número de filas que se vieron afectadas.
Si no se afectó ninguna fila, se detecta un conflicto de simultaneidad y EF Core genera la excepción
DbUpdateConcurrencyException .

Por ejemplo, queremos configurar LastName en Person como token de simultaneidad. Luego, toda operación de
actualización en Person incluirá la comprobación de la simultaneidad en la cláusula WHERE :
UPDATE [Person] SET [FirstName] = @p1
WHERE [PersonId] = @p0 AND [LastName] = @p2;

Resolución de los conflictos de simultaneidad


Siguiendo con el ejemplo anterior, si un usuario intenta guardar algunos cambios en una Person pero otro
usuario ya cambió LastName , se generará una excepción.
En este punto, la aplicación simplemente podría informar al usuario que la actualización no se realizó
correctamente debido a cambios en conflicto y siguió adelante. Pero podría ser conveniente pedirle al usuario que
se asegure de que este registro sigue representando a la misma persona real y que reintente la operación.
Este proceso es un ejemplo de cómo resolver un conflicto de simultaneidad.
Resolver un conflicto de simultaneidad implica combinar los cambios pendientes del DbContext actual con los
valores de la base de datos. Los valores que se van a combinar variarán en función de la aplicación y los puede
dirigir el mismo usuario.
Existen tres conjuntos de valores disponibles para ayudar a resolver un conflicto de simultaneidad:
Los valores actuales son los valores que la aplicación intentó escribir en la base de datos.
Los valores originales son los valores que se recuperaron originalmente de la base de datos, antes de realizar
cualquier edición.
Los valores de base de datos son los valores actualmente almacenados en la base de datos.
El enfoque general para controlar un conflicto de simultaneidad es:
1. Detecte DbUpdateConcurrencyException durante SaveChanges .
2. Use [Link] para preparar un nuevo conjunto de cambios para las entidades
afectadas.
3. Actualice los valores originales del token de simultaneidad para reflejar los valores actuales en la base de datos.
4. Reintente el proceso hasta que no haya ningún conflicto.
En el ejemplo siguiente, [Link] y [Link] están configurados como tokens de simultaneidad.
Hay un comentario // TODO: en la ubicación donde se incluye la lógica específica de la aplicación para elegir el
valor que se guardará.
using (var context = new PersonContext())
{
// Fetch a person from database and change phone number
var person = [Link](p => [Link] == 1);
[Link] = "555-555-5555";

// Change the person's name in the database to simulate a concurrency conflict


[Link](
"UPDATE [Link] SET FirstName = 'Jane' WHERE PersonId = 1");

var saved = false;


while (!saved)
{
try
{
// Attempt to save changes to the database
[Link]();
saved = true;
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in [Link])
{
if ([Link] is Person)
{
var proposedValues = [Link];
var databaseValues = [Link]();

foreach (var property in [Link])


{
var proposedValue = proposedValues[property];
var databaseValue = databaseValues[property];

// TODO: decide which value should be written to database


// proposedValues[property] = <value to be saved>;
}

// Refresh original values to bypass next concurrency check


[Link](databaseValues);
}
else
{
throw new NotSupportedException(
"Don't know how to handle concurrency conflicts for "
+ [Link]);
}
}
}
}
}
Usar transacciones
08/04/2020 • 8 minutes to read • Edit Online

Las transacciones permiten procesar varias operaciones de base de datos de manera atómica. Si se confirma la
transacción, todas las operaciones se aplicaron correctamente a la base de datos. Si se revierte la transacción,
ninguna de las operaciones se aplicó a la base de datos.

TIP
Puede ver un ejemplo de este artículo en GitHub.

Comportamiento predeterminado de las transacciones


De manera predeterminada, si el proveedor de base de datos admite las transacciones, todos los cambios de una
llamada sencilla a SaveChanges() se aplican a una transacción. Si cualquiera de los cambios presenta un error, la
transacción se revertirá y no se aplicará ninguno de los cambios a la base de datos. Esto significa que se garantiza
que SaveChanges() se complete correctamente o deje sin modificaciones la base de datos si se produce un error.
Este comportamiento predeterminado es suficiente para la mayoría de las aplicaciones. Solo debe controlar
manualmente las transacciones si los requisitos de la aplicación lo consideran necesario.

Control de las transacciones


Puede usar la API [Link] para iniciar, confirmar y revertir las transacciones. En el ejemplo siguiente se
muestran dos operaciones SaveChanges() y una consulta LINQ que se ejecuta en una sola transacción.
No todos los proveedores de base de datos admiten transacciones. Algunos proveedores pueden generar una
excepción o una operación no efectiva cuando se llaman a las API de transacción.
using (var context = new BloggingContext())
{
using (var transaction = [Link]())
{
try
{
[Link](new Blog { Url = "[Link] });
[Link]();

[Link](new Blog { Url = "[Link] });


[Link]();

var blogs = [Link]


.OrderBy(b => [Link])
.ToList();

// Commit transaction if all commands succeed, transaction will auto-rollback


// when disposed if either commands fails
[Link]();
}
catch (Exception)
{
// TODO: Handle failure
}
}
}

Transacción de contexto cruzado (solo bases de datos relacionales)


También puede compartir una transacción en varias instancias de contexto. Esta funcionalidad solo está disponible
cuando se usa un proveedor de base de datos relacionales porque requiere el uso de DbTransaction y
DbConnection , específicos para las bases de datos relacionales.

Para compartir una transacción, los contextos deben compartir tanto DbConnection como DbTransaction .
Permitir conexiones proporcionadas externamente
Compartir una DbConnection requiere la capacidad de pasar una conexión a un contexto cuando se construya.
La manera más sencilla de permitir que DbConnection se proporcione de manera externa es dejar de usar el
método [Link] para configurar el contexto y crear externamente DbContextOptions y pasarlas al
constructor del contexto.

TIP
DbContextOptionsBuilder es la API que usó en [Link] para configurar el contexto y ahora va a usarla
para crear externamente DbContextOptions .

public class BloggingContext : DbContext


{
public BloggingContext(DbContextOptions<BloggingContext> options)
: base(options)
{ }

public DbSet<Blog> Blogs { get; set; }


}

Una alternativa es seguir usando [Link] , pero aceptar una DbConnection que se guarda y luego
se usa en [Link] .
public class BloggingContext : DbContext
{
private DbConnection _connection;

public BloggingContext(DbConnection connection)


{
_connection = connection;
}

public DbSet<Blog> Blogs { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
[Link](_connection);
}
}

Compartir conexión y transacción


Ahora puede crear varias instancias de contexto que comparten la misma conexión. Luego use la API
[Link](DbTransaction) para inscribir ambos contextos en la misma transacción.

var options = new DbContextOptionsBuilder<BloggingContext>()


.UseSqlServer(new SqlConnection(connectionString))
.Options;

using (var context1 = new BloggingContext(options))


{
using (var transaction = [Link]())
{
try
{
[Link](new Blog { Url = "[Link] });
[Link]();

using (var context2 = new BloggingContext(options))


{
[Link]([Link]());

var blogs = [Link]


.OrderBy(b => [Link])
.ToList();
}

// Commit transaction if all commands succeed, transaction will auto-rollback


// when disposed if either commands fails
[Link]();
}
catch (Exception)
{
// TODO: Handle failure
}
}
}

Uso de DbTransactions externas (solo bases de datos relacionales)


Si usa varias tecnologías de acceso a datos para acceder a una base de datos relacional, es posible que quiera
compartir una transacción entre las operaciones que estas distintas tecnologías realizan.
En el ejemplo siguiente se muestra cómo realizar una operación SqlClient de [Link] y una operación de Entity
Framework Core en la misma transacción.
using (var connection = new SqlConnection(connectionString))
{
[Link]();

using (var transaction = [Link]())


{
try
{
// Run raw [Link] command in the transaction
var command = [Link]();
[Link] = transaction;
[Link] = "DELETE FROM [Link]";
[Link]();

// Run an EF Core command in the transaction


var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;

using (var context = new BloggingContext(options))


{
[Link](transaction);
[Link](new Blog { Url = "[Link] });
[Link]();
}

// Commit transaction if all commands succeed, transaction will auto-rollback


// when disposed if either commands fails
[Link]();
}
catch ([Link])
{
// TODO: Handle failure
}
}
}

Utilizar [Link]
NOTE
Esta característica es nueva en EF Core 2.1.

Es posible usar transacciones de ambiente si necesita coordinar en un ámbito mayor.


using (var scope = new TransactionScope(
[Link],
new TransactionOptions { IsolationLevel = [Link] }))
{
using (var connection = new SqlConnection(connectionString))
{
[Link]();

try
{
// Run raw [Link] command in the transaction
var command = [Link]();
[Link] = "DELETE FROM [Link]";
[Link]();

// Run an EF Core command in the transaction


var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;

using (var context = new BloggingContext(options))


{
[Link](new Blog { Url = "[Link] });
[Link]();
}

// Commit transaction if all commands succeed, transaction will auto-rollback


// when disposed if either commands fails
[Link]();
}
catch ([Link])
{
// TODO: Handle failure
}
}
}

También es posible inscribir en una transacción explícita.


using (var transaction = new CommittableTransaction(
new TransactionOptions { IsolationLevel = [Link] }))
{
var connection = new SqlConnection(connectionString);

try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;

using (var context = new BloggingContext(options))


{
[Link]();
[Link](transaction);

// Run raw [Link] command in the transaction


var command = [Link]();
[Link] = "DELETE FROM [Link]";
[Link]();

// Run an EF Core command in the transaction


[Link](new Blog { Url = "[Link] });
[Link]();
[Link]();
}

// Commit transaction if all commands succeed, transaction will auto-rollback


// when disposed if either commands fails
[Link]();
}
catch ([Link])
{
// TODO: Handle failure
}
}

Limitaciones de [Link]
1. EF Core se basa en los proveedores de base de datos para implementar la compatibilidad con
[Link]. Si bien la compatibilidad es bastante común entre los proveedores de [Link] para
.NET Framework, la API solo se agregó recientemente a .NET Core y, por tanto, la compatibilidad no siempre
está tan extendida. Si un proveedor no implementa la compatibilidad con [Link], es posible
que las llamadas a estas API se omitan completamente. SqlClient para .NET no lo admite desde la versión 2.1
en adelante. SqlClient para .NET Core 2.0 generará una excepción si intent usar la característica.

IMPORTANT
Se recomienda probar que la API se comporte correctamente con el proveedor antes de usarla para administrar las
transacciones. Si no es así, recomendamos que se ponga en contacto con el mantenedor del proveedor de base de
datos.

2. A partir de la versión 2.1, la implementación de [Link] en .NET Core no incluye compatibilidad


con transacciones distribuidas, por lo que no puede usar TransactionScope ni CommittableTransaction para
coordinar las transacciones entre varios administradores de recursos.
Guardado asincrónico
08/04/2020 • 2 minutes to read • Edit Online

El guardado asincrónico evita bloquear un subproceso mientras se escriben los cambios en la base de datos. Esto
puede resultar útil para evitar la inmovilización de la interfaz de usuario de una aplicación cliente pesada. Las
operaciones asincrónicas también pueden aumentar el rendimiento de una aplicación web, donde se puede liberar
el subproceso para atender otras solicitudes mientras se completa la operación de la base de datos. Para más
información, consulte Programación asincrónica en C#.

WARNING
EF Core no admite que varias operaciones en paralelo se ejecuten en la misma instancia de contexto. Siempre debe esperar
que se complete una operación antes de iniciar la siguiente. Habitualmente, para esto se usa la palabra clave await en cada
una de las operaciones asincrónicas.

Entity Framework Core proporciona [Link]() como una alternativa asincrónica para
[Link]() .

public static async Task AddBlogAsync(string url)


{
using (var context = new BloggingContext())
{
var blog = new Blog { Url = url };
[Link](blog);
await [Link]();
}
}
Entidades desconectadas
08/04/2020 • 15 minutes to read • Edit Online

Una DbContext realizará seguimiento automático de las entidades que se devuelven de la base de datos. De ese
modo, los cambios hechos en estas entidades se detectarán cuando se llame a SaveChanges y la base de datos se
actualizará según sea necesario. Consulte Basic Save (Guardado básico) y Related Data (Datos relacionados) para
información detallada.
Sin embargo, en algunas ocasiones las entidades se consultan mediante el uso de una instancia de contexto y
luego se guardan con una instancia distinta. Esto suele ocurrir en escenarios "desconectados", como una aplicación
web, en los que las entidades se consultan, se envían al cliente, se modifican, se envían de vuelta al servidor en una
solicitud y, a continuación, se guardan. En este caso, la segunda instancia de contexto debe saber si las entidades
son nuevas (y se deben insertar) o existentes (y se deben actualizar).

TIP
Puede ver un ejemplo de este artículo en GitHub.

TIP
EF Core solo puede hacer seguimiento de una instancia de una entidad con un valor de clave principal determinado. La mejor
manera de evitar que esto se convierta en un problema es usar un contexto de corta duración para cada unidad de trabajo
de manera que el contexto empiece vacío, tenga entidades asociadas, guarde esas entidades y, luego, se elimine y descarte el
contexto.

Identificación de unidades nuevas


El cliente identifica las unidades nuevas
El caso más sencillo de abordar es cuando el cliente informa al servidor si la entidad es nueva o existente. Por
ejemplo, a menudo la solicitud para insertar una entidad nueva es distinta de la solicitud para actualizar una
entidad existente.
En el resto de esta sección se analizan los casos en los que resulta necesario determinar de otro modo si se debe
realizar una inserción o una actualización.
Con claves generadas automáticamente
El valor de una clave generada automáticamente a menudo se puede usar para determinar si una entidad se debe
insertar o actualizar. Si no se estableció la clave (es decir, si todavía tiene el valor predeterminado de CLR de NULL,
cero, etc.), la entidad debe ser nueva y se debe insertar. Por el contrario, si el valor de la clave sí se estableció, ya se
debe haber guardado anteriormente y ahora se debe actualizar. En otras palabras, si la clave tiene un valor, es
porque la entidad ya se consultó, se envió al cliente y ahora vuelve para que la actualicen.
Resulta sencillo comprobar si una clave no se estableció cuando se conoce el tipo de entidad:

public static bool IsItNew(Blog blog)


=> [Link] == 0;

Sin embargo, EF también tiene una manera integrada de hacer esto con cualquier tipo de entidad y cualquier tipo
de clave:
public static bool IsItNew(DbContext context, object entity)
=> ![Link](entity).IsKeySet;

TIP
Las claves se establecen tan pronto como el contexto hace seguimiento de las entidades, incluso si la entidad tiene el estado
Added (Agregada). Esto resulta útil cuando se recorre un grafo de entidades y se decide qué hacer con cada una de ellas,
como cuándo usar TrackGraph API. El valor de la clave solo se debe usar como se indica aquí antes de cualquier llamada para
hacer seguimiento de la entidad.

Con otras claves


Es necesario algún otro mecanismo para identificar las entidades nuevas cuando los valores de clave no se generan
automáticamente. Aquí existen dos enfoques generales:
Consulta para la entidad
Paso de una marca desde el cliente
Para consulta la entidad, simplemente use el método Find:

public static bool IsItNew(BloggingContext context, Blog blog)


=> [Link]([Link]) == null;

Mostrar el código completo para pasar una marca desde un cliente va más allá del ámbito del presente documento.
En una aplicación web, habitualmente significa hacer distintas solicitudes para acciones diferentes o pasar algún
estado en la solicitud para luego extraerlo en el controlador.

Guardado de entidades únicas


Cuando se sabe si es necesario o no realizar una inserción o una actualización, las acciones de agregar o actualizar
se pueden usar correctamente:

public static void Insert(DbContext context, object entity)


{
[Link](entity);
[Link]();
}

public static void Update(DbContext context, object entity)


{
[Link](entity);
[Link]();
}

Sin embargo, si la entidad usa valores de clave generados automáticamente, el método Update se puede usar para
ambos casos:

public static void InsertOrUpdate(DbContext context, object entity)


{
[Link](entity);
[Link]();
}

Habitualmente, el método Update marca la entidad para actualización y no para inserción. Sin embargo, si la
entidad tiene una clave generada automáticamente y no se estableció ningún valor de clave, la entidad se marca
automáticamente para inserción.

TIP
Este comportamiento se introdujo en EF Core 2.0. En las versiones anteriores siempre es necesario elegir explícitamente si
agregar o actualizar.

Si la entidad no usa claves generadas automáticamente, la aplicación debe decidir si la entidad se debe inserta ro
actualizar. Por ejemplo:

public static void InsertOrUpdate(BloggingContext context, Blog blog)


{
var existingBlog = [Link]([Link]);
if (existingBlog == null)
{
[Link](blog);
}
else
{
[Link](existingBlog).[Link](blog);
}

[Link]();
}

Estos son los pasos:


Si Find devuelve un valor NULL, la base de datos todavía no contiene el blog con su identificador, por lo que
llamamos a Add para marcarlo para inserción.
Si Find devuelve una entidad es porque existe en la base de datos y ahora el contexto hace seguimiento de esa
entidad existente.
Luego usamos SetValues para establecer los valores de todas las propiedades de esta entidad en los
valores que provienen del cliente.
La llamada a SetValues marcará la entidad para actualizarla según sea necesario.

TIP
SetValues solo marcará como modificadas las propiedades que tengan valores distintos a los de la entidad con seguimiento.
Esto significa que, cuando se envíe la actualización, solo se actualizarán las columnas que se hayan modificado realmente. (Si
no se modificó nada, no se enviará ninguna actualización).

Trabajo con grafos


Resolución de identidad
Como se indicó anteriormente, EF Core solo puede hacer seguimiento de una instancia de una entidad con un valor
de clave principal determinado. Cuando se trabaja con grafos, idealmente el grafo se debe crear de manera tal que
se mantenga esta invariable y el contexto se debe usar solo para una unidad de trabajo. Si el grafo contiene
duplicados, será necesario procesarlo antes de enviarlo a EF para consolidar varias instancias en una sola. Es
posible que esta acción no sea trivial cuando haya instancias con valores y relaciones en conflicto, por lo que la
consolidación de los duplicados se debe hacer tan pronto como sea posible en la canalización de aplicación para
evitar la resolución de conflictos.
Todas las entidades nuevas o todas las entidades existentes
Un ejemplo de trabajar con grafos es insertar o actualizar un blog junto con su colección de entradas asociadas. Si
las entidades del grafo se deben insertar o actualizar en su totalidad, el proceso es el mismo que se describió
anteriormente para las entidades únicas. Por ejemplo, un grafo de blogs y entradas creado de esta manera:

var blog = new Blog


{
Url = "[Link]
Posts = new List<Post>
{
new Post {Title = "Post 1"},
new Post {Title = "Post 2"},
}
};

se puede insertar así:

public static void InsertGraph(DbContext context, object rootEntity)


{
[Link](rootEntity);
[Link]();
}

La llamada a Add marcará el blog y todas las entradas para su inserción.


Del mismo modo, si es necesario actualizar todas las entidades de un grafo, se puede usar Update:

public static void UpdateGraph(DbContext context, object rootEntity)


{
[Link](rootEntity);
[Link]();
}

El blog y todas las entradas se marcarán para su actualización.


Combinación de entidades nuevas y entidades existentes
Con las claves generadas automáticamente, Update se puede volver a usar tanto para inserciones como para
actualizaciones, incluso si el grafo contiene una combinación de entidades que requiere inserción y las que se
deben actualizar:

public static void InsertOrUpdateGraph(DbContext context, object rootEntity)


{
[Link](rootEntity);
[Link]();
}

Update marcará una entidad en el grafo, ya sea el blog o una entrada, para inserción si no tiene establecido un
valor de clave, mientras que todas las demás entidades se marcarán para actualización.
Como antes, cuando no se usan claves generadas automáticamente, es posible usar una consulta y algún
procesamiento:
public static void InsertOrUpdateGraph(BloggingContext context, Blog blog)
{
var existingBlog = [Link]
.Include(b => [Link])
.FirstOrDefault(b => [Link] == [Link]);

if (existingBlog == null)
{
[Link](blog);
}
else
{
[Link](existingBlog).[Link](blog);
foreach (var post in [Link])
{
var existingPost = [Link]
.FirstOrDefault(p => [Link] == [Link]);

if (existingPost == null)
{
[Link](post);
}
else
{
[Link](existingPost).[Link](post);
}
}
}

[Link]();
}

Control de eliminaciones
Puede ser difícil controlar las eliminaciones porque, habitualmente, la ausencia de una entidad implica que se debe
eliminar. Una manera de solucionar esto es usar las "eliminaciones temporales" en que la entidad se marca como
eliminada en lugar de eliminarla realmente. Luego, las eliminaciones pasan a ser iguales a las actualizaciones. Las
eliminaciones temporales se pueden implementar usando filtros de consulta.
En el caso de las eliminaciones reales, un patrón común es usar una extensión del modelo de consulta para realizar
lo que esencialmente es una diferencia de grafo. Por ejemplo:
public static void InsertUpdateOrDeleteGraph(BloggingContext context, Blog blog)
{
var existingBlog = [Link]
.Include(b => [Link])
.FirstOrDefault(b => [Link] == [Link]);

if (existingBlog == null)
{
[Link](blog);
}
else
{
[Link](existingBlog).[Link](blog);
foreach (var post in [Link])
{
var existingPost = [Link]
.FirstOrDefault(p => [Link] == [Link]);

if (existingPost == null)
{
[Link](post);
}
else
{
[Link](existingPost).[Link](post);
}
}

foreach (var post in [Link])


{
if (![Link](p => [Link] == [Link]))
{
[Link](post);
}
}
}

[Link]();
}

TrackGraph
De manera interna, Add, Attach y Update usan el recorrido de grafo con una determinación hecha para cada
entidad a fin de saber si se debe marcar como Added (para inserción), Modified (para actualización), Unchanged
(para no hacer nada) o Deleted (para eliminación). Este mecanismo se expone a través de TrackGraph API. Por
ejemplo, supongamos que cuando el cliente envió de vuelta un grafo de entidades, estableció alguna marca en
cada entidad para indicar cómo se debe controlar. Entonces se puede usar TrackGraph para procesar esta marca:
public static void SaveAnnotatedGraph(DbContext context, object rootEntity)
{
[Link](
rootEntity,
n =>
{
var entity = (EntityBase)[Link];
[Link] = [Link]
? [Link]
: [Link]
? [Link]
: [Link]
? [Link]
: [Link];
});

[Link]();
}

Las marcas solo se muestran como parte de la entidad para simplificar el ejemplo. Habitualmente, las marcas
serían parte de una DTO o alguno otro estado incluido en la solicitud.
Establecimiento de valores explícitos para
propiedades generadas
08/04/2020 • 6 minutes to read • Edit Online

Una propiedad generada es una propiedad cuyo valor se genera (ya sea por medio de EF o de la base de dato