0% encontró este documento útil (0 votos)
448 vistas1309 páginas

Novedades de EF Core 5 en español

Este documento compara Entity Framework Core (EF Core) y Entity Framework 6 (EF6), y proporciona información sobre la portabilidad entre ambos. EF Core es un asignador de base de datos moderno para .NET, mientras que EF6 es un producto estable pero ya no se desarrolla activamente. Se comparan las características disponibles en cada versión y se explica cómo portar un modelo desde EF6 a EF Core.

Cargado por

yunior.camejo
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)
448 vistas1309 páginas

Novedades de EF Core 5 en español

Este documento compara Entity Framework Core (EF Core) y Entity Framework 6 (EF6), y proporciona información sobre la portabilidad entre ambos. EF Core es un asignador de base de datos moderno para .NET, mientras que EF6 es un producto estable pero ya no se desarrolla activamente. Se comparan las características disponibles en cada versión y se explica cómo portar un modelo desde EF6 a EF Core.

Cargado por

yunior.camejo
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
¡Bienvenido!
Novedades en EF Core 5.0
Introducción
Información general sobre EF Core
Instalación de EF Core
Su primera aplicación de EF Core
Paquetes NuGet
Tutorial de [Link] Core >>
Guía de Blazor Server con EF Core >>
Tutorial de .NET Core para WPF
Tutorial de Xamarin
Versiones y planeamiento (plan de desarrollo)
Versiones actuales y planeadas
Proceso de planeamiento de versiones
EF Core 6.0
Plan de alto nivel
Novedades
Cambios importantes
EF Core 5.0
Plan de alto nivel
Novedades
Cambios importantes
EF Core 3.1
Características nuevas
Cambios importantes
EF Core 2.1
Fuera de soporte técnico
EF Core 3.0
EF Core 2.2
EF Core 2.0
Nuevas características
Actualización desde la versión 1.x
EF Core 1.1
EF Core 1.0
Configuración e inicialización de DbContext
Información general
Creación de un modelo
Información general
Tipos de entidad
Propiedades de entidad
Teclas
Valores generados
Tokens de simultaneidad
Propiedades de propiedad reemplazada e indizador
Relaciones
Índices
Herencia
Secuencias
Campos de respaldo
Conversiones de valores
Comparadores 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
Administración de migraciones
Aplicación de migraciones
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
Carga de datos relacionados
Información general
Carga diligente
Carga explícita
Carga diferida
Datos relacionados y serialización
Consultas divididas
Operadores de consulta complejos
Consultas SQL sin formato
Funciones de base de datos
Asignación de funciones definidas por el usuario
Filtros de consulta global
Etiquetas de consulta
Comparaciones con valores NULL en las consultas
Funcionamiento de las consultas
Guardar datos
Información general
Guardado básico
Datos relacionados
Eliminación en cascada
Conflictos de simultaneidad
Transacciones
Entidades desconectadas
Seguimiento de cambios
Información general
Seguimiento explícito de entidades
Acceso a entidades sometidas a seguimiento
Cambio de las claves externas y las navegaciones
Detección y notificaciones de cambios
Resolución de identidad
Características adicionales de seguimiento de cambios
Depuración de la herramienta de seguimiento de cambios
Registro, eventos y diagnósticos
Información general
Registro sencillo
[Link]
Eventos
Interceptores
Escuchas de diagnóstico
Contadores de eventos
Pruebas
Pruebas de código que usa EF Core
Ejemplo de prueba de EF Core
Compartir bases de datos entre pruebas
Pruebas con SQLite
Pruebas con InMemory
Rendimiento
Introducción
Diagnóstico del rendimiento
Consultas eficaces
Actualizaciones eficaces
Modelado para el rendimiento
Temas de rendimiento avanzados
Varios
Implementaciones de .NET compatibles
Programación asincrónica
Tipos de referencia que aceptan valores NULL
Intercalaciones y distinción de mayúsculas y minúsculas
Resistencia de la conexión
Cadenas de conexión
Agrupación de contexto
Proveedores de bases de datos
Información general
Microsoft SQL Server
Información general
Generación de valor
Asignaciones de funciones
Índices
Tablas optimizadas para memoria
Datos espaciales
Especificación de opciones de Azure SQL Database
SQLite
Información general
Limitaciones de SQLite
Asignaciones de funciones
Datos espaciales
[Link] >>
Cosmos
Información general
Trabajo con datos no estructurados
Limitaciones de Cosmos
Asignaciones de funciones
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
Primeros pasos
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 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)
Creación de un modelo
Información general
Uso de Code First
Flujos de trabajo
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
Flujos de trabajo
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
Consulta
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
Avanzadas
Formato de archivo EDMX
Definición de consulta
Varios conjuntos de resultados
Funciones con valores de tabla
Métodos abreviados de 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 propiedades
Control de conflictos de simultaneidad
Uso de transacciones
Validación de datos
Recursos adicionales
Blogs
Casos prácticos
Contribuir
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 >>
Comparar EF Core y EF6
12/03/2021 • 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 Sí Stretch para la versión 5.0 (n.º 3170) (1)
(TPC)

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 un 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 3.0


valores 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 un modelo desde base de datos: Sí No hay soporte técnico planeado (2)
asistente de VS

Consultar 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 de tipo derivados

Carga de datos relacionados: diferida Sí 2.1

Carga de datos relacionados: explícita 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 procesar: componer 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: instantánea 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 Sí 1.0


base de datos

Datos de inicialización Sí 2.1

Resistencia de la conexión Sí 1,1

Interceptores Sí 3.0

Eventos 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
12/03/2021 • 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
12/03/2021 • 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
12/03/2021 • 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
12/03/2021 • 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
12/03/2021 • 7 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 actuar como asignador relacional de objetos, que se encarga de lo siguiente:
Permite a los desarrolladores de .NET trabajar con una base de datos usando objetos .NET.
Permite prescindir de la mayor parte del código de acceso a datos que normalmente es necesario 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. Este objeto de contexto permite consultar y
guardar datos. Para más información, vea Creación de un modelo.
EF admite los siguientes métodos de desarrollo de modelos:
Generar un modelo a partir de una base de datos existente.
Codificar un modelo manualmente para que coincida con la base de datos.
Una vez creado un modelo, usar Migraciones de EF para crear una base de datos a partir del modelo.
Migraciones permite que la base de datos evolucione a medida que el modelo va cambiando.
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 por medio de Language Integrated
Query (LINQ). Para más información, vea Consulta de datos.

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]();
}
Consideraciones de EF O/RM
Mientras que EF Core es bueno extrayendo muchos detalles de programación, existen algunos procedimientos
recomendados válidos para cualquier O/RM que ayudan a evitar errores comunes en las aplicaciones de
producción:
Tener un conocimiento medio o superior del servidor de base de datos subyacente es esencial para generar
perfiles, diseñar, depurar y migrar datos en aplicaciones de producción de alto rendimiento (por ejemplo,
conocer las claves principales y externas, las restricciones, los índices, la normalización, las instrucciones DML
y DDL, los tipos de datos, los perfiles, etc.).
Pruebas funcionales y de integración. Es importante replicar el entorno de producción de la forma más
próxima posible para permitir lo siguiente:
Encontrar problemas en la aplicación que solo se revelan cuando se usa una edición o una versión
específica del servidor de base de datos.
Detectar cambios importantes al actualizar EF Core y otras dependencias (por ejemplo, agregar o
actualizar marcos como [Link] Core, OData o AutoMapper). Estas dependencias pueden afectar a
EF Core de formas imprevistas.
Pruebas de rendimiento y esfuerzo con cargas representativas. El uso irreflexivo de algunas características no
escala bien. Por ejemplo, varias colecciones Includes, el uso intensivo de cargas diferidas, las consultas
condicionales en columnas no indexadas, las inserciones y actualizaciones masivas con valores generados
por el almacén, la falta de control de la simultaneidad, el uso de modelos grandes o unas directivas
inadecuadas de almacenamiento en memoria caché.
Revisión de seguridad: por ejemplo, el control de las cadenas de conexión y otros secretos, los permisos de
base de datos para operaciones de no implementación, la validación de entradas para SQL sin procesar o el
cifrado de datos confidenciales.
Asegúrese de que las capacidades de registro y diagnóstico son suficientes y utilizables, por ejemplo, una
configuración de registros adecuada, etiquetas de consulta y Application Insights.
Recuperación de errores. Prepare contingencias para afrontar escenarios de error comunes, como
reversiones de versión, servidores de reserva, escalabilidad horizontal y equilibrio de carga, la mitigación de
DoS y copias de seguridad de datos.
Implementación y migración de aplicaciones. Planee cómo se van a aplicar las migraciones durante la
implementación, ya que hacerlo en el inicio de una aplicación puede derivar en problemas de simultaneidad
y requiere permisos más elevados de lo necesario para lograr un funcionamiento normal. Use el
almacenamiento provisional para facilitar la recuperación de errores irrecuperables durante la migración.
Para más información, vea Aplicación de migraciones.
Examen y prueba detallados de las migraciones generadas. Las migraciones se deben comprobar
detenidamente antes de aplicarse a los datos de producción. La forma del esquema y los tipos de columna
no son fáciles de cambiar una vez que las tablas contienen datos de producción. Por ejemplo, en SQL Server,
nvarchar(max) y decimal(18, 2) no suelen ser los mejores tipos para las columnas asignadas a propiedades
de cadena y decimal, pero son los valores predeterminados que EF emplea porque desconoce cuál es su
escenario específico.

Pasos siguientes
Para consultar tutoriales de introducción, vea Introducción a Entity Framework Core.
Instalación de Entity Framework Core
12/03/2021 • 10 minutes to read • Edit Online

Requisitos previos
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.
dotnet ef debe estar instalado como herramienta global o local. La mayoría de los desarrolladores
prefieren instalar dotnet ef como herramienta global con el siguiente comando:

dotnet tool install --global dotnet-ef

dotnet ef también se puede usar como herramienta local. Para usarlo como herramienta local, restaure
las dependencias de un proyecto que lo declare como dependencia de herramientas mediante un archivo
de manifiesto de herramientas.
Para actualizar las herramientas, use el comando dotnet tool update .
Instale la versión más reciente del paquete [Link] .

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 la versión 1.0 no son compatibles con la versión 2.0 del
entorno de ejecución 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].
Introducción a EF Core
07/04/2021 • 6 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; }

// The following configures EF to create a Sqlite database file as `C:\[Link]`.


// For Mac or Linux, change this to `/tmp/[Link]` or any other absolute path.
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> [Link](@"Data Source=C:\[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: Esta aplicación lo hace todo más fácil de forma intencionada. Las cadenas de conexión no se deben
almacenar en el código para aplicaciones de producción. Además, es recomendable que divida cada clase de C#
en su archivo correspondiente.

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
{
internal class Program
{
private static void Main()
{
using (var db = new BloggingContext())
{
// Note: This sample requires the database to be created before running.

// 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.
Paquetes NuGet de EF Core
12/03/2021 • 7 minutes to read • Edit Online

Entity Framework Core (EF Core) se distribuye como paquetes NuGet. Los paquetes que necesita una aplicación
dependen de lo siguiente:
Tipo de sistema de base de datos que se usa (SQL Server, SQLite, etc.)
Características de EF Core que se necesitan
El proceso habitual que debe seguir para instalar paquetes es este:
Elija un proveedor de bases de datos e instale el paquete adecuado (véase a continuación).
Instale también [Link] y [Link] si usa un
proveedor relacional. Esto ayuda a garantizar que se usen versiones coherentes. Además, NuGet le informará
de cuándo se distribuyen las nuevas versiones del paquete.
Opcionalmente, decida qué tipo de herramientas necesita e instale los paquetes adecuados para ello (véase a
continuación).
Si quiere obtener ayuda para empezar a usar EF Core, consulte el tutorial de introducción a Entity Framework
Core.

Versiones de paquete
Asegúrese de instalar la misma versión de todos los paquetes de EF Core enviados por Microsoft. Por ejemplo, si
se instala la versión 5.0.3 de [Link], la versión de todos los demás paquetes
de [Link].* también debe ser la 5.0.3.
Asegúrese también de que los paquetes externos sean compatibles con la versión de EF Core que se usa. En
concreto, compruebe que el proveedor de bases de datos externas es compatible con la versión de EF Core que
se usa. Las nuevas versiones principales de EF Core suelen requerir un proveedor de bases de datos actualizado.

WARNING
NuGet no obliga a usar versiones de paquete coherentes. Compruebe siempre cuidadosamente las versiones a las que
hace referencia en el archivo csproj (o equivalente).

Proveedores de bases de datos


EF Core admite distintos sistemas de base de datos mediante el uso de "proveedores de bases de datos". Cada
sistema tiene su propio proveedor de bases de datos, que se distribuye como paquete NuGet. Las aplicaciones
deben instalar uno o varios de estos paquetes del proveedor.
En la tabla siguiente se indican proveedores de bases de datos comunes. Consulte Proveedores de bases de
datos para obtener una lista más completa de los proveedores disponibles.

SIST EM A DE B A SE DE DATO S PA Q UET E

SQL Server y SQL Azure [Link]

SQLite [Link]
SIST EM A DE B A SE DE DATO S PA Q UET E

Azure Cosmos DB [Link]

PostgreSQL [Link]*

MySQL [Link]*

Base de datos en memoria de EF Core** [Link]

* Se trata de proveedores de código abierto populares y de alta calidad, desarrollados y distribuidos por la
comunidad. Microsoft se encarga de la distribución de los demás proveedores enumerados.
** Considere detenidamente si le conviene usar el proveedor en memoria. No está diseñado para su uso en
producción y podría no ser la mejor solución para las pruebas.

Herramientas
El uso de herramientas para migraciones de EF Core y de técnicas de ingeniería inversa (scaffolding) a partir de
una base de datos existente requiere la instalación del paquete de herramientas adecuado:
[Link] para herramientas de PowerShell que funcionan en la consola del
administrador de paquetes de Visual Studio
dotnet-ef y [Link] para herramientas de línea de comandos multiplataforma
Consulte Referencia sobre las herramientas de Entity Framework Core para obtener más información sobre el
uso de las herramientas de EF Core, incluido cómo instalar correctamente la herramienta dotnet-ef en un
proyecto o globalmente.

TIP
De forma predeterminada, el paquete [Link] se instala de manera que no se implemente
con la aplicación. Esto también significa que sus tipos no se pueden usar de forma transitiva en otros proyectos. Use un
elemento PackageReference normal en el archivo .csproj (o equivalente) si necesita acceder a los tipos del paquete.
Consulte Servicios en tiempo de diseño para obtener más información.

Paquetes de extensión
Hay muchas extensiones para EF Core publicadas por Microsoft y terceros como paquetes NuGet. Estos son
algunos de los paquetes usados comúnmente:

F UN C IO N A L IDA D PA Q UET E DEP EN DEN C IA S A DIC IO N A L ES

Servidores proxy para la carga diferida [Link] [Link]


y el seguimiento de cambios

Compatibilidad espacial con [Link] NetTopologySuite y


SQL Server [Link] [Link]

Compatibilidad espacial con SQLite [Link]. NetTopologySuite y


NetTopologySuite [Link]
F UN C IO N A L IDA D PA Q UET E DEP EN DEN C IA S A DIC IO N A L ES

Compatibilidad espacial con [Link] NetTopologySuite y


PostgreSQL [Link] [Link] (mediante
[Link])

Compatibilidad espacial con MySQL [Link].N NetTopologySuite


etTopologySuite

Otros paquetes
Otros paquetes de EF Core se extraen como dependencias del paquete del proveedor de bases de datos. Aun así,
puede que le interese agregar referencias de paquete explícitas para estos paquetes, de modo que NuGet
proporcione notificaciones cuando se publiquen nuevas versiones.

F UN C IO N A L IDA D PA Q UET E

Funcionalidad básica de EF Core [Link]

Funcionalidad común de bases de datos relacionales [Link]

Paquete ligero para atributos de EF Core, etc. [Link]

Analizadores de código de Roslyn para el uso de EF Core [Link]

Proveedor de SQLite para EF Core sin dependencia de [Link]


SQLite nativa

Paquetes para pruebas del proveedor de bases de datos


Los siguientes paquetes se usan para probar los proveedores de bases de datos integrados en repositorios de
GitHub externos. Consulte [Link] y [Link] para obtener ejemplos. Las
aplicaciones no deben instalar estos paquetes.

F UN C IO N A L IDA D PA Q UET E

Pruebas para cualquier proveedor de bases de datos [Link]

Pruebas para proveedores de bases de datos relacionales [Link]

Paquetes obsoletos
No instale los siguientes paquetes obsoletos y quítelos si están instalados actualmente en los proyectos:
[Link]
[Link]
[Link]
[Link]
[Link]
Introducción a WPF
07/04/2021 • 16 minutes to read • Edit Online

En este tutorial paso a paso se muestra cómo enlazar tipos POCO a controles de WPF en un formulario "main-
detail". La aplicación utiliza las API de Entity Framework para rellenar los objetos con datos de la base de datos,
realizar un seguimiento de los cambios y conservar los datos en la base de datos.
El modelo define dos tipos que participan en una relación de uno a varios: Categor y (principal\main) y
Product (dependent\detail). El marco de enlace de datos de WPF permite la navegación entre objetos
relacionados: la selección de filas en la vista maestra hace que la vista de detalles se actualice con los datos
secundarios correspondientes.
Las capturas de pantalla y las listas de código de este tutorial se han tomado de Visual Studio 2019 16.6.5.

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

Requisitos previos
Debe tener Visual Studio 2019 16.3 o posterior instalado con la carga de trabajo de escritorio de .NET
seleccionada para completar este tutorial. Para obtener más información sobre cómo instalar la última versión
de Visual Studio, vea Instalación de Visual Studio.

Crear la aplicación
1. Apertura de Visual Studio
2. En la ventana de inicio, elija Crear proyecto .
3. Busque "WPF", elija Aplicación WPF (.NET Core) y después seleccione Siguiente .
4. En la pantalla siguiente, asigne un nombre al proyecto, por ejemplo, GetStar tedWPF , y elija Crear .

Instalación de los paquetes de NuGet Entity Framework


1. Haga clic con el botón derecho en la solución y elija Administrar paquetes NuGet para la solución...
2. Escriba [Link] en el cuadro de búsqueda.
3. Instale el paquete [Link] .
4. Compruebe el proyecto en el panel derecho y haga clic en Instalar .

5. Repita los pasos para buscar [Link] e instalar


[Link] .
NOTE
Al instalar el paquete de Sqlite, se extrae automáticamente el paquete de base [Link]
relacionado. El paquete [Link] proporciona compatibilidad con los datos de "carga
diferida". Esto significa que, si tiene entidades con entidades secundarias, solo se capturan los elementos primarios en la
carga inicial. Los proxies detectan cuándo se produce un intento de acceso a las entidades secundarias, y las cargan
automáticamente a petición.

Definición de un modelo
En este tutorial, implementará un modelo con "Code First". Esto significa que EF Core creará las tablas de base
de datos y el esquema en función de las clases de C# que defina.
Agregue una nueva clase. Asígnele el nombre [Link] y rellénelo como se indica a continuación:
[Link]

namespace GetStartedWPF
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }

public int CategoryId { get; set; }


public virtual Category Category { get; set; }
}
}

A continuación, agregue una clase denominada [Link] y rellénela con el código siguiente:
[Link]

using [Link];
using [Link];

namespace GetStartedWPF
{
public class Category
{
public int CategoryId { get; set; }
public string Name { get; set; }

public virtual ICollection<Product>


Products
{ get; private set; } =
new ObservableCollection<Product>();
}
}

La propiedad Products de la clase Categor y y la propiedad Categor y de la clase Product son propiedades de
navegación. En Entity Framework, las propiedades de navegación proporcionan una manera de navegar por una
relación entre dos tipos de entidad.
Además de definir entidades, debe definir una clase que derive de DbContext y exponga las propiedades
DbSet<TEntity>. Las propiedades DbSet<TEntity> permiten que el contexto sepa qué tipos desea incluir en el
modelo.
Una instancia del tipo derivado de DbContext administra los objetos de entidad durante el tiempo de ejecución,
lo que incluye rellenar los objetos con datos de una base de datos, el seguimiento de cambios y la persistencia
de datos en la base de datos.
Agregue una nueva clase [Link] al proyecto con la siguiente definición:
[Link]

using [Link];

namespace GetStartedWPF
{
public class ProductContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }

protected override void OnConfiguring(


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

DbSet informa a EF Core de qué entidades de C# se deben asignar a la base de datos.


Hay varias maneras de configurar DbContext de EF Core. Puede leer sobre ellas en: Configuración de un
DbContext.
En este ejemplo se usa la invalidación OnConfiguring para especificar un archivo de datos de Sqlite.
La llamada a UseLazyLoadingProxies indica a EF Core que implemente la carga diferida, por lo que las
entidades secundarias se cargan automáticamente cuando se obtiene acceso a ellas desde el elemento
primario.
Presione CTRL + MAYÚS + B o vaya a Compilar > Compilar solución para compilar el proyecto.

TIP
Obtenga información sobre los distintos pasos para mantener sincronizados la base de datos y los modelos de EF Core:
Administración de esquemas de base de datos.

Carga diferida
La propiedad Products de la clase Categor y y la propiedad Categor y de la clase Product son propiedades de
navegación. En Entity Framework Core, las propiedades de navegación proporcionan una manera de navegar
por una relación entre dos tipos de entidad.
EF Core ofrece una opción para cargar automáticamente las entidades relacionadas desde la base de datos la
primera vez que se accede a la propiedad de navegación. Con este tipo de carga (denominado carga diferida),
tenga en cuenta que la primera vez que se accede a cada propiedad de navegación se ejecutará una consulta
independiente en la base de datos si el contenido no está ya en el contexto.
Al usar tipos de entidad "Plain Old C# Object" (POCO), EF Core logra la carga diferida mediante la creación de
instancias de tipos de proxy derivados durante el tiempo de ejecución y, después, la invalidación de las
propiedades virtuales de las clases para agregar el enlace de carga. Para obtener la carga diferida de los objetos
relacionados, debe declarar captadores de propiedades de navegación como público y vir tual (Overridable
en Visual Basic), y la clase no debe sellarse (NotOverridable en Visual Basic). Al usar Database First, las
propiedades de navegación se convierten automáticamente en virtuales para habilitar la carga diferida.

Enlace de objetos a controles


Agregue las clases que se definen en el modelo como orígenes de datos para esta aplicación WPF.
1. Haga doble clic en [Link] en el Explorador de soluciones para abrir el formulario principal.
2. Elija la pestaña XAML para editar el código XAML.
3. Inmediatamente después de la etiqueta de apertura Window , agregue los orígenes siguientes para
conectarse a las entidades de EF Core.

<Window x:Class="[Link]"
xmlns="[Link]
xmlns:x="[Link]
xmlns:d="[Link]
xmlns:mc="[Link]
xmlns:local="clr-namespace:GetStartedWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
<[Link]>
<CollectionViewSource x:Key="categoryViewSource"/>
<CollectionViewSource x:Key="categoryProductsViewSource"
Source="{Binding Products, Source={StaticResource
categoryViewSource}}"/>
</[Link]>

4. De esta forma, se configura el origen para las categorías "principales" y el segundo origen para los
productos "detallados".
5. A continuación, agregue el marcado siguiente al código XAML después de la etiqueta de cierre
[Link] .

<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False"


EnableRowVirtualization="True"
ItemsSource="{Binding Source={StaticResource categoryViewSource}}"
Margin="13,13,43,229" RowDetailsVisibilityMode="VisibleWhenSelected">
<[Link]>
<DataGridTextColumn Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name"
Width="*"/>
</[Link]>
</DataGrid>

6. Tenga en cuenta que el elemento CategoryId se establece en ReadOnly porque está asignado por la base
de datos y no se puede cambiar.

Adición de una cuadrícula de detalles


Ahora que la cuadrícula existe para mostrar las categorías, se puede agregar la cuadrícula de detalles para
mostrar los productos.
[Link]
<DataGrid x:Name="productsDataGrid" AutoGenerateColumns="False"
EnableRowVirtualization="True"
ItemsSource="{Binding Source={StaticResource categoryProductsViewSource}}"
Margin="13,205,43,108" RowDetailsVisibilityMode="VisibleWhenSelected"
RenderTransformOrigin="0.488,0.251">
<[Link]>
<DataGridTextColumn Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding ProductId}" Header="Product Id"
Width="SizeToHeader" IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="*"/>
</[Link]>
</DataGrid>

Por último, agregue un botón Save y una conexión en el evento de clic a Button_Click .

<Button Content="Save" HorizontalAlignment="Center" Margin="0,240,0,0"


Click="Button_Click" Height="20" Width="123"/>

La vista de diseño debería tener un aspecto similar a este:

Adición de código que controla la interacción con los datos


Es el momento de agregar algunos controladores de eventos a la ventana principal.
1. En la ventana de XAML, haga clic en el elemento <Ventana> , para seleccionar la ventana principal.
2. En la ventana Propiedades , elija Eventos en la parte superior derecha y, a continuación, haga doble clic
en el cuadro de texto que se encuentra a la derecha de la etiqueta Cargado .
Esto lo lleva al código subyacente del formulario; ahora, se editará el código para usar el elemento
ProductContext para ejecutar el acceso a los datos. Actualice el código como se muestra a continuación.

El código declara una instancia de larga duración de ProductContext . El objeto ProductContext se utiliza para
consultar y guardar datos en la base de datos. A continuación, se llama al método Dispose() en la instancia de
ProductContext desde el método OnClosing invalidado. Los comentarios del código explican qué hace cada
paso.
[Link]
using [Link];
using [Link];
using [Link];
using [Link];

namespace GetStartedWPF
{
/// <summary>
/// Interaction logic for [Link]
/// </summary>
public partial class MainWindow : Window
{
private readonly ProductContext _context =
new ProductContext();

private CollectionViewSource categoryViewSource;

public MainWindow()
{
InitializeComponent();
categoryViewSource =
(CollectionViewSource)FindResource(nameof(categoryViewSource));
}

private void Window_Loaded(object sender, RoutedEventArgs e)


{
// this is for demo purposes only, to make it easier
// to get up and running
_context.[Link]();

// load the entities into EF Core


_context.[Link]();

// bind to the source


[Link] =
_context.[Link]();
}

private void Button_Click(object sender, RoutedEventArgs e)


{
// all changes are automatically tracked, including
// deletes!
_context.SaveChanges();

// this forces the grid to refresh to latest values


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

protected override void OnClosing(CancelEventArgs e)


{
// clean up database connections
_context.Dispose();
[Link](e);
}
}
}
NOTE
El código usa una llamada a EnsureCreated() para compilar la base de datos en la primera ejecución. Esto es aceptable
para demostraciones, pero en las aplicaciones de producción debe examinar migraciones para administrar el esquema. El
código también se ejecuta de forma sincrónica porque usa una base de datos SQLite local. En escenarios de producción
que normalmente implican un servidor remoto, considere la posibilidad de utilizar las versiones asincrónicas de los
métodos Load y SaveChanges .

Prueba de una aplicación WPF


Para compilar y ejecutar la aplicación, presione F5 o seleccione Depurar > Iniciar depuración . La base de
datos debe crearse automáticamente con un archivo denominado [Link] . Escriba un nombre de categoría
y presione ENTRAR y, a continuación, agregue productos a la cuadrícula inferior. Haga clic en Guardar y observe
la actualización de la cuadrícula con los identificadores proporcionados por la base de datos. Resalte una fila y
presione Eliminar para quitarla. La entidad se eliminará al hacer clic en Guardar .

Notificación de cambio de propiedad


Este ejemplo se basa en cuatro pasos para sincronizar las entidades con la interfaz de usuario.
1. La llamada inicial a _context.[Link]() carga los datos de categorías.
2. Los proxies de carga diferida cargan los datos de los productos dependientes.
3. El seguimiento de cambios integrado de EF Core realiza las modificaciones necesarias en las entidades,
incluidas las inserciones y eliminaciones, cuando se llama a _context.SaveChanges() .
4. Las llamadas a [Link]() fuerzan una recarga con los identificadores recién generados.

Esto sirve para el ejemplo de introducción, pero puede que necesite código adicional para otros escenarios. Los
controles de WPF representan la interfaz de usuario mediante la lectura de los campos y las propiedades de las
entidades. Cuando se edita un valor en la interfaz de usuario (UI), ese valor se pasa a la entidad. Al cambiar el
valor de una propiedad directamente en la entidad, como cargarla desde la base de datos, WPF no reflejará
inmediatamente los cambios en la interfaz de usuario. El motor de representación debe recibir notificaciones de
los cambios. El proyecto lo hizo llamando manualmente a Refresh() . Una manera sencilla de automatizar esta
notificación es mediante la implementación de la interfaz INotifyPropertyChanged. Los componentes de WPF
detectarán automáticamente la interfaz y se registrarán para los eventos de cambio. La entidad es responsable
de generar estos eventos.
TIP
Para obtener más información sobre cómo administrar los cambios, lea: Implementación de la notificación de cambio de
propiedad.

Pasos siguientes
Más información sobre la Configuración de un DbContext.
Primeros pasos con EF Core y Xamarin
07/04/2021 • 10 minutes to read • Edit Online

En este tutorial, crearemos una aplicación de consola de [Link] que realiza el acceso a datos en una
base de datos de SQLite mediante Entity Framework Core.
Puede seguir el tutorial mediante Visual Studio en Windows o Visual Studio para Mac.

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

Requisitos previos
Instale la opción que corresponda:
Visual Studio 2019, versión 16.3 o posterior, con esta carga de trabajo:
Desarrollo móvil con .NET
Visual Studio para Mac
En esta documentación se proporcionan instrucciones de instalación paso a paso detalladas para cada
plataforma.

Descarga y ejecución del proyecto de ejemplo


Para ejecutar y explorar esta aplicación de ejemplo, descargue el código disponible en GitHub.
Una vez descargado, abra el archivo de solución [Link] en Visual Studio o Visual Studio para
Mac y ejecute la aplicación en la plataforma de su elección.
Cuando se inicie la aplicación por primera vez, rellenará la base de datos de SQLite local con dos entradas que
representan blogs.
Haga clic en el botón Agregar de la barra de herramientas.
Aparecerá una nueva página que le permitirá escribir información sobre un nuevo blog.
Rellene toda la información y haga clic en Guardar en la barra de herramientas. El nuevo blog se guardará en la
base de datos de SQLite de la aplicación y se mostrará en la lista.
Puede hacer clic en una de las entradas de blog de la lista y ver cualquier publicación de dicho blog.
En la barra de herramientas, haga clic en Agregar .
Aparecerá una página que le permitirá rellenar información sobre una nueva entrada de blog.
Rellene toda la información y haga clic en Guardar en la barra de herramientas.
La nueva publicación se asociará a la entrada de blog en la que hizo clic en el paso anterior, se guardará en la
base de datos de SQLite de la aplicación y se mostrará en la lista.
Vuelva a la página de la lista de blogs. Haga clic en Eliminar todo en la barra de herramientas. Todos los blogs
y sus publicaciones correspondientes se eliminarán de la base de datos de SQLite de la aplicación.
Exploración del código
Las secciones siguientes le guiarán a través del código del proyecto de ejemplo que lee, crea, actualiza y elimina
datos de una base de datos de SQLite mediante EF Core con [Link].
Se da por hecho que está familiarizado con los temas de [Link] relacionados con la visualización de
datos y la navegación por las páginas.

IMPORTANT
Entity Framework Core usa la reflexión para invocar funciones que el enlazador de [Link] puede quitar mientras está
en las configuraciones de modo de versión . Puede evitarlo de dos maneras.
La primera es agregar --linkskip [Link] a los argumentos de mtouch adicionales en las opciones de
compilación de iOS.
También puede establecer el compor tamiento del enlazador de [Link] en Don't Link en las opciones de
compilación de iOS. En este artículo se explica más sobre el enlazador de [Link], incluido cómo establecer el
comportamiento en [Link]. (Este enfoque no es idóneo, ya que puede dar lugar a un rechazo del almacén).

Paquetes NuGet de Entity Framework Core


Para crear aplicaciones de [Link] con EF Core, instale el paquete de los proveedores de bases de datos
de EF Core que quiera establecer como destino en todos los proyectos de la solución de [Link]. En este
tutorial se usa el proveedor SqLite.
El siguiente paquete de NuGet es necesario en cada uno de los proyectos de la solución de [Link].
[Link]

Clases de modelo
Cada tabla de la base de datos de SQLite a la que se tiene acceso a través de EF Core se modela en una clase. En
este ejemplo, se usan dos clases, Blog y Post , que se pueden encontrar en la carpeta Models .
Las clases de modelo se componen únicamente de propiedades, que modelan las columnas de la base de datos.
[Link]

using System;
using [Link];

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

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


}
}

La propiedad Posts permite definir una relación de elemento primario y secundario entre Blog y Post .
[Link]

using System;
namespace EFGetStarted
{
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; }
}
}

Las propiedades BlogId y Blog vuelven a relacionarse con el objeto Blog primario de la instancia de
Post .

Contexto de datos
La clase BloggingContext se encuentra en la carpeta Services y hereda de la clase EF Core DbContext .
DbContext se utiliza para agrupar las consultas y los cambios de la base de datos.
using System;
using [Link];
using [Link];
using [Link];

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

public BloggingContext()
{
SQLitePCL.Batteries_V2.Init();

[Link]();
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
string dbPath = [Link]([Link], "blogs.db3");

optionsBuilder
.UseSqlite($"Filename={dbPath}");
}
}
}

Ambas propiedades de esta clase de tipo DbSet se usan para operar en las tablas subyacentes que
representan blogs y publicaciones.
SQLitePCL.Batteries_V2.Init() es necesario en el constructor para iniciar SQLite en iOS.
La función OnConfiguring configura la ubicación de la base de datos de SQLite en el dispositivo físico.

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


A continuación, se muestran algunas instancias de la aplicación en las que se usa EF Core para acceder a SQLite.
Leer
Permite devolver todos los registros.
La función OnAppearing de [Link] devuelve todos los registros Blog y los almacena en
una variable List .

using (var blogContext = new BloggingContext())


{
var theBlogs = [Link]();
}

Permite devolver registros específicos.


La función OnAppearing de [Link] devuelve registros Post que contienen un BlogId
específico.

using (var blogContext = new BloggingContext())


{
var postList = [Link]
.Where(p => [Link] == BlogId)
.ToList();
}
Crear
Permite insertar un nuevo registro.
La función Save_Clicked de [Link] inserta un nuevo objeto Blog en la base de datos
de SQLite.

var blog = new Blog { Url = [Link] };

using (var blogContext = new BloggingContext())


{
[Link](blog);

await [Link]();
}

Actualizar
Permite actualizar un registro existente.
La función Save_Clicked de [Link] actualiza un objeto Blog existente con un Post
nuevo.

var newPost = new Post


{
BlogId = BlogId,
Content = [Link],
Title = [Link]
};

using (var blogContext = new BloggingContext())


{
var blog = await blogContext
.Blogs
.FirstAsync(b => [Link] == BlogId);

[Link](newPost);

await [Link]();
}

Eliminar
Permite eliminar todos los registros en cascada en los registros secundarios.
La función DeleteAll_Clicked de [Link] elimina todos los registros de Blog de la base
de datos de SQLite y elimina en cascada todos los registros de Post secundarios de Blog .

using (var blogContext = new BloggingContext())


{
[Link]([Link]);

await [Link]();
}

Pasos siguientes
En esta introducción, ha aprendido a usar una aplicación de [Link] para tener acceso a una base de
datos de SQLite mediante Entity Framework Core.
Otros temas de Entity Framework Core de interés para los desarrolladores de Xamarin:
Configuración de DbContext
Obtenga más información sobre las expresiones de consulta LINQ.
Configure su modelo para especificar aspectos como requerido y longitud máxima.
Versiones y planeamiento de EF Core
12/03/2021 • 5 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 5.0 .NET Standard 2.1 Mediados de febrero de Anuncio / Cambios


2022 importantes

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


(LTS)

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.x para la
"x" más elevada posible.
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 6.0
La siguiente versión estable planeada es EF Core 6.0 , programada para noviembre de 2021 .
Se ha creado un plan de alto nivel para EF Core 6.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.
Obtenerlo ahora
Los paquetes de EF Core 6.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 75 000 pruebas en cada compilación.
Además, a medida que avanzamos, se enviarán versiones preliminares a NuGet con frecuencia. Tenga en cuenta
que las versiones preliminares 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
12/03/2021 • 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 6.0
07/04/2021 • 22 minutes to read • Edit Online

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


plan para la versión 6.0 de Entity Framework Core (EF Core).
A diferencia de las versiones anteriores, este plan no intenta abarcar todo el trabajo de la versión 6.0. En su
lugar, indica dónde y cómo se pretende invertir en esta versión, pero con flexibilidad para ajustar el ámbito o
incorporar el nuevo trabajo a medida que se recopilen comentarios y se aprenda mientras se trabaja en la
versión.

IMPORTANT
Este plan no es un compromiso. 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 6.0. Es posible que algunos
aspectos planeados en la actualidad se eliminen de la versión 6.0.

Información general
Número de versión y fecha de lanzamiento
EF Core 6.0 es la siguiente versión después de EF Core 5.0 y actualmente está programada para su lanzamiento
en noviembre de 2021 al mismo tiempo que .NET 6.
Plataformas admitidas
Actualmente, EF Core 6.0 se destina a .NET 5. Lo más probable es que se actualice a .NET 6 a medida que se
acerque el lanzamiento. EF Core 6.0 no tiene como destino ninguna versión de .NET Standard; para obtener más
información, vea El futuro de .NET Standard.
EF Core 6.0 no se ejecutará en .NET Framework.
EF Core 6.0 se alineará con .NET 6 como una versión de soporte técnico a largo plazo (LTS).
Últimos cambios
EF Core 6.0 contendrá un pequeño número de cambios importantes a medida que EF Core y la plataforma .NET
sigan evolucionando. El objetivo es permitir que la gran mayoría de las aplicaciones se actualicen sin
interrupciones.

Temas
Las siguientes áreas constituirán la base de las principales inversiones en EF Core 6.0.

Características muy solicitadas


Como siempre, una entrada importante del proceso de planificación procede de la votación ( ) de
características en GitHub. Para EF Core 6.0 está previsto trabajar en las siguientes características muy solicitadas:
Tablas temporales de SQL Server
Número de seguimiento: 4693
Estado: Sin iniciar
Talla de camiseta: Grande
Las tablas temporales admiten consultas de datos almacenados en la tabla en cualquier momento, en lugar de
solo los más recientes, como sucede con las tablas normales. EF Core 6.0 permitirá crear tablas temporales
mediante Migraciones, además de permitir el acceso a los datos a través de consultas LINQ.
Inicialmente, el ámbito de este trabajo se limita como se describe en la incidencia. Es posible que se incorpore
soporte adicional en función de los comentarios durante el lanzamiento.
Columnas JSON
Número de seguimiento: 4021
Estado: Sin iniciar
Talla de camiseta: Medium
Esta característica presentará un mecanismo común y patrones para la compatibilidad con JSON que cualquier
proveedor de bases de datos puede implementar. Se trabajará con la comunidad para alinear las
implementaciones existentes de Npgsql y Pomelo MySQL, además de agregar compatibilidad con SQL Server y
SQLite.
[Link]
Número de seguimiento: 10059
Estado: Sin iniciar
Talla de camiseta: Pequeña
Esta característica permitirá el orden arbitrario de las columnas al crear una tabla con Migraciones o
EnsureCreated . Tenga en cuenta que para cambiar el orden de las columnas en las tablas existentes es necesario
volver a generar la tabla, y esto es algo que no está previsto admitir en ninguna versión de EF Core.

Rendimiento
Aunque por lo general EF Core es más rápido que EF6, todavía hay áreas en las que se puede mejorar
significativamente el rendimiento. Está previsto abordar varias de estas áreas en EF Core 6.0, a la vez que se
mejoran la infraestructura y las pruebas de rendimiento.
Este tema implicará una gran cantidad de investigación iterativa, que informará de los recursos elegidos como
destino. Está previsto comenzar con lo siguiente:
Infraestructura de rendimiento y nuevas pruebas
Estado: Sin iniciar
Talla de camiseta: Medium
El código base de EF Core ya contiene un conjunto de bancos de pruebas de rendimiento que se ejecutan todos
los días. En la versión 6.0, está previsto mejorar la infraestructura de estas pruebas, así como agregar otras
nuevas. También se generarán perfiles de escenarios de rendimiento principales y se corregirá cualquier
instancia de nivel inferior.
Modelos compilados
Número de seguimiento: 1906
Estado: Sin iniciar
Talla de camiseta: Extra grande
Los modelos compilados permitirán la generación de un formato compilado del modelo de EF. Esto
proporcionará un mejor rendimiento de inicio, así como un mejor rendimiento general al acceder al modelo.
TechEmpower Fortunes
Número de seguimiento: 23611
Estado: Sin iniciar
Talla de camiseta: Extra grande
Durante varios años se han ejecutado los bancos de pruebas de TechEmpower estándar del sector en .NET sobre
una base de datos PostgreSQL. El banco de pruebas de Fortune es especialmente relevante para los escenarios
de EF. Se dispone de varias variaciones de este banco de pruebas, entre las que se incluyen las siguientes:
Una implementación en la que se usa [Link] directamente. Esta es la implementación más rápida de las
tres que se muestran aquí.
Una implementación en la que se usa Dapper. Es más lenta que cuando se usa [Link] directamente, pero
sigue siendo rápida.
Una implementación en la que se usa EF Core. Actualmente esta es la implementación más lenta de las tres.
El objetivo de EF Core 6.0 es conseguir que el rendimiento de EF Core coincida con el de Dapper en el banco de
pruebas de TechEmpower Fortunes. (Es un reto importante, pero se hará todo lo posible para acercarse al
máximo).
Enlazador/AOT
Número de seguimiento: 10963
Estado: Sin iniciar
Talla de camiseta: Medium
EF Core realiza grandes cantidades de generación de código en tiempo de ejecución. Esto supone un reto para
los modelos de aplicación que dependen de la modificación del árbol del enlazador, como Xamarin y Blazor, y
para las plataformas que no permiten la compilación dinámica, como iOS. Se seguirá investigando en este
espacio como parte de EF Core 6.0 y, siempre que se pueda, se intentarán realizar mejoras concretas. Pero no se
espera solventar todas las diferencias por completo durante el intervalo de tiempo de la versión 6.0.

Migraciones e implementación
A partir de las investigaciones realizadas para EF Core 5.0, está previsto incorporar compatibilidad mejorada
para la administración de migraciones y la implementación de bases de datos. Esto incluye dos áreas
principales:
Agrupaciones de migraciones
Número de seguimiento: 19693
Estado: Sin iniciar
Talla de camiseta: Medium
Una agrupación de migraciones es un archivo ejecutable independiente que aplica migraciones a una base de
datos de producción. El comportamiento coincidirá con dotnet ef database update , pero debe facilitar
considerablemente la implementación de SSH, Docker o PowerShell, ya que todo lo que se necesita se incluye
en el ejecutable único.
Administración de migraciones
Número de seguimiento: 22945
Estado: Sin iniciar
Talla de camiseta: Grande
El número de migraciones creadas para una aplicación puede aumentar hasta convertirse en una carga.
Además, estas migraciones se suelen implementar con la aplicación aunque no sea necesario. En EF Core 6.0, el
plan es mejorar esto mediante mejores herramientas y administración de proyectos y ensamblados. Dos
problemas específicos que está previsto abordar son la inclusión de muchas migraciones en una y la
regeneración de una instantánea de modelo limpia.

Mejora de las características existentes y corrección de errores


Actualmente, está previsto para esta versión cualquier incidencia o error asignado al hito 6.0.0. Esto incluye
muchas mejoras pequeñas y correcciones de errores.
Paridad de consultas de EF6
Seguimiento por incidencias etiquetadas con "ef6-parity" y en el hito 6.0
Estado: Sin iniciar
Talla de camiseta: Grande
EF Core 5.0 admite la mayoría de los patrones de consulta admitidos por EF6, además de los que no se admiten
en EF6. Para EF Core 6.0, está previsto reducir las diferencias y hacer que las consultas de EF Core admitidas
sean un superconjunto real de las admitidas en EF6. Esto se controla mediante la investigación de las
diferencias, pero ya se incluyen incidencias de GroupBy como la traducción de GroupBy seguido de
FirstOrDefault y consultas SQL sin procesar para tipos primitivos y no asignados.
Objetos de valor
Número de seguimiento: 9906
Estado: Sin iniciar
Talla de camiseta: Medium
Anteriormente, la vista de equipo era la propietaria de las entidades, destinadas a la compatibilidad agregada, lo
que también sería una aproximación razonable a los objetos de valor. La experiencia ha demostrado que este no
es el caso. Por tanto, en EF Core 6.0 está previsto introducir una mejor experiencia centrada en las necesidades
de los objetos de valor en el diseño controlado por dominios. Este enfoque se basará en convertidores de
valores, en lugar de entidades de propiedad.
Inicialmente, el ámbito de este trabajo es permitir convertidores de valores que se asignan a varias columnas. Es
posible que se incorpore soporte adicional en función de los comentarios durante el lanzamiento.
Proveedor de base de datos Cosmos
Seguimiento por incidencias etiquetadas con "area-cosmos" y en el hito 6.0
Estado: Sin iniciar
Talla de camiseta: Grande
De forma activa se recopilan comentarios sobre las mejoras que se deben llevar a cabo en el proveedor Cosmos
en EF Core 6.0. Este documento se actualizará a medida que se obtenga más información. Por ahora, asegúrese
de votar ( ) por las características de Cosmos que necesite.
Exposición de convenciones de creación de modelos para las aplicaciones
Número de seguimiento: 214
Estado: Sin iniciar
Talla de camiseta: Medium
EF Core usa un conjunto de convenciones para crear un modelo a partir de tipos de .NET. Actualmente, estas
convenciones las controla el proveedor de base de datos. En EF Core 6.0 está previsto permitir que las
aplicaciones se conecten a estas convenciones y las cambien.
Equilibrio de cero errores (ZBB )
Seguimiento por incidencias etiquetadas con type-bug en el hito 6.0
Estado: En curso
Talla de camiseta: Grande
Está previsto corregir todos los errores pendientes durante el período de tiempo de EF Core 6.0. Algunos
aspectos que se deben tener en cuenta:
Esto se aplica específicamente a las incidencias con la etiqueta type-bug.
Habrá excepciones, como cuando el error requiere un cambio de diseño o una característica nueva para
corregirlo correctamente. Estas incidencias se marcarán con la etiqueta blocked .
Los errores se puntuarán en función del riesgo cuando sea necesario, lo normal a medida que se aproxima
una versión de GA/RTM.
Características varias
Seguimiento por incidencias etiquetadas con type-enhancement en el hito 6.0
Estado: En curso
Talla de camiseta: Grande
Las características varias planeadas para EF 6.0 incluyen, entre otras, las siguientes:
División de consultas para colecciones que no son de navegación
Detección de tablas de combinación simples en técnicas de ingeniería inversa y creación de relaciones de
varios a varios
Finalización de la búsqueda de texto completo o libre en SQLite y SQL Server
Índices espaciales de SQL Server
Mecanismo o API para especificar una conversión predeterminada para cualquier propiedad de un tipo
determinado en el modelo
Uso de la nueva API de procesamiento por lotes de [Link]

integración en .NET
El equipo de EF Core también trabaja en varias tecnologías relacionadas pero independientes. En concreto, está
previsto trabajar en lo siguiente:
Mejoras en [Link]
Seguimiento por incidencias en el repositorio dotnet\runtime etiquetadas con [Link] en el hito 6.0
Estado: Sin iniciar
Talla de camiseta: Grande
Este trabajo incluye lo siguiente:
Implementación de la nueva API de procesamiento por lotes.
Trabajo continuado con otros equipos de .NET y la comunidad para comprender y desarrollar [Link].
Uso estándar de DiagnosticSource para el seguimiento en componentes [Link].*.
Mejoras en [Link]
Seguimiento por incidencias etiquetadas con type-enhancement y area-adonet-sqlite en el hito 6.0
Estado: En curso
Talla de camiseta: Medium
Se han planificado varias mejoras pequeñas para [Link], incluidas la agrupación de conexiones y
las instrucciones preparadas para el rendimiento.
Tipos de referencia que aceptan valores NULL
Número de seguimiento: 14150
Estado: En curso
Talla de camiseta: Grande
Se anotará el código de EF Core para usar tipos de referencia que aceptan valores NULL.

Experimentos e investigaciones
Durante el período de tiempo de EF Core 6.0, el equipo de EF tiene previsto invertir en la experimentación e
investigación en dos áreas. Se trata de un proceso de aprendizaje y, como tal, no se prevé ninguna entrega
concreta para la versión 6.0.
[Link]
Seguimiento en el repositorio de laboratorios de datos de .NET
Estado: Sin iniciar
Talla de camiseta: En curso
[Link] es un proveedor de base de datos de [Link] con todas las características para
SQL Server. Admite una amplia gama de características de SQL Server, tanto en .NET Core como en
.NET Framework. Pero también es un código base grande y antiguo con muchas interacciones complejas entre
sus comportamientos. Esto dificulta la investigación de las posibles ventajas que se pueden realizar con las
nuevas características de .NET Core. Por tanto, se va a iniciar un experimento en colaboración con la comunidad
para determinar las posibilidades de un controlador de SQL Server de alta rendimiento para .NET.

IMPORTANT
La inversión en [Link] no va a cambiar. Seguirá siendo la forma recomendada de conectarse a
SQL Server y SQL de Azure, tanto con EF Core como sin EF Core. Seguirá admitiendo nuevas características de SQL Server
a medida que se introduzcan.

GraphQL
Estado: Sin iniciar
Talla de camiseta: En curso
En los últimos años GraphQL ha cobrado importancia en una variedad de plataformas. Estás previsto investigar
el espacio y encontrar formas de mejorar la experiencia con .NET. Esto implicará trabajar con la comunidad para
comprender y respaldar el ecosistema existente. También puede implicar una inversión específica por parte de
Microsoft, ya sea en forma de contribuciones a trabajos existentes o en el desarrollo de elementos gratuitos en
la pila de Microsoft.
DataVerse (anteriormente Data Services comunes)
Estado: Sin iniciar
Talla de camiseta: En curso
DataVerse es un almacén de datos basado en columnas diseñado para el desarrollo rápido de aplicaciones
empresariales. Controla automáticamente tipos de datos complejos como los objetos binarios (BLOB) y tiene
entidades y relaciones integradas como organizaciones y contactos. Existe un SDK, pero los desarrolladores
pueden beneficiarse de tener un proveedor de EF Core para usar consultas LINQ avanzadas, aprovechar las
ventajas de la unidad de trabajo y tener una API de acceso a datos coherente. El equipo considerará diferentes
maneras de mejorar la experiencia de conexión a DataVerse para los desarrolladores de .NET.

Por debajo de la línea de corte


Seguimiento por problemas etiquetados con consider-for-current-release

Se trata de correcciones de errores y mejoras no programadas actualmente para la versión 6.0, pero que se
examinarán en función de los comentarios a lo largo del lanzamiento junto con el progreso realizado en el
trabajo anterior. Estas incidencias se pueden incorporar en EF Core 6.0 y se convierten automáticamente en
candidatas para la próxima versión.
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. Asegúrese de que ha votado ( ) por las características que necesita.

Sugerencias
Sus comentarios sobre la planeación son importantes. La mejor manera de indicar la importancia de una
incidencia consiste en votar ( ) por esa incidencia 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 6.0
07/04/2021 • 20 minutes to read • Edit Online

EF Core 6.0 está actualmente en desarrollo. Esta página contiene información general sobre los cambios
interesantes introducidos en cada versión preliminar.
Esta página no duplica el plan para EF Core 6.0. En el plan se describen los temas generales relativos a
EF Core 6.0, incluido todo lo que se piensa incluir antes de publicar la versión final.

TIP
Puede ejecutar todos los ejemplos de la versión preliminar 1 que se muestran a continuación y depurar en ellos. Para
hacerlo, descargue el código de ejemplo de GitHub.

EF Core 6.0, versión preliminar 2


Conservación del contexto de sincronización en SaveChangesAsync
Problema de GitHub número 23971.
En la versión 5.0, hemos modificado el código de EF Core para establecer [Link] en false en
todas las ubicaciones donde se use el operador await para el código asincrónico. En general, esta suele ser una
mejor opción al usar EF Core. De todas formas, SaveChangesAsync es un caso especial, porque EF Core
establece los valores generados en entidades de las que se hace un seguimiento tras completar la operación de
una base de datos asincrónica. Es posible que estos cambios desencadenen notificaciones que, por ejemplo,
deban ejecutarse en el subproceso de la interfaz de usuario. Por lo tanto, en EF Core 6.0, vamos a revertir este
cambio solo para el método SaveChangesAsync.
Traducción de [Link] con varios argumentos
Problema de GitHub número 23859. Esta característica la ha aportado @wmeints.
A partir de EF Core 6.0, las llamadas a [Link] con varios argumentos se traducen a SQL. Por ejemplo, la
consulta siguiente:

var shards = [Link]


.Where(e => [Link](e.Token1, e.Token2, e.Token3) != [Link]).ToList();

Se traducirá al siguiente código SQL al usar SQL Server:

SELECT [s].[Id], [s].[Token1], [s].[Token2], [s].[Token3], [s].[TokensProcessed]


FROM [Shards] AS [s]
WHERE ((COALESCE([s].[Token1], N'') + (COALESCE([s].[Token2], N'') + COALESCE([s].[Token3], N''))) <> [s].
[TokensProcessed]) OR [s].[TokensProcessed] IS NULL

Integración más fluida con [Link]


Problema de GitHub número 24041.
El paquete [Link] agrega procesamiento LINQ asincrónico del lado cliente. En versiones anteriores
de EF Core, usar este paquete era algo engorroso, ya que había un conflicto relativo al espacio de nombres para
los métodos LINQ asincrónicos. En EF Core 6.0, hemos aprovechado la coincidencia de patrones de C# para
IAsyncEnumerable<T>, de tal modo que el elemento DbSet<TEntity> expuesto de EF Core no necesite
implementar la interfaz directamente.
Tenga en cuenta que la mayoría de aplicaciones no necesitan usar [Link], ya que las consultas de
EF Core normalmente se traducen por completo en el servidor.
Búsqueda de texto libre más flexible
Problema de GitHub número 23921.
En EF Core 6.0, hemos relajado los requisitos de los parámetros para FreeText(DbFunctions, String, String) y
Contains. Esto permite usar dichas funciones con columnas binarias o con columnas asignadas mediante un
convertidor de valores. Por ejemplo, pongamos por caso un tipo de entidad con una propiedad Name definida
como objeto de valor:

public class Customer


{
public int Id { get; set; }

public Name Name{ get; set; }


}

public class Name


{
public string First { get; set; }
public string MiddleInitial { get; set; }
public string Last { get; set; }
}

Se asigna a un elemento JSON en la base de datos:

[Link]<Customer>()
.Property(e => [Link])
.HasConversion(
v => [Link](v, null),
v => [Link]<Name>(v, null));

Ahora se puede ejecutar una consulta con Contains o FreeText , aunque el tipo de la propiedad sea Name , no
string . Por ejemplo:

var result = [Link](e => [Link]([Link], "Martin")).ToList();

Al usar SQL Server, esto genera el código SQL siguiente:

SELECT [c].[Id], [c].[Name]


FROM [Customers] AS [c]
WHERE CONTAINS([c].[Name], N'Martin')

EF Core 6.0, versión preliminar 1


UnicodeAttribute
Problema de GitHub número 19794. Esta característica la ha aportado @RaymondHuy.
A partir de EF Core 6.0, una propiedad de cadena ahora puede asignarse a una columna no Unicode mediante
un atributo de asignación sin especificar el tipo de base de datos directamente. Por ejemplo, considere un tipo
de entidad Book con una propiedad para el ISBN (Número Internacional Normalizado del Libro) con el formato
"ISBN 978-3-16-148410-0":
public class Book
{
public int Id { get; set; }
public string Title { get; set; }

[Unicode(false)]
[MaxLength(22)]
public string Isbn { get; set; }
}

Dado que los ISBN no pueden contener caracteres no Unicode, el atributo Unicode hará que se use un tipo de
cadena no Unicode. Además, se usa MaxLength para limitar el tamaño de la columna de base de datos. Por
ejemplo, al usar SQL Server, esto da como resultado una columna de base de datos varchar(22) :

CREATE TABLE [Book] (


[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Isbn] varchar(22) NULL,
CONSTRAINT [PK_Book] PRIMARY KEY ([Id]));

NOTE
De forma predeterminada, EF Core asigna propiedades de cadena a las columnas Unicode. UnicodeAttribute se omite
cuando el sistema de base de datos solo admite tipos Unicode.

PrecisionAttribute
Problema de GitHub número 17914. Esta característica la ha aportado @RaymondHuy.
Ahora se pueden configurar la precisión y la escala de una columna de base de datos mediante atributos de
asignación sin especificar el tipo de base de datos directamente. Por ejemplo, considere un tipo de entidad
Product con una propiedad Price decimal:

public class Product


{
public int Id { get; set; }

[Precision(precision: 10, scale: 2)]


public decimal Price { get; set; }
}

EF Core asignará esta propiedad a una columna de base de datos con una precisión de 10 y una escala de 2. Por
ejemplo, en SQL Server:

CREATE TABLE [Product] (


[Id] int NOT NULL IDENTITY,
[Price] decimal(10,2) NOT NULL,
CONSTRAINT [PK_Product] PRIMARY KEY ([Id]));

EntityTypeConfigurationAttribute
Problema de GitHub número 23163. Esta característica la ha aportado @KaloyanIT.
Las instancias de IEntityTypeConfiguration<TEntity> permiten la configuración de ModelBuilder para cada tipo
de entidad que se incluya en su propia clase de configuración. Por ejemplo:
public class BookConfiguration : IEntityTypeConfiguration<Book>
{
public void Configure(EntityTypeBuilder<Book> builder)
{
builder
.Property(e => [Link])
.IsUnicode(false)
.HasMaxLength(22);
}
}

Normalmente, es necesario crear una instancia de esta clase de configuración y llamarla desde
[Link]. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
new BookConfiguration().Configure([Link]<Book>());
}

A partir de EF Core 6.0, se puede colocar un elemento EntityTypeConfigurationAttribute en el tipo de entidad


para que EF Core pueda encontrar y usar la configuración adecuada. Por ejemplo:

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

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

public class BooksContext : DbContext


{
public DbSet<Book> Books { get; set; }

//...

O bien, mediante su registro en OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Book>();
}

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

Traducción de ToString en SQLite


Problema de GitHub número 17223. Esta característica la ha aportado @ralmsdeveloper.
Las llamadas a ToString() ahora se traducen a SQL cuando se usa el proveedor de base de datos de SQLite. Esto
puede ser útil para las búsquedas de texto que implican columnas que no son de cadena. Por ejemplo, considere
un tipo de entidad User que almacena los números de teléfono como valores numéricos:

public class User


{
public int Id { get; set; }
public string Username { get; set; }
public long PhoneNumber { get; set; }
}

Se puede usar ToString para convertir el número en una cadena de la base de datos. Después, se puede usar
esta cadena con una función como LIKE para buscar números que coincidan con un patrón. Por ejemplo, para
buscar todos los números que contienen 555:

var users = [Link](u => [Link]([Link](), "%555%")).ToList();

Esto se traduce en el siguiente código SQL cuando se usa una base de datos de SQLite:

SELECT COUNT(*)
FROM "Users" AS "u"
WHERE CAST("u"."PhoneNumber" AS TEXT) LIKE '%555%'

Tenga en cuenta que la traducción de ToString() para SQL Server ya se admite en EF Core 5.0 y también puede
ser compatible con otros proveedores de bases de datos.
[Link]
Problema de GitHub número 16141. Esta característica la ha aportado @RaymondHuy.
[Link] se asigna a una función de base de datos que devuelve un número seudoaleatorio entre 0
y 1, ambos no incluidos. Las traducciones se han implementado en el repositorio de EF Core para SQL Server,
SQLite y Cosmos. Por ejemplo, considere un tipo de entidad User con una propiedad Popularity :

public class User


{
public int Id { get; set; }
public string Username { get; set; }
public int Popularity { get; set; }
}

Popularity puede tener valores entre 1 y 5, ambos incluidos. Con [Link] , podemos escribir una
consulta para devolver todos los usuarios con una popularidad elegida aleatoriamente:

var users = [Link](u => [Link] == (int)([Link]() * 5.0) + 1).ToList();

Esto se traduce en el siguiente código SQL cuando se usa una base de datos de SQL Server:

SELECT [u].[Id], [u].[Popularity], [u].[Username]


FROM [Users] AS [u]
WHERE [u].[Popularity] = (CAST((RAND() * 5.0E0) AS int) + 1)

Compatibilidad con columnas dispersas de SQL Server


Problema de GitHub número 8023.
Las columnas dispersas de SQL Server son columnas ordinarias que se optimizan para almacenar valores NULL.
Esto puede ser útil cuando se usa la asignación de herencia de tabla por jerarquía, donde las propiedades de un
subtipo poco usado darán como resultado valores de columna NULL para la mayoría de las filas de la tabla. Por
ejemplo, considere una clase ForumModerator que se extiende desde ForumUser :

public class ForumUser


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

public class ForumModerator : ForumUser


{
public string ForumName { get; set; }
}

Puede haber millones de usuarios, pero solo unos pocos de ellos son moderadores. Esto significa que, en este
caso, puede tener sentido la asignación de ForumName como disperso. Esto ahora puede configurarse mediante
IsSparse en OnModelCreating. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<ForumModerator>()
.Property(e => [Link])
.IsSparse();
}

Después, las migraciones de EF Core marcarán la columna como dispersa. Por ejemplo:

CREATE TABLE [ForumUser] (


[Id] int NOT NULL IDENTITY,
[Username] nvarchar(max) NULL,
[Discriminator] nvarchar(max) NOT NULL,
[ForumName] nvarchar(max) SPARSE NULL,
CONSTRAINT [PK_ForumUser] PRIMARY KEY ([Id]));

NOTE
Las columnas dispersas tienen limitaciones. Le recomendamos que lea la documentación sobre columnas dispersas de
SQL Server para asegurarse de que las columnas dispersas son la opción correcta para su escenario.

Base de datos en memoria: valide que las propiedades obligatorias no son NULL
Problema de GitHub número 10613. Esta característica la ha aportado @fagnercarvalho.
La base de datos en memoria de EF Core ahora producirá una excepción si se intenta guardar un valor NULL
para una propiedad marcada como obligatoria. Por ejemplo, considere un tipo User con una propiedad
Username obligatoria:

public class User


{
public int Id { get; set; }

[Required]
public string Username { get; set; }
}
Si intenta guardar una entidad con un valor Username NULL, se producirá la siguiente excepción:

[Link]: Required properties '{'Username'}' are missing for the


instance of entity type 'User' with the key value '{Id: 1}' ([Link]:
faltan las propiedades "{'Username'}" obligatorias para la instancia de tipo de entidad "User" con el valor de
clave "{Id: 1}").

Esta validación se puede deshabilitar si es necesario. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder
.LogTo([Link], new[] { [Link] })
.UseInMemoryDatabase("UserContextWithNullCheckingDisabled")
.EnableNullabilityCheck(false);
}

Mejora de la traducción de SQL Server para IsNullOrWhitespace


Problema de GitHub número 22916. Esta característica la ha aportado @Marusyk.
Considere la consulta siguiente:

var users = [Link](


e => [Link]([Link])
|| [Link]([Link])).ToList();

Antes de EF Core 6.0, esto se traducía a lo siguiente en SQL Server:

SELECT [u].[Id], [u].[FirstName], [u].[LastName]


FROM [Users] AS [u]
WHERE ([u].[FirstName] IS NULL OR (LTRIM(RTRIM([u].[FirstName])) = N'')) OR ([u].[LastName] IS NULL OR
(LTRIM(RTRIM([u].[LastName])) = N''))

Esta traducción se ha mejorado para EF Core 6.0 a:

SELECT [u].[Id], [u].[FirstName], [u].[LastName]


FROM [Users] AS [u]
WHERE ([u].[FirstName] IS NULL OR ([u].[FirstName] = N'')) OR ([u].[LastName] IS NULL OR ([u].[LastName] =
N''))

Cambio de los comentarios de las bases de datos a comentarios de código con scaffolding
Problema de GitHub número 19113. Esta característica la ha aportado @ErikEJ.
Ahora se aplica scaffolding a los comentarios en tablas y columnas de SQL para convertirlos en los tipos de
entidad que se crean al aplicar ingeniería inversa a un modelo de EF Core de una base de datos de SQL Server
existente. Por ejemplo:
/// <summary>
/// The Blog table.
/// </summary>
public partial class Blog
{
/// <summary>
/// The primary key.
/// </summary>
[Key]
public int Id { get; set; }
}

[Link] 6.0, versión preliminar 1


TIP
Puede ejecutar todos los ejemplos de la versión preliminar 1 que se muestran a continuación y depurar en ellos. Para
hacerlo, descargue el código de ejemplo de GitHub.

API de puntos de retorno


Problema de GitHub número 20228.
Hemos estandarizado una API común para puntos de retorno en proveedores de [Link]. [Link]
ahora admite esta API, incluido lo siguiente:
Save(String) para crear un punto de retorno en la transacción
Rollback(String) para revertir a un punto de retorno anterior
Release(String) para liberar un punto de retorno
El uso de un punto de retorno permite que parte de una transacción se revierta sin necesidad de revertirla
entera. Por ejemplo, el código siguiente:
Crea una transacción.
Envía una actualización a la base de datos.
Crea un punto de retorno.
Envía otra actualización a la base de datos.
Revierte al punto de retorno creado previamente.
Confirma la transacción.
using var connection = new SqliteConnection("DataSource=[Link]");
[Link]();

using var transaction = [Link]();

using (var command = [Link]())


{
[Link] = @"UPDATE Users SET Username = 'ajcvickers' WHERE Id = 1";
[Link]();
}

[Link]("MySavepoint");

using (var command = [Link]())


{
[Link] = @"UPDATE Users SET Username = 'wfvickers' WHERE Id = 2";
[Link]();
}

[Link]("MySavepoint");

[Link]();

Esto hará que la primera actualización se confirme en la base de datos, pero no la segunda porque el punto de
retorno se revirtió antes de confirmar la transacción.
Tiempo de espera del comando en la cadena de conexión
Problema de GitHub número 22505. Esta característica la ha aportado @nmichels.
Los proveedores de [Link] admiten dos tiempos de espera distintos:
El tiempo de espera de conexión, que determina el tiempo máximo de espera al establecer una conexión con
la base de datos.
El tiempo de espera del comando, que determina el tiempo máximo que hay que esperar hasta que se
complete la ejecución de un comando.
El tiempo de espera del comando se puede establecer desde el código mediante
[Link]. Muchos proveedores ahora también exponen el tiempo de espera del
comando en la cadena de conexión. [Link] sigue esta tendencia con la palabra clave
Command Timeout de la cadena de conexión. Por ejemplo, "Command Timeout=60;DataSource=[Link]" usará
60 segundos como tiempo de espera predeterminado para los comandos que cree la conexión.

TIP
SQLite trata Default Timeout como sinónimo de Command Timeout , por lo que se puede usar en su lugar si se
prefiere.
Cambios importantes en EF Core 6.0
12/03/2021 • 2 minutes to read • Edit Online

Es posible que los siguientes cambios de API y comportamiento puedan interrumpir las aplicaciones existentes
cuando se actualicen a EF Core 6.0.0.

NOTE
Próximamente.

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

Cambios de impacto medio


Cambios de impacto bajo
Plan para Entity Framework Core 5.0
12/03/2021 • 21 minutes to read • Edit Online

IMPORTANT
Se ha publicado EF Core 5.0. Esta página se conserva como un registro histórico del plan.

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.

Información general
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 de .NET Standard 2.1, incluido .NET 5.0. Esto
forma parte de la convergencia más general a nivel de .NET de las plataformas a .NET Core.
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 drásticos 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.

Asignación de varios a varios totalmente transparente por convención


Jefes de desarrollo: @smitpatel, @AndriySvyryd y @lajones
Número de seguimiento: 10508
Talla de camiseta: L
Estado: ¡Listo!
Varios a varios es la característica más solicitada (506 votos aproximadamente) en el trabajo pendiente de
GitHub.
La compatibilidad con las relaciones varios a varios se puede dividir en tres áreas principales:
Propiedades de navegación por omisión, que se describen en el siguiente tema.
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. Número de seguimiento: 9914.
Facilitar la configuración de relaciones varios a varios.
Además de la compatibilidad con la navegación por omisión, ahora vamos a incorporar estas otras áreas de
varios a varios a EF Core 5.0 para proporcionar una experiencia completa.

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: ¡Listo!
Tal y como se describe en el primer tema, la compatibilidad de varios a varios tiene varios aspectos que cabe
destacar. En este tema se realiza un seguimiento específico del uso de las navegaciones por omisión. 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.

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


Jefe de desarrollo: @AndriySvyryd y @smitpatel
Seguimiento realizado por #2266
Talla de camiseta: XL
Estado: ¡Listo!
TPT se va a incluir porque se trata de una característica muy solicitada (aproximadamente 289 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: ¡Listo!
Inclusión filtrada es una característica muy solicitada (aproximadamente 376 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.

División de Include
Jefe de desarrollo: @smitpatel
Número de seguimiento: 20892
Talla de camiseta: L
Estado: ¡Listo!
EF Core 3.0 cambió el comportamiento predeterminado para crear una consulta SQL única para una consulta
LINQ determinada. Esto provocó una gran cantidad de degradaciones de rendimiento para las consultas que
usan Include para varias colecciones.
En EF Core 5.0, se mantiene el nuevo comportamiento predeterminado. Sin embargo, EF Core 5.0 ahora permite
la generación de varias consultas para los métodos Include de la colección en los que tener una sola consulta
provoca deficiencias en el rendimiento.

Dependencias de uno a uno obligatorias


Jefes de desarrollo: @AndriySvyryd y @smitpatel
Número de seguimiento: 12100
Talla de camiseta: M
Estado: ¡Listo!
En EF Core 3.0, todas las dependencias, incluidos los tipos de propiedad, son opcionales (por ejemplo,
[Link] puede ser NULL). En EF Core 5.0, se pueden configurar las dependencias según sea necesario.

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


Jefes de desarrollo: @AndriySvyryd y @smitpatel
Seguimiento realizado por #17270
Talla de camiseta: L
Estado: ¡Listo!
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: ¡Listo!
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: Ámbito y trabajo hecho
Ámbito: la característica de conjunto de migraciones se ha aplazado hasta después de la versión EF Core 5.0. Sin
embargo, en EF Core 5.0 se incluirán otras mejoras de destino relacionadas con las migraciones.
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: Ámbito o hecho
Ámbito: la guía y los ejemplos de la plataforma se publican para Blazor, Xamarin, WinForms y WPF. Xamarin y
otros trabajos de AOT o enlazador ahora están previstos para EF Core 6.0.
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: Ámbito y trabajo hecho
Ámbito: se han completado las principales mejoras de rendimiento en el proveedor Npgsql. Ahora se ha
planificado otro trabajo de rendimiento para EF Core 6.0.
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: Cut
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
Actualización: Desafortunadamente, este plan era demasiado ambicioso. Seguimos creyendo que es un aspecto
importante, pero lamentablemente no se incluirá en EF Core 5.0.

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: ¡Listo!
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.

Sugerencias
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
07/04/2021 • 24 minutes to read • Edit Online

En la lista siguiente se incluyen las principales características nuevas de EF Core 5.0. Para obtener una lista
completa de los problemas de la versión, consulte nuestro sitio de seguimiento de problemas.
Como versión principal, EF Core 5.0 también presenta varios cambios importantes, que son mejoras en la API o
cambios de comportamiento que podrían afectar negativamente a las aplicaciones existentes.

Varios a varios
EF Core 5.0 admite relaciones de varios a varios sin asignar explícitamente la tabla de combinación.
Por ejemplo, considere estos tipos de entidad:

public class Post


{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Tag> Tags { get; set; }
}

public class Tag


{
public int Id { get; set; }
public string Text { get; set; }
public ICollection<Post> Posts { get; set; }
}

EF Core 5.0 reconoce esto como una relación de varios a varios por convención y crea automáticamente una
tabla combinada PostTag en la base de datos. Los datos se pueden consultar y actualizar sin hacer referencia
explícita a la tabla combinada, lo que simplifica considerablemente el código. La tabla combinada se sigue
pudiendo personalizar y consultar explícitamente si es necesario.
Para obtener más información, vea la documentación completa sobre las relaciones de varios a varios.

Consultas divididas
A partir de EF Core 3.0, EF Core siempre genera una única consulta SQL para cada consulta LINQ. Esto garantiza
la coherencia de los datos devueltos de acuerdo con las restricciones del modo de transacción en uso. Sin
embargo, esta operación puede ser muy lenta si la consulta utiliza Include o una proyección para devolver
varias colecciones relacionadas.
Ahora, EF Core 5.0 permite dividir en varias consultas SQL una única consulta LINQ con colecciones
relacionadas. Esto puede mejorar de forma significativa el rendimiento, pero puede dar lugar a incoherencias en
los resultados devueltos si los datos de las dos consultas son diferentes. Las transacciones serializables o de
instantáneas se pueden usar para mitigar esta situación y lograr la coherencia con las consultas divididas, pero
esto puede conllevar otros costos de rendimiento y diferencias de comportamiento.
Imaginemos, por ejemplo, que tenemos una consulta que extrae dos niveles de colecciones relacionadas
mediante Include :
var artists = [Link]
.Include(e => [Link])
.ToList();

De forma predeterminada, EF Core generará el siguiente código SQL al usar el proveedor SQLite:

SELECT a."Id", a."Name", a0."Id", a0."ArtistId", a0."Title"


FROM "Artists" AS a
LEFT JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id", a0."Id"

Con las consultas divididas, en su lugar se genera este código SQL:

SELECT a."Id", a."Name"


FROM "Artists" AS a
ORDER BY a."Id"

SELECT a0."Id", a0."ArtistId", a0."Title", a."Id"


FROM "Artists" AS a
INNER JOIN "Album" AS a0 ON a."Id" = a0."ArtistId"
ORDER BY a."Id"

Las consultas divididas se pueden habilitar colocando el nuevo operador AsSplitQuery en cualquier parte de la
consulta LINQ o globalmente en el método OnConfiguring del modelo. Para obtener más información, vea la
documentación completa sobre las consultas divididas.

Registro sencillo y diagnósticos mejorados


EF Core 5.0 presenta una forma sencilla de configurar el registro a través del nuevo método LogTo . Lo siguiente
hará que los mensajes de registro se escriban en la consola, incluido todo el código SQL generado por EF Core:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link]([Link]);

Además, ahora es posible llamar a ToQueryString en cualquier consulta LINQ recuperando el código SQL que
ejecutaría la consulta:

[Link](
[Link]
.Where(a => [Link] == "Pink Floyd")
.ToQueryString());

Por último, se han incorporado varios tipos de EF Core con una propiedad DebugView mejorada, la cual
proporciona una vista detallada de los elementos internos. Por ejemplo, se puede consultar la propiedad
[Link] para ver exactamente las entidades de las que se realiza un seguimiento en un
momento determinado.
Para obtener más información, consulte la documentación sobre el registro y la interceptación.

Inclusión filtrada
El método Include ahora admite el filtrado de las entidades incluidas:
var blogs = [Link]
.Include(e => [Link](p => [Link]("Cheese")))
.ToList();

Esta consulta devolverá los blogs y las publicaciones asociadas, pero solo cuando el título de la publicación
contenga la palabra "Cheese".
Para obtener más información, consulte la documentación completa sobre la inclusión filtrada.

Asignación de tabla por tipo (TPT)


De forma predeterminada, EF Core asigna una jerarquía de herencia de tipos .NET a una tabla de base de datos
única. Esto se conoce como asignación de tabla por jerarquía (TPH). EF Core 5.0 también permite asignar cada
tipo .NET de una jerarquía de herencia a una tabla de base de datos diferente; esto se conoce como asignación
de tabla por tipo (TPT).
Por ejemplo, piense en este modelo con una jerarquía asignada:

public class Animal


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

public class Cat : Animal


{
public string EducationLevel { get; set; }
}

public class Dog : Animal


{
public string FavoriteToy { get; set; }
}

Con TPT, se crea una tabla de base de datos para cada tipo en la jerarquía:

CREATE TABLE [Animals] (


[Id] int NOT NULL IDENTITY,
[Name] nvarchar(max) NULL,
CONSTRAINT [PK_Animals] PRIMARY KEY ([Id])
);

CREATE TABLE [Cats] (


[Id] int NOT NULL,
[EdcuationLevel] nvarchar(max) NULL,
CONSTRAINT [PK_Cats] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Cats_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
);

CREATE TABLE [Dogs] (


[Id] int NOT NULL,
[FavoriteToy] nvarchar(max) NULL,
CONSTRAINT [PK_Dogs] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Dogs_Animals_Id] FOREIGN KEY ([Id]) REFERENCES [Animals] ([Id]) ON DELETE NO ACTION,
);

Para obtener más información, vea la documentación completa sobre la característica de tabla por tipo.

Asignación de entidades flexible


Normalmente, los tipos de entidad se asignan a tablas o vistas, de modo que EF Core extraerá el contenido de la
tabla o vista al consultar ese tipo. EF Core 5.0 incluye opciones de asignación adicionales, donde una entidad se
puede asignar a una consulta SQL (denominada "consulta de definición") o a una función con valores de tabla
(TVF):

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>().ToSqlQuery(
@"SELECT Id, Name, Category, BlogId FROM posts
UNION ALL
SELECT Id, Name, ""Legacy"", BlogId from legacy_posts");

[Link]<Blog>().ToFunction("BlogsReturningFunction");
}

Las funciones con valores de tabla también se pueden asignar a un método de .NET en lugar de a una instancia
de DbSet, lo que permite pasar parámetros; la asignación se puede configurar con HasDbFunction.
Por último, ahora es posible asignar una entidad a una vista cuando se consulta (o a una función o una consulta
de definición) y a una tabla cuando se actualiza:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<Blog>()
.ToTable("Blogs")
.ToView("BlogsView");
}

Tipos de entidad de tipo compartido y contenedores de propiedades


EF Core 5.0 permite asignar el mismo tipo de CLR a varios tipos distintos de entidad; estos tipos se conocen
como tipos de entidad de tipo compartido. Aunque se puede usar cualquier tipo de CLR con esta característica,
el diccionario de .NET ( Dictionary ) ofrece un caso de uso especialmente atractivo al que llamamos
"contenedores de propiedades":

public class ProductsContext : DbContext


{
public DbSet<Dictionary<string, object>> Products => Set<Dictionary<string, object>>("Product");

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Dictionary<string, object>>("Product", b =>
{
[Link]<int>("Id");
[Link]<string>("Name").IsRequired();
[Link]<decimal>("Price");
});
}
}

Después, estas entidades se pueden consultar y actualizar como los tipos de entidad normales con su propio
tipo de CLR dedicado. Para obtener más información, vea la documentación sobre contenedores de
propiedades.

Dependientes de 1:1 requeridos


En EF Core 3.1, el extremo dependiente de una relación uno a uno se consideraba siempre opcional. Esto era
más que evidente al usar entidades en propiedad, ya que todas las columnas de una entidad en propiedad se
creaban para aceptar valores NULL en la base de datos, incluso si se habían configurado como requeridas en el
modelo.
En EF Core 5.0, la navegación a una entidad en propiedad se puede configurar como una entidad dependiente
requerida. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Person>(b =>
{
[Link](e => [Link],
b =>
{
[Link](e => [Link]).IsRequired();
[Link](e => [Link]).IsRequired();
});
[Link](e => [Link]).IsRequired();
});
}

DbContextFactory
EF Core 5.0 introduce AddDbContextFactory y AddPooledDbContextFactory para registrar una fábrica que crea
instancias de DbContext en el contenedor de inserción de dependencias de la aplicación. Esto puede ser útil
cuando el código de la aplicación necesita crear y eliminar instancias de contexto manualmente.

[Link]<SomeDbContext>(b =>
[Link](@"Server=(localdb)\mssqllocaldb;Database=Test"));

En este punto, los servicios de aplicaciones, como los controladores de [Link] Core, se pueden insertar con la
interfaz IDbContextFactory<TContext> y pueden usarla para crear instancias de contexto:

public class MyController


{
private readonly IDbContextFactory<SomeDbContext> _contextFactory;

public MyController(IDbContextFactory<SomeDbContext> contextFactory)


=> _contextFactory = contextFactory;

public void DoSomeThing()


{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
}

Para obtener más información, vea la documentación completa sobre DbContextFactory.

Recompilaciones de tabla de SQLite


En comparación con otras bases de datos, SQLite está relativamente limitada en sus capacidades de
manipulación de esquemas; por ejemplo, no se admite la eliminación de una columna de una tabla existente.
EF Core 5.0 pone fin a estas limitaciones mediante la creación de una nueva tabla, la copia de los datos de la
tabla anterior, la eliminación de dicha tabla anterior y el cambio de nombre de la nueva tabla, todo esto forma
automática. Esto "recompila" la tabla y permite aplicar de forma segura operaciones de migración no admitidas
previamente.
Para obtener más detalles sobre qué operaciones de migración se admiten ahora a través de las
recompilaciones de tabla, consulte esta página de documentación.

Intercalaciones de bases de datos


EF Core 5.0 incluye compatibilidad con la especificación de intercalaciones de texto a nivel de base de datos,
columna o consulta. Esto permite que la distinción de mayúsculas y minúsculas y otros aspectos textuales se
configuren de forma flexible y no afecten al rendimiento de las consultas.
Por ejemplo, el siguiente código configurará la columna Name para que distinga entre mayúsculas y minúsculas
en SQL Server, y los índices creados en la columna funcionarán según corresponda:

modelBuilder
.Entity<User>()
.Property(e => [Link])
.UseCollation("SQL_Latin1_General_CP1_CS_AS");

Para obtener más información, vea la documentación completa sobre intercalaciones y distinción entre
mayúsculas y minúsculas.

Contadores de eventos
EF Core 5.0 expone contadores de eventos que se pueden usar para realizar un seguimiento del rendimiento de
la aplicación y detectar diversas anomalías. Solo es necesario asociar a un proceso que ejecuta EF con la
herramienta dotnet-counters:

> dotnet counters monitor [Link] -p 49496

[[Link]]
Active DbContexts 1
Execution Strategy Operation Failures (Count / 1 sec) 0
Execution Strategy Operation Failures (Total) 0
Optimistic Concurrency Failures (Count / 1 sec) 0
Optimistic Concurrency Failures (Total) 0
Queries (Count / 1 sec) 1,755
Queries (Total) 98,402
Query Cache Hit Rate (%) 100
SaveChanges (Count / 1 sec) 0
SaveChanges (Total) 1

Para obtener más información, vea la documentación completa sobre los contadores de eventos.

Otras características
Creación de modelos
Las API de creación de modelos se han agregado para facilitar la configuración de comparadores de valores.
Ahora, las columnas calculadas se pueden configurar como almacenadas o virtuales.
Ahora la precisión y la escala se pueden configurar a través de la API fluida.
Se han introducido nuevas API de creación de modelos para las propiedades de navegación.
Se han agregado nuevas API de creación de modelos para los campos, de forma similar a las propiedades.
Los tipos de .NET PhysicalAddress y IPAddress ahora se pueden asignar a columnas de cadena de la base de
datos.
Ahora se puede configurar un campo de respaldo a través del nuevo atributo [BackingField] .
Ahora se permiten los campos de respaldo que aceptan valores NULL, lo que proporciona una mejor
compatibilidad con los valores predeterminados generados por el almacén donde el valor predeterminado
de CLR no es un buen centinela (en particular, los valores bool ).
Se puede usar un nuevo atributo [Index] en un tipo de entidad para especificar un índice, en lugar de usar
la API fluida.
Se puede usar un nuevo atributo [Keyless] para configurar un tipo de entidad sin clave.
De forma predeterminada, EF Core ahora se refiere a los discriminadores como completos, lo que significa
que espera no ver nunca valores de discriminador no configurados por la aplicación en el modelo. Esto
permite algunas mejoras en el rendimiento y se puede deshabilitar en caso de que la columna
discriminadora pueda contener valores desconocidos.
Consultar
Las excepciones de errores de traducción de consultas ahora contienen motivos más explícitos sobre las
causas del error para ayudar a identificar el problema.
Las consultas de no seguimiento ahora pueden realizar la resolución de identidad, lo que evita que se
devuelvan varias instancias de entidad para el mismo objeto de base de datos.
Se ha agregado compatibilidad con GroupBy con agregados condicionales (por ejemplo,
GroupBy(o => [Link]).Select(g => [Link](i => [Link] != null)) ).
Se ha agregado compatibilidad con la traducción del operador Distinct en los elementos de grupo antes del
agregado.
Traducción de Reverse .
Traducción de objetos DateTime mejorada para SQL Server (por ejemplo, DateDiffWeek y DateFromParts ).
Traducción de nuevos métodos en matrices de bytes (por ejemplo, Contains , Length y SequenceEqual ).
Traducción de algunos operadores bit a bit adicionales, como el complemento de dos.
Traducción de FirstOrDefault en las cadenas.
Traducción de consultas mejorada en cuanto a la semántica de valores NULL, lo que da lugar a consultas más
precisas y eficaces.
Ahora se pueden anotar funciones asignadas por el usuario para controlar la propagación de valores NULL,
lo que también da como resultado consultas más precisas y eficaces.
El código SQL que contiene bloques de casos ahora es bastante más conciso.
Ahora se puede llamar a la función DATELENGTH de SQL Server en las consultas a través del nuevo método
[Link] .
EnableDetailedErrors agrega detalles adicionales a las excepciones.

Guardando
Interceptación y eventos de SaveChanges.
Se han incorporado API para controlar puntos de retorno de transacciones. Además, EF Core creará
automáticamente un punto de retorno cuando se llame a SaveChanges y ya haya una transacción en curso, y
se revertirá a él en caso de error.
La aplicación puede establecer explícitamente un identificador de transacción, lo que permite una correlación
más sencilla de los eventos de transacción en el registro y en cualquier otro lugar.
El tamaño máximo predeterminado del lote de SQL Server ha cambiado a 42 debido a un análisis del
rendimiento del procesamiento por lotes.
Migraciones y scaffolding
Ahora, las tablas se pueden excluir de las migraciones.
Un nuevo comando dotnet ef migrations list muestra las migraciones que todavía no se han aplicado a la
base de datos ( Get-Migration hace lo mismo en la consola de Administración de paquetes).
Ahora, los scripts de migración contienen instrucciones de transacción cuando corresponde para mejorar la
administración de los casos en los que se producen errores al aplicar la migración.
Las columnas de las clases base no asignadas se ordenan ahora después de otras columnas para los tipos de
entidad asignados. Tenga en cuenta que esto solo afecta a las tablas recién creadas; el orden de las columnas
de las tablas existentes permanece sin cambios.
La generación de migraciones ahora puede tener en cuenta si la migración que se está generando es
idempotente y si la salida se ejecutará inmediatamente o se generará como un script.
Se han agregado nuevos parámetros de la línea de comandos para especificar los espacios de nombres en
migraciones y scaffolding.
El comando dotnet ef database update ahora acepta un nuevo parámetro --connection para especificar la
cadena de conexión.
La aplicación de scaffolding a las bases de datos existentes ahora pone en singular los nombres de las tablas,
por lo que las tablas denominadas People y Addresses pasarán a ser tipos de entidad denominados
Person y Address al aplicarles scaffolding. Los nombres de las bases de datos originales se pueden
conservar.
La nueva opción --no-onconfiguring puede indicar a EF Core que excluya el método OnModelConfiguring
cuando se aplique scaffolding a un modelo.
Cosmos
Se han ampliado las opciones de conexión de Cosmos.
Ahora se admite la simultaneidad optimista en Cosmos mediante el uso de ETags.
El nuevo método WithPartitionKey permite incluir la clave de partición de Cosmos en el modelo y en las
consultas.
Ahora se traducen los métodos de cadena Contains , StartsWith y EndsWith para Cosmos.
El operador is de C# ahora se traduce en Cosmos.
SQLite
Ahora se admiten columnas calculadas.
La recuperación de datos binarios y de cadena con GetBytes, GetChars y GetTextReader ahora es más eficaz
gracias al uso de SqliteBlob y flujos.
La inicialización de SqliteConnection ahora es diferida.
Otros
Se pueden generar proxies de seguimiento de cambios que implementen automáticamente
INotifyPropertyChanging y INotifyPropertyChanged. Esto proporciona un enfoque alternativo para el
seguimiento de cambios que no busca cambios cuando se llama a SaveChanges .
Ahora se puede cambiar un objeto DbConnection o una cadena de conexión en una instancia ya inicializada
de DbContext.
El nuevo método
[Link].0#Microsoft_EntityFrameworkCore_ChangeTracking_ChangeTracker_Clear) borra la
instancia de DbContext de todas las entidades sometidas a seguimiento. Normalmente, esto no es necesario
si se aplica el procedimiento recomendado de crear una nueva instancia de contexto de corta duración para
cada unidad de trabajo. Pero si es necesario restablecer el estado de una instancia de DbContext, el uso del
nuevo método Clear() es más eficaz y robusto que la separación masiva de todas las entidades.
Las herramientas de línea de comandos de EF Core ahora configuran automáticamente las variables de
entorno ASPNETCORE_ENVIRONMENT y DOTNET_ENVIRONMENT en "desarrollo". Esto aporta la experiencia al usar el
host genérico en línea con la experiencia de [Link] Core durante el desarrollo.
Los argumentos personalizados de la línea de comandos pueden fluir en
IDesignTimeDbContextFactory<TContext>, lo que permite que las aplicaciones controlen cómo se crea e
inicializa el contexto.
Ahora, el factor de relleno de índices se puede configurar en SQL Server.
La nueva propiedad IsRelational se puede usar para distinguir cuándo se usa un proveedor relacional o uno
no relacional (como InMemory).
Cambios importantes en EF Core 5.0
07/04/2021 • 35 minutes to read • Edit Online

Es posible que los siguientes cambios de API y comportamiento puedan interrumpir las aplicaciones actuales
cuando se actualicen a EF Core 5.0.0.

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

EF Core 5.0 no admite .NET Framework Media

[Link]() está obsoleto Media

Los decimales necesitan precisión y escala Media

El requisito relativo a la navegación de una entidad de Media


seguridad a un elemento dependiente tiene una semántica
diferente.

La definición de la consulta se reemplaza por métodos Media


específicos del proveedor

Las consultas no sobrescriben las navegaciones de referencia Media


no nula

ToView() se trata de forma diferente en las migraciones Media

ToTable(null) marca el tipo de entidad como no asignado a Media


una tabla

Se ha quitado el método HasGeometricDimension de la Bajo


extensión de SQLite NTS

Cosmos: la clave de partición se ha agregado ahora a la Bajo


clave principal

Cosmos: el nombre de la propiedad id se ha cambiado a Bajo


__id

Cosmos: byte[] se almacena ahora como cadena Base64 y no Bajo


como matriz de números

Cosmos: se ha cambiado el nombre de GetPropertyName y Bajo


SetPropertyName

Se llama a los generadores de valores cuando se cambia el Bajo


estado de la entidad de Desasociado a Sin cambios,
Actualizado o Eliminado

IMigrationsModelDiffer ahora usa IRelationalModel. Bajo


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

Los discriminadores son de solo lectura. Bajo

Los métodos [Link] específicos del proveedor se Bajo


inician para el proveedor InMemory

[Link] ahora está obsoleto Bajo

Ahora se incluye un pluralizador para el scaffolding de los Bajo


modelos de ingeniería inversa

INavigationBase reemplaza a INavigation en algunas API Bajo


para admitir la omisión de navegaciones

Ya no se admiten algunas consultas con colecciones Bajo


correlacionadas que también usan Distinct o GroupBy

No se admite el uso de una colección de tipo consultable en Bajo


la proyección

Cambios de impacto medio


EF Core 5.0 no admite .NET Framework
Problema de seguimiento n.º 15498
Comportamiento anterior
EF Core 3.1 tiene como destino .NET Standard 2.0, que es compatible con .NET Framework.
Comportamiento nuevo
EF Core 5.0 tiene como destino .NET Standard 2.1, que no es compatible con .NET Framework. Esto significa que
EF Core 5.0 no se puede usar con aplicaciones de .NET Framework.
Por qué
Esto forma parte de un mayor movimiento entre los equipos de .NET destinado a la unificación en una sola
plataforma .NET de destino. Para obtener más información, vea El futuro de .NET Standard.
Mitigaciones
Las aplicaciones de .NET Framework pueden seguir usando EF Core 3.1, que es una versión de soporte a largo
plazo (LTS). Como alternativa, es posible actualizar las aplicaciones para que usen .NET Core 3.1 o .NET 5, puesto
que ambas plataformas admiten .NET Standard 2.1.

[Link] () está obsoleto


Incidencia de seguimiento n.º 2266
Comportamiento anterior
GetColumnName() devolvía el nombre de la columna a la que se asigna una propiedad.
Comportamiento nuevo
GetColumnName() todavía devuelve el nombre de la columna a la que se asigna una propiedad, pero este
comportamiento ahora es ambiguo, ya que EF Core 5 admite el modelo de tabla por tipo y la asignación
simultánea a una vista o una función en la que estas asignaciones podrían usar nombres de columna diferentes
para la misma propiedad.
Por qué
Hemos marcado este método como obsoleto para guiar a los usuarios a una sobrecarga más precisa:
GetColumnName(IProperty, StoreObjectIdentifier).
Mitigaciones
Use el siguiente código para obtener el nombre de columna de una tabla específica:

var columnName = [Link]([Link]("Users", null)));

Los decimales necesitan precisión y escala


Incidencia de seguimiento n.º 19293
Comportamiento anterior
Normalmente, en EF Core no se establecía la precisión y la escala en los objetos SqlParameter. Esto significa que
la precisión y la escala completa se enviaban a SQL Server, donde se redondeaba en función de la precisión y la
escala de la columna de base de datos.
Comportamiento nuevo
Ahora en EF Core se establece la precisión y la escala de los parámetros con los valores configurados para las
propiedades en el modelo de EF Core. Esto significa que ahora el redondeo se produce en SqlClient. Por tanto, si
la precisión y la escala configuradas no coinciden con las de la base de datos, es posible que cambie el redondeo
que se ve.
Por qué
Para las características más recientes de SQL Server, incluida Always Encrypted, es necesario que las facetas de
parámetro se especifiquen de forma completa. Además, SqlClient ha realizado un cambio para redondear los
valores decimales en lugar de truncarlos, lo que coincide con el comportamiento de SQL Server. Esto ha hecho
posible que EF Core establezca estas facetas sin cambiar el comportamiento de los decimales configurados
correctamente.
Mitigaciones
Asigne las propiedades decimales mediante un nombre de tipo que incluya precisión y escala. Por ejemplo:

public class Blog


{
public int Id { get; set; }

[Column(TypeName = "decimal(16, 5")]


public decimal Score { get; set; }
}

O bien, use HasPrecision en las API de creación de modelos. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>().Property(e => [Link]).HasPrecision(16, 5);
}

El requisito relativo a la navegación de una entidad de seguridad a un elemento dependiente tiene una
semántica diferente.
Incidencia de seguimiento n.º 17286
Comportamiento anterior
Solo las navegaciones a la entidad de seguridad se podían configurar como necesarias. Por lo tanto, el uso de
RequiredAttribute en la navegación a un elemento dependiente (la entidad que contiene la clave externa)
creaba en su lugar la clave externa en el tipo de entidad de definición.
Comportamiento nuevo
Gracias a la compatibilidad agregada con los elementos dependientes necesarios, ahora es posible marcar
cualquier navegación de referencia como obligatoria, lo que significa que, en el caso anterior, la clave externa se
definirá en el otro lado de la relación y las propiedades no se marcarán como obligatorias.
Llamar a IsRequired antes de especificar el extremo dependiente ahora es ambiguo:

[Link]<Blog>()
.HasOne(b => [Link])
.WithOne(i => [Link])
.IsRequired()
.HasForeignKey<BlogImage>(b => [Link]);

Por qué
El comportamiento nuevo es obligatorio para habilitar la compatibilidad con los dependientes necesarios (vea
n.º 12100).
Mitigaciones
Quite RequiredAttribute de la navegación al elemento dependiente y colóquelo en su lugar en la navegación de
la entidad de seguridad o configure la relación en OnModelCreating :

[Link]<Blog>()
.HasOne(b => [Link])
.WithOne(i => [Link])
.HasForeignKey<BlogImage>(b => [Link])
.IsRequired();

La definición de la consulta se reemplaza por métodos específicos del proveedor


Problema de seguimiento n.º 18903
Comportamiento anterior
Los tipos de entidad se asignaban en la definición de consultas en el nivel básico. Cuando el tipo de entidad se
usaba en la raíz de la consulta del tipo de entidad se sustituía por la consulta de definición de cualquier
proveedor.
Comportamiento nuevo
Las API de definición de consulta han quedado en desuso. Se introdujeron nuevas API específicas del proveedor.
Por qué
Aunque la definición de consulta se implementaba como consulta de reemplazo siempre que se usaba la raíz de
la consulta en la consulta, tenía algunos problemas:
Si la definición de consulta establece la proyección del tipo de entidad con new { ... } en el método
Select , su identificación como entidad requería trabajo adicional y lo hacía incoherente con el modo en que
EF Core trata los tipos nominales en la consulta.
En el caso de los proveedores relacionales FromSql todavía hay que pasar la cadena SQL en forma de
expresión LINQ.
Las definiciones de consulta se introdujeron inicialmente como vistas del lado cliente para su uso con el
proveedor en memoria para las entidades sin clave (similares a las vistas de base de datos de las bases de datos
relacionales). Esta definición facilita la prueba de la aplicación en la base de datos en memoria. Después, se
convirtieron en ampliamente aplicables, lo que resultaba útil pero no era coherente y resultaba difícil de
entender. Por tanto, decidimos simplificar el concepto. Hemos creado una consulta de definición basada en LINQ
exclusiva para el proveedor en memoria, que tratamos de otra forma. Para más información, consulte este
problema.
Mitigaciones
En el caso de los proveedores relacionales, use el método ToSqlQuery en OnModelCreating y pase una cadena
SQL que se utilice como tipo de entidad. En el caso del proveedor en memoria, use el método ToInMemoryQuery
en OnModelCreating y pase una consulta LINQ que se utilice como tipo de entidad.

Las consultas no sobrescriben las navegaciones de referencia no nula


Incidencia de seguimiento n.º 2693
Comportamiento anterior
En EF Core 3.1, las instancias de la entidad de la base de datos sobrescribirán las navegaciones de referencia que
se inicializan de manera diligente en valores que no sean NULL, con independencia de si coinciden o no los
valores de claves. Sin embargo, en otros casos, EF Core 3.1 haría lo contrario y dejaría el valor no NULL
existente.
Comportamiento nuevo
A partir de EF Core 5.0, las instancias devueltas por una consulta nunca sobrescriben las navegaciones de
referencia no NULL.
Tenga en cuenta que todavía se admite la inicialización diligente de una navegación de colección en una
colección vacía.
Por qué
La inicialización de una propiedad de navegación de referencia en una instancia de entidad "vacía" da como
resultado un estado ambiguo. Por ejemplo:

public class Blog


{
public int Id { get; set; }
public Author Author { get; set; ) = new Author();
}

Normalmente, una consulta de blogs y autores primero creará instancias Blog y, después, establecerá las
instancias Author apropiadas en función de los datos devueltos por la base de datos. Sin embargo, en este caso,
cada propiedad [Link] ya se ha inicializado en una instancia Author vacía. Excepto EF Core, no tiene
ninguna manera de saber que esta instancia está "vacía". Por lo tanto, si se sobrescribe esta instancia, es posible
que se elimine de forma silenciosa una instancia Author válida. Por lo tanto, EF Core 5.0 ahora no sobrescribe
de forma coherente una navegación que ya está inicializada.
Este nuevo comportamiento también se alinea con el comportamiento de EF6 en la mayoría de los casos,
aunque en la investigación también encontramos algunos casos de incoherencia en EF6.
Mitigaciones
Si se detecta esta interrupción, la solución consiste en detener la inicialización diligente de las propiedades de
navegación de referencia.

ToView() se trata de forma diferente en las migraciones


Incidencia de seguimiento n.º 2725
Comportamiento anterior
Las llamadas a ToView(string) hacían que las migraciones omitieran el tipo de entidad y lo asignaran a una
vista.
Comportamiento nuevo
Ahora ToView(string) marca el tipo de entidad como no asignado a una tabla y lo asigna a una vista. Como
resultado, la primera migración después de actualizar a EF Core 5 intenta quitar la tabla predeterminada para
este tipo de entidad, puesto que ya no se omite.
Por qué
EF Core ahora permite asignar un tipo de entidad a una tabla y una vista simultáneamente, de modo que
ToView ya no es un indicador válido que las migraciones deben omitir.

Mitigaciones
Use el siguiente código para marcar la tabla asignada como excluida de las migraciones:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<User>().ToTable("UserView", t => [Link]());
}

ToTable (null) marca el tipo de entidad como no asignado a una tabla


Incidencia de seguimiento n.º 21172
Comportamiento anterior
ToTable(null) restablecía el nombre de la tabla al valor predeterminado.
Comportamiento nuevo
Ahora, ToTable(null) marca el tipo de entidad como no asignado a ninguna tabla.
Por qué
EF Core ahora permite asignar un tipo de entidad a una tabla y una vista simultáneamente, de modo que
ToTable(null) se usa para indicar que no está asignado a ninguna tabla.

Mitigaciones
Use el siguiente código para restablecer el nombre de la tabla al valor predeterminado si no está asignado a una
vista o una función DbFunction:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<User>().[Link]([Link]);
}

Cambios de impacto bajo


Se ha quitado el método HasGeometricDimension de la extensión de SQLite NTS
Problema de seguimiento n.º 14257
Comportamiento anterior
Se ha utilizado HasGeometricDimension para habilitar dimensiones adicionales (Z y M) en columnas de
geometría. Sin embargo, solo ha afectado a la creación de la base de datos. No era necesario especificarlo para
consultar los valores con dimensiones adicionales. Tampoco ha funcionado correctamente al introducir o
actualizar valores con dimensiones adicionales (consulte el problema n.º 14257).
Comportamiento nuevo
Para habilitar la inserción y la actualización de valores de geometría con dimensiones adicionales (Z y M), la
dimensión debe especificarse como parte del nombre del tipo de columna. Esta API coincide más estrechamente
con el comportamiento subyacente de la función AddGeometryColumn de SpatiaLite.
Por qué
El uso de HasGeometricDimension después de especificar la dimensión en el tipo de columna es innecesario y
redundante, por lo que se ha quitado HasGeometricDimension por completo.
Mitigaciones
Use HasColumnType para especificar la dimensión:
[Link]<GeoEntity>(
x =>
{
// Allow any GEOMETRY value with optional Z and M values
[Link](e => [Link]).HasColumnType("GEOMETRYZM");

// Allow only POINT values with an optional Z value


[Link](e => [Link]).HasColumnType("POINTZ");
});

Cosmos: la clave de partición se ha agregado ahora a la clave principal


Incidencia de seguimiento n.º 15289
Comportamiento anterior
La propiedad de clave de partición solo se agregaba a la clave alternativa que incluye id .
Comportamiento nuevo
La propiedad de clave de partición ahora también se agrega por convención a la clave principal.
Por qué
Este cambio hace que el modelo se alinee mejor con la semántica de Azure Cosmos DB y mejora el rendimiento
de Find y algunas consultas.
Mitigaciones
Para evitar que la propiedad de clave de partición se agregue a la clave principal, configúrela en
OnModelCreating .

[Link]<Blog>()
.HasKey(b => [Link]);

Cosmos: el nombre de la propiedad id se ha cambiado a __id

Problema de seguimiento n.º 17751


Comportamiento anterior
La propiedad Shadow asignada a la propiedad id de JSON también se llama id .
Comportamiento nuevo
La propiedad Shadow creada por convención se denomina ahora __id .
Por qué
Este cambio hace menos probable que la propiedad id entre en conflicto con una propiedad existente en el
tipo de entidad.
Mitigaciones
Para volver al comportamiento de la versión 3.x, configure la propiedad id en OnModelCreating .

[Link]<Blog>()
.Property<string>("id")
.ToJsonProperty("id");

Cosmos: byte [] se almacena ahora como cadena Base64 y no como matriz de números
Problema de seguimiento n.º 17306
Comportamiento anterior
Las propiedades de tipo byte[] se almacenaban como matriz de números.
Comportamiento nuevo
Las propiedades de tipo byte[] se almacenan ahora como cadena Base64.
Por qué
Esta representación de byte [] se alinea mejor con las expectativas y es el comportamiento predeterminado de
las principales bibliotecas de serialización de JSON.
Mitigaciones
Los datos existentes almacenados como matrices de números se seguirán consultando correctamente, pero
actualmente no hay ninguna manera compatible de volver a cambiar el comportamiento de inserción. Si esta
limitación bloquea su escenario, haga un comentario en este problema

Cosmos: se ha cambiado el nombre de GetPropertyName y SetPropertyName


Problema de seguimiento n.º 17874
Comportamiento anterior
Anteriormente, se llamaba a los métodos de extensión GetPropertyName y SetPropertyName

Comportamiento nuevo
La API antigua se ha quitado y se han agregado métodos nuevos: GetJsonPropertyName y SetJsonPropertyName .
Por qué
Este cambio elimina la ambigüedad en torno a lo que estos métodos configuran.
Mitigaciones
Use la API nueva.

Se llama a los generadores de valores cuando se cambia el estado de la entidad de Desasociado a Sin
cambios, Actualizado o Eliminado
Incidencia de seguimiento n.º 15289
Comportamiento anterior
Solo se llamaba a los generadores de valores cuando el estado de la entidad cambiaba a Agregado.
Comportamiento nuevo
Ahora se llama a los generadores de valores cuando se cambia el estado de la entidad de Desasociado a Sin
cambios, Actualizado o Eliminado, y la propiedad incluye los valores predeterminados.
Por qué
Este cambio era necesario para mejorar la experiencia con propiedades que no se conservan en el almacén de
datos y que su valor se genera siempre en el cliente.
Mitigaciones
Para evitar que se llame al generador de valores, asigne un valor no predeterminado a la propiedad antes de
cambiar el estado.

IMigrationsModelDiffer ahora usa IRelationalModel.


Incidencia de seguimiento n.º 20305
Comportamiento anterior
IMigrationsModelDiffer API se definía mediante IModel .
Comportamiento nuevo
IMigrationsModelDiffer API ahora usa IRelationalModel . Sin embargo, la instantánea del modelo todavía
contiene solo IModel , ya que este código forma parte de la aplicación y Entity Framework no puede cambiarla
sin hacer un cambio más importante.
Por qué
IRelationalModel es una representación recién agregada del esquema de la base de datos. Su uso para
encontrar diferencias es más rápido y preciso.
Mitigaciones
Use el código siguiente para comparar el modelo de snapshot con el modelo de context :

var dependencies = [Link]<ProviderConventionSetBuilderDependencies>();


var relationalDependencies = [Link]<RelationalConventionSetBuilderDependencies>();

var typeMappingConvention = new TypeMappingConvention(dependencies);


[Link](((IConventionModel)[Link]).Builder, null);

var relationalModelConvention = new RelationalModelConvention(dependencies, relationalDependencies);


var sourceModel = [Link]([Link]);

var modelDiffer = [Link]<IMigrationsModelDiffer>();


var hasDifferences = [Link](
((IMutableModel)sourceModel).FinalizeModel().GetRelationalModel(),
[Link]());

Tenemos previsto mejorar esta experiencia en la versión 6.0 (vea n.º 22031).

Los discriminadores son de solo lectura.


Incidencia de seguimiento n.º 21154
Comportamiento anterior
Era posible cambiar el valor del discriminador antes de llamar a SaveChanges .
Comportamiento nuevo
En el caso anterior, se producirá una excepción.
Por qué
EF no espera que el tipo de entidad cambie mientras se sigue realizando el seguimiento, por lo que cambiar el
valor del discriminador deja el contexto en un estado incoherente, lo que podría dar como resultado un
comportamiento inesperado.
Mitigaciones
Si es necesario cambiar el valor del discriminador y el contexto se va a eliminar inmediatamente después de
llamar a SaveChanges , el discriminador se puede convertir en mutable:

[Link]<BaseEntity>()
.Property<string>("Discriminator")
.[Link]([Link]);

Los métodos [Link] específicos del proveedor se inician para el proveedor InMemory.
Incidencia de seguimiento n.º 20294
Comportamiento anterior
Los métodos [Link] específicos del proveedor incluían la implementación para la ejecución del cliente, lo
cual les permitía ejecutarse en el proveedor de InMemory. Por ejemplo, [Link] es un método
específico de SQL Server que funcionaba en el proveedor InMemory.
Comportamiento nuevo
Los métodos específicos del proveedor se han actualizado para producir una excepción en el cuerpo del método
a fin de bloquear su evaluación en el lado cliente.
Por qué
Los métodos específicos del proveedor se asignan a una función de base de datos. El cálculo realizado por la
función de base de datos asignada no siempre se puede replicar en el lado cliente en LINQ. Esto puede provocar
que el resultado del servidor sea diferente al ejecutar el mismo método en el cliente. Dado que estos métodos
se usan en LINQ para traducir a funciones específicas de base de datos, no es necesario que se evalúen en el
lado cliente. Dado que el proveedor InMemory es una base de datos diferente, estos métodos no están
disponibles para este proveedor. Al intentar ejecutarlos para el proveedor InMemory o cualquier otro proveedor
que no traduzca estos métodos, se produce una excepción.
Mitigaciones
Dado que no hay forma de imitar el comportamiento de las funciones de base de datos con precisión, debe
probar las consultas que las contienen en el mismo tipo de base de datos que en producción.

[Link] ahora está obsoleto


Incidencia de seguimiento n.º 21089
Comportamiento anterior
Anteriormente, solo se podía definir un índice en un conjunto determinado de propiedades. El nombre de la
base de datos de un índice se configuró mediante [Link].
Comportamiento nuevo
Ahora se permiten varios índices en el mismo conjunto o las mismas propiedades. Ahora, estos índices se
distinguen por un nombre en el modelo. Por convención, el nombre del modelo se utiliza como nombre de la
base de datos; sin embargo, también se puede configurar de forma independiente utilizando
HasDatabaseName.
Por qué
En el futuro, nos gustaría habilitar índices ascendentes y descendentes o índices con distintas intercalaciones en
el mismo conjunto de propiedades. Este cambio nos hace dar otro paso en esa dirección.
Mitigaciones
Cualquier código que llamara anteriormente a [Link] debe actualizarse para llamar a
HasDatabaseName en su lugar.
Si su proyecto incluye migraciones generadas antes de la versión 2.0.0 de EF Core, puede ignorar la advertencia
en esos archivos de forma segura y suprimirla agregando #pragma warning disable 612, 618 .

Ahora se incluye un pluralizador para el scaffolding de los modelos de ingeniería inversa


Incidencia de seguimiento n.º 11160
Comportamiento anterior
Anteriormente, tenía que instalar un paquete de pluralizador independiente para pluralizar los nombres de
navegación de colección y DbSet y singularizar los nombres de tabla al aplicar scaffolding a DbContext y los
tipos de entidad mediante la utilización de técnicas de ingeniería inversa en un esquema de base de datos.
Comportamiento nuevo
EF Core incluye ahora un pluralizador que usa la biblioteca Humanizer. Se trata de la misma biblioteca que
Visual Studio usa para recomendar nombres de variable.
Por qué
El uso de formas plurales de palabras para las propiedades de colección y de formas singulares para los tipos y
las propiedades de referencia es idiomático en .NET.
Mitigaciones
Para deshabilitar el pluralizador, use la opción --no-pluralize en dotnet ef dbcontext scaffold o el
modificador -NoPluralize en Scaffold-DbContext .

INavigationBase reemplaza a INavigation en algunas API para admitir la omisión de navegaciones


Incidencia de seguimiento n.º 2568
Comportamiento anterior
Las versiones de EF Core anteriores a la 5.0 solo admiten una forma de propiedad de navegación, representada
por la interfaz INavigation .
Comportamiento nuevo
EF Core 5.0 presenta relaciones varios a varios que usan las "navegaciones por omisión". Se representan
mediante la interfaz ISkipNavigation , y la mayor parte de la funcionalidad de INavigation se ha insertado en
una interfaz base común: INavigationBase .
Por qué
La mayor parte de la funcionalidad entre las navegaciones normal y por omisión es la misma. Sin embargo, las
navegaciones por omisión tienen una relación diferente con las claves externas con respecto a las navegaciones
normales, ya que las claves externas implicadas no están directamente en ninguno de los extremos de la
relación, sino en la entidad join.
Mitigaciones
En muchos casos, las aplicaciones pueden cambiar al uso de la nueva interfaz base sin realizar ningún otro
cambio. Sin embargo, en los casos en los que se usa la navegación para acceder a las propiedades de clave
externa, el código de aplicación se debe restringir solo a las navegaciones normales, o bien actualizarse para
hacer lo adecuado para las navegaciones normal y por omisión.

Ya no se admiten algunas consultas con colecciones correlacionadas que también usan Distinct o GroupBy

Incidencia de seguimiento n.º 15873


Compor tamiento anterior
Anteriormente, las consultas que implicaban colecciones correlacionadas seguidas de GroupBy , así como
algunas consultas con Distinct que permitimos ejecutar.
Ejemplo de GroupBy:

[Link]
.Select(p => [Link]
.GroupBy(c => [Link])
.Select(g => [Link]))

Ejemplo de Distinct : en concreto, las consultas Distinct en las que la proyección de la colección interna no
contiene la clave principal:

[Link]
.Select(p => [Link]
.Select(c => [Link])
.Distinct())

Estas consultas podrían devolver resultados incorrectos si la colección interna contuviera duplicados, pero
funcionaría correctamente si todos los elementos de la colección interna fueran únicos.
Compor tamiento nuevo
Estas consultas ya no son compatibles. Se produce una excepción que indica que no hay suficiente información
para compilar los resultados correctamente.
Por qué
En el caso de los escenarios de colecciones correlacionadas, es necesario conocer la clave principal de la entidad
para asignar entidades de colección al elemento primario correcto. Cuando la colección interna no utiliza
GroupBy ni Distinct , la clave principal que falta se puede agregar simplemente a la proyección. Sin embargo,
en el caso de GroupBy y Distinct , no se puede hacer porque cambiaría el resultado de la operación GroupBy o
Distinct .

Mitigaciones
Vuelva a escribir la consulta para que no use las operaciones GroupBy o Distinct en la colección interna y, en
su lugar, realice estas operaciones en el cliente.

[Link]
.Select(p => [Link](c => [Link]))
.ToList()
.Select(x => [Link](c => c).Select(g => [Link]))

[Link]
.Select(p => [Link](c => [Link]))
.ToList()
.Select(x => [Link]())

No se admite el uso de una colección de tipo consultable en la proyección


Incidencia de seguimiento n.º 16314
Compor tamiento anterior
Anteriormente, era posible usar la colección de un tipo consultable dentro de la proyección en algunos casos,
por ejemplo, como un argumento de un constructor List<T> :

[Link]
.Select(b => new List<Post>([Link](p => [Link] == [Link])))

Compor tamiento nuevo


Estas consultas ya no son compatibles. Se produce una excepción que indica que no se puede crear un objeto de
tipo consultable y, además, se sugiere cómo corregir este problema.
Por qué
No se puede materializar un objeto de un tipo consultable, por lo que, en su lugar, se creará automáticamente
con el tipo List<T> . Esto suele provocar una excepción debido a una falta de coincidencia de tipos que no era
muy clara y podría ser sorprendente para algunos usuarios. Decidimos reconocer el patrón y producir una
excepción más significativa.
Mitigaciones
Agregue la llamada a ToList() después del objeto consultable en la proyección:

[Link](b => [Link](p => [Link] == [Link]).ToList())


Características nuevas de Entity Framework Core 3.x
12/03/2021 • 13 minutes to read • Edit Online

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


Como versión principal, EF Core 3.x 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.x, 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.x 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.x 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.x 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.x 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.x 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 await foreach
estándar y se pueden usar con IAsyncEnumerable<T> .

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.x 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 DbCommandInterceptor :

public class HintCommandInterceptor : DbCommandInterceptor


{
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> 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.x, 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.x, 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 migrar a la versión más reciente de EF 6 para que se ejecute en .NET Core 3.x.
Para más detalles, consulte Novedades de EF 6.

Características pospuestas
Algunas características planeadas originalmente para EF Core 3.x 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.x
12/03/2021 • 88 minutes to read • Edit Online

Es posible que los siguientes cambios de API y comportamiento interrumpan las aplicaciones existentes cuando
se actualicen a las versiones 3.x. 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

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

Se ha quitado el elemento UseRowNumberForPaging Media


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

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

Los valores de clave temporal ya no se establecen en Bajo


instancias de entidad

Ahora, las entidades dependientes que comparten la tabla Bajo


con la entidad de seguridad son 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

Ahora, las propiedades heredadas de tipos sin asignar se Bajo


asignan 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

Ahora, la conexión de base de datos 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

Ahora [Link] realiza una operación Bajo


DetectChanges local

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


forma predeterminada

Ahora ILoggerFactory es 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
C A M B IO IM P O RTA N T E IM PA C TO

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

Ahora los valores char se almacenan como TEXT en SQLite Bajo

Ahora los id. de migración 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 Bajo


claves 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]

Se deben configurar varias relaciones de referencia Bajo


automática ambiguas
C A M B IO IM P O RTA N T E IM PA C TO

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


para estar en el esquema predeterminado del modelo

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


Standard 2.0 Revertido

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


depuración Revertido

Cambios de impacto alto


Las consultas LINQ ya no se evalúan en el cliente
Problema de seguimiento n.° 14935 Consulte también el problema n.° 12795
Comportamiento 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.
Comportamiento 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.

Cambios de impacto medio


Entity Framework Core ya no forma parte del marco compartido [Link] Core
Anuncios del problema de seguimiento n.º 325
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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.

Cambios de impacto bajo


FromSql, ExecuteSql y ExecuteSqlAsync han cambiado de nombre
Problema de seguimiento n.º 10996

IMPORTANT
ExecuteSqlCommand y ExecuteSqlCommandAsync están en desuso. En su lugar, use estos métodos.

Comportamiento 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.
Comportamiento 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
Comportamiento 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();

Comportamiento 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í.
Mitigaciones
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
Comportamiento anterior
Antes de EF Core 3.0, el método FromSql podía especificarse en cualquier lugar en la consulta.
Comportamiento 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
Comportamiento 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. Fijémonos en
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.
Comportamiento 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.

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


Problema de seguimiento n.º 12378
Comportamiento 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.
Comportamiento 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
Comportamiento anterior
Antes de EF Core 3.0, se realizaba el seguimiento en el estado Added de las entidades sin seguimiento
detectadas por DetectChanges y se insertaban como una fila nueva cuando se llamaba a SaveChanges .
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.

Comportamiento 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
Comportamiento 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).
Comportamiento 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]<>() .
IQueryTypeConfiguration<TQuery> : en su lugar se debe usar IEntityTypeConfiguration<TEntity> .

NOTE
Debido a un problema en la versión 3.x, al consultar entidades sin clave que tienen todas las propiedades establecidas en
null se devolverá un null en lugar de una entidad, si este problema es aplicable a su escenario, agregue también
lógica para administrar null en los resultados.

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
Comportamiento 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 .
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento anterior
Antes de EF Core 3.0, las entidades en propiedad se podían consultar como cualquier otra navegación.

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

Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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();


}
}

Comportamiento 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
Comportamiento anterior
Antes de EF Core 3.0, se usaba un generador de valores compartidos para todas las propiedades de clave entera
en memoria.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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");

Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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[] .
Comportamiento 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
Comportamiento anterior
Antes de EF Core 3.0, ILoggerFactory se registraba como un servicio de singleton.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento anterior
Antes, los siguientes métodos asincrónicos devolvían Task<T> :
[Link]()
[Link]()
[Link]()
[Link]()
[Link]() (y las clases derivadas)
Comportamiento 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
Comportamiento anterior
El nombre de anotación para las anotaciones de asignación de tipos era "Relational:TypeMapping".
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento anterior
Antes de EF Core 3.0, ForSqlServerHasIndex().ForSqlServerInclude() proporcionaba una manera de configurar
las columnas que se usaban con INCLUDE .
Comportamiento nuevo
A partir de EF Core 3.0, ya se admite el uso de Include en un índice en el nivel relacional. Use
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
Comportamiento 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
Comportamiento 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
Comportamiento anterior
Antes de EF Core 3.0, EF Core enviaba PRAGMA foreign_keys = 1 cuando se abría una conexión con SQLite.
Comportamiento 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


Comportamiento anterior
Antes de EF Core 3.0, en EF Core se usaba SQLitePCLRaw.bundle_green .
Comportamiento 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
Comportamiento anterior
Antes, los valores GUID se almacenaban como valores BLOB en SQLite.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento anterior
Los identificadores de migración se generaban de forma involuntaria con el calendario de la referencia cultural
actual.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento anterior
IDbContextOptionsExtension incluía métodos para proporcionar metadatos sobre la extensión.
Comportamiento 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
Change
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
Comportamiento 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];

Comportamiento 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
Comportamiento anterior
Antes de EF Core 3.0, estos métodos estaban protegidos.
Comportamiento 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
Comportamiento 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.
Comportamiento 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
Comportamiento anterior
[Link] dependía anteriormente de la versión 1.1.12 de [Link].
Comportamiento 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
Comportamiento anterior
Los paquetes espaciales anteriormente dependían de la versión 1.15.1 de NetTopologySuite.
Comportamiento 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
Comportamiento anterior
[Link] dependía anteriormente de la versión [Link].
Comportamiento 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
Comportamiento 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; }
}

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

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

EF Core 3.0 tiene como destino .NET Standard 2.1, y no .NET Standard 2.0 Revertido
Problema de seguimiento n.º 15498
EF Core 3.0 tiene como destino .NET Standard 2.1, que es un cambio importante que excluye las aplicaciones de
.NET Framework. En EF Core 3.1 se revirtió esto y ahora tiene como destino .NET Standard 2.0 de nuevo.

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])));
Novedades de EF Core 2.1
12/03/2021 • 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 3.0
12/03/2021 • 2 minutes to read • Edit Online

La versión 3.0 de Entity Framework Core (EF Core) ya no es compatible. Todas las características nuevas
agregadas a la versión 3.0 están disponibles en la 3.1. Consulte los vínculos siguientes relacionados con la
versión 3.1.
Nuevas características:
Cambios importantes
Novedades de EF Core 2.2
12/03/2021 • 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.


Nuevas características de EF Core 2.0
12/03/2021 • 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;

Algunas observaciones:
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 está
informado 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 .

Consulta
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.
A continuación se muestra 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.
Actualización de las aplicaciones de las versiones
anteriores a EF Core 2.0
12/03/2021 • 15 minutes to read • Edit Online

Hemos aprovechado la oportunidad de pulir bastante las API y los comportamientos existentes en la
versión 2.0. Hay algunas mejoras que pueden requerir la modificación del código existente de las aplicaciones,
aunque creemos que para casi todas el impacto será bajo. En la mayoría de casos, para reemplazar las API
obsoletas solo es necesario volver a efectuar la compilación y realizar unos cambios mínimos guiados.
La actualización de una aplicación existente a EF Core 2.0 puede requerir lo siguiente:
1. Actualizar la implementación de la aplicación .NET de destino a una que admita .NET Standard 2.0. Vea
Implementaciones de .NET compatibles para obtener más detalles.
2. Identificar un proveedor para la base de datos de destino que sea compatible con EF Core 2.0. Vea debajo
EF Core 2.0 requiere un proveedor de base de datos 2.0.
3. Actualizar todos los paquetes de EF Core (entorno de ejecución y herramientas) a la versión 2.0. Consulte
Instalación de EF Core para obtener información detallada.
4. Realizar los cambios de código necesarios para compensar los cambios importantes descritos en el resto
de este documento.

EF Core, ahora incluido en [Link] 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 destinadas a versiones anteriores de
[Link] Core deben actualizarse a [Link] Core 2.0 para usar EF Core 2.0. Para obtener información detallada
sobre la actualización de aplicaciones [Link] Core a la versión 2.0, vea 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 a la versión 2.0 de un
modo que ha afectado a la lógica de EF Core en tiempo de diseño utilizada en las versiones 1.x. Anteriormente,
en tiempo de diseño, EF Core intentaba invocar [Link] directamente para acceder 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 en sí ya no es suficiente. Si actualiza una aplicación [Link] Core 1.x, es
posible que reciba el error siguiente 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 en la plantilla predeterminada de [Link] Core 2.0. El
método [Link] estático permite a EF Core acceder al proveedor de servicios de la aplicación en
tiempo de diseño. Si va a actualizar 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 patrón nuevo al actualizar aplicaciones a la versión 2.0 es muy recomendable y necesaria
para que funcionen características de producto como las migraciones de Entity Framework Core. La otra
alternativa común es implementar IDesignTimeDbContextFactory<TContext>.

Cambio de nombre de IDbContextFactory


Con el fin de admitir varios 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 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 y confuso para algunos usuarios, quienes acababan intentando
reutilizarlo 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
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, IDbContextFactory<TContext> todavía existe, pero está marcado como obsoleto.

Eliminación de DbContextFactoryOptions
Debido a los cambios en [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 [Link]

ContentRootPath [Link]()

EnvironmentName [Link]("ASPNETCORE_ENVIR
ONMENT")

Cambio del directorio de trabajo en tiempo de diseño


Los cambios en [Link] Core 2.0 también requerían que el directorio de trabajo que usaba dotnet ef se
alineara con el que usaba Visual Studio al ejecutar la aplicación. Un efecto secundario observable de esto es que
los nombres de archivo de SQLite ahora se refieren al directorio del proyecto y no al de salida, como solía ser.

EF Core 2.0 requiere un proveedor de base de datos de la versión 2.0


Para EF Core 2.0, hemos realizado muchas simplificaciones y mejoras en el funcionamiento de 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, PostgreSQL y MySQL
se están actualizando a la versión 2.0. Para el resto de proveedores, póngase en contacto con el escritor en
cuestión.

Cambios en los eventos de diagnóstico y registro


Nota: Estos cambios no deberían afectar a gran parte del código de aplicación.
Los id. de evento de los mensajes enviados a un elemento ILogger han cambiado en la versión 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 DiagnosticSource ahora usan los mismos nombres de id. de evento que los mensajes de ILogger
correspondientes. Todas las cargas de evento son tipos nominales derivados de EventData.
Los id. de eventos, tipos de carga y categorías se documentan en las clases CoreEventId y RelationalEventId.
Los id. también se han pasado de [Link] al nuevo espacio de nombres
[Link].

Cambios en la API de metadatos relacionales de 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 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. Por ejemplo, un código de 1.1.x 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 o los metadatos que se definen para todos los
proveedores relacionales. La API y los metadatos siguen siendo los mismos cuando son específicos de un único
proveedor. Por ejemplo, los índices en clúster son específicos de SQL Server, por lo que se deben seguir usando
ForSqlServerIsClustered y .SqlServer().IsClustered() .

No tomar el control del proveedor de servicios de EF


EF Core usa un elemento IServiceProvider interno (un contenedor de inserción de dependencias) para su
implementación interna. Las aplicaciones deben permitir que EF Core cree y administre este proveedor, excepto
en casos especiales. Valore seriamente la posibilidad de quitar todas las llamadas a UseInternalServiceProvider .
Si una aplicación necesita llamar a UseInternalServiceProvider , sopese tramitar una incidencia 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 igual que antes.

Obligatoriedad de nombre de las bases de datos en memoria


La base de datos global en memoria 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 "MyDatabase". 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 en 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 insertarlo en una fila de base de datos ( BeforeSaveBehavior ) o al actualizar una fila de base de
datos existente ( AfterSaveBehavior ).
Las propiedades marcadas como [Link] (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 de la que se realiza el seguimiento. Esto se puede cambiar estableciendo
un elemento Before\AfterSaveBehavior distinto.

Comportamiento de eliminación de ClientSetNull nuevo


En versiones anteriores, [Link] tenía un comportamiento para las entidades de las que el
contexto realizaba un seguimiento que coincidía más estrechamente con la semántica SetNull . En EF Core 2.0,
se ha introducido un comportamiento de ClientSetNull nuevo como valor predeterminado para las relaciones
opcionales. Este comportamiento tiene la semántica SetNull para las entidades sometidas a seguimiento y el
comportamiento Restrict para las bases de datos creadas mediante EF Core. La experiencia nos indica que
estos son los comportamientos más esperados y útiles para las entidades sometidas a seguimiento y la base de
datos. [Link] ahora se admite para entidades sometidas a seguimiento cuando se establece
para relaciones opcionales.
Eliminación de los paquetes en tiempo de diseño del proveedor
El paquete [Link] se ha eliminado. Su contenido se ha consolidado
en [Link] y [Link] .
Esto se propaga a los paquetes en tiempo de diseño del proveedor. Estos 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 del 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" />
Características nuevas en EF Core 1.1
12/03/2021 • 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
12/03/2021 • 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.

Guardado de 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.
Duración, configuración e inicialización de
DbContext
12/03/2021 • 23 minutes to read • Edit Online

En este artículo se muestran los patrones básicos para la inicialización y configuración de una instancia de
DbContext.

La duración de DbContext
La duración de DbContext comienza cuando se crea la instancia y finaliza cuando la instancia se elimina. Una
instancia de DbContext está diseñada para usarse para una única unidad de trabajo. Esto significa que la
duración de una instancia de DbContext suele ser muy breve.

TIP
Por citar a Martin Fowler, del vínculo anterior, "una unidad de trabajo hace un seguimiento de todas las acciones que
realiza durante una transacción comercial que pueden afectar a la base de datos. Cuando ha terminado, determina todo lo
que se debe hacer para modificar la base de datos como resultado de su trabajo".

Una unidad de trabajo típica al utilizar Entity Framework Core (EF Core) implica lo siguiente:
Creación de una instancia de DbContext .
Seguimiento de las instancias de entidad por el contexto. Seguimiento de las entidades mediante
devolución desde una consulta
adición o asociación al contexto
Se realizan cambios en las entidades sometidas a seguimiento según sea necesario para implementar la
regla empresarial.
Se llama a SaveChanges o SaveChangesAsync. EF Core detecta los cambios realizados y los escribe en la
base de datos.
Se elimina la instancia de DbContext .

IMPORTANT
Es muy importante eliminar DbContext tras su uso. De este modo, se garantiza que se liberen los recursos no
administrados y que se anule el registro de todos los eventos u otros enlaces para evitar pérdidas de memoria en caso
de que se siga haciendo referencia a la instancia.
DbContext no es seguro para subprocesos . No comparta contextos entre subprocesos. Asegúrese de esperar
todas las llamadas asincrónicas antes de continuar usando la instancia de contexto.
Un objeto InvalidOperationException generado por código de EF Core puede poner el contexto en un estado
irrecuperable. Estas excepciones indican un error del programa y no están diseñadas para recuperarse.

DbContext en la inserción de dependencias para [Link] Core


En muchas aplicaciones web, cada solicitud HTTP corresponde a una sola unidad de trabajo. Esto hace que
vincular la vida útil del contexto a la de la solicitud sea un buen valor predeterminado para las aplicaciones web.
Las aplicaciones de [Link] Core se configuran mediante la inserción de dependencias. EF Core se puede
agregar a esta configuración mediante AddDbContext en el método ConfigureServices de [Link] . Por
ejemplo:

public void ConfigureServices(IServiceCollection services)


{
[Link]();

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

En este ejemplo se registra una subclase DbContext denominada ApplicationDbContext como un servicio con
ámbito en el proveedor de servicios de aplicación de [Link] Core (también conocido como contenedor de
inserción de dependencias). El contexto se configura para utilizar el proveedor de base de datos de SQL Server y
leerá la cadena de conexión de la configuración de [Link] Core. Por lo general, no importa dónde en
ConfigureServices se realiza la llamada a AddDbContext .

La clase debe exponer un constructor público con un parámetro


ApplicationDbContext
DbContextOptions<ApplicationDbContext> . Así es cómo se pasa la configuración de contexto de AddDbContext a
DbContext . Por ejemplo:

public class ApplicationDbContext : DbContext


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

ApplicationDbContext se puede usar en controladores de [Link] Core u otros servicios a través de la inserción
de constructores. Por ejemplo:

public class MyController


{
private readonly ApplicationDbContext _context;

public MyController(ApplicationDbContext context)


{
_context = context;
}
}

El resultado final es una instancia de ApplicationDbContext creada para cada solicitud y que se pasa al
controlador para realizar una unidad de trabajo antes de que se elimine cuando finalice la solicitud.
Lea más adelante en este artículo para obtener más información sobre las opciones de configuración. Además,
consulte Inicio de la aplicación en [Link] Core e Inserción de dependencias en [Link] Core para obtener más
información sobre la configuración y la inserción de dependencias en [Link] Core.

Inicialización de DbContext simple con "New"


Las instancias de DbContext se pueden construir de la manera normal de .NET, por ejemplo, con new en C#. La
configuración se puede realizar invalidando el método OnConfiguring o pasando opciones al constructor. Por
ejemplo:
public class ApplicationDbContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
[Link](@"Server=(localdb)\mssqllocaldb;Database=Test");
}
}

Este patrón también facilita el paso de la configuración, como la cadena de conexión, a través del constructor
DbContext . Por ejemplo:

public class ApplicationDbContext : DbContext


{
private readonly string _connectionString;

public ApplicationDbContext(string connectionString)


{
_connectionString = connectionString;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
[Link](_connectionString);
}
}

Como alternativa, DbContextOptionsBuilder se puede usar para crear un objeto DbContextOptions que se pasa a
continuación al constructor DbContext . Esto permite que el elemento DbContext configurado para la inserción
de dependencias también se construya explícitamente. Por ejemplo, al usar el elemento ApplicationDbContext
definido para aplicaciones web de [Link] Core que vimos anteriormente:

public class ApplicationDbContext : DbContext


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

DbContextOptions se puede crear y se puede llamar al constructor explícitamente:

var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()


.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test")
.Options;

using var context = new ApplicationDbContext(contextOptions);

Uso de un generador de DbContext (por ejemplo, para Blazor)


Algunos tipos de aplicaciones (por ejemplo, [Link] Core Blazor) utilizan la inserción de dependencias, pero no
crean un ámbito de servicio que se alinee con la duración de DbContext deseada. Incluso en los casos en los que
exista una alineación, es posible que la aplicación tenga que realizar varias unidades de trabajo en este ámbito.
Por ejemplo, varias unidades de trabajo en una única solicitud HTTP.
En estos casos, se puede usar AddDbContextFactory para registrar un generador para la creación de instancias
de DbContext . Por ejemplo:
public void ConfigureServices(IServiceCollection services)
{
[Link]<ApplicationDbContext>(
options =>
[Link](@"Server=(localdb)\mssqllocaldb;Database=Test"));
}

La clase ApplicationDbContextdebe exponer un constructor público con un parámetro


DbContextOptions<ApplicationDbContext> . Este es el mismo patrón que se usa en la sección de [Link] Core
tradicional anterior.

public class ApplicationDbContext : DbContext


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

El generador de DbContextFactory se puede utilizar en otros servicios a través de la inserción de constructores.


Por ejemplo:

private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;

public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)


{
_contextFactory = contextFactory;
}

A continuación, el generador insertado se puede usar para construir instancias de DbContext en el código del
servicio. Por ejemplo:

public void DoSomething()


{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}

Tenga en cuenta que las instancias de DbContext creadas de este modo no están administradas por el
proveedor de servicios de la aplicación y, por lo tanto, la aplicación debe eliminarlas.
Consulte Blazor Server de [Link] Core con Entity Framework Core (EF Core) para obtener más información
sobre el uso de EF Core con Blazor.

DbContextOptions
El punto inicial de toda la configuración de DbContext es DbContextOptionsBuilder. Hay tres maneras de
obtener este generador:
En AddDbContext y métodos relacionados
En OnConfiguring
Construido explícitamente con new
En las secciones anteriores se muestran ejemplos de cada una de ellas. Se puede aplicar la misma configuración
independientemente de la procedencia del constructor. Además, siempre se llama a OnConfiguring
independientemente de cómo se construya el contexto. Esto significa que OnConfiguring se puede usar para
realizar una configuración adicional incluso cuando se utiliza AddDbContext .
Configuración del proveedor de base de datos
Cada instancia de DbContext debe estar configurada para usar un único proveedor de bases de datos. (Se
pueden usar diferentes instancias de un subtipo DbContext con diferentes proveedores de bases de datos, pero
una sola instancia solo debe usar uno). Un proveedor de base de datos se configura mediante una llamada de
Use* " específica. Por ejemplo, para usar el proveedor de base de datos de SQL Server:

public class ApplicationDbContext : DbContext


{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
[Link](@"Server=(localdb)\mssqllocaldb;Database=Test");
}
}

Estos métodos de Use* " son métodos de extensión implementados por el proveedor de bases de datos. Esto
significa que el paquete NuGet del proveedor de bases de datos debe estar instalado para poder usar el método
de extensión.

TIP
Los proveedores de bases de datos de EF Core hacen un uso extensivo de métodos de extensión. Si el compilador indica
que no se puede encontrar un método, asegúrese de que el paquete NuGet del proveedor está instalado y de que tiene
using [Link]; en el código.

En la tabla siguiente se incluyen ejemplos de proveedores de bases de datos comunes.

SIST EM A DE B A SE DE DATO S E JEM P LO DE C O N F IGURA C IÓ N DET EC C IÓ N DE

SQL Server o Azure SQL .UseSqlServer(connectionString) [Link]


ver

Azure Cosmos DB .UseCosmos(connectionString, [Link]


databaseName) s

SQLite .UseSqlite(connectionString) [Link]

Base de datos en memoria de EF Core .UseInMemoryDatabase(databaseName) [Link]


mory

PostgreSQL* .UseNpgsql(connectionString) [Link]


QL

MySQL/MariaDB* .UseMySql((connectionString) [Link]

Oracle* .UseOracle(connectionString) [Link]

*Microsoft no entrega estos proveedores de bases de datos. Vea Proveedores de bases de datos para obtener
más información acerca de los proveedores de bases de datos.
WARNING
La base de datos en memoria de EF Core no está diseñada para uso en producción. Además, es posible que no sea la
mejor opción incluso para las pruebas. Consulte Pruebas de código que usa EF Core para obtener más información.

Consulte Cadenas de conexión para obtener más información sobre el uso de cadenas de conexión con EF Core.
La configuración opcional específica del proveedor de bases de datos se realiza en un generador adicional
específico del proveedor. Por ejemplo, el uso de EnableRetryOnFailure para configurar reintentos para la
resistencia de la conexión al conectarse a Azure SQL:

public class ApplicationDbContext : DbContext


{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=Test",
providerOptions => { [Link](); });
}
}

TIP
Se usa el mismo proveedor de bases de datos para SQL Server y Azure SQL. Sin embargo, se recomienda usar la
resistencia de la conexión para la conexión a SQL Azure.

Vea Proveedores de bases de datos para obtener más información sobre la configuración específica del
proveedor.
Otra configuración de DbContext
Otra configuración de DbContext se puede encadenar antes o después (no hay diferencia) de la llamada de
Use* . Por ejemplo, para activar el registro de datos confidenciales:

public class ApplicationDbContext : DbContext


{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.EnableSensitiveDataLogging()
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=Test");
}
}

En la tabla siguiente se incluyen ejemplos de métodos comunes a los que se llama en DbContextOptionsBuilder .

M ÉTO DO
DB C O N T EXTO P T IO N SB UIL DER Q UÉ H A C E M Á S IN F O RM A C IÓ N

UseQueryTrackingBehavior Establece el comportamiento de Comportamiento del seguimiento de


seguimiento predeterminado para las las consultas
consultas.

LogTo Una manera sencilla de obtener Registro, eventos y diagnósticos


registros de EF Core (EF Core 5.0 y
versiones posteriores)
M ÉTO DO
DB C O N T EXTO P T IO N SB UIL DER Q UÉ H A C E M Á S IN F O RM A C IÓ N

UseLoggerFactory Registra un generador de Registro, eventos y diagnósticos


[Link] .

EnableSensitiveDataLogging Incluye datos de aplicación en Registro, eventos y diagnósticos


excepciones y registro.

EnableDetailedErrors Errores de consulta más detallados (a Registro, eventos y diagnósticos


costa del rendimiento).

ConfigureWarnings Omite o inicia advertencias y otros Registro, eventos y diagnósticos


eventos.

AddInterceptors Registra los interceptores de EF Core. Registro, eventos y diagnósticos

UseLazyLoadingProxies Usa servidores proxy dinámicos para la Carga diferida


carga diferida.

UseChangeTrackingProxies Usa servidores proxy dinámicos para el Próximamente...


seguimiento de cambios.

NOTE
UseLazyLoadingProxies y UseChangeTrackingProxies son métodos de extensión de los paquetes NuGet
[Link]. Este tipo de llamada de ".UseSomething()" es la forma recomendada de configurar
o usar las extensiones de EF Core incluidas en otros paquetes.

Diferencias entre DbContextOptions y DbContextOptions<TContext>

La mayoría de las subclases DbContext que aceptan DbContextOptions deben usar la variación
DbContextOptions<TContext> genérica. Por ejemplo:

public sealed class SealedApplicationDbContext : DbContext


{
public SealedApplicationDbContext(DbContextOptions<SealedApplicationDbContext> contextOptions)
: base(contextOptions)
{
}
}

Esto garantiza que las opciones correctas para el subtipo DbContext específico se resuelvan a partir de la
inserción de dependencias, incluso cuando se registran varios subtipos DbContext .

TIP
No es necesario que el DbContext esté sellado, pero el sellado es el procedimiento recomendado para las clases que no
están diseñadas para heredarse.

Sin embargo, si el subtipo DbContext va a heredarse, debe exponer un constructor protegido que tome un
elemento DbContextOptions no genérico. Por ejemplo:
public abstract class ApplicationDbContextBase : DbContext
{
protected ApplicationDbContextBase(DbContextOptions contextOptions)
: base(contextOptions)
{
}
}

Esto permite que varias subclases concretas llamen a este constructor base mediante sus diferentes instancias
de DbContextOptions<TContext> genéricas. Por ejemplo:

public sealed class ApplicationDbContext1 : ApplicationDbContextBase


{
public ApplicationDbContext1(DbContextOptions<ApplicationDbContext1> contextOptions)
: base(contextOptions)
{
}
}

public sealed class ApplicationDbContext2 : ApplicationDbContextBase


{
public ApplicationDbContext2(DbContextOptions<ApplicationDbContext2> contextOptions)
: base(contextOptions)
{
}
}

Observe que este es exactamente el mismo patrón que al heredar de DbContext directamente. Es decir, el propio
constructor DbContext acepta un elemento DbContextOptions no genérico por esta razón.
Una subclase DbContext de la que se va a crear una instancia y se puede heredar debe exponer ambas formas
de constructor. Por ejemplo:

public class ApplicationDbContext : DbContext


{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> contextOptions)
: base(contextOptions)
{
}

protected ApplicationDbContext(DbContextOptions contextOptions)


: base(contextOptions)
{
}
}

Configuración de DbContext en tiempo de diseño


Es necesario que herramientas en tiempo de diseño de EF Core, como aquellas para migraciones de EF Core,
puedan detectar y crear una instancia de trabajo de un tipo 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 tal forma que se configure de manera
similar a como se configuraría en tiempo de ejecución.
Aunque cualquier patrón que proporcione la información de configuración necesaria al DbContext puede
funcionar en tiempo de ejecución, las herramientas que requieren que se use un DbContext en tiempo de
diseño solo pueden funcionar con un número de patrones limitado. Estos se tratan con más detalle en Creación
de contexto en tiempo de diseño.
Evitar problemas con el subprocesamiento de DbContext
Entity Framework Core no admite que varias operaciones en paralelo se ejecuten en la misma instancia de
DbContext . Esto incluye la ejecución en paralelo de consultas asincrónicas y cualquier uso simultáneo explícito
desde varios subprocesos. Por tanto, espere siempre llamadas asincrónicas de inmediato mediante el operador
await o use instancias de DbContext independientes para operaciones que se ejecuten en paralelo.

Cuando EF Core detecte un intento de usar una instancia de DbContext simultáneamente, verá un elemento
InvalidOperationException con un mensaje como este:

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ño en los datos.
Hay errores comunes que pueden dar lugar accidentalmente a un acceso simultáneo en la misma instancia de
DbContext :

Errores de operaciones asincrónicas


Los métodos asincrónicos habilitan EF Core para iniciar operaciones con acceso a la base de datos sin bloqueos.
Pero si un llamador no espera a que finalice uno de estos métodos y sigue realizando otras operaciones en el
DbContext , el estado del DbContext puede estar (y muy probablemente lo estará) dañado.

Espere siempre métodos asincrónicos de EF Core de inmediato.


Uso compartido implícito de instancias de DbContext mediante la inserción de dependencias
El método de extensión AddDbContext registra tipos de DbContext con una duración de ámbito de forma
predeterminada.
Está protegido frente a problemas de acceso simultáneo en la mayoría de las aplicaciones [Link] Core, ya que
solo hay un subproceso ejecutando cada solicitud de cliente en un momento dado, y cada solicitud obtiene un
ámbito de inserción de dependencias independiente (y, por tanto, una instancia de DbContext independiente).
Para el modelo de hospedaje de Blazor Server, se usa una solicitud lógica para mantener el circuito de usuario
de Blazor y, por tanto, solo hay disponible una instancia de DbContext con ámbito por circuito de usuario si se
usa el ámbito de inserción predeterminado.
Cualquier código que ejecute explícitamente varios subprocesos en paralelo debe garantizar que no se tenga
nunca acceso a las instancias de DbContext simultáneamente.
Con la inserción de dependencias, esto se puede lograr registrando el contexto como con ámbito y creando
ámbitos (mediante IServiceScopeFactory ) para cada subproceso, o bien registrando el DbContext como
transitorio (mediante la sobrecarga de AddDbContext , que toma un parámetro ServiceLifetime ).

Más lectura
Lea Inserción de dependencias para obtener más información sobre el uso de la inserción de dependencias.
Lea Pruebas para obtener más información.
Creación y configuración de un modelo
07/04/2021 • 3 minutes to read

Entity Framework Core 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]
{
internal 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; }
}
}

Configuración de agrupación
Para reducir el tamaño del método OnModelCreating, toda la configuración de un tipo de entidad se puede
extraer en una clase independiente que implemente IEntityTypeConfiguration<TEntity>.
public class BlogEntityTypeConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder
.Property(b => [Link])
.IsRequired();
}
}

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

new BlogEntityTypeConfiguration().Configure([Link]<Blog>());

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


ensamblado determinado.

[Link](typeof(BlogEntityTypeConfiguration).Assembly);

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

Uso de 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]
{
internal 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
12/03/2021 • 9 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 OnModelCreating método, 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 [Link] propiedad de navegación.
AuditEntry porque se especifica en OnModelCreating .

internal 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; }
}

Exclusión de las migraciones

NOTE
La capacidad de excluir las tablas de las migraciones se presentó en EF Core 5,0.

A veces resulta útil tener el mismo tipo de entidad asignado en varios DbContext tipos. Esto es especialmente
cierto cuando se usan contextos delimitados, para los que es común tener un DbContext tipo diferente para
cada contexto enlazado.

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<IdentityUser>()
.ToTable("AspNetUsers", t => [Link]());
}

Con esta configuración, las migraciones no crearán la AspNetUsers tabla, pero IdentityUser se incluirán en el
modelo y se pueden usar con normalidad.
Si necesita empezar a administrar la tabla con las migraciones de nuevo, se debe crear una nueva migración en
la que AspNetUsers no se excluya. La siguiente migración contendrá los cambios realizados en la tabla.

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 dbo esquema (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.

Vista de la asignación
Los tipos de entidad pueden asignarse a vistas de base de datos mediante la API fluida.

NOTE
EF supondrá que la vista a la que se hace referencia ya existe en la base de datos, no la creará automáticamente en una
migración.

[Link]<Blog>()
.ToView("blogsView", schema: "blogging");

La asignación a una vista quitará la asignación de tabla predeterminada, pero a partir de EF 5,0 el tipo de
entidad también se puede asignar explícitamente a una tabla. En este caso, la asignación de consultas se usará
para las consultas y la asignación de tabla se usará para las actualizaciones.

TIP
Para probar los tipos de entidad asignados a las vistas mediante el proveedor en memoria, asígnelo a una consulta a
través de ToInMemoryQuery . Vea un ejemplo ejecutable mediante esta técnica para obtener más detalles.

Asignación de funciones con valores de tabla


Es posible asignar un tipo de entidad a una función con valores de tabla (TVF) en lugar de a una tabla de la base
de datos. Para ilustrar esto, vamos a definir otra entidad que represente el blog con varias publicaciones. En el
ejemplo, la entidad es una entrada sin llave, pero no es necesario que sea.

public class BlogWithMultiplePosts


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

A continuación, cree la siguiente función con valores de tabla en la base de datos, que solo devuelve blogs con
varias publicaciones, así como el número de entradas asociadas a cada uno de estos blogs:

CREATE FUNCTION [Link]()


RETURNS TABLE
AS
RETURN
(
SELECT [Link], COUNT([Link]) AS PostCount
FROM Blogs AS b
JOIN Posts AS p ON [Link] = [Link]
GROUP BY [Link], [Link]
HAVING COUNT([Link]) > 1
)

Ahora, la entidad BlogWithMultiplePost se puede asignar a esta función de la siguiente manera:

[Link]<BlogWithMultiplePosts>().HasNoKey().ToFunction("BlogsWithMultiplePosts");

NOTE
Para asignar una entidad a una función con valores de tabla, la función no debe tener parámetros.

Convencionalmente, las propiedades de la entidad se asignarán a las columnas coincidentes devueltas por la
función TVF. Si las columnas devueltas por TVF tienen un nombre diferente que la propiedad de entidad, se
puede configurar mediante el HasColumnName método, al igual que cuando se asigna a una tabla normal.
Cuando el tipo de entidad se asigna a una función con valores de tabla, la consulta:

var query = from b in [Link]<BlogWithMultiplePosts>()


where [Link] > 3
select new { [Link], [Link] };

Produce el siguiente SQL:

SELECT [b].[Url], [b].[PostCount]


FROM [dbo].[BlogsWithMultiplePosts]() AS [b]
WHERE [b].[PostCount] > 3

Comentarios de tabla
Puede establecer un Comentario de texto arbitrario que se establece en la tabla de base de datos, lo que le
permite documentar el esquema en la base de datos:

Anotaciones de datos
API fluida
NOTE
La configuración de comentarios a través de anotaciones de datos se presentó en EF Core 5,0.

[Comment("Blogs managed on the website")]


public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
Propiedades de entidad
12/03/2021 • 14 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 como fragmento de código
siguiente:

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 DateTime propiedades a datetime2(7) las columnas y string las propiedades
a nvarchar(max) las columnas (o a nvarchar(450) para las propiedades que se usan 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 la precisión y la 5 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; }
}

Precisión y escala
A partir de EFCore 5,0, puede usar la API fluida para configurar la precisión y la escala. Indica al proveedor de
base de datos cuánto espacio de almacenamiento se necesita para una columna determinada. Solo se aplica a
los tipos de datos en los que el proveedor permite que la precisión y la escala varíen, normalmente decimal y
DateTime .

En decimal el caso de las propiedades, precisión define el número máximo de dígitos necesarios para expresar
cualquier valor que contenga la columna y escala define el número máximo de posiciones decimales necesarias.
En DateTime el caso de las propiedades, precisión define el número máximo de dígitos necesarios para expresar
fracciones de segundos y no se usa la escala.

NOTE
Entity Framework no realiza ninguna validación de precisión o escala antes de pasar los datos al proveedor. Depende del
proveedor o del almacén de datos que se validen según corresponda. Por ejemplo, cuando el destino es SQL Server, una
columna de tipo de datos no datetime permite establecer la precisión, mientras que una datetime2 puede tener una
precisión de entre 0 y 7, ambos inclusive.

En el ejemplo siguiente, la configuración de la Score propiedad para que tenga la precisión 14 y la escala 2
hará que se cree una columna de tipo decimal(14,2) en SQL Server y la configuración de la LastUpdated
propiedad para que tenga la precisión 3 producirá una columna de tipo datetime2(3) :

Anotaciones de datos
API fluida

La precisión y la escala no se pueden configurar actualmente a través de anotaciones de datos.

Propiedades obligatorias y opcionales


Una propiedad se considera opcional si es válida para que la 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 como
necesario y todas las propiedades con tipos de valor de .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 (NRT), 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 afecta al comportamiento del 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 se configurarán según
la nulabilidad de C# de su tipo .NET: se string? configurarán como opcionales, pero se string
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 NRT (valor predeterminado)


Con NRT
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 fluye la nulabilidad expresada en
el código de C# para EF Core modelo de y a 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; }
}

Intercalaciones de columna
NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

Una intercalación se puede definir en columnas de texto, determinando cómo se comparan y ordenan. Por
ejemplo, el siguiente fragmento de código configura una columna de SQL Server para que no distinga entre
mayúsculas y minúsculas:
[Link]<Customer>().Property(c => [Link])
.UseCollation("SQL_Latin1_General_CP1_CI_AS");

Si todas las columnas de una base de datos necesitan usar una intercalación determinada, defina la intercalación
en el nivel de base de datos en su lugar.
Puede encontrar información general sobre la compatibilidad de EF Core con las intercalaciones en la página de
documentación de la Intercalación.

Comentarios de columna
Puede establecer un Comentario de texto arbitrario que se establece en la columna de base de datos, lo que le
permite documentar el esquema en la base de datos:

Anotaciones de datos
API fluida

NOTE
La configuración de comentarios a través de anotaciones de datos se presentó en EF Core 5,0.

public class Blog


{
public int BlogId { get; set; }

[Comment("The URL of the blog")]


public string Url { get; set; }
}
Claves
12/03/2021 • 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).

Configuración de una clave principal


Por Convención, se Id configurará una propiedad denominada o <type name>Id como la clave principal de una
entidad.

internal class Car


{
public string Id { get; set; }

public string Make { get; set; }


public string Model { get; set; }
}

internal 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

internal 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] });
}

Generación de valor
En el caso de las claves principales no compuestas y de GUID, EF Core configura la generación de valores por
Convención. Por ejemplo, una clave principal numérica en SQL Server se configura automáticamente para que
sea una columna de identidad. Para obtener más información, consulte la documentación sobre la generación
de valores.

Nombre de clave principal


Por Convención, en las bases de datos relacionales, las claves principales se crean 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, string
incluidos 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.

internal 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 guiones bajos). 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
07/04/2021 • 11 minutes to read

Las columnas de la base de datos pueden generar sus valores de varias maneras: las columnas de clave
principal suelen ser enteros de incremento automático, otras columnas tienen valores predeterminados o
calculados, etc. En esta página se detallan varios patrones para la generación de valores de configuración con EF
Core.

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

Columnas calculadas
En la mayoría de las 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:

[Link]<Person>()
.Property(p => [Link])
.HasComputedColumnSql("[LastName] + ', ' + [FirstName]");

Lo anterior crea una columna calculada virtual , cuyo valor se calcula cada vez que se captura de la base de
datos. También puede especificar que una columna calculada se almacene (a veces denominada Persist), lo que
significa que se calcula en cada actualización de la fila y se almacena en el disco junto con las columnas
normales:

[Link]<Person>()
.Property(p => [Link])
.HasComputedColumnSql("LEN([LastName]) + LEN([FirstName])", stored: true);
NOTE
La compatibilidad con la creación de columnas calculadas almacenadas se agregó en EF Core 5,0.

Claves principales
Por Convención, las claves principales no compuestas de tipo Short, int, Long o GUID están configuradas para
que se generen valores 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.
Para obtener más información, consulte la documentación acerca de las claves.

Configurar explícitamente la generación de valores


Vimos lo anterior que EF Core configura automáticamente la generación de valores para las claves principales,
pero es posible que deseemos hacer lo mismo para las propiedades que no son de clave. 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; }
}

Del mismo modo, se puede configurar una propiedad para que se genere su valor 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
A diferencia de los valores predeterminados o las columnas calculadas, no se especifica Cómo se van a generar los valores.
depende del proveedor de base de datos que se utiliza. 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, en SQL Server, cuando se configura una propiedad GUID como valor generado en Add, el proveedor realiza
automáticamente la generación de valores en el lado cliente, utilizando un algoritmo para generar valores GUID
secuenciales óptimos. Sin embargo, especificar ValueGeneratedOnAdd en una propiedad DateTime no tendrá ningún
efecto (consulte la sección siguiente para la generación de valores DATETIME).
Del mismo modo, las propiedades Byte [] configuradas como generadas en Add o Update y marcadas como tokens de
simultaneidad se configuran con el tipo de datos rowversion, de modo que los valores se generan automáticamente en la
base de datos. Sin embargo, la especificación de ValueGeneratedOnAdd no tiene ningún efecto.

NOTE
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 SaveChanges() . Para obtener más
información, vea los documentos sobre valores temporales.

Generación de valores de fecha y hora


Una solicitud común es tener una columna de base de datos que contenga la fecha y hora de la primera vez que
se insertó la columna (valor generado al agregar) o para la última actualización (valor generado al agregar o
actualizar). Como hay varias estrategias para hacerlo, los proveedores de EF Core no suelen configurar la
generación de valores automáticamente para las columnas de fecha y hora, por lo que debe configurarla usted
mismo.
Marca de tiempo de creación
La configuración de una columna de fecha y hora para que tenga la marca de tiempo de creación de la fila suele
ser cuestión de configurar un valor predeterminado con la función SQL adecuada. Por ejemplo, en SQL Server
puede utilizar lo siguiente:

protected override void OnModelCreating(ModelBuilder modelBuilder)


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

Asegúrese de seleccionar la función adecuada, ya que pueden existir varias (por ejemplo, GETDATE() vs
GETUTCDATE() ).

Actualizar marca de tiempo


Aunque las columnas calculadas almacenadas parecen una buena solución para administrar las marcas de
tiempo de la última actualización, las bases de datos no suelen permitir la especificación de funciones como
GETDATE() en una columna calculada. Como alternativa, puede configurar un desencadenador de base de datos
para lograr el mismo efecto:
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

Para obtener información sobre la creación de desencadenadores, consulte la documentación sobre el uso de
SQL sin procesar en las migraciones.

Reemplazar la generación de valores


Aunque una propiedad está configurada para la generación de valores, en muchos casos todavía puede
especificar explícitamente un valor para ella. El hecho de que esto funcione realmente depende del mecanismo
de generación de valores específico que se ha configurado. Aunque puede especificar un valor explícito en lugar
de usar el valor predeterminado de una columna, no se puede hacer lo mismo con las columnas calculadas.
Para invalidar la generación de valores con un valor explícito, basta con establecer la propiedad en cualquier
valor que no sea el valor predeterminado de CLR para el tipo de esa propiedad (para, para, null string 0
int [Link] Guid etc.).

NOTE
De forma predeterminada, se produce un error al intentar insertar valores explícitos en SQL Server identidad. vea estos
documentos para obtener una solución alternativa.

Para proporcionar un valor explícito para las propiedades que se han configurado como valor generado al
agregar o actualizar, también debe configurar la propiedad como se indica a continuación:

protected override void OnModelCreating(ModelBuilder modelBuilder)


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

Sin generación de valores


Además de los escenarios específicos, como los descritos anteriormente, las propiedades normalmente no
tienen ninguna generación de valores configurada. Esto significa que depende de la aplicación proporcionar
siempre un valor que se va a guardar en la base de datos. Este valor se debe asignar a las nuevas entidades
antes de que se agreguen al contexto.
Sin embargo, en algunos casos puede que desee deshabilitar la generación de valores configurada por
Convención. Por ejemplo, una clave principal de tipo int normalmente se configura de forma implícita como una
columna de identidad generada por el valor (por ejemplo, la columna de identidad en SQL Server). 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
12/03/2021 • 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 de sombra e indexador
12/03/2021 • 7 minutes to read

Las propiedades de sombra son propiedades que no se definen 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 de sombra son útiles cuando hay datos
en la base de datos que no deben exponerse en los tipos de entidad asignados.
Las propiedades del indexador son propiedades de tipo de entidad, que están respaldadas por un indexador en
la clase de entidad de .net. Se puede tener acceso a ellos mediante el indizador en las instancias de clase .NET.
También permite agregar propiedades adicionales al tipo de entidad sin cambiar la clase de CLR.

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 propiedades de navegación entre los tipos de entidad.
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 será simplemente
<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 Post entidad:

internal 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:

internal 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 Property método 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 ChangeTracker API:

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

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

var blogs = [Link]


.OrderBy(b => [Link]<DateTime>(b, "LastUpdated"));

No se puede tener acceso a las propiedades de sombra después de una consulta sin seguimiento, ya que el
seguimiento de cambios no realiza un seguimiento de las entidades devueltas.

Configuración de las propiedades del indexador


Puede usar la API fluida para configurar las propiedades del indexador. Una vez que haya llamado al método
IndexerProperty , puede encadenar cualquiera de las llamadas de configuración que desee para otras
propiedades. En el ejemplo siguiente, Blog tiene un indexador definido y se utilizará para crear una propiedad
de indizador.

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>().IndexerProperty<DateTime>("LastUpdated");
}
Si el nombre proporcionado al IndexerProperty método coincide con el nombre de una propiedad de indizador
existente, el código configurará esa propiedad existente. Si el tipo de entidad tiene una propiedad, que está
respaldada por una propiedad en la clase de entidad, se produce una excepción, ya que solo se debe tener
acceso a las propiedades del indexador a través del indexador.

Tipos de entidad de contenedor de propiedades


NOTE
La compatibilidad con los tipos de entidad del contenedor de propiedades se presentó en EF Core 5,0.

Los tipos de entidad que contienen solo propiedades de indizador se conocen como tipos de entidad de
contenedor de propiedades. Estos tipos de entidad no tienen propiedades de sombra; en su lugar, EF creará las
propiedades del indexador. Actualmente solo Dictionary<string, object> se admite como un tipo de entidad de
contenedor de propiedades. Debe configurarse como un tipo de entidad compartida con un nombre único y la
DbSet propiedad correspondiente debe implementarse mediante una Set llamada.

internal class MyContext : DbContext


{
public DbSet<Dictionary<string, object>> Blogs => Set<Dictionary<string, object>>("Blog");

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Dictionary<string, object>>(
"Blog", bb =>
{
[Link]<int>("BlogId");
[Link]<string>("Url");
[Link]<DateTime>("LastUpdated");
});
}
}
Relaciones
12/03/2021 • 34 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] 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 debe a que, si BlogId 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
va a comenzar 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 colección.
internal 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.

internal 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; }
}
Configurar propiedades de navegación

NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

Una vez creada la propiedad de navegación, puede que necesite configurarla más adelante.

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.HasMany(b => [Link])
.WithOne();

[Link]<Blog>()
.Navigation(b => [Link])
.UsePropertyAccessMode([Link]);
}

NOTE
Esta llamada no se puede usar para crear una propiedad de navegación. Solo se usa para configurar una propiedad de
navegación que se ha creado previamente definiendo una relación o una Convención.

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:
internal 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).
internal 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 _ <dependent type name> _ <principal type name> _ <foreign key property name> . En el caso de las claves
externas compuestas, <foreign key property name> 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.
internal 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 clave alternativa.

Clave simple
Clave compuesta
internal 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).
Las propiedades de clave externa se encuentran en el tipo de entidad dependiente, por lo que si se configuran
como requeridas, significa que todas las entidades dependientes deben tener una entidad principal
correspondiente.

protected override void OnModelCreating(ModelBuilder modelBuilder)


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

NOTE
IsRequired(false) La llamada 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 HasOne WithOne métodos y.
Al configurar la clave externa, debe especificar el tipo de entidad dependiente: Observe el parámetro genérico
que se proporciona 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.
internal 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; }
}

El lado dependiente se considera opcional de forma predeterminada, pero se puede configurar según sea
necesario. Sin embargo, EF no validará si se proporcionó una entidad dependiente, por lo que esta configuración
solo marcará una diferencia cuando la asignación de base de datos permita su aplicación. Un escenario común
para esto son los tipos de referencia que usan la división de tablas de forma predeterminada.

[Link]<Order>(
ob =>
{
[Link](
o => [Link],
sa =>
{
[Link](p => [Link]).IsRequired();
[Link](p => [Link]).IsRequired();
});

[Link](o => [Link])


.IsRequired();
});

Con esta configuración, las columnas correspondientes a se ShippingAddress marcarán como que no aceptan
valores NULL en la base de datos.

NOTE
Si usa tipos de referencia que no aceptan valores NULL , IsRequired no es necesario llamar a.
NOTE
La capacidad de configurar si el dependiente es necesario se presentó en EF Core 5,0.

Varios a varios
Una relación de varios a varios requiere una propiedad de navegación de colección en ambos lados. Se
detectarán por Convención como otros tipos de relaciones.

public class Post


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

public ICollection<Tag> Tags { get; set; }


}

public class Tag


{
public string TagId { get; set; }

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


}

La forma en que se implementa esta relación en la base de datos es mediante una tabla de combinación que
contiene claves externas a Post y Tag . Por ejemplo, esto es lo que EF creará en una base de datos relacional
para el modelo anterior.

CREATE TABLE [Posts] (


[PostId] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Content] nvarchar(max) NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([PostId])
);

CREATE TABLE [Tags] (


[TagId] nvarchar(450) NOT NULL,
CONSTRAINT [PK_Tags] PRIMARY KEY ([TagId])
);

CREATE TABLE [PostTag] (


[PostsId] int NOT NULL,
[TagsId] nvarchar(450) NOT NULL,
CONSTRAINT [PK_PostTag] PRIMARY KEY ([PostsId], [TagsId]),
CONSTRAINT [FK_PostTag_Posts_PostsId] FOREIGN KEY ([PostsId]) REFERENCES [Posts] ([PostId]) ON DELETE
CASCADE,
CONSTRAINT [FK_PostTag_Tags_TagsId] FOREIGN KEY ([TagsId]) REFERENCES [Tags] ([TagId]) ON DELETE CASCADE
);

Internamente, EF crea un tipo de entidad para representar la tabla de combinación a la que se hará referencia
como el tipo de entidad de combinación. Dictionary<string, object> Actualmente se utiliza para controlar
cualquier combinación de propiedades de clave externa, consulte tipos de entidad del contenedor de
propiedades para obtener más información. Puede haber más de una relación de varios a varios en el modelo,
por lo que el tipo de entidad de combinación debe recibir un nombre único, en este caso PostTag . La
característica que lo permite se denomina tipo de entidad de tipo compartido.
IMPORTANT
El tipo CLR que se usa para los tipos de entidad de combinación por Convención puede cambiar en futuras versiones para
mejorar el rendimiento. No dependa del tipo de combinación a menos que se haya Dictionary<string, object>
configurado explícitamente, como se describe en la sección siguiente.

Las navegaciones de varios a varios se llaman omitir navegaciones, ya que omiten el tipo de entidad de
combinación. Si está empleando la configuración masiva, todas las navegaciones de omitir se pueden obtener
de GetSkipNavigations .

foreach (var entityType in [Link]())


{
foreach (var skipNavigation in [Link]())
{
[Link]([Link]() + "." + [Link]);
}
}

Combinación de la configuración del tipo de entidad


Es habitual aplicar la configuración al tipo de entidad de combinación. Esta acción se puede realizar a través de
UsingEntity .

modelBuilder
.Entity<Post>()
.HasMany(p => [Link])
.WithMany(p => [Link])
.UsingEntity(j => [Link]("PostTags"));

Se pueden proporcionar datos de inicialización del modelo para el tipo de entidad de combinación mediante el
uso de tipos anónimos. Puede examinar la vista de depuración del modelo para determinar los nombres de
propiedad creados por la Convención.

modelBuilder
.Entity<Post>()
.HasData(new Post { PostId = 1, Title = "First" });

modelBuilder
.Entity<Tag>()
.HasData(new Tag { TagId = "ef" });

modelBuilder
.Entity<Post>()
.HasMany(p => [Link])
.WithMany(p => [Link])
.UsingEntity(j => [Link](new { PostsPostId = 1, TagsTagId = "ef" }));

Los datos adicionales se pueden almacenar en el tipo de entidad de combinación, pero para ello es mejor crear
un tipo de CLR de tipo. Al configurar la relación con un tipo de entidad de combinación personalizada, se deben
especificar explícitamente las claves externas.
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}

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


public DbSet<Tag> Tags { get; set; }

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasMany(p => [Link])
.WithMany(p => [Link])
.UsingEntity<PostTag>(
j => j
.HasOne(pt => [Link])
.WithMany(t => [Link])
.HasForeignKey(pt => [Link]),
j => j
.HasOne(pt => [Link])
.WithMany(p => [Link])
.HasForeignKey(pt => [Link]),
j =>
{
[Link](pt => [Link]).HasDefaultValueSql("CURRENT_TIMESTAMP");
[Link](t => new { [Link], [Link] });
});
}
}

public class Post


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

public ICollection<Tag> Tags { get; set; }


public List<PostTag> PostTags { get; set; }
}

public class Tag


{
public string TagId { get; set; }

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


public List<PostTag> PostTags { get; set; }
}

public class PostTag


{
public DateTime PublicationDate { get; set; }

public int PostId { get; set; }


public Post Post { get; set; }

public string TagId { get; set; }


public Tag Tag { get; set; }
}

Unirse a la configuración de relaciones


EF usa relaciones de 2 1 a varios en el tipo de entidad de combinación para representar la relación de varios a
varios. Puede configurar estas relaciones en los UsingEntity argumentos.
[Link]<Post>()
.HasMany(p => [Link])
.WithMany(p => [Link])
.UsingEntity<Dictionary<string, object>>(
"PostTag",
j => j
.HasOne<Tag>()
.WithMany()
.HasForeignKey("TagId")
.HasConstraintName("FK_PostTag_Tags_TagId")
.OnDelete([Link]),
j => j
.HasOne<Post>()
.WithMany()
.HasForeignKey("PostId")
.HasConstraintName("FK_PostTag_Posts_PostId")
.OnDelete([Link]));

NOTE
La capacidad de configurar relaciones varios a varios se presentó en EF Core 5,0, para la versión anterior, use el siguiente
enfoque.

Relaciones de varios a varios indirectas


También puede representar una relación de varios a varios agregando el tipo de entidad de combinación y
asignando dos relaciones uno a varios independientes.
public class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}

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 DateTime PublicationDate { get; set; }

public int PostId { get; set; }


public Post Post { get; set; }

public string TagId { get; set; }


public Tag Tag { get; set; }
}

NOTE
Todavía no se ha agregado compatibilidad con la aplicación de scaffolding a relaciones de varios a varios desde la base de
datos. Vea la incidencia de seguimiento.

Recursos adicionales
EF Core sesión reuniónde la comunidad, con un análisis profundo de varios a varios y de la infraestructura
subyacente.
Índices
12/03/2021 • 6 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. Vea la sección índices de la documentación de rendimiento para obtener más información sobre el uso
de índices correctos.
Puede especificar un índice en una columna de la manera siguiente:
Anotaciones de datos
API fluida

[Index(nameof(Url))]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}

NOTE
La configuración de índices a través de anotaciones de datos se ha introducido en EF Core 5,0.

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 configura 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.

Índice compuesto
Un índice también puede abarcar más de una columna:
Anotaciones de datos
API fluida

[Index(nameof(FirstName), nameof(LastName))]
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

Los índices de varias columnas, también conocidos como índices compuestos, agilizan las consultas que filtran
las columnas del índice, pero también las consultas que solo filtran en las primeras columnas que se incluyen en
el índice. Vea los documentos de rendimiento para obtener más informació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:
Anotaciones de datos
API fluida

[Index(nameof(Url), IsUnique = true)]


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

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 establecer el nombre del índice creado en la base de datos:

Anotaciones de datos
API fluida

[Index(nameof(Url), Name = "Index_Url")]


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

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 SQL Server el proveedor EF agrega un 'IS NOT NULL' filtro para todas las columnas que aceptan
valores NULL que forman parte de un índice único. Para invalidar esta Convención, puede proporcionar un
null valor.

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 Url columna 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 Title
PublishedOn columnas y 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
12/03/2021 • 9 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.

Asignación de jerarquía de tipos de entidad


Por Convención, EF no buscará automáticamente tipos base o derivados; Esto significa que si desea que se
asigne un tipo CLR en la jerarquía, debe especificar explícitamente ese tipo en el modelo. Por ejemplo, si solo se
especifica el tipo base de una jerarquía, EF Core incluirá implícitamente todos sus subtipos.
En el ejemplo siguiente se expone un DbSet para Blog y su subclase RssBlog . Si Blog tiene cualquier otra
subclase, no se incluirá en el modelo.

internal 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; }
}

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 RssUrl columna acepta valores NULL porque Blog las instancias normales no tienen esa propiedad.

Si no desea exponer un DbSet para una o más 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 el tipo base explícitamente mediante HasBaseType . También puede
usar .HasBaseType((Type)null) para quitar un tipo de entidad de la jerarquía.

Configuración de tabla por jerarquía y discriminador


De forma predeterminada, EF asigna la herencia mediante 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.
El modelo anterior se asigna al siguiente esquema de la base de datos (tenga en cuenta la columna creado
implícitamente Discriminator , que identifica qué tipo de Blog se almacena en cada fila).

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

Al consultar las entidades derivadas, que usan el patrón TPH, EF Core agrega un predicado sobre la columna
discriminadora en la consulta. Este filtro garantiza que no se obtienen filas adicionales para los tipos base o los
tipos del mismo nivel que no están en el resultado. Este predicado de filtro se omite para el tipo de entidad base,
ya que la consulta de la entidad base obtendrá resultados para todas las entidades de la jerarquía. Cuando se
materializan los resultados de una consulta, si se llega a través de un valor de discriminador, que no está
asignado a ningún tipo de entidad del modelo, se produce una excepción, ya que no sabemos cómo materializar
los resultados. Este error solo se produce si la base de datos contiene filas con valores de discriminador que no
están asignados en el modelo EF. Si tiene estos datos, puede marcar la asignación de discriminador en EF Core
modelo como incompleta para indicar que siempre debemos agregar el predicado de filtro para consultar
cualquier tipo de la jerarquía. IsComplete(false) la llamada a en la configuración del discriminador marca la
asignación como incompleta.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
[Link]<Blog>()
.HasDiscriminator()
.IsComplete(false);
}

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

Configuración de tabla por tipo


NOTE
La característica tabla por tipo (TPT) se presentó en EF Core 5,0. La tabla por tipo específico (TPC) es compatible con EF6,
pero aún no es compatible con EF Core.

En el patrón de asignación de TPT, todos los tipos se asignan a tablas individuales. Las propiedades que
pertenecen solamente a un tipo base o a un tipo derivado se almacenan en una tabla que se asigna a ese tipo.
Las tablas que se asignan a tipos derivados también almacenan una clave externa que une la tabla derivada con
la tabla base.

[Link]<Blog>().ToTable("Blogs");
[Link]<RssBlog>().ToTable("RssBlogs");
EF creará el siguiente esquema de la base de datos para el modelo anterior.

CREATE TABLE [Blogs] (


[BlogId] int NOT NULL IDENTITY,
[Url] nvarchar(max) NULL,
CONSTRAINT [PK_Blogs] PRIMARY KEY ([BlogId])
);

CREATE TABLE [RssBlogs] (


[BlogId] int NOT NULL,
[RssUrl] nvarchar(max) NULL,
CONSTRAINT [PK_RssBlogs] PRIMARY KEY ([BlogId]),
CONSTRAINT [FK_RssBlogs_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([BlogId]) ON DELETE NO
ACTION
);

NOTE
Si se cambia el nombre de la restricción primary key, el nuevo nombre se aplicará a todas las tablas asignadas a la
jerarquía, las versiones futuras de EF permitirán cambiar el nombre de la restricción solo para una tabla determinada
cuando se corrija el problema 19970 .

Si está empleando la configuración masiva, puede recuperar el nombre de columna de una tabla concreta
mediante una llamada a GetColumnName(IProperty, StoreObjectIdentifier) .

foreach (var entityType in [Link]())


{
var tableIdentifier = [Link](entityType, [Link]);

[Link]($"{[Link]()}\t\t{tableIdentifier}");
[Link](" Property\tColumn");

foreach (var property in [Link]())


{
var columnName = [Link]([Link]);
[Link]($" {[Link],-10}\t{columnName}");
}

[Link]();
}

WARNING
En muchos casos, TPT muestra un rendimiento inferior en comparación con TPH. Vea los documentos de rendimiento
para obtener más información.
Secuencias
12/03/2021 • 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
12/03/2021 • 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 Url propiedad se configura para tener _url como su 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 mediante una anotación de datos (disponible en EFCore 5,0)
o la API fluida, por ejemplo, si el nombre del campo no se corresponde con las convenciones anteriores:
Anotaciones de datos
API fluida
public class Blog
{
private string _validatedUrl;

public int BlogId { get; set; }

[BackingField(nameof(_validatedUrl))]
public string Url
{
get { return _validatedUrl; }
}

public void SetUrl(string url)


{
// put your validation code here

_validatedUrl = url;
}
}

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 Property(...) API:
internal 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 [Link](...) método en una consulta LINQ para hacer
referencia al campo:

var blogs = [Link](b => [Link]<string>(b, "_validatedUrl"));


Conversiones de valores
12/03/2021 • 35 minutes to read

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).

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Información general
Los convertidores de valores se especifican en términos de ModelClrType y 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 mediante dos Func árboles de expresión: uno de ModelClrType a ProviderClrType
y el otro de ProviderClrType a ModelClrType . Los árboles de expresión se usan para que se puedan compilar en
el delegado de acceso a la base de datos para conversiones eficaces. El árbol de expresión puede contener una
llamada simple a un método de conversión para conversiones complejas.

NOTE
Una propiedad que se ha configurado para la conversión de valores también puede necesitar especificar un
ValueComparer<T> . Vea los ejemplos siguientes y la documentación de comparadores de valores para obtener más
información.

Configurar un convertidor de valores


Las conversiones de valores se configuran en [Link] . 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
}

Las conversiones se pueden configurar en OnModelCreating para almacenar los valores de enumeración como
cadenas como "Donkey", "Mule", etc. en la base de datos; solo tiene que proporcionar una función que convierta
de ModelClrType a ProviderClrType y otra para la conversión opuesta:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion(
v => [Link](),
v => (EquineBeast)[Link](typeof(EquineBeast), v));
}

NOTE
null Nunca se pasará un valor a un convertidor de valores. Un valor null en una columna de base de datos siempre es
un valor null en la instancia de la entidad y viceversa. 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. Vea el problema
de GitHub #13850 para obtener más información.

Conversiones predefinidas
EF Core contiene muchas conversiones predefinidas que evitan la necesidad de escribir funciones de conversión
manualmente. En su lugar, EF Core seleccionará la conversión que se va a usar en función del tipo de propiedad
en el modelo y el tipo de proveedor de base de datos solicitado.
Por ejemplo, la enumeración de las conversiones de cadenas se usa como ejemplo anterior, pero EF Core
realmente lo hará automáticamente cuando el tipo de proveedor esté configurado como string con el tipo
genérico de HasConversion :

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion<string>();
}

Lo mismo se puede lograr especificando explícitamente el tipo de columna de la base de datos. Por ejemplo, si el
tipo de entidad se define de la manera siguiente:
Anotaciones de datos
API fluida

public class Rider2


{
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
configuración adicional en OnModelCreating .
La clase ValueConverter
Al llamar a HasConversion como se muestra anteriormente, se creará una ValueConverter<TModel,TProvider>
instancia de y se establecerá en la propiedad. ValueConverter En su lugar, se puede crear explícitamente. Por
ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
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.

Convertidores integrados
Como se mencionó anteriormente, EF Core se suministra con un conjunto de clases predefinidas
ValueConverter<TModel,TProvider> , que se encuentra en el
[Link] espacio de nombres. En muchos casos, EF elegirá el
convertidor integrado adecuado en función del tipo de la propiedad en el modelo y del tipo solicitado en la base
de datos, como se mostró anteriormente para las enumeraciones. Por ejemplo, .HasConversion<int>() el uso de
en una propiedad hará que bool EF Core convierta valores bool a cero numérico y un valor:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<User>()
.Property(e => [Link])
.HasConversion<int>();
}

Esto es funcionalmente lo mismo que crear una instancia del integrado BoolToZeroOneConverter<TProvider> y
establecerlo explícitamente:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
var converter = new BoolToZeroOneConverter<int>();

modelBuilder
.Entity<User>()
.Property(e => [Link])
.HasConversion(converter);
}

En la tabla siguiente se resumen las conversiones predefinidas usadas con frecuencia de tipos de modelo o
propiedad a tipos de proveedor de bases de datos. En la tabla any_numeric_type , una de int ,,, short long
byte , uint , ushort , ulong , sbyte , char , decimal , float o double .
T IP O DE T IP O DE B A SE DE
M O DELO / P RO P IEDA D DATO S/ P RO VEEDO R C O N VERSIÓ N USO

bool any_numeric_type False/true a 0/1 .HasConversion<any_numeric_type>


()

any_numeric_type False/true para dos Use


números cualesquiera BoolToTwoValuesConverter
<TProvider>

string False/true en "Y"/"N" .HasConversion<string>


()

string False/true para dos cadenas Use BoolToStringConverter


cualesquiera

any_numeric_type bool de 0/1 a false/true .HasConversion<bool>()

any_numeric_type Conversión simple .HasConversion<any_numeric_type>


()

string El número como una .HasConversion<string>


cadena ()

Enumeración any_numeric_type Valor numérico de la .HasConversion<any_numeric_type>


enumeración. ()

string Representación de cadena .HasConversion<string>


del valor de enumeración. ()

string bool Analiza la cadena como un .HasConversion<bool>()


valor booleano.

any_numeric_type Analiza la cadena como el .HasConversion<any_numeric_type>


tipo numérico especificado ()

char Primer carácter de la .HasConversion<char>()


cadena.

DateTime Analiza la cadena como un .HasConversion<DateTime>


valor de fecha y hora ()

DateTimeOffset Analiza la cadena como .HasConversion<DateTimeOffset>


DateTimeOffset. ()

TimeSpan Analiza la cadena como un .HasConversion<TimeSpan>


intervalo de tiempo ()

Guid Analiza la cadena como un .HasConversion<Guid>()


GUID.

byte[] La cadena como UTF8 bytes .HasConversion<byte[]>


()

char string Una cadena de un solo .HasConversion<string>


carácter ()
T IP O DE T IP O DE B A SE DE
M O DELO / P RO P IEDA D DATO S/ P RO VEEDO R C O N VERSIÓ N USO

DateTime long Fecha y hora de la .HasConversion<long>()


conservación de DateTime.
Kind

long Pasos Use


DateTimeToTicksConverter

string Cadena de fecha y hora de .HasConversion<string>


la referencia cultural de ()
todos los idiomas

DateTimeOffset long Fecha y hora codificadas .HasConversion<long>()


con desplazamiento

string Cadena de fecha y hora de .HasConversion<string>


la referencia cultural de ()
todos los idiomas con
desplazamiento

TimeSpan long Pasos .HasConversion<long>()

string Cadena de intervalo de .HasConversion<string>


tiempo de referencia ()
cultural de todos los
idiomas

Identificador URI string El URI como una cadena .HasConversion<string>


()

PhysicalAddress string La dirección como una .HasConversion<string>


cadena ()

byte[] Bytes en el orden de red .HasConversion<byte[]>


Big-Endian ()

IPAddress string La dirección como una .HasConversion<string>


cadena ()

byte[] Bytes en el orden de red .HasConversion<byte[]>


Big-Endian ()

Guid string GUID en formato ' .HasConversion<string>


dddddddd-dddd-dddd- ()
dddd-dddddddddddd '

byte[] Bytes en el orden de .HasConversion<byte[]>


serialización binaria de .NET ()

Tenga en cuenta que estas conversiones suponen que el formato del valor es adecuado para la conversión. Por
ejemplo, la conversión de cadenas en números producirá un error si los valores de cadena no se pueden
analizar como números.
La lista completa de convertidores integrados es:
Convertir propiedades bool:
BoolToStringConverter -Bool a cadenas como "Y" y "N"
BoolToTwoValuesConverter<TProvider> -Bool a dos valores cualesquiera
BoolToZeroOneConverter<TProvider> -Bool a cero y uno
Convertir propiedades de matriz de bytes:
BytesToStringConverter -Matriz de bytes a cadena codificada en Base64
Cualquier conversión que requiera solo una conversión de tipos
CastingConverter<TModel,TProvider> -Conversiones que requieren solo una conversión de tipo
Convertir propiedades char:
CharToStringConverter -Char a cadena de carácter único
Convertir DateTimeOffset propiedades:
DateTimeOffsetToBinaryConverter - DateTimeOffset en el valor de 64 bits con codificación binaria
DateTimeOffsetToBytesConverter - DateTimeOffset a la matriz de bytes
DateTimeOffsetToStringConverter - DateTimeOffset a cadena
Convertir DateTime propiedades:
DateTimeToBinaryConverter - DateTime en valor de 64 bits, incluido DateTimeKind
DateTimeToStringConverter - DateTime a cadena
DateTimeToTicksConverter - DateTime a TICs
Convertir propiedades de enumeración:
EnumToNumberConverter<TEnum,TNumber> -Enum al número subyacente
EnumToStringConverter<TEnum> -Enum a cadena
Convertir Guid propiedades:
GuidToBytesConverter - Guid a la matriz de bytes
GuidToStringConverter - Guid a cadena
Convertir IPAddress propiedades:
IPAddressToBytesConverter - IPAddress a la matriz de bytes
IPAddressToStringConverter - IPAddress a cadena
Convertir propiedades numéricas (int, Double, decimal, etc.):
NumberToBytesConverter<TNumber> -Cualquier valor numérico a matriz de bytes
NumberToStringConverter<TNumber> -Cualquier valor numérico a cadena
Convertir PhysicalAddress propiedades:
PhysicalAddressToBytesConverter - PhysicalAddress a la matriz de bytes
PhysicalAddressToStringConverter - PhysicalAddress a cadena
Convertir propiedades de cadena:
StringToBoolConverter -Cadenas como "Y" y "N" a bool
StringToBytesConverter -Cadena en bytes UTF8
StringToCharConverter -Cadena a carácter
StringToDateTimeConverter : Cadena que se va a DateTime
StringToDateTimeOffsetConverter : Cadena que se va a DateTimeOffset
StringToEnumConverter<TEnum> -Cadena que se va a enumerar
StringToGuidConverter : Cadena que se va a Guid
StringToNumberConverter<TNumber> -Cadena a tipo numérico
StringToTimeSpanConverter : Cadena que se va a TimeSpan
StringToUriConverter : Cadena que se va a Uri
Convertir TimeSpan propiedades:
TimeSpanToStringConverter - TimeSpan a cadena
TimeSpanToTicksConverter - TimeSpan a TICs
Convertir Uri propiedades:
UriToStringConverter - Uri a cadena
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.

Aspectos de las columnas y sugerencias de asignación


Algunos tipos de base de datos tienen aspectos que modifican la forma en que se almacenan. Entre ellas se
incluyen las siguientes:
Precisión y escala para decimales y columnas de fecha y hora
Tamaño/longitud de las columnas binarias y de cadena
Unicode para columnas de cadena
Estas caras se pueden configurar de forma normal para una propiedad que utiliza un convertidor de valores, y
se aplicarán al tipo de base de datos convertido. Por ejemplo, al convertir de una enumeración a cadenas,
podemos especificar que la columna de base de datos no debe ser Unicode y almacenar hasta 20 caracteres:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion<string>()
.HasMaxLength(20)
.IsUnicode(false);
}

O bien, al crear el convertidor explícitamente:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
var converter = new ValueConverter<EquineBeast, string>(
v => [Link](),
v => (EquineBeast)[Link](typeof(EquineBeast), v));

modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion(converter)
.HasMaxLength(20)
.IsUnicode(false);
}

Esto da como resultado una varchar(20) columna al utilizar EF Core migraciones en SQL Server:

CREATE TABLE [Rider] (


[Id] int NOT NULL IDENTITY,
[Mount] varchar(20) NOT NULL,
CONSTRAINT [PK_Rider] PRIMARY KEY ([Id]));

Sin embargo, si, de forma predeterminada, todas EquineBeast las columnas deben ser varchar(20) , esta
información se puede proporcionar al convertidor de valores como ConverterMappingHints . Por ejemplo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var converter = new ValueConverter<EquineBeast, string>(
v => [Link](),
v => (EquineBeast)[Link](typeof(EquineBeast), v),
new ConverterMappingHints(size: 20, unicode: false));

modelBuilder
.Entity<Rider>()
.Property(e => [Link])
.HasConversion(converter);
}

Ahora, cada vez que se use este convertidor, la columna de la base de datos no será Unicode con una longitud
máxima de 20. Sin embargo, estas son solo sugerencias, ya que se reemplazan por cualquier aspecto establecida
explícitamente en la propiedad asignada.

Ejemplos
Objetos de valor simple
En este ejemplo se usa un tipo simple para encapsular un tipo primitivo. Esto puede ser útil si desea que el tipo
del modelo sea más específico (y, por tanto, más seguro de tipos) que un tipo primitivo. En este ejemplo, ese
tipo es Dollars , que ajusta el primitivo decimal:

public readonly struct Dollars


{
public Dollars(decimal amount)
=> Amount = amount;

public decimal Amount { get; }

public override string ToString()


=> $"${Amount}";
}

Se puede usar en un tipo de entidad:

public class Order


{
public int Id { get; set; }

public Dollars Price { get; set; }


}

Y se convierten en los subyacentes decimal cuando se almacenan en la base de datos:

[Link]<Order>()
.Property(e => [Link])
.HasConversion(
v => [Link],
v => new Dollars(v));

NOTE
Este objeto de valor se implementa como una estructura de solo lectura. Esto significa que EF Core puede realizar
instantáneas y comparar valores sin problemas. Consulte comparadores de valores para obtener más información.
Objetos de valor compuesto
En el ejemplo anterior, el tipo de objeto de valor solo contenía una propiedad única. Es más común que un tipo
de objeto de valor componga varias propiedades que juntos forman un concepto de dominio. Por ejemplo, un
Money tipo general que contiene la cantidad y la moneda:

public readonly struct Money


{
[JsonConstructor]
public Money(decimal amount, Currency currency)
{
Amount = amount;
Currency = currency;
}

public override string ToString()


=> (Currency == [Link] ? "$" : "£") + Amount;

public decimal Amount { get; }


public Currency Currency { get; }
}

public enum Currency


{
UsDollars,
PoundsStirling
}

Este objeto de valor se puede usar en un tipo de entidad como antes:

public class Order


{
public int Id { get; set; }

public Money Price { get; set; }


}

Los convertidores de valores solo pueden convertir actualmente valores en una sola columna de base de datos.
Esta limitación significa que todos los valores de propiedad del objeto se deben codificar en un valor de
columna única. Normalmente, esto se controla mediante la serialización del objeto tal como entra en la base de
datos y, a continuación, la deserialización de nuevo en el sentido. Por ejemplo, mediante [Link] :

[Link]<Order>()
.Property(e => [Link])
.HasConversion(
v => [Link](v, null),
v => [Link]<Money>(v, null));

NOTE
Tenemos previsto permitir la asignación de un objeto a varias columnas en EF Core 6,0, lo que elimina la necesidad de
usar la serialización aquí. Esto se realiza mediante el problema de GitHub #13947.
NOTE
Como en el ejemplo anterior, este objeto de valor se implementa como una estructura de solo lectura. Esto significa que
EF Core puede realizar instantáneas y comparar valores sin problemas. Consulte comparadores de valores para obtener
más información.

Colecciones de primitivas
La serialización también se puede utilizar para almacenar una colección de valores primitivos. Por ejemplo:

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public string Contents { get; set; }

public ICollection<string> Tags { get; set; }


}

[Link] a usar:

[Link]<Post>()
.Property(e => [Link])
.HasConversion(
v => [Link](v, null),
v => [Link]<List<string>>(v, null),
new ValueComparer<ICollection<string>>(
(c1, c2) => [Link](c2),
c => [Link](0, (a, v) => [Link](a, [Link]())),
c => (ICollection<string>)[Link]()));

ICollection<string> representa un tipo de referencia mutable. Esto significa que ValueComparer<T> se


necesita un para que EF Core pueda realizar un seguimiento de los cambios y detectarlos correctamente.
Consulte comparadores de valores para obtener más información.
Colecciones de objetos de valor
Combinando los dos ejemplos anteriores juntos podemos crear una colección de objetos de valor. Por ejemplo,
considere un AnnualFinance tipo que modela las finanzas del blog para un solo año:

public readonly struct AnnualFinance


{
[JsonConstructor]
public AnnualFinance(int year, Money income, Money expenses)
{
Year = year;
Income = income;
Expenses = expenses;
}

public int Year { get; }


public Money Income { get; }
public Money Expenses { get; }
public Money Revenue => new Money([Link] - [Link], [Link]);
}

Este tipo compone algunos de los tipos que Money hemos creado anteriormente:
public readonly struct Money
{
[JsonConstructor]
public Money(decimal amount, Currency currency)
{
Amount = amount;
Currency = currency;
}

public override string ToString()


=> (Currency == [Link] ? "$" : "£") + Amount;

public decimal Amount { get; }


public Currency Currency { get; }
}

public enum Currency


{
UsDollars,
PoundsStirling
}

A continuación, podemos agregar una colección de AnnualFinance a nuestro tipo de entidad:

public class Blog


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

public IList<AnnualFinance> Finances { get; set; }


}

Y usan de nuevo la serialización para almacenar esto:

[Link]<Blog>()
.Property(e => [Link])
.HasConversion(
v => [Link](v, null),
v => [Link]<List<AnnualFinance>>(v, null),
new ValueComparer<IList<AnnualFinance>>(
(c1, c2) => [Link](c2),
c => [Link](0, (a, v) => [Link](a, [Link]())),
c => (IList<AnnualFinance>)[Link]()));

NOTE
Como antes, esta conversión requiere un ValueComparer<T> . Consulte comparadores de valores para obtener más
información.

Objetos de valor como claves


A veces, las propiedades de clave primitivas se pueden encapsular en objetos de valor para agregar un nivel
adicional de seguridad de tipos en la asignación de valores. Por ejemplo, podríamos implementar un tipo de
clave para los blogs y un tipo de clave para las publicaciones:
public readonly struct BlogKey
{
public BlogKey(int id) => Id = id;
public int Id { get; }
}

public readonly struct PostKey


{
public PostKey(int id) => Id = id;
public int Id { get; }
}

Estos se pueden usar en el modelo de dominio:

public class Blog


{
public BlogKey Id { get; set; }
public string Name { get; set; }

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


}

public class Post


{
public PostKey Id { get; set; }

public string Title { get; set; }


public string Content { get; set; }

public BlogKey? BlogId { get; set; }


public Blog Blog { get; set; }
}

Observe que [Link] no se puede asignar accidentalmente un PostKey y [Link] no se le puede asignar
accidentalmente un BlogKey . Del mismo modo, [Link] se debe asignar a la propiedad de clave externa
BlogKey .

NOTE
La visualización de este patrón no significa que se recomiende. Considere detenidamente si este nivel de abstracción está
ayudando o dificultando su experiencia de desarrollo. Además, considere la posibilidad de usar navegaciones y claves
generadas en lugar de tratar directamente con los valores de clave.

Después, estas propiedades de clave se pueden asignar mediante convertidores de valores:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
var blogKeyConverter = new ValueConverter<BlogKey, int>(
v => [Link],
v => new BlogKey(v));

[Link]<Blog>().Property(e => [Link]).HasConversion(blogKeyConverter);

[Link]<Post>(
b =>
{
[Link](e => [Link]).HasConversion(v => [Link], v => new PostKey(v));
[Link](e => [Link]).HasConversion(blogKeyConverter);
});
}
NOTE
Actualmente, las propiedades clave con conversiones no pueden utilizar valores de clave generados. Vote por el problema
de GitHub #11597 para quitar esta limitación.

Usar ulong para timestamp/rowversion


SQL Server admite la simultaneidad optimista automática mediante rowversion / timestamp columnas binarias
de 8 bytes. Siempre se leen y se escriben en la base de datos mediante una matriz de 8 bytes. Sin embargo, las
matrices de bytes son un tipo de referencia mutable, lo que hace que sean bastante difíciles de tratar. Los
convertidores de valores permiten rowversion que se asigne en su lugar a una ulong propiedad, que es mucho
más adecuada y fácil de usar que la matriz de bytes. Por ejemplo, considere una Blog entidad con un token de
simultaneidad ulong:

public class Blog


{
public int Id { get; set; }
public string Name { get; set; }
public ulong Version { get; set; }
}

Se puede asignar a una columna de SQL Server rowversion mediante un convertidor de valores:

[Link]<Blog>()
.Property(e => [Link])
.IsRowVersion()
.HasConversion<byte[]>();

Especificar DateTime. Kind al leer fechas


SQL Server descarta la [Link] marca al almacenar un DateTime como datetime o datetime2 . Esto
significa que los valores DateTime que se devuelven de la base de datos siempre tienen un DateTimeKind de
Unspecified .

Los convertidores de valores se pueden usar de dos maneras para solucionar este. En primer lugar, EF Core
tiene un convertidor de valores que crea un valor opaco de 8 bytes que conserva la Kind marca. Por ejemplo:

[Link]<Post>()
.Property(e => [Link])
.HasConversion<long>();

Esto permite mezclar valores de fecha y hora con Kind marcas diferentes en la base de datos.
El problema de este enfoque es que la base de datos ya no tiene datetime columnas o reconocibles datetime2 .
Por lo tanto, es habitual almacenar siempre la hora UTC (o, con menos frecuencia, la hora local siempre) y, a
continuación, omitir la Kind marca o establecerla en el valor adecuado mediante un convertidor de valores. Por
ejemplo, el convertidor siguiente garantiza que el DateTime valor leído de la base de datos tendrá
DateTimeKind UTC :

[Link]<Post>()
.Property(e => [Link])
.HasConversion(
v => v,
v => new DateTime([Link], [Link]));
Si se establece una combinación de valores locales y UTC en instancias de entidad, el convertidor se puede usar
para convertir correctamente antes de la inserción. Por ejemplo:

[Link]<Post>()
.Property(e => [Link])
.HasConversion(
v => [Link](),
v => new DateTime([Link], [Link]));

NOTE
Considere cuidadosamente la posibilidad de unificar todo el código de acceso a la base de datos para usar la hora UTC
todo el tiempo, solo para tratar con la hora local al presentar los datos a los usuarios.

Usar claves de cadena que no distinguen mayúsculas de minúsculas


Algunas bases de datos, incluidas SQL Server, realizan comparaciones de cadenas que no distinguen
mayúsculas de minúsculas de forma predeterminada. .NET, por otro lado, realiza comparaciones de cadenas que
distinguen entre mayúsculas y minúsculas de forma predeterminada. Esto significa que un valor de clave
externa como "DotNet" coincidirá con el valor de clave principal "dotnet" en SQL Server, pero no coincidirá en EF
Core. Un comparador de valores para las claves se puede usar para forzar la EF Core en comparaciones de
cadenas sin distinción entre mayúsculas y minúsculas como en la base de datos. Por ejemplo, considere un
modelo de blog o publicaciones con claves de cadena:

public class Blog


{
public string Id { get; set; }
public string Name { get; set; }

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


}

public class Post


{
public string Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public string BlogId { get; set; }


public Blog Blog { get; set; }
}

Esto no funcionará según lo esperado si algunos de los [Link] valores tienen distintas mayúsculas y
minúsculas. Los errores causados por esto dependerán de lo que esté haciendo la aplicación, pero normalmente
implican gráficos de objetos que no se han corregido correctamente y/o actualizaciones que generan un error
porque el valor de FK es incorrecto. Se puede utilizar un comparador de valores para corregir este:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var comparer = new ValueComparer<string>(
(l, r) => [Link](l, r, [Link]),
v => [Link]().GetHashCode(),
v => v);

[Link]<Blog>()
.Property(e => [Link])
.[Link](comparer);

[Link]<Post>(
b =>
{
[Link](e => [Link]).[Link](comparer);
[Link](e => [Link]).[Link](comparer);
});
}

NOTE
Las comparaciones de cadenas de .NET y las comparaciones de cadenas de base de datos pueden diferir en algo más que
la distinción de mayúsculas Este modelo funciona con claves ASCII simples, pero puede producir un error en las claves con
cualquier tipo de caracteres específicos de la referencia cultural. Consulte intercalaciones y distinción entre mayúsculas y
minúsculas para obtener más información.

Controlar cadenas de base de datos de longitud fija


El ejemplo anterior no necesita un convertidor de valores. Sin embargo, un convertidor puede ser útil para los
tipos de cadena de base de datos de longitud fija como char(20) o nchar(20) . Las cadenas de longitud fija se
rellenan hasta su longitud completa cada vez que se inserta un valor en la base de datos. Esto significa que el
valor de clave " dotnet " se leerá desde la base de datos como " dotnet.............. ", donde . representa
un carácter de espacio. Esto no se comparará correctamente con los valores de clave que no se rellenan.
Se puede usar un convertidor de valores para recortar el relleno al leer los valores de clave. Se puede combinar
con el comparador de valores en el ejemplo anterior para comparar correctamente las claves ASCII de longitud
fija que no distinguen mayúsculas de minúsculas. Por ejemplo:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
var converter = new ValueConverter<string, string>(
v => v,
v => [Link]());

var comparer = new ValueComparer<string>(


(l, r) => [Link](l, r, [Link]),
v => [Link]().GetHashCode(),
v => v);

[Link]<Blog>()
.Property(e => [Link])
.HasColumnType("char(20)")
.HasConversion(converter, comparer);

[Link]<Post>(
b =>
{
[Link](e => [Link]).HasColumnType("char(20)").HasConversion(converter, comparer);
[Link](e => [Link]).HasColumnType("char(20)").HasConversion(converter, comparer);
});
}

Cifrar valores de propiedad


Los convertidores de valores se pueden usar para cifrar los valores de propiedad antes de enviarlos a la base de
datos y, a continuación, descifrarlos de forma que salgan. Por ejemplo, el uso de la inversión de cadenas como
sustituto de un algoritmo de cifrado real:

[Link]<User>().Property(e => [Link]).HasConversion(


v => new string([Link]().ToArray()),
v => new string([Link]().ToArray()));

NOTE
Actualmente no hay ninguna manera de obtener una referencia al DbContext actual u otro estado de sesión, desde
dentro de un convertidor de valores. Esto limita los tipos de cifrado que se pueden usar. Vote por el problema de GitHub
#11597 para quitar esta limitación.

WARNING
Asegúrese de comprender todas las implicaciones si implementa su propio cifrado para proteger los datos confidenciales.
En su lugar, considere la posibilidad de usar mecanismos de cifrado predefinidos, como Always Encrypted en SQL Server.

Limitaciones
Hay algunas limitaciones actuales conocidas del sistema de conversión de valores:
Actualmente no hay ninguna manera de especificar en un lugar que cada propiedad de un tipo determinado
debe usar el mismo convertidor de valores. Por favor, vote ( ) para obtener el problema de github #10784
si es algo que necesita.
Como se indicó anteriormente, null no se puede convertir. Por favor, vote ( ) para obtener el problema
de github #13850 si es algo que necesita.
Actualmente no hay ninguna manera de distribuir una conversión de una propiedad a varias columnas o
viceversa. Por favor, vote ( ) para obtener el problema de github #13947 si es algo que necesita.
La mayoría de las claves asignadas a través de convertidores de valores no admiten la generación de valores.
Por favor, vote ( ) para obtener el problema de github #11597 si es algo que necesita.
Las conversiones de valores no pueden hacer referencia a la instancia de DbContext actual. Por favor, vote (
) para obtener el problema de github #11597 si es algo que necesita.
La eliminación de estas limitaciones se tiene en cuenta para futuras versiones.
Comparadores de valores
12/03/2021 • 12 minutes to read

NOTE
Esta característica se incluyó por primera vez en EF Core 3.0.

TIP
El código de este documento se puede encontrar en GitHub como un ejemplo ejecutable.

Fondo
El seguimiento de cambios significa que EF Core determina automáticamente qué cambios realizó la aplicación
en una instancia de entidad cargada, de modo que esos cambios se pueden volver a guardar en la base de datos
cuando SaveChanges se llama a. Normalmente, EF Core realiza esta tarea tomando una instantánea de la
instancia cuando se carga desde la base de datos y comparando esa instantánea con la instancia entregada a la
aplicación.
EF Core incluye lógica integrada para la creación de instantáneas y la comparación de la mayoría de los tipos
estándar que se usan en las bases de datos, por lo que los usuarios no suelen tener que preocuparse de este
tema. Sin embargo, cuando se asigna una propiedad a través de un convertidor de valores, EF Core debe
realizar la comparación en los tipos de usuario arbitrarios, que pueden ser complejos. De forma
predeterminada, EF Core usa la comparación de igualdad predeterminada definida por los tipos (por ejemplo
Equals , el método); para la toma de instantáneas, los tipos de valor se copian para generar la instantánea,
mientras que para los tipos de referencia no se realiza ninguna copia y se usa la misma instancia como la
instantánea.
En los casos en los que el comportamiento de comparación integrado no es adecuado, los usuarios pueden
proporcionar un comparador de valores, que contiene la lógica para la toma de instantáneas, la comparación y
el cálculo de un código hash. Por ejemplo, lo siguiente configura la conversión de valores para List<int> que la
propiedad se convierta en una cadena JSON en la base de datos y define también un comparador de valores
adecuado:

modelBuilder
.Entity<EntityType>()
.Property(e => [Link])
.HasConversion(
v => [Link](v, null),
v => [Link]<List<int>>(v, null),
new ValueComparer<List<int>>(
(c1, c2) => [Link](c2),
c => [Link](0, (a, v) => [Link](a, [Link]())),
c => [Link]()));

Vea clases mutables a continuación para obtener más detalles.


Tenga en cuenta que los comparadores de valores también se usan para determinar si dos valores de clave son
iguales al resolver las relaciones; Esto se explica a continuación.
Comparación superficial frente a profunda
En el caso de los tipos de valor pequeños e inmutables como int , la lógica predeterminada de EF Core
funciona bien: el valor se copia tal cual cuando se realiza instantáneas y se compara con la comparación de
igualdad integrada del tipo. Al implementar su propio comparador de valores, es importante tener en cuenta si
la lógica de comparación profunda o superficial (y de la instantánea) es adecuada.
Considere las matrices de bytes, que pueden ser arbitrariamente grandes. Se pueden comparar:
Por referencia, de modo que solo se detecta una diferencia si se usa una nueva matriz de bytes
Mediante una comparación profunda, de modo que se detecta la mutación de los bytes de la matriz
De forma predeterminada, EF Core usa el primero de estos enfoques para las matrices de bytes que no son de
clave. Es decir, solo se comparan las referencias y se detecta un cambio solo cuando una matriz de bytes
existente se reemplaza por otra nueva. Se trata de una decisión pragmática que evita copiar matrices completas
y compararlas de byte a byte al ejecutarse SaveChanges . Significa que el escenario común de sustitución, por
ejemplo, de una imagen por otra, se trata de un modo eficaz.
Por otro lado, la igualdad de referencia no funcionaría cuando se usan matrices de bytes para representar claves
binarias, ya que es muy poco probable que una propiedad FK esté establecida en la misma instancia que una
propiedad PK en la que se debe comparar. Por lo tanto, EF Core utiliza comparaciones en profundidad para las
matrices de bytes que actúan como claves. no es probable que esto afecte al rendimiento, ya que las claves
binarias suelen ser cortas.
Tenga en cuenta que la comparación y la lógica de la instantánea elegidos deben corresponderse entre sí: la
comparación profunda requiere una instantánea profunda para funcionar correctamente.

Clases inmutables simples


Considere una propiedad que utiliza un convertidor de valores para asignar una clase simple e inmutable.

public sealed class ImmutableClass


{
public ImmutableClass(int value)
{
Value = value;
}

public int Value { get; }

private bool Equals(ImmutableClass other)


=> Value == [Link];

public override bool Equals(object obj)


=> ReferenceEquals(this, obj) || obj is ImmutableClass other && Equals(other);

public override int GetHashCode()


=> [Link]();
}

modelBuilder
.Entity<MyEntityType>()
.Property(e => [Link])
.HasConversion(
v => [Link],
v => new ImmutableClass(v));

Las propiedades de este tipo no necesitan comparaciones especiales ni instantáneas porque:


La igualdad se invalida para que las distintas instancias se comparen correctamente
El tipo es inmutable, por lo que no existe la posibilidad de alterar un valor de instantánea
Por lo tanto, en este caso, el comportamiento predeterminado de EF Core es correcto.

Structs inmutables simples


La asignación para Structs simples también es sencilla y no requiere comparaciones o instantáneas especiales.

public readonly struct ImmutableStruct


{
public ImmutableStruct(int value)
{
Value = value;
}

public int Value { get; }


}

modelBuilder
.Entity<EntityType>()
.Property(e => [Link])
.HasConversion(
v => [Link],
v => new ImmutableStruct(v));

EF Core tiene compatibilidad integrada para generar comparaciones compiladas miembro a miembro de
propiedades de struct. Esto significa que no es necesario que los Structs tengan una igualdad invalidada para EF
Core, pero todavía puede optar por otras razones. Además, no es necesario realizar una instantánea especial, ya
que las estructuras son inmutables y siempre se copian de miembro a miembro. (Esto también se aplica a las
estructuras mutables, pero las estructuras mutables deberían evitarse en general).

Clases mutables
Se recomienda usar tipos inmutables (clases o Structs) con convertidores de valores siempre que sea posible.
Esto suele ser más eficaz y tiene una semántica más clara que el uso de un tipo mutable. Sin embargo, es
habitual usar las propiedades de los tipos que la aplicación no puede cambiar. Por ejemplo, asignar una
propiedad que contenga una lista de números:

public List<int> MyListProperty { get; set; }

La clase List<T>:
Tiene igualdad de referencia; dos listas que contienen los mismos valores se tratan como diferentes.
Es mutable; los valores de la lista se pueden agregar y quitar.
Una conversión de valor típica en una propiedad de lista puede convertir la lista a y desde JSON:

EF Core 5.0
Versiones anteriores
modelBuilder
.Entity<EntityType>()
.Property(e => [Link])
.HasConversion(
v => [Link](v, null),
v => [Link]<List<int>>(v, null),
new ValueComparer<List<int>>(
(c1, c2) => [Link](c2),
c => [Link](0, (a, v) => [Link](a, [Link]())),
c => [Link]()));

El ValueComparer<T> constructor acepta tres expresiones:


Expresión para comprobar la igualdad
Expresión para generar un código hash.
Una expresión para la instantánea de un valor
En este caso, la comparación se realiza comprobando si las secuencias de números son iguales.
Del mismo modo, el código hash se genera a partir de esta misma secuencia. (Tenga en cuenta que se trata de
un código hash sobre valores mutables y, por lo tanto, puede causar problemas. En su lugar, puede ser
inmutable si es posible).
La instantánea se crea mediante la clonación de la lista con ToList . De nuevo, esto solo es necesario si se van a
mutar las listas. En su lugar, puede ser inmutable si es posible.

NOTE
Los convertidores de valores y los comparadores se construyen mediante expresiones en lugar de delegados simples. Esto
se debe a que EF Core inserta estas expresiones en un árbol de expresión mucho más complejo que se compila en un
delegado de forma de entidad. Conceptualmente, esto es similar a la inserción del compilador. Por ejemplo, una
conversión simple solo puede ser una compilada en la conversión, en lugar de una llamada a otro método para realizar la
conversión.

Comparadores de claves
En la sección de fondo se explica por qué las comparaciones de claves pueden requerir una semántica especial.
Asegúrese de crear un comparador que sea adecuado para las claves al establecerlo en una propiedad principal,
principal o de clave externa.
Use SetKeyValueComparer en los casos poco frecuentes en los que se requiere una semántica diferente en la
misma propiedad.

NOTE
SetStructuralValueComparer se ha quedado obsoleto en EF Core 5,0. En su lugar, use SetKeyValueComparer.

Reemplazar el comparador predeterminado


En ocasiones, es posible que la comparación predeterminada usada por EF Core no sea adecuada. Por ejemplo,
la mutación de matrices de bytes no se detecta, de forma predeterminada, en EF Core. Esto se puede invalidar si
se establece un comparador diferente en la propiedad:
modelBuilder
.Entity<EntityType>()
.Property(e => [Link])
.Metadata
.SetValueComparer(
new ValueComparer<byte[]>(
(c1, c2) => [Link](c2),
c => [Link](0, (a, v) => [Link](a, [Link]())),
c => [Link]()));

EF Core ahora comparará las secuencias de bytes y, por tanto, detectará las mutaciones de matriz de bytes.
Propagación de datos
12/03/2021 • 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


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.

Como ejemplo, se configurarán los datos de inicialización de un 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, no EnsureCreated() 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 planea 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
Los datos que son grandes (la propagación de datos se capturan en instantáneas de migración, y los datos
grandes pueden conducir rápidamente a grandes archivos y a un rendimiento degradado).
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
12/03/2021 • 11 minutes to read

Es posible definir un constructor con parámetros y hacer que EF Core llame 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
Actualmente, 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 es un readonly campo para que se puedan usar 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 de carga diferida para
obtener más detalles.
IEntityType -los metadatos de EF Core asociados a este tipo de entidad

NOTE
Actualmente, 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 el null fin de controlar los
casos en los que EF Core no está creando 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
12/03/2021 • 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") a para


asignarla [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 incluyó por primera vez 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
12/03/2021 • 16 minutes to read

EF Core permite modelar tipos de entidad que solo 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
Los tipos de entidad de propiedad nunca se incluyen en EF Core en el modelo por Convención. Puede usar el
OwnsOne método en OnModelCreating o anotar el tipo con OwnedAttribute 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 utilizar el OwnsOne método en OnModelCreating para especificar que la ShippingAddress
propiedad es una entidad propiedad del Order tipo de entidad y para configurar aspectos adicionales si es
necesario.

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

Si la ShippingAddress propiedad es privada en el Order tipo, puede usar la versión de cadena del OwnsOne
método:

[Link]<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");

El modelo anterior se asigna al siguiente esquema de la base de datos:


Vea el proyecto de ejemplo completo para obtener más contexto.

TIP
El tipo de entidad de propiedad se puede marcar como requerido; consulte los dependientes uno a uno obligatorios para
obtener más información.

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 StreetAddress tipo 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


Para configurar una colección de tipos de propiedad, utilice 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, por lo que la clave del propietario no es suficiente para
proporcionar una identidad única para cada instancia de propiedad.
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 elemento
primario {1} tiene un elemento secundario {1} , el elemento primario {2} no puede tener un elemento
secundario {1} ), 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 elemento primario tiene un
elemento {1} secundario, el elemento {1,1} primario {2} todavía puede tener un elemento secundario {2,1} ).
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 vamos a usar la Distributor clase.
public class Distributor
{
public int Id { get; set; }
public ICollection<StreetAddress> ShippingCenters { get; set; }
}

De forma predeterminada, la clave principal utilizada para el tipo de propiedad al que se hace referencia a través
de la ShippingCenters propiedad de navegación será ("DistributorId", "Id") donde sea "DistributorId" el
FK y "Id" sea un int valor único.
Para configurar una llamada de clave principal diferente HasKey .

[Link]<Distributor>().OwnsMany(
p => [Link], a =>
{
[Link]().HasForeignKey("OwnerId");
[Link]<int>("Id");
[Link]("Id");
});

El modelo anterior se asigna al siguiente esquema de la base de datos:

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
StreetAddress propiedades aparecerán en la tabla ' Orders ' con los nombres ' ShippingAddress_Street ' y '
ShippingAddress_City '.
Puede usar el HasColumnName método 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 de EF Core, la navegación de definición forma
parte de la identidad del tipo junto con el tipo .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 BillingAddress , posee y ShippingAddress , que son ambos StreetAddress tipos.
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
que 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; }
}

Configuración de tipos de propiedad


Es posible encadenar el OwnsOne método en una llamada fluida para configurar este modelo:

[Link]<DetailedOrder>().OwnsOne(
p => [Link], od =>
{
[Link](d => [Link]);
[Link](d => [Link]).UsePropertyAccessMode([Link]);
[Link](c => [Link]);
[Link](c => [Link]);
});

Observe la WithOwner llamada usada para definir la propiedad de navegación que señala hacia atrás en el
propietario. Para definir 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.
También es posible lograr este resultado con OwnedAttribute en OrderDetails y StreetAddress .
Además, observe la Navigation llamada a. En EFCore 5,0, las propiedades de navegación a los tipos de
propiedad se pueden configurar aún más para las propiedades de navegación que noson de propiedad.
El modelo anterior se asigna al siguiente esquema de la base de datos:

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 a ToTable y proporcionar un nombre de tabla diferente. En el ejemplo
siguiente se asignarán OrderDetails y sus dos direcciones a una tabla independiente de DetailedOrder :

[Link]<DetailedOrder>().OwnsOne(p => [Link], od => { [Link]("OrderDetails"); });


También es posible usar 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, varios tipos de entidad se asignarían 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 Include método, incluso si los tipos de propiedad se almacenan en una tabla independiente. En función del
modelo descrito anteriormente, se obtendrá la siguiente consulta Order OrderDetails y las dos propiedad
StreetAddresses 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 .
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 actuales
Los tipos de entidad de propiedad no pueden tener jerarquías de herencia
Deficiencias en versiones anteriores
En EF Core 2. x 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.
En EF Core 3. x las columnas de los tipos de entidad que se asignan a la misma tabla que el propietario
siempre se marcan como valores NULL.
Tipos de entidad sin llave
12/03/2021 • 8 minutes to read

NOTE
Esta característica se ha agregado 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. La [Keyless] anotación de datos estuvo disponible en EFCore 5,0.

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.

Definición de tipos de entidad sin entrada


Los tipos de entidad sin llave pueden definirse mediante la anotación de datos o la API fluida:
Anotaciones de datos
API fluida

[Keyless]
public class BlogPostsCount
{
public string BlogName { get; set; }
public int PostCount { get; set; }
}

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 y las columnas de la base de datos de destino mediante métodos de API fluida o
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 una [Keyless] anotación de datos o una .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 a tablas que no tienen una clave principal definida.
Asignación a las consultas definidas en el modelo.

Asignar a 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 la
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 ToTable método 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 blog y un modelo post sencillos:

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, se define una vista de base de datos simple que nos permitirá consultar el número de
publicaciones asociadas 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, se define una clase para que contenga 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 HasNoKey API. 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 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.
TIP
Para probar los tipos de entidad sin entrada asignados a las vistas mediante el proveedor en memoria, asígnelo a una
consulta a través de ToInMemoryQuery . Vea un ejemplo ejecutable mediante esta técnica para obtener más detalles.
Alternar entre varios modelos con el mismo tipo
DbContext
12/03/2021 • 2 minutes to read

El modelo integrado OnModelCreating puede utilizar una propiedad en el contexto para cambiar el modo 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
solo una 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 IModelCacheKeyFactory para generar claves de caché para los modelos; de forma predeterminada, EF
supone que para un tipo de contexto determinado 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 modelos
diferentes a partir del mismo tipo de contexto, debe reemplazar el IModelCacheKeyFactory servicio con la
implementación correcta; la clave generada se comparará con otras claves del modelo mediante el Equals
método, teniendo en cuenta todas las variables que afectan al modelo.
La siguiente implementación tiene UseIntProperty en cuenta 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 el nuevo IModelCacheKeyFactory en su contexto OnConfiguring :

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
12/03/2021 • 8 minutes to read

NOTE
Esta característica se presentó 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.

Instalando
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

[Link] Pomelo. EntityFrameworkCore. MySql. NetTopologySuite

[Link] Devart. Data. MySql. EFCore. NetTopologySuite

[Link] Devart. Data. PostgreSql. EFCore. NetTopologySuite

[Link] Devart. Data. SQLite. EFCore. NetTopologySuite

[Link] Teradata. EntityFrameworkCore. NetTopologySuite

NetTopologySuite
NetTopologySuite (NTS) 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
[Link] espacio de nombres.

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.

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 latitude, longitude formato en el
que normalmente se ven estos valores.

Consulta de datos
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")]


internal class City
{
public int CityID { get; set; }

public string CityName { get; set; }

public Point Location { get; set; }


}
[Table("Countries", Schema = "Application")]
internal class Country
{
public int CountryID { get; set; }

public string CountryName { get; set; }

// Database includes both Polygon and MultiPolygon values


public Geometry Border { get; set; }
}

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. Consulte la
documentación del proveedor para saber qué métodos se admiten.

// Find the nearest city


var nearestCity = [Link]
.OrderBy(c => [Link](currentLocation))
.FirstOrDefault();

// Find the containing country


var currentCountry = [Link]
.FirstOrDefault(c => [Link](currentLocation));

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.

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 ProjNet (para
GeoAPI).

NOTE
Use el paquete NuGet de ProjNetmás reciente, no el paquete anterior denominado ProjNet4GeoAPI.

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 ProjNet para calcular la distancia entre dos ciudades.

internal static class GeometryExtensions


{
private static readonly CoordinateSystemServices _coordinateSystemServices
= new CoordinateSystemServices(
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;
}

private class MathTransformFilter : ICoordinateSequenceFilter


{
private 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 x = [Link](i);
var y = [Link](i);
var z = [Link](i);
_transform.Transform(ref x, ref y, ref z);
[Link](i, x);
[Link](i, y);
[Link](i, z);
}
}
}
var seattle = new Point(-122.333056, 47.609722) { SRID = 4326 };
var redmond = new Point(-122.123889, 47.669444) { SRID = 4326 };

// In order to get the distance in meters, we need to project to an appropriate


// coordinate system. In this case, we're using SRID 2855 since it covers the
// geographic area of our data
var distanceInDegrees = [Link](redmond);
var distanceInMeters = [Link](2855).Distance([Link](2855));

NOTE
4326 hace referencia a WGS 84, un estándar que se usa en GPS y en otros sistemas geográficos.

Recursos adicionales
Información específica de la base de datos
Asegúrese de leer la documentación del proveedor para obtener información adicional sobre cómo trabajar con
datos espaciales.
Datos espaciales en el proveedor de SQL Server
Datos espaciales en el proveedor de SQLite
Datos espaciales en el proveedor Npgsql
Otros recursos
Documentos de NetTopologySuite
EF Core sesión reuniónde la comunidad, centrándose en los datos espaciales y NetTopologySuite.
Administración de esquemas de base de datos
12/03/2021 • 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.
Descripción general de las migraciones
07/04/2021 • 9 minutes to read

En proyectos reales, los modelos de datos cambian a medida que se implementan características: se agregan o
se quitan nuevas entidades o propiedades, y los esquemas de base de datos se deben cambiar según
corresponda para mantenerlos sincronizados con la aplicación. 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.
A nivel general, las migraciones funcionan de esta forma:
Cuando se introduce un cambio en el modelo de datos, el desarrollador usa herramientas de EF Core para
agregar una migración correspondiente en la que se describan las actualizaciones necesarias para mantener
sincronizado el esquema de la base de datos. EF Core compara el modelo actual con una instantánea del
modelo anterior para determinar las diferencias y genera los archivos de origen de la migración, de los que
se puede realizar el seguimiento en el control de código fuente del proyecto como cualquier otro archivo de
código fuente.
Una vez que se ha generado una migración nueva, haya varias maneras de aplicarla a una base de datos.
EF Core registra todas las migraciones aplicadas en una tabla de historial especial, lo que le permite saber
qué migraciones se han aplicado y cuáles no.
El resto de esta página es una guía para principiantes paso a paso sobre el uso de las migraciones. Consulte las
demás páginas de esta sección para obtener información más detallada.

Introducción
Imagine que acaba de completar la primera aplicación de EF Core, que contiene el siguiente modelo simple:

public class Blog


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

Durante el desarrollo, es posible que haya usado las API Create y Drop para realizar una iteración rápida y
cambiar el modelo según las necesidades, pero ahora que la aplicación se destina a producción, necesita una
manera de desarrollar de forma segura el esquema sin quitar la base de datos completa.
Instalar las herramientas
En primer lugar, tendrá que instalar las herramientas de línea de comandos de EF Core:
Por lo general, se recomienda usar las herramientas de la CLI de .NET Core, que funcionan en todas las
plataformas.
Si se siente más cómodo trabajando en Visual Studio o tiene experiencia con las migraciones de EF6, también
puede usar las herramientas de la consola del administrador de paquetes.
Creación de la primera migración
Ya está listo para agregar la primera migración. Indique a EF Core que cree una migración llamada
InitialCreate :
CLI de .NET Core
Visual Studio

dotnet ef migrations add InitialCreate

EF Core creará un directorio denominado Migrations (Migraciones) en el proyecto y generará varios archivos.
Es recomendable inspeccionar lo que EF Core ha generado exactamente y, posiblemente, rectificarlo, pero este
paso se omitirá por ahora.
Creación de la base de datos y el esquema
En este momento puede hacer que EF cree la base de datos y el esquema a partir de la migración. Esto también
se puede hacer mediante:
CLI de .NET Core
Visual Studio

dotnet ef database update

Eso es todo: la aplicación está lista para ejecutarse en la base de datos nueva y no es necesario escribir una sola
línea de SQL. Tenga en cuenta que esta manera de aplicar migraciones resulta idónea para el desarrollo local,
pero es menos adecuada para los entornos de producción; vea la página Aplicación de migraciones para
obtener más información.
Evolución del modelo
Han pasado unos días y le piden que agregue una marca de tiempo de creación a los blogs. Ha realizado los
cambios necesarios en la aplicación y el ahora modelo tiene este aspecto:

public class Blog


{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreatedTimestamp { get; set; }
}

Ahora el modelo y la base de datos de producción están desincronizados; tendrá que agregar una nueva
columna al esquema de la base de datos. Cree una migración para esto:

CLI de .NET Core


Visual Studio

dotnet ef migrations add AddBlogCreatedTimestamp

Tenga en cuenta que a las migraciones se les proporciona un nombre descriptivo para que después sea más fácil
entender el historial del proyecto.
Como no se trata de la primera migración del proyecto, ahora EF Core compara el modelo actualizado con una
instantánea del modelo anterior, antes de que se agregara la columna; la instantánea del modelo es uno de los
archivos generados por EF Core cuando se agrega una migración y se inserta en el control de código fuente. En
función de esa comparación, EF Core detecta que se ha agregado una columna y agrega la migración adecuada.
Ahora puede aplicar la migración como antes:
CLI de .NET Core
Visual Studio

dotnet ef database update

Tenga en cuenta que, en esta ocasión, EF detecta que la base de datos ya existe. Además, cuando antes se ha
aplicado la primera migración, esta operación se ha registrado en una tabla de historial de migraciones especial
en la base de datos, lo que permite que EF solo aplique de forma automática la nueva migración.
Exclusión de elementos del modelo

NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

En ocasiones, es posible que quiera consultar tipos de otro DbContext. Esto puede dar lugar a conflictos de
migración. Para evitarlo, excluya el tipo de las migraciones de uno de los elementos DbContext.

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<IdentityUser>()
.ToTable("AspNetUsers", t => [Link]());
}

Pasos siguientes
Lo anterior solo era una breve introducción a las migraciones. Consulte las demás páginas de documentación
para obtener más información sobre cómo administrar migraciones, aplicarlas y otros aspectos. La referencia de
herramientas de la CLI de .NET Core también contiene información útil sobre los distintos comandos

Recursos adicionales
Referencia de herramientas de Entity Framework Core - CLI de .NET Core: incluye comandos para actualizar,
quitar, agregar, etc.
Referencia de herramientas de Entity Framework Core - consola del administrador de paquetes en
Visual Studio: incluye comandos para actualizar, quitar, agregar y mucho más.
Sesión de Reunión de la comunidad de EF Core donde se analizan las nuevas características de migración de
EF Core 5.0.
Administrar migraciones
07/04/2021 • 11 minutes to read

A medida que cambia el modelo, las migraciones se agregan y se quitan como parte del desarrollo normal y los
archivos de migración se protegen en el control de código fuente del proyecto. Para administrar las migraciones,
primero debe instalar las herramientas de línea de comandos de EF Core.

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.

Agregar una migración


Una vez cambiado el modelo, puede Agregar una migración para ese cambio:
CLI de .NET Core
Visual Studio

dotnet ef migrations add AddBlogCreatedTimestamp

El nombre de la migración se puede usar como mensaje de confirmación en un sistema de control de versiones.
Por ejemplo, puede elegir un nombre como AddBlogCreatedTimestamp si el cambio es una nueva
CreatedTimestamp propiedad en la Blog entidad.

Se agregan tres archivos al proyecto en el directorio Migraciones :


XXXXXXXXXXXXXX_AddCreatedTimestamp. CS : el archivo de migraciones principal. Contiene las
operaciones necesarias para aplicar la migración (en Up ) y para revertirla (en Down ).
XXXXXXXXXXXXXX_AddCreatedTimestamp. Designer. CS : el archivo de metadatos de 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.
Espacios de nombres
Puede mover los archivos de Migraciones y cambiar su espacio de nombres manualmente cuando quiera. Se
crean nuevas migraciones como elementos del mismo nivel de la última migración. También puede especificar
el directorio en el momento de la generación como se indica a continuación:
CLI de .NET Core
Visual Studio

dotnet ef migrations add InitialCreate --output-dir Your/Directory


NOTE
En EF Core 5,0, también puede cambiar el espacio de nombres independientemente del directorio mediante
--namespace .

Personalizar el código de migración


Aunque en general EF Core crea migraciones precisas, siempre debe revisar el código y asegurarse de que se
corresponde con el cambio deseado; en algunos casos, incluso es necesario hacerlo.
Cambiar nombre de columna
Un ejemplo importante donde se requiere la personalización de las migraciones es al cambiar el nombre de una
propiedad. Por ejemplo, si cambia el nombre de una propiedad de Name a FullName , EF Core generará la
siguiente migración:

[Link](
name: "Name",
table: "Customers");

[Link]<string>(
name: "FullName",
table: "Customers",
nullable: true);

Por lo general, EF Core no puede saber si la intención es quitar una columna y crear una nueva (dos cambios
independientes) y cuándo debe cambiarse el nombre de una columna. Si la migración anterior se aplica tal cual,
se perderán todos los nombres de cliente. Para cambiar el nombre de una columna, reemplace la migración
anterior generada por lo siguiente:

[Link](
name: "Name",
table: "Customers",
newName: "FullName");

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.

Agregar SQL sin procesar


Aunque el cambio de nombre de una columna se puede lograr a través de una API integrada, en muchos casos
no es posible. Por ejemplo, es posible que desee reemplazar FirstName LastName las propiedades y existentes
por una sola FullName propiedad nueva. La migración generada por EF Core será la siguiente:
[Link](
name: "FirstName",
table: "Customer");

[Link](
name: "LastName",
table: "Customer");

[Link]<string>(
name: "FullName",
table: "Customer",
nullable: true);

Como antes, esto provocaría una pérdida de datos no deseada. Para transferir los datos de las columnas
anteriores, se reorganizan las migraciones y se introduce una operación SQL sin procesar de la siguiente
manera:

[Link]<string>(
name: "FullName",
table: "Customer",
nullable: true);

[Link](
@"
UPDATE Customer
SET FullName = FirstName + ' ' + LastName;
");

[Link](
name: "FirstName",
table: "Customer");

[Link](
name: "LastName",
table: "Customer");

Cambios arbitrarios a través de SQL sin formato


SQL sin formato también se puede usar para administrar objetos de base de datos que EF Core no tienen en
cuenta. Para ello, agregue una migración sin realizar ningún cambio en el modelo; se generará una migración
vacía, que se puede rellenar con operaciones SQL sin procesar.
Por ejemplo, la siguiente migración crea un SQL Server procedimiento almacenado:

[Link](
@"
EXEC ('CREATE PROCEDURE getFullName
@LastName nvarchar(50),
@FirstName nvarchar(50)
AS
RETURN @LastName + @FirstName;')");

TIP
EXEC se utiliza cuando una instrucción debe ser la primera o solo una en un lote de SQL. También se puede usar para
solucionar los errores del analizador en scripts de migración idempotente que se pueden producir cuando las columnas a
las que se hace referencia no existen actualmente en una tabla.

Se puede usar para administrar cualquier aspecto de la base de datos, incluidos:


Procedimientos almacenados
Búsqueda de texto completo
Funciones
Desencadenadores
Vistas
En la mayoría de los casos, EF Core ajustará automáticamente cada migración en su propia transacción al aplicar
las migraciones. Desafortunadamente, algunas operaciones de migración no se pueden realizar en una
transacción en algunas bases de datos. en estos casos, puede rechazar la transacción pasando
suppressTransaction: true a [Link] .

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.

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.

WARNING
Evite quitar las migraciones que ya se hayan aplicado a las bases de datos de producción. Si lo hace, no podrá revertir
esas migraciones desde las bases de datos y puede romper las suposiciones realizadas por migraciones posteriores.

Enumerar migraciones
Puede enumerar todas las migraciones existentes como se indica a continuación:

CLI de .NET Core


Visual Studio

dotnet ef migrations list

Restableciendo todas las migraciones


En algunos casos extremos, puede que sea necesario quitar todas las migraciones y empezar de nuevo. Esto se
puede hacer fácilmente si se elimina la carpeta Migrations y se quita la base de datos. en ese momento, puede
crear una nueva migración inicial, que contendrá todo el esquema actual.
También es posible restablecer todas las migraciones y crear una sola sin perder los datos. A veces, esto se
denomina "" "en" ", y implica algún trabajo manual:
Eliminación de la carpeta Migrations
Cree una nueva migración y genere un script SQL para ella.
En la base de datos, elimine todas las filas de la tabla de historial de migraciones
Inserte una sola fila en el historial de migraciones para registrar que la primera migración ya se ha aplicado,
ya que las tablas ya están allí. La instrucción INSERT SQL es la última operación del script SQL que se generó
anteriormente.

WARNING
Cualquier código de migración personalizado se perderá cuando se elimine la carpeta Migrations . Las personalizaciones
deben aplicarse a la nueva migración inicial manualmente para que se conserven.

Recursos adicionales
Referencia de herramientas de Entity Framework Core - CLI de .NET Core: incluye comandos para actualizar,
quitar, agregar, etc.
Referencia de herramientas de Entity Framework Core - consola del administrador de paquetes en
Visual Studio: incluye comandos para actualizar, quitar, agregar y mucho más.
Aplicación de migraciones
12/03/2021 • 10 minutes to read

Una vez agregadas las migraciones, deben implementarse y aplicarse a las bases de datos. Existen varias
estrategias para hacerlo, con algunas más adecuadas para entornos de producción y otras para el ciclo de vida
de desarrollo.

NOTE
Sea cual sea su estrategia de implementación, inspeccione siempre las migraciones generadas y pruébelos antes de
aplicarlas a una base de datos de producción. Una migración puede quitar una columna cuando el intento era cambiarle el
nombre o puede producir un error por diversos motivos cuando se aplica a una base de datos.

Scripts SQL
La manera recomendada de implementar las migraciones en una base de datos de producción es mediante la
generación de scripts SQL. Entre las ventajas de esta estrategia se incluyen las siguientes:
Se puede revisar la precisión de los scripts SQL; Esto es importante, ya que la aplicación de cambios de
esquema en las bases de datos de producción es una operación potencialmente peligrosa que podría
implicar la pérdida de datos.
En algunos casos, los scripts se pueden optimizar para ajustarse a las necesidades específicas de una base de
datos de producción.
Los scripts SQL se pueden usar junto con una tecnología de implementación, y incluso pueden generarse
como parte del proceso de CI.
Se pueden proporcionar scripts SQL a un DBA y se pueden administrar y archivar por separado.

CLI de .NET Core


Visual Studio

Uso básico
Lo siguiente genera un script SQL desde una base de datos vacía a la migración más reciente:

dotnet ef migrations script

Con From (To implícito)


Lo siguiente genera un script SQL a partir de la migración dada a la última migración.

dotnet ef migrations script AddNewTables

Con From y To
Lo siguiente genera un script SQL a partir de la from migración especificada a la to migración especificada.

dotnet ef migrations script AddNewTables AddAuditTable

Puede usar un valor from que sea más reciente que el valor to para generar un script de reversión.
WARNING
Tome nota de los posibles escenarios de pérdida de datos.

La generación de script acepta los dos argumentos siguientes para indicar qué intervalo de migraciones se debe
generar:
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.

Scripts SQL idempotentes


Los scripts SQL generados anteriormente solo se pueden aplicar para cambiar el esquema de una migración a
otra; es su responsabilidad aplicar el script adecuadamente y solo en la base de datos en el estado de migración
correcto. EF Core también admite la generación de scripts idempotente , que internamente comprueban qué
migraciones ya se han aplicado (a través de la tabla de historial de migraciones) y solo aplican las que faltan.
Esto resulta útil si no sabe exactamente cuál fue la última migración aplicada a la base de datos, o si está
implementando en varias bases de datos que pueden estar en una migración diferente.
Lo siguiente genera migraciones idempotente:

CLI de .NET Core


Visual Studio

dotnet ef migrations script --idempotent

Herramientas de línea de comandos


Las herramientas de línea de comandos de EF se pueden usar para aplicar migraciones a una base de datos.
Aunque es productivo para el desarrollo y las pruebas locales de las migraciones, este enfoque no es idóneo
para la administración de bases de datos de producción:
Los comandos SQL se aplican directamente a la herramienta, sin que el desarrollador tenga la oportunidad
de inspeccionarlos o modificarlos. Esto puede ser peligroso en un entorno de producción.
El SDK de .NET y la herramienta EF deben instalarse en servidores de producción.

CLI de .NET Core


Visual Studio

La siguiente actualización de la base de datos a la migración más reciente:

dotnet ef database update

La siguiente actualización de la base de datos a una migración determinada:

dotnet ef database update AddNewTables

Tenga en cuenta que esto puede usarse también para revertir a una migración anterior.
WARNING
Tome nota de los posibles escenarios de pérdida de datos.

Para obtener más información sobre cómo aplicar migraciones a través de las herramientas de línea de
comandos, consulte la referencia de herramientas de EF Core.

Aplicar migraciones en tiempo de ejecución


Es posible que la propia aplicación aplique migraciones mediante programación, normalmente durante el inicio.
Aunque es productivo para el desarrollo y las pruebas locales de las migraciones, este enfoque no es apropiado
para la administración de bases de datos de producción, por las siguientes razones:
Si se están ejecutando varias instancias de la aplicación, ambas aplicaciones podrían intentar aplicar la
migración simultáneamente y producir un error (o peor, causar daños en los datos).
Del mismo modo, si una aplicación obtiene acceso a la base de datos mientras otra aplicación la migra, esto
puede provocar problemas graves.
La aplicación debe tener acceso elevado para modificar el esquema de la base de datos. Por lo general, se
recomienda limitar los permisos de base de datos de la aplicación en producción.
Es importante poder revertir una migración aplicada en caso de que se produzca un problema. Las otras
estrategias proporcionan esto de forma sencilla y rápida.
Los comandos SQL los aplica directamente el programa, sin ofrecer al desarrollador una oportunidad de
inspeccionarlos o modificarlos. Esto puede ser peligroso en un entorno de producción.
Para aplicar las migraciones mediante programación, llame a [Link]() . Por ejemplo, una
aplicación típica de [Link] puede hacer lo siguiente:

public static void Main(string[] args)


{
var host = CreateHostBuilder(args).Build();

using (var scope = [Link]())


{
var db = [Link]<ApplicationDbContext>();
[Link]();
}

[Link]();
}

Tenga en cuenta que Migrate() se basa en el IMigrator servicio, que se puede usar para escenarios más
avanzados. Use [Link]().GetService<IMigrator>() para acceder a él.

WARNING
Considere atentamente antes de usar este enfoque en producción. La experiencia ha demostrado que la simplicidad de
esta estrategia de implementación se ve compensada por los problemas que crea. Considere la posibilidad de generar
scripts SQL a partir de migraciones.
No llame a EnsureCreated() antes de Migrate() . EnsureCreated() omite las migraciones para crear el esquema,
lo cual provoca un error de Migrate() .
Migraciones en entornos de equipo
12/03/2021 • 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
12/03/2021 • 4 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 Sql() método o mediante la definición de
MigrationOperation objetos 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.

private static OperationBuilder<SqlOperation> CreateUser(


this MigrationBuilder migrationBuilder,
string name,
string password)
=> [Link]($"CREATE USER {name} WITH PASSWORD '{password}';");

TIP
Utilice la EXEC función cuando una instrucción debe ser la primera o solo una en un lote de SQL. También podría ser
necesario para solucionar los errores del analizador en scripts de migración idempotente que se pueden producir cuando
las columnas a las que se hace referencia no existen actualmente en una tabla.

Si las migraciones necesitan admitir varios proveedores de bases de datos, puede utilizar la
[Link] propiedad. Este es un ejemplo que admite tanto Microsoft SQL Server como
PostgreSQL.
private static OperationBuilder<SqlOperation> 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}';");
}

throw new Exception("Unexpected provider.");


}

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 la suya 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.

internal 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] .

private static OperationBuilder<CreateUserOperation> CreateUser(


this MigrationBuilder migrationBuilder,
string name,
string password)
{
var operation = new CreateUserOperation { Name = name, Password = password };
[Link](operation);

return new OperationBuilder<CreateUserOperation>(operation);


}

Este enfoque requiere que cada proveedor sepa cómo generar SQL para esta operación en su
IMigrationsSqlGenerator servicio. Este es un ejemplo invalidando el generador del SQL Server para administrar
la nueva operación.
internal class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
public MyMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
IRelationalAnnotationProvider 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
12/03/2021 • 2 minutes to read

Puede que desee almacenar las migraciones en un proyecto diferente del que contiene su 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.

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

Pasos
1. Cree una nueva biblioteca de clases.
2. Agregue una referencia al proyecto 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 proyecto de migraciones no contiene una migración existente, el comando Add-Migration
no podrá encontrar DbContext.

4. Configure el ensamblado de migraciones:

[Link]<ApplicationDbContext>(
options =>
[Link](
[Link]("DefaultConnection"),
x => [Link]("[Link]")));

5. Agregue una referencia al proyecto de migraciones desde el proyecto de Inicio .

<ItemGroup>
<ProjectReference Include="..\[Link]\[Link]">
</ItemGroup>

Si esto provoca una dependencia circular, puede actualizar en su lugar la ruta de acceso de salida base del
proyecto de migraciones :

<PropertyGroup>
<BaseOutputPath>..\WebApplication1\bin\</BaseOutputPath>
</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
12/03/2021 • 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.
Para controlar esto, se mantienen varios conjuntos de migraciones, uno para cada proveedor, y se agrega una
migración a cada uno de ellos para cada cambio de modelo.

Usar varios tipos de contexto


Una manera de crear varios conjuntos de migración es usar un tipo DbContext por proveedor.

class SqliteBlogContext : BlogContext


{
protected override void OnConfiguring(DbContextOptionsBuilder options)
=> [Link]("Data Source=[Link]");
}

Especifique el tipo de contexto al agregar nuevas migraciones.


CLI de .NET Core
Visual Studio

dotnet ef migrations add InitialCreate --context BlogContext --output-dir Migrations/SqlServerMigrations


dotnet ef migrations add InitialCreate --context SqliteBlogContext --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.

Usar un tipo de contexto


También es posible usar un tipo DbContext. Esto requiere actualmente mover las migraciones a un ensamblado
independiente. Consulte uso de un proyecto de migración independiente para obtener instrucciones sobre la
configuración de los proyectos.

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

A partir de EF Core 5,0, puede pasar argumentos a la aplicación desde las herramientas. Esto puede habilitar un
flujo de trabajo más simplificado, lo que evita tener que realizar cambios manuales en el proyecto cuando se
ejecutan las herramientas.
Este es un patrón que funciona bien cuando se usa un host genérico.
public static IHostBuilder CreateHostBuilder(string[] args)
=> [Link](args)
.ConfigureServices(
(hostContext, services) =>
{
[Link]<Worker>();

// Set the active provider via configuration


var configuration = [Link];
var provider = [Link]("Provider", "SqlServer");

[Link]<BlogContext>(
options => _ = provider switch
{
"Sqlite" => [Link](
[Link]("SqliteConnection"),
x => [Link]("SqliteMigrations")),

"SqlServer" => [Link](


[Link]("SqlServerConnection"),
x => [Link]("SqlServerMigrations")),

_ => throw new Exception($"Unsupported provider: {provider}")


});
});

Dado que el generador de hosts predeterminado lee la configuración de los argumentos de línea de comandos,
puede especificar el proveedor al ejecutar las herramientas.

CLI de .NET Core


Visual Studio

dotnet ef migrations add MyMigration --project ../SqlServerMigrations -- --provider SqlServer


dotnet ef migrations add MyMigration --project ../SqliteMigrations -- --provider Sqlite

TIP
El -- token dirige dotnet ef para tratar todo lo que sigue como argumento y no intentar analizarlos como opciones.
Los argumentos adicionales que no use dotnet ef se reenvían a la aplicación.

NOTE
La capacidad de especificar argumentos adicionales para la aplicación se agregó en EF Core 5,0. Si utiliza una versión
anterior, especifique en su lugar valores de configuración con variables de entorno.
Tabla de historial de migraciones personalizadas
12/03/2021 • 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 nombre de esquema y de tabla mediante el MigrationsHistoryTable() método de
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 específico del proveedor
IHistoryRepository . 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.
internal 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
12/03/2021 • 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
12/03/2021 • 14 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 Scaffold-DbContext comando de EF Core
herramientas de la consola del administrador de paquetes (PMC) o el dotnet ef dbcontext scaffold comando
de las herramientas de la interfaz de la línea de comandos (CLI) de .net.

Instalando
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 \ .

CLI de .NET Core


Visual Studio

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 Name=<connection-string> sintaxis 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 ConnectionStrings:Chinook "Data Source=(localdb)\MSSQLLocalDB;Initial


Catalog=Chinook"
dotnet ef dbcontext scaffold Name=ConnectionStrings:Chinook [Link]

Nombre del 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.
CLI de .NET Core
Visual Studio

La --schema opción se puede usar para incluir todas las tablas de un esquema, mientras --table que se puede
usar para incluir tablas específicas.
Para incluir varias tablas, 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. Al especificar el -UseDatabaseNames
modificador en PMC o la --use-database-names opción en el CLI de .net Core, se deshabilitará este
comportamiento conservando 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 de .net Core) para usar 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 el CLI de .net Core.

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.

CLI de .NET Core


Visual Studio
Puede especificar el directorio en el que se usan las clases scaffolding usando --output-dir y --context-dir se
puede usar para aplicar scaffolding a la clase DbContext en un directorio independiente de las clases de tipo de
entidad:

dotnet ef dbcontext scaffold ... --context-dir Data --output-dir Models

De forma predeterminada, el espacio de nombres será el espacio de nombres raíz más los nombres de los
subdirectorios del directorio raíz del proyecto. Sin embargo, desde EFCore 5,0 en adelante, puede invalidar el
espacio de nombres para todas las clases de salida mediante --namespace . También puede invalidar el espacio
de nombres solo para la clase DbContext mediante --context-namespace :

dotnet ef dbcontext scaffold ... --namespace [Link] --context-namespace [Link]

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 característica de tipo de referencia que acepta valores NULL de C# 8 no se admite actualmente en técnicas
de ingeniería inversa: EF Core siempre genera código C# que supone que la característica está deshabilitada.
Por ejemplo, las columnas de texto que aceptan valores NULL se scaffolding como una propiedad con
string el tipo, 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
nulabilidad de C#. 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 importantes no son tan fáciles de hacer manualmente. 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
12/03/2021 • 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). Las consultas siempre se ejecutan en la base de datos incluso si las entidades devueltas en el
resultado ya existen en el contexto.

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

Filtrado
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, consulte Cómo funcionan las
consultas.
Evaluación de cliente frente a servidor
07/04/2021 • 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 las consultas para saber cómo EF Core
determina 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();

TIP
Si está usando AsAsyncEnumerable y desea componer la consulta en mayor grado en el lado del cliente, puede usar la
biblioteca [Link] que define operadores para enumerables asincrónicos. Para obtener más información,
vea Operadores LINQ asincrónicos del lado cliente.

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
07/04/2021 • 11 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]();

Cuando los resultados se devuelven en una consulta de seguimiento, EF Core comprobará si la entidad ya está
en el contexto. Si EF Core encuentra una entidad existente, se devuelve la misma instancia. EF Core no
sobrescribirá los valores actuales y originales de las propiedades de la entidad en la entrada con los valores de
la base de datos. Si no se encuentra la entidad en el contexto, EF Core creará una nueva instancia de la entidad y
la asociará al contexto. Los resultados de la consulta no contienen ninguna entidad, que se agrega al contexto
pero aún no se guarda en la base de datos.

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. Una consulta de no
seguimiento también proporcionará los resultados en función de lo que haya en la base de datos, sin tener en
cuenta los cambios locales ni las entidades agregadas.
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.
A partir de EF Core 5.0, puede combinar los dos comportamientos anteriores en la misma consulta. Es decir,
puede tener una consulta de no seguimiento, que llevará a cabo la resolución de identidades en los resultados.
Al igual que el operador consultable AsNoTracking() , hemos agregado otro operador
AsNoTrackingWithIdentityResolution() . También hay una entrada asociada agregada en la enumeración
QueryTrackingBehavior. Cuando se configura la consulta para usar la resolución de identidad sin seguimiento,
se usa un seguimiento de cambios independiente en segundo plano al generar los resultados de la consulta, por
lo que cada instancia se materializa solo una vez. Dado que esta herramienta de seguimiento de cambios es
diferente de la que se encuentra en el contexto, el contexto no realiza el seguimiento de los resultados. Una vez
que se completa la enumeración de la consulta, la herramienta de seguimiento de cambios queda fuera de
ámbito y se recopilan los elementos no utilizados según sea necesario.

var blogs = [Link]


.AsNoTrackingWithIdentityResolution()
.ToList();

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], [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.
Carga de datos relacionados
12/03/2021 • 2 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 los ejemplos que hay en esta sección en GitHub.
Carga diligente de datos relacionados
12/03/2021 • 8 minutes to read • Edit Online

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

Cau t i on

La carga diligente de navegación de una colección en una sola consulta puede producir problemas de
rendimiento. Para obtener más información, vea Consultas únicas frente a consultas divididas.

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 todas las llamadas 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 ello, 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 mezclará 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();
}

TIP
También puede cargar varias navegaciones mediante un único método Include . Esto es posible para las "cadenas" de
navegación que son todas las referencias, o cuando terminan con una sola colección.

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.ThenInclude(post => [Link])
.ToList();
}

Inclusión filtrada
NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

Al aplicar la inclusión para cargar datos relacionados, puede agregar determinadas operaciones enumerables en
la navegación de colección incluida, lo que permite filtrar y ordenar los resultados.
Las operaciones que se admiten son: Where , OrderBy , OrderByDescending , ThenBy , ThenByDescending , Skip y
Take .

Dichas operaciones se deben aplicar en la navegación de colección en la expresión lambda que se pasa al
método Include, como se muestra en el ejemplo siguiente:

using (var context = new BloggingContext())


{
var filteredBlogs = [Link]
.Include(
blog => [Link]
.Where(post => [Link] == 1)
.OrderByDescending(post => [Link])
.Take(5))
.ToList();
}

Cada navegación incluida solo permite un único conjunto de operaciones de filtro. En los casos en los que se
aplican varias operaciones Include para una navegación de colección determinada ( [Link] en los ejemplos
siguientes), las operaciones de filtro solo se pueden especificar en una de ellas:

using (var context = new BloggingContext())


{
var filteredBlogs = [Link]
.Include(blog => [Link](post => [Link] == 1))
.ThenInclude(post => [Link])
.Include(blog => [Link])
.ThenInclude(post => [Link](postTag => [Link]).Skip(3))
.ToList();
}

En lugar de eso, se pueden aplicar operaciones idénticas para cada navegación que esté incluida varias veces:

using (var context = new BloggingContext())


{
var filteredBlogs = [Link]
.Include(blog => [Link](post => [Link] == 1))
.ThenInclude(post => [Link])
.Include(blog => [Link](post => [Link] == 1))
.ThenInclude(post => [Link](postTag => [Link]).Skip(3))
.ToList();
}

Cau t i on

En el caso de las consultas de seguimiento, los resultados de Inclusión filtrada pueden ser inesperados debido a
la corrección de la navegación. Todas las entidades pertinentes que se hayan consultado anteriormente y que se
hayan almacenado en el seguimiento de cambios estarán presentes en los resultados de la consulta Include
filtrada aunque no cumplan los requisitos del filtro. Valore la posibilidad de usar consultas NoTracking o de
volver a crear el elemento DbContext al emplear Inclusión filtrada en esas situaciones.
Ejemplo:
var orders = [Link](o => [Link] > 1000).ToList();

// customer entities will have references to all orders where Id > 1000, rather than > 5000
var filtered = [Link](c => [Link](o => [Link] > 5000)).ToList();

NOTE
En el caso de las consultas de seguimiento, se considera que se ha cargado la navegación en la que se aplicó la inclusión
filtrada. Esto significa que EF Core no intentará volver a cargar sus valores mediante la carga explícita ni la carga diferida,
aunque aún falten algunos elementos.

Inclusión en tipos derivados


Puede incluir datos relacionados provenientes de la navegación que se define 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 de datos relacionados
12/03/2021 • 2 minutes to read • Edit Online

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 la consulta
materializa una entidad, EF Core establece automáticamente las propiedades de navegación de la entidad recién
cargada para hacer referencia a cualquier entidad ya cargada. También establece las propiedades de navegación
de las entidades ya cargadas para hacer referencia a la entidad recientemente 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 aplicar operadores adicionales en la consulta. Por ejemplo, aplicar 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 de datos relacionados
12/03/2021 • 4 minutes to read • Edit Online

Carga diferida con servidores proxy


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


}

WARNING
La carga diferida puede provocar recorridos de ida y vuelta a la base de datos adicionales e innecesarios (el problema
conocido como N+1), y conviene tener cuidado para evitar esto. Consulte la sección sobre el rendimiento para obtener
más información.

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

Este método 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 el
impacto al depender de él es poco significativo. Sin embargo, para evitar depender por completo de ningún
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
12/03/2021 • 2 minutes to read • Edit Online

Puesto que EF Core corregirá automáticamente las propiedades de navegación, puede terminar con ciclos en el
gráfico 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 estos 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 gráfico de
objetos. Esta configuración se lleva a cabo 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 divididas
12/03/2021 • 5 minutes to read • Edit Online

Consultas únicas
En las bases de datos relacionales, todas las entidades relacionadas se cargan mediante la introducción de
instrucciones JOIN en consultas únicas.

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 [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]

Si un blog típico tiene varias entradas relacionadas, las filas de estas entradas duplicarán la información del
blog, lo que genera un problema conocido como "explosión cartesiana". A medida que se cargan más relaciones
uno a varios, la cantidad de datos duplicados puede crecer y afectar negativamente al rendimiento de la
aplicación.

Consultas divididas
NOTE
Esta característica se incluyó por primera vez en EF Core 5.0. Solo funciona cuando se usa Include . Este problema realiza
un seguimiento de la compatibilidad con la consulta dividida al cargar datos relacionados en la proyección sin Include .

EF le permite especificar que una consulta LINQ determinada se debe dividir en varias consultas SQL. En lugar
de instrucciones JOIN, las consultas divididas generan una consulta SQL adicional por cada navegación de
colección incluida:

using (var context = new BloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.AsSplitQuery()
.ToList();
}

Generará la consulta SQL siguiente:

SELECT [b].[BlogId], [b].[OwnerId], [b].[Rating], [b].[Url]


FROM [Blogs] AS [b]
ORDER BY [b].[BlogId]

SELECT [p].[PostId], [p].[AuthorId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title], [b].[BlogId]


FROM [Blogs] AS [b]
INNER JOIN [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId]
NOTE
Las entidades relacionadas uno a uno se cargan siempre mediante instrucciones JOIN en la misma consulta, ya que esto
no afecta al rendimiento.

Habilitación de consultas divididas globalmente


También puede configurar consultas divididas como el valor predeterminado para el contexto de la aplicación:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


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

Cuando las consultas divididas están configuradas como valor predeterminado, todavía es posible configurar
consultas específicas para que se ejecuten como consultas únicas:

using (var context = new SplitQueriesBloggingContext())


{
var blogs = [Link]
.Include(blog => [Link])
.AsSingleQuery()
.ToList();
}

EF Core usa el modo de consulta única de forma predeterminada si no existe ninguna configuración. Dado que
puede producir problemas de rendimiento, EF Core genera una advertencia cuando se cumplen las condiciones
siguientes:
EF Core detecta que la consulta carga varias colecciones.
El usuario no ha configurado el modo de división de consultas globalmente.
El usuario no ha usado el operador AsSingleQuery / AsSplitQuery en la consulta.
Para desactivar la advertencia, configure el modo de división de consultas globalmente o en el nivel de consulta
en un valor adecuado.

Características de las consultas divididas


Si bien las consultas divididas evitan las incidencias de rendimiento asociadas a las instrucciones JOIN y la
explosión cartesiana, también existen algunas desventajas:
Aunque la mayoría de las bases de datos garantizan la coherencia de los datos en las consultas únicas, no
sucede lo mismo en el caso de varias consultas. Si la base de datos se actualiza de forma simultánea al
ejecutar las consultas, es posible que los datos resultantes no sean coherentes. Esto se puede mitigar
mediante el encapsulado de las consultas en una transacción serializable o de instantáneas, aunque esto
puede generar incidencias propias de rendimiento. Para obtener más información, vea la documentación de
la base de datos.
Cada consulta implica actualmente un ciclo adicional de ida y vuelta de red a la base de datos. Varios ciclos
de ida y vuelta de red pueden degradar el rendimiento, sobre todo si la latencia en la base de datos es alta
(por ejemplo, servicios en la nube).
Aunque algunas bases de datos permiten el consumo de los resultados de varias consultas al mismo tiempo
(SQL Server con MARS, SQLite), la mayoría de ellas solo permiten sola consulta activa en un momento dado.
Por lo tanto, todos los resultados de las consultas anteriores deben almacenarse en búfer en la memoria de
la aplicación antes de ejecutar consultas posteriores, lo que llevará a un aumento en los requisitos de
memoria.
Por desgracia, no hay una estrategia para cargar entidades relacionadas que se ajuste a todos los escenarios.
Tenga en cuenta las ventajas y desventajas de las consultas únicas y divididas, para seleccionar la que mejor se
ajuste a sus necesidades.
Operadores de consulta complejos
07/04/2021 • 14 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.

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]

Además, si los selectores de claves 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 new { Id = (int?)[Link], [Link] }
equals new { Id = [Link], Caption = "SN" }
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] AND ([p0].[Caption] = N'SN'))

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]

GroupBy
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


Media
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.
Consultas SQL sin formato
07/04/2021 • 10 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();

Pasar 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 FromSqlRaw o ExecuteSqlRaw una cadena concatenada o interpolada ( $"" ) 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();

NOTE
Orden de los parámetros Entity Framework Core pasa parámetros basados en el orden de la matriz de
SqlParameter[] . Al pasar varios SqlParameter , el orden de la cadena SQL debe coincidir con el orden de los
parámetros en la definición del procedimiento almacenado. Si no lo hace, al ejecutar el procedimiento pueden producirse
excepciones de conversión de tipos o un comportamiento inesperado.

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 = "Lorem ipsum";

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 = "Lorem ipsum";

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 = "Lorem ipsum";

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.
Funciones de base de datos
12/03/2021 • 9 minutes to read • Edit Online

Las funciones de base de datos son el equivalente a los métodos de C# en las bases de datos. Se puede invocar
una función de base de datos sin ningún parámetro o con varios, y esta calcula el resultado en función de los
valores de los parámetros. La mayoría de las bases de datos, que utilizan SQL para realizar consultas, admiten
las funciones de base de datos. Por lo tanto, la salida SQL generada por la traducción de consultas de EF Core
también permite invocar funciones de base de datos. Los métodos de C# no tienen por qué traducirse
estrictamente en funciones de base de datos en EF Core.
Un método de C# puede no tener una función de base de datos equivalente.
El método [Link] se traduce en una comprobación de valores NULL y en una
comparación con una cadena vacía en la base de datos, y no en una función.
El método [Link](String, StringComparison) no tiene una base de datos equivalente, ya que la
comparación de cadenas no puede representarse ni imitarse fácilmente en una base de datos.
Una función de base de datos puede no tener un método de C# equivalente. El operador ?? en C#, que no
tiene ningún método, se traduce en la función COALESCE en la base de datos.

Tipos de funciones de base de datos


La generación de la salida SQL de EF Core admite un subconjunto de funciones que se pueden usar en las bases
de datos. Esta limitación proviene de la capacidad de representar una consulta en LINQ para la función de base
de datos determinada. Además, cada base de datos tiene una compatibilidad variable con las funciones de base
de datos, por lo que EF Core proporciona un subconjunto común. Un proveedor de bases de datos es libre de
ampliar la generación de salidas SQL de EF Core para admitir más patrones. A continuación, se indican los tipos
de funciones de base de datos que EF Core admite e identifica de forma exclusiva. Estos términos también
ayudan a comprender qué traducciones se integran con los proveedores de EF Core.
Funciones integradas frente a las definidas por el usuario
Las funciones integradas incluyen las bases de datos predefinidas, pero las definidas por el usuario las define
explícitamente el usuario en la base de datos. Cuando EF Core traduce las consultas para usar funciones de base
de datos, usa funciones integradas para asegurarse de que la función siempre está disponible en la base de
datos. Es necesario distinguir las funciones integradas en algunas bases de datos para generar las salidas SQL
correctamente. Por ejemplo, SqlServer requiere que se invoquen todas las funciones definidas por el usuario
con un nombre calificado con el esquema. Sin embargo, las funciones integradas en SqlServer no tienen un
esquema. PostgreSQL define la función integrada en el esquema public , pero se puede invocar con nombres
calificados con el esquema.
Comparación entre las funciones de agregado, escalares y con valores de tabla
Las funciones escalares adoptan valores escalares (como enteros o cadenas) como parámetros y devuelven
un valor escalar como resultado. Las funciones escalares se pueden usar en cualquier parte de SQL donde se
pueda pasar un valor escalar.
Las funciones de agregado adoptan un flujo de valores escalares como parámetros y devuelven un valor
escalar como resultado. Las funciones de agregado se aplican en todo el conjunto de resultados de consulta
o en un grupo de valores generados al aplicar el operador GROUP BY .
Las funciones con valores de tabla adoptan valores escalares como parámetros y devuelven un flujo de filas
como resultado. Las funciones con valores de tabla se utilizan como un origen de tabla en la cláusula FROM .
Funciones niládicas
Las funciones niládicas son funciones de base de datos especiales que no tienen ningún parámetro y deben
invocarse sin paréntesis. Son similares al acceso a propiedades o campos en una instancia de C#. Las funciones
niládicas se diferencian de las funciones sin parámetros, ya que las últimas requieren paréntesis vacíos. No hay
ningún nombre especial para las funciones de base de datos que siempre requieran paréntesis. Otro
subconjunto de funciones de base de datos basado en el número de parámetros son las funciones variádicas.
Las funciones variádicas pueden adoptar un número variable de parámetros cuando se invocan.

Asignaciones de funciones de base de datos en EF Core


EF Core admite tres formas diferentes de asignación entre las funciones de C# y las funciones de base de datos.
Asignación de funciones integradas
De forma predeterminada, los proveedores de EF Core proporcionan asignaciones para varias funciones
integradas mediante tipos primitivos. Por ejemplo, [Link]() se traduce en LOWER en SqlServer. Esta
funcionalidad permite a los usuarios escribir consultas en LINQ sin problemas. Normalmente, se ofrece una
traducción en la base de datos que genera el mismo resultado que la función de C# proporciona en el lado
cliente. A veces, para lograrlo, la traducción real puede ser algo más complicada que una función de base de
datos. En algunos escenarios, también proporcionamos la traducción más adecuada en lugar de la semántica de
C# coincidente. La misma característica también se usa para proporcionar traducciones comunes para algunos
de los accesos de los miembros de C#. Por ejemplo, [Link] se traduce en LEN en SqlServer. Además de
los proveedores, los escritores de complementos también pueden agregar traducciones adicionales. Esta
extensibilidad es útil cuando los complementos agregan compatibilidad con más tipos, como los primitivos, y
desean traducir métodos a partir de ellos.
Asignación de [Link]
Puesto que no todas las funciones de base de datos tienen funciones de C# equivalentes, los proveedores de
EF Core tienen métodos de C# especiales para invocar determinadas funciones de base de datos. Estos métodos
se definen como métodos de extensión mediante [Link] que se usarán en consultas LINQ. Estos
métodos son específicos del proveedor, ya que están estrechamente relacionados con funciones de base de
datos concretas. Por lo tanto, es probable que un método que funciona para un proveedor no funcione para
ningún otro proveedor. Además, puesto que la intención de estos métodos es invocar una función de base de
datos en la consulta traducida, intentar evaluarlos en el cliente produce una excepción.
Asignación de funciones definidas por el usuario
Además de las asignaciones proporcionadas por los proveedores de EF Core, los usuarios también pueden
definir asignaciones personalizadas. Una asignación definida por el usuario amplía la traducción de la consulta
según las necesidades del usuario. Esta funcionalidad es útil cuando hay funciones definidas por el usuario en la
base de datos, que el usuario desea invocar con su consulta LINQ.

Consulte también
Asignaciones de funciones integradas de SqlServer
Asignaciones de funciones integradas de Sqlite
Asignaciones de funciones integradas de Cosmos
Asignación de funciones definidas por el usuario
12/03/2021 • 10 minutes to read • Edit Online

EF Core permite usar funciones SQL definidas por el usuario en las consultas. Para ello, las funciones deben
asignarse a un método CLR durante la configuración del modelo. Al traducir la consulta LINQ a SQL, se llama a
la función definida por el usuario en lugar de a la función CLR a la que se ha asignado.

Asignación de un método a una función SQL


Para mostrar cómo funciona la asignación de funciones definidas por el usuario, vamos a definir las siguientes
entidades:

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 Rating { get; set; }
public int BlogId { get; set; }

public Blog Blog { get; set; }


public List<Comment> Comments { get; set; }
}

public class Comment


{
public int CommentId { get; set; }
public string Text { get; set; }
public int Likes { get; set; }
public int PostId { get; set; }

public Post Post { get; set; }


}

Y la siguiente configuración del modelo:

[Link]<Blog>()
.HasMany(b => [Link])
.WithOne(p => [Link]);

[Link]<Post>()
.HasMany(p => [Link])
.WithOne(c => [Link]);

El blog puede tener muchas publicaciones, y cada publicación puede tener muchos comentarios.
Después, creamos la función definida por el usuario CommentedPostCountForBlog , que devuelve el recuento de
publicaciones con al menos un comentario para un blog determinado, en función del Id del blog:

CREATE FUNCTION [Link](@id int)


RETURNS int
AS
BEGIN
RETURN (SELECT COUNT(*)
FROM [Posts] AS [p]
WHERE ([p].[BlogId] = @id) AND ((
SELECT COUNT(*)
FROM [Comments] AS [c]
WHERE [p].[PostId] = [c].[PostId]) > 0));
END

Para usar esta función en EF Core, definimos el siguiente método CLR y lo asignamos a la función definida por el
usuario:

public int ActivePostCountForBlog(int blogId)


=> throw new NotSupportedException();

El cuerpo del método CLR no es importante. No se invocará el método del lado cliente a menos que EF Core no
pueda traducir sus argumentos. Si los argumentos se pueden traducir, EF Core solo tiene en cuenta la firma del
método.

NOTE
En este ejemplo, el método se define en DbContext , pero también se puede definir como un método estático dentro de
otras clases.

Esta definición de función ahora se puede asociar a una función definida por el usuario en la configuración del
modelo:

[Link](typeof(BloggingContext).GetMethod(nameof(ActivePostCountForBlog), new[] {
typeof(int) }))
.HasName("CommentedPostCountForBlog");

De forma predeterminada, EF Core intenta asignar la función CLR a una función con el mismo nombre definida
por el usuario. Si los nombres difieren, se puede usar HasName para proporcionar el nombre correcto para la
función definida por el usuario a la que queremos asignar el método.
Después, ejecutamos la siguiente consulta:

var query1 = from b in [Link]


where [Link]([Link]) > 1
select b;

Se generará esta función SQL:

SELECT [b].[BlogId], [b].[Rating], [b].[Url]


FROM [Blogs] AS [b]
WHERE [dbo].[CommentedPostCountForBlog]([b].[BlogId]) > 1

Asignación de un método a una función SQL personalizada


EF Core también permite funciones definidas por el usuario que se convierten en una función SQL específica. La
expresión SQL se proporciona mediante el método HasTranslation durante la configuración de la función
definida por el usuario.
En el ejemplo siguiente, crearemos una función que calcula la diferencia de porcentaje entre dos enteros.
El método CLR es el siguiente:

public double PercentageDifference(double first, int second)


=> throw new NotSupportedException();

La definición de la función es la siguiente:

// 100 * ABS(first - second) / ((first + second) / 2)


[Link](
typeof(BloggingContext).GetMethod(nameof(PercentageDifference), new[] { typeof(double), typeof(int)
}))
.HasTranslation(
args =>
new SqlBinaryExpression(
[Link],
new SqlConstantExpression(
[Link](100),
new IntTypeMapping("int", DbType.Int32)),
new SqlBinaryExpression(
[Link],
new SqlFunctionExpression(
"ABS",
new SqlExpression[]
{
new SqlBinaryExpression(
[Link],
[Link](),
[Link](1).First(),
[Link]().Type,
[Link]().TypeMapping)
},
nullable: true,
argumentsPropagateNullability: new[] { true, true },
type: [Link]().Type,
typeMapping: [Link]().TypeMapping),
new SqlBinaryExpression(
[Link],
new SqlBinaryExpression(
[Link],
[Link](),
[Link](1).First(),
[Link]().Type,
[Link]().TypeMapping),
new SqlConstantExpression(
[Link](2),
new IntTypeMapping("int", DbType.Int32)),
[Link]().Type,
[Link]().TypeMapping),
[Link]().Type,
[Link]().TypeMapping),
[Link]().Type,
[Link]().TypeMapping));

Una vez definida la función, se puede usar en la consulta. En lugar de llamar a la función de base de datos,
EF Core traducirá el cuerpo del método directamente a SQL basándose en el árbol de expresión SQL construido
a partir del método HasTranslation. La siguiente consulta LINQ:
var query2 = from p in [Link]
select [Link]([Link], 3);

Produce el siguiente SQL:

SELECT 100 * (ABS(CAST([p].[BlogId] AS float) - 3) / ((CAST([p].[BlogId] AS float) + 3) / 2))


FROM [Posts] AS [p]

Configuración de la nulabilidad de funciones definidas por el usuario


en función de sus argumentos
Si la función definida por el usuario solo puede devolver null cuando uno o varios de sus argumentos son
null , EF Core proporciona una manera de especificarlo, lo que da lugar a código SQL de mayor rendimiento.
Para ello, se puede agregar una llamada a PropagatesNullability() a la configuración del modelo de
parámetros de función pertinente.
Para ilustrarlo, defina la función de usuario ConcatStrings :

CREATE FUNCTION [dbo].[ConcatStrings] (@prm1 nvarchar(max), @prm2 nvarchar(max))


RETURNS nvarchar(max)
AS
BEGIN
RETURN @prm1 + @prm2;
END

Y dos métodos CLR que se asignan a la función:

public string ConcatStrings(string prm1, string prm2)


=> throw new InvalidOperationException();

public string ConcatStringsOptimized(string prm1, string prm2)


=> throw new InvalidOperationException();

La configuración del modelo (dentro del método OnModelCreating ) es la siguiente:

modelBuilder
.HasDbFunction(typeof(BloggingContext).GetMethod(nameof(ConcatStrings), new[] { typeof(string),
typeof(string) }))
.HasName("ConcatStrings");

[Link](
typeof(BloggingContext).GetMethod(nameof(ConcatStringsOptimized), new[] { typeof(string), typeof(string)
}),
b =>
{
[Link]("ConcatStrings");
[Link]("prm1").PropagatesNullability();
[Link]("prm2").PropagatesNullability();
});

La primera función se configura de la forma habitual. La segunda función se configura para aprovechar las
ventajas de la optimización de la propagación de nulabilidad, lo que proporciona más información sobre cómo
se comporta la función en relación a los parámetros NULL.
Al emitir las consultas siguientes:
var query3 = [Link](e => [Link]([Link], [Link]()) !=
"[Link]
var query4 = [Link](
e => [Link]([Link], [Link]()) != "[Link]

Se obtiene este código SQL:

SELECT [b].[BlogId], [b].[Rating], [b].[Url]


FROM [Blogs] AS [b]
WHERE ([dbo].[ConcatStrings]([b].[Url], CONVERT(VARCHAR(11), [b].[Rating])) <> N'Lorem ipsum...') OR [dbo].
[ConcatStrings]([b].[Url], CONVERT(VARCHAR(11), [b].[Rating])) IS NULL

SELECT [b].[BlogId], [b].[Rating], [b].[Url]


FROM [Blogs] AS [b]
WHERE ([dbo].[ConcatStrings]([b].[Url], CONVERT(VARCHAR(11), [b].[Rating])) <> N'Lorem ipsum...') OR ([b].
[Url] IS NULL OR [b].[Rating] IS NULL)

La segunda consulta no tiene que volver a evaluar la función para probar su nulabilidad.

NOTE
Esta optimización únicamente se debe usar si la función solo puede devolver null cuando sus parámetros son null .

Asignación de una función consultable a una función con valores de


tabla
EF Core también admite la asignación a una función con valores de tabla mediante un método CLR definido por
el usuario que devuelve un objeto IQueryable de tipos de entidad, lo que permite a EF Core asignar funciones
con valores de tabla (TVF) con parámetros. El proceso es similar a la asignación de una función escalar definida
por el usuario a una función SQL: necesitamos una TVF en la base de datos, una función CLR que se usa en las
consultas LINQ y una asignación entre las dos.
Como ejemplo, usaremos una función con valores de tabla que devuelve todas las publicaciones que tienen al
menos un comentario que cumple un umbral de "Me gusta" determinado:

CREATE FUNCTION [Link](@likeThreshold int)


RETURNS TABLE
AS
RETURN
(
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]
FROM [Posts] AS [p]
WHERE (
SELECT COUNT(*)
FROM [Comments] AS [c]
WHERE ([p].[PostId] = [c].[PostId]) AND ([c].[Likes] >= @likeThreshold)) > 0
)

La firma del método CLR es la siguiente:

public IQueryable<Post> PostsWithPopularComments(int likeThreshold)


=> FromExpression(() => PostsWithPopularComments(likeThreshold));
TIP
La llamada a FromExpression en el cuerpo de la función CLR permite usar la función en lugar de un objeto DbSet
normal.

A continuación se muestra la asignación:

[Link]<Post>().ToTable("Posts");
[Link](typeof(BloggingContext).GetMethod(nameof(PostsWithPopularComments), new[] {
typeof(int) }));

Cau t i on

Hasta que se corrija la incidencia 23408, la asignación a un objeto IQueryable de tipos de entidad reemplaza la
asignación predeterminada a una tabla para el objeto DbSet. Si es necesario, por ejemplo, cuando la entidad
tiene clave, la asignación a la tabla se debe especificar explícitamente mediante el método ToTable .

NOTE
La función consultable debe estar asignada a una función con valores de tabla y no puede usar el método
HasTranslation .

Cuando se asigna la función, se ejecuta la siguiente consulta:

var likeThreshold = 3;
var query5 = from p in [Link](likeThreshold)
orderby [Link]
select p;

Esta consulta genera lo siguiente:

SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Rating], [p].[Title]


FROM [dbo].[PostsWithPopularComments](@likeThreshold) AS [p]
ORDER BY [p].[Rating]
Filtros de consulta global
07/04/2021 • 9 minutes to read • Edit Online

Los filtros de consulta global son predicados de consulta LINQ que se aplican a los tipos de entidad en el
modelo de metadatos (normalmente en OnModelCreating ). Un predicado de consulta es una expresión booleana
que se pasa normalmente al operador de consulta Where de LINQ. EF Core aplica estos filtros automáticamente
a cualquier consulta LINQ que implique esos tipos de entidad. EF Core también los aplica a los tipos de entidad, a
los que se hace referencia de forma indirecta mediante el uso de la propiedad de navegación o Include. 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 eliminación temporal en un modelo sencillo 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


{
#pragma warning disable IDE0051, CS0169 // Remove unused private members
private string _tenantId;
#pragma warning restore IDE0051, CS0169 // Remove unused private members

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 Blog Blog { get; set; }


}

Tenga en cuenta la declaración de un campo _tenantId en la entidad Blog . Este campo se usará para asociar
cada instancia de blog a 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 ha eliminado 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 .

[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#).

Uso de navegaciones
También se pueden usar las navegaciones en la definición de filtros de consulta global. El uso de navegaciones
en el filtro de consulta hará que los filtros de consulta se apliquen de forma recursiva. Cuando EF Core expande
las navegaciones que se usan en los filtros de consulta, también aplica los filtros de consulta definidos en las
entidades a las que se hace referencia.
Para ilustrar esto, configure los filtros de consulta en OnModelCreating de la siguiente manera: [!code-
csharpMain]
A continuación, consulte todas las entidades Blog : [!code-csharpMain]
Esta consulta genera el siguiente código SQL, que aplica los filtros de consulta definidos para las entidades
Blog y Post :

SELECT [b].[BlogId], [b].[Name], [b].[Url]


FROM [Blogs] AS [b]
WHERE (
SELECT COUNT(*)
FROM [Posts] AS [p]
WHERE ([p].[Title] LIKE N'%fish%') AND ([b].[BlogId] = [p].[BlogId])) > 0

NOTE
Actualmente EF Core no detecta ciclos en las definiciones de filtros de consulta global, por lo que debe tener cuidado al
definirlas. Si se especifican incorrectamente, los ciclos pueden provocar bucles infinitos durante la traslación de consultas.

Acceso a una entidad con filtro de consultas mediante la navegación


necesaria
Cau t i on

El uso de la navegación necesaria para acceder a la entidad que tiene definido un filtro de consulta global puede
producir resultados inesperados.
La navegación necesaria espera que la entidad relacionada esté siempre presente. Si el filtro de consulta filtra la
entidad relacionada necesaria, es posible que la entidad primaria no esté en el resultado. Por lo tanto, puede que
en el resultado obtenga menos elementos de los esperados.
Para ilustrar el problema, podemos usar las entidades Blog y Post especificadas anteriormente y el siguiente
método OnModelCreating :

[Link]<Blog>().HasMany(b => [Link]).WithOne(p => [Link]).IsRequired();


[Link]<Blog>().HasQueryFilter(b => [Link]("fish"));

El modelo se puede inicializar con los siguientes datos:

[Link](
new Blog
{
Url = "[Link]
Posts = new List<Post>
{
new Post { Title = "Fish care 101" },
new Post { Title = "Caring for tropical fish" },
new Post { Title = "Types of ornamental fish" }
}
});

[Link](
new Blog
{
Url = "[Link]
Posts = new List<Post>
{
new Post { Title = "Cat care 101" },
new Post { Title = "Caring for tropical cats" },
new Post { Title = "Types of ornamental cats" }
}
});

Se puede observar el problema al ejecutar dos consultas:

var allPosts = [Link]();


var allPostsWithBlogsIncluded = [Link](p => [Link]).ToList();

Con la configuración anterior, la primera consulta devuelve las 6 Post ; sin embargo, la segunda consulta solo
devuelve 3. Esta falta de coincidencia se produce porque el método Include de la segunda consulta carga las
entidades Blog relacionadas. Dado que se requiere la navegación entre Blog y Post , EF Core utiliza
INNER JOIN al construir la consulta:

SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[IsDeleted], [p].[Title], [t].[BlogId], [t].[Name],


[t].[Url]
FROM [Posts] AS [p]
INNER JOIN (
SELECT [b].[BlogId], [b].[Name], [b].[Url]
FROM [Blogs] AS [b]
WHERE [b].[Url] LIKE N'%fish%'
) AS [t] ON [p].[BlogId] = [t].[BlogId]

El uso de INNER JOIN filtra todos los valores Post cuyos Blog relacionados se han quitado mediante un filtro
de consulta global.
Se puede solucionar mediante el uso de la navegación opcional, en lugar de la necesaria. De este modo, la
primera consulta se mantiene igual que antes, pero la segunda consulta generará LEFT JOIN y devolverá 6
resultados.

[Link]<Blog>().HasMany(b => [Link]).WithOne(p => [Link]).IsRequired(false);


[Link]<Blog>().HasQueryFilter(b => [Link]("fish"));

Un enfoque alternativo consiste en especificar filtros coherentes en las entidades Blog y Post . De este modo,
los filtros coincidentes se aplican tanto a Blog como a Post . Las Post que podrían acabar en un estado
inesperado se quitan y ambas consultas devuelven 3 resultados.

[Link]<Blog>().HasMany(b => [Link]).WithOne(p => [Link]).IsRequired();


[Link]<Blog>().HasQueryFilter(b => [Link]("fish"));
[Link]<Post>().HasQueryFilter(p => [Link]("fish"));

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
07/04/2021 • 2 minutes to read • Edit Online

Las etiquetas de consulta ayudan 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()
:

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

var myLocation = new Point(1, 2);


var nearestPeople = (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) [p].[Id], [p].[Location]


FROM [People] AS [p]
ORDER BY [p].[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:

private static IQueryable<Person> GetNearestPeople(SpatialContext context, Point myLocation)


=> from f in [Link]("GetNearestPeople")
orderby [Link](myLocation) descending
select f;

private static IQueryable<T> Limit<T>(IQueryable<T> source, int limit) =>


[Link]("Limit").Take(limit);

La siguiente consulta:

var results = Limit(GetNearestPeople(context, new Point(1, 2)), 25).ToList();

Se traduce en:

-- GetNearestPeople

-- Limit

SELECT TOP(@__p_1) [p].[Id], [p].[Location]


FROM [People] AS [p]
ORDER BY [p].[Location].STDistance(@__myLocation_0) DESC

También es posible utilizar cadenas de varias líneas como etiquetas de consulta. Por ejemplo:
var results = Limit(GetNearestPeople(context, new Point(1, 2)), 25).TagWith(
@"This is a multi-line
string").ToList();

Produce el siguiente SQL:

-- GetNearestPeople

-- Limit

-- This is a multi-line
-- string

SELECT TOP(@__p_1) [p].[Id], [p].[Location]


FROM [People] AS [p]
ORDER BY [p].[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.
Semántica de valores NULL en las consultas
12/03/2021 • 7 minutes to read • Edit Online

Introducción
Las bases de datos SQL operan en una lógica de tres valores ( true , false , null ) al realizar comparaciones, en
lugar de la lógica booleana de C#. Al traducir consultas LINQ a SQL, EF Core intenta compensar la diferencia
mediante la introducción de comprobaciones de valores NULL adicionales para algunos elementos de la
consulta. Para ilustrarlo, se definirá la siguiente entidad:

public class NullSemanticsEntity


{
public int Id { get; set; }
public int Int { get; set; }
public int? NullableInt { get; set; }
public string String1 { get; set; }
public string String2 { get; set; }
}

Y se emitirán varias consultas:

var query1 = [Link](e => [Link] == [Link]);


var query2 = [Link](e => [Link] == [Link]);
var query3 = [Link](e => [Link] != [Link]);
var query4 = [Link](e => e.String1 == e.String2);
var query5 = [Link](e => e.String1 != e.String2);

Las dos primeras consultas producen comparaciones simples. En la primera consulta, las dos columnas no
aceptan valores NULL, por lo que no se necesitan comprobaciones de valores NULL. En la segunda consulta,
NullableInt podría contener null , pero Id no acepta valores NULL; la comparación de null con los
resultados no NULL da como resultado null , que se filtraría mediante la operación WHERE . Por tanto, no se
necesita ningún término adicional.

SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]


FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[Int]

SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]


FROM [Entities] AS [e]
WHERE [e].[Id] = [e].[NullableInt]

La tercera consulta introduce una comprobación de valores NULL. Cuando NullableInt es null , la
comparación Id <> NullableInt devuelve null , que se filtraría mediante la operación WHERE . Pero desde el
punto de vista de la lógica booleana, este caso se debe devolver como parte del resultado. Por tanto, EF Core
agrega la comprobación necesaria para garantizarlo.

SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]


FROM [Entities] AS [e]
WHERE ([e].[Id] <> [e].[NullableInt]) OR [e].[NullableInt] IS NULL

La cuarta y quinta consultas muestran el patrón cuando las dos columnas aceptan valores NULL. Merece la pena
mencionar que la operación <> genera una consulta más complicada (y potencialmente más lenta) que la
operación == .

SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]


FROM [Entities] AS [e]
WHERE ([e].[String1] = [e].[String2]) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL)

SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]


FROM [Entities] AS [e]
WHERE (([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].
[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)

Tratamiento de valores que admiten un valor NULL en las funciones


Muchas funciones de SQL solo pueden devolver un resultado null si algunos de sus argumentos son null .
EF Core aprovecha esto para generar consultas más eficaces. En la consulta siguiente se muestra la
optimización:

var query = [Link](e => [Link](0, [Link]) == null);

El código SQL generado es el siguiente (no es necesario evaluar la función SUBSTRING , ya que solo será NULL
cuando alguno de los argumentos sea NULL):

SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]


FROM [Entities] AS [e]
WHERE [e].[String1] IS NULL OR [e].[String2] IS NULL

La optimización también se puede usar para funciones definidas por el usuario. Vea la página de asignación de
funciones definidas por el usuario para obtener más detalles.

Escritura de consultas eficaces


La comparación de las columnas que no aceptan valores NULL es más sencilla y rápida que la de las
columnas que aceptan valores NULL. Siempre que sea posible, considere la posibilidad de marcar las
columnas como que no admiten valores NULL.
La comprobación de igualdad ( == ) es más sencilla y rápida que la de no igualdad ( != ), ya que la
consulta no necesita distinguir entre el resultado null y false . Use la comparación de igualdad
siempre que sea posible. Sin embargo, simplemente la negación de la comparación == es en realidad lo
mismo que != , por lo que no se mejora el rendimiento.
En algunos casos, es posible simplificar una comparación compleja si se filtran de forma explícita los
valores null de una columna; por ejemplo, cuando no hay ningún valor null o estos valores no son
relevantes en el resultado. Considere el ejemplo siguiente:

var query1 = [Link](e => e.String1 != e.String2 || [Link] == [Link]);


var query2 = [Link](
e => e.String1 != null && e.String2 != null && (e.String1 != e.String2 || [Link] ==
[Link]));

Estas consultas generan el siguiente código SQL:


SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]
FROM [Entities] AS [e]
WHERE ((([e].[String1] <> [e].[String2]) OR ([e].[String1] IS NULL OR [e].[String2] IS NULL)) AND ([e].
[String1] IS NOT NULL OR [e].[String2] IS NOT NULL)) OR ((CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].
[String2]) AS int)) OR ([e].[String1] IS NULL AND [e].[String2] IS NULL))

SELECT [e].[Id], [e].[Int], [e].[NullableInt], [e].[String1], [e].[String2]


FROM [Entities] AS [e]
WHERE ([e].[String1] IS NOT NULL AND [e].[String2] IS NOT NULL) AND (([e].[String1] <> [e].[String2]) OR
(CAST(LEN([e].[String1]) AS int) = CAST(LEN([e].[String2]) AS int)))

En la segunda consulta, los resultados null se filtran explícitamente de la columna String1 . EF Core puede
tratar de forma segura la columna String1 como que no acepta valores NULL durante la comparación, lo que
da lugar a una consulta más sencilla.

Uso de la semántica relacional de valores NULL


Es posible deshabilitar la compensación de la comparación de valores NULL y usar directamente la semántica
relacional de valores NULL. Esto se puede hacer mediante la llamada al método UseRelationalNulls(true) del
generador de opciones dentro del método OnConfiguring :

new SqlServerDbContextOptionsBuilder(optionsBuilder).UseRelationalNulls();

WARNING
Cuando se usa la semántica relacional de valores NULL, las consultas LINQ ya no tienen el mismo significado que en C# y
pueden producir resultados diferentes de los esperados. Hay que ser prudentes al usar este modo.
Funcionamiento de las consultas
12/03/2021 • 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.

NOTE
Este artículo no está actualizado y algunas de sus partes deben actualizarse para recoger los cambios que se produjeron
en el diseño de la canalización de consultas. Si tiene dudas sobre cualquier comportamiento mencionado aquí, formule
una pregunta.

La duración de una consulta


La descripción siguiente es 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 de 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
Uso de operadores como ToList , ToArray , Single y Count , o 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
12/03/2021 • 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
07/04/2021 • 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](e => [Link]).Last();
[Link](lastBlog);

[Link]();
}
Guardado de datos relacionados
07/04/2021 • 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
07/04/2021 • 31 minutes to read • Edit Online

Entity Framework Core (EF Core) representa relaciones mediante claves externas. Una entidad con una clave
externa es la entidad secundaria o dependiente en la relación. El valor de clave externa de esta entidad debe
coincidir con el de clave principal (o con uno de clave alternativa) de la entidad de seguridad o primaria
relacionada.
Si se elimina la entidad de seguridad o primaria, los valores de clave externa de las entidades dependientes o
secundarias dejarán de coincidir con la clave principal o la clave alternativa de cualquier entidad de seguridad o
primaria. Este estado no es válido y producirá una infracción de la restricción referencial en la mayoría de las
bases de datos.
Hay dos maneras de evitar esta infracción de la restricción referencial:
1. Establecer los valores de CE (clave externa) en NULL
2. Eliminar también las entidades dependientes o secundarias
La primera opción solo es válida para las relaciones opcionales en las que la propiedad de clave externa, así
como la columna de la base de datos a la que está asignada, deben admitir valores NULL.
La segunda opción es válida para cualquier tipo de relación y se conoce como "eliminación en cascada".

TIP
En este documento se describen las eliminaciones en cascada (y la eliminación de entidades huérfanas) desde el punto de
vista de la actualización de una base de datos. Se hace un uso intensivo de los conceptos introducidos en los artículos
Herramienta de seguimiento de cambios en EF Core y Cambio de las claves externas y las navegaciones. Debe entender
perfectamente estos conceptos antes de abordar el material de este artículo.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Cuándo se producen comportamientos en cascada


Las eliminaciones en cascada son necesarias cuando una entidad dependiente o secundaria ya no se puede
asociar a su entidad de seguridad o a su entidad primaria actual. Esto puede ocurrir porque la entidad de
seguridad o primaria se elimina, o bien cuando la entidad de seguridad o primaria todavía existe, pero la entidad
dependiente o secundaria ya no está asociada a ella.
Eliminación de una entidad de seguridad o primaria
Considere este modelo sencillo en el que Blog es la entidad de seguridad o primaria en una relación con Post ,
que es la entidad dependiente o secundaria. [Link] es una propiedad de clave externa cuyo valor debe
coincidir con el de la clave principal [Link] de la publicación a la que pertenece el blog.
public class Blog
{
public int Id { get; set; }

public string Name { get; set; }

public IList<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 int BlogId { get; set; }


public Blog Blog { get; set; }
}

Por convención, esta relación se configura como obligatoria, ya que la propiedad de clave externa [Link]
no acepta valores NULL. Las relaciones obligatorias están configuradas para usar eliminaciones en cascada de
manera predeterminada. Consulte el artículo Relaciones para obtener más información sobre las relaciones de
modelado.
Al eliminar un blog, se eliminan todas las publicaciones en cascada. Por ejemplo:

using var context = new BlogsContext();

var blog = [Link](e => [Link]).Include(e => [Link]).First();

[Link](blog);

[Link]();

SaveChanges genera el siguiente código SQL, usando SQL Server como ejemplo:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Interrupción de una relación


En lugar de eliminar el blog, podríamos interrumpir la relación entre cada publicación y su blog. Esto se puede
hacer estableciendo la propiedad de navegación de referencia [Link] en NULL para cada publicación:
using var context = new BlogsContext();

var blog = [Link](e => [Link]).Include(e => [Link]).First();

foreach (var post in [Link])


{
[Link] = null;
}

[Link]();

La relación también se puede interrumpir quitando todas las publicaciones de la navegación de colección
[Link] :

using var context = new BlogsContext();

var blog = [Link](e => [Link]).Include(e => [Link]).First();

[Link]();

[Link]();

En cualquier caso, el resultado es el mismo: el blog no se elimina, solo se eliminan las publicaciones que ya no
están asociadas a ningún blog:

-- Executed DbCommand (1ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p0='2'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [Posts]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

La eliminación de entidades que ya no están asociadas a ninguna entidad de seguridad o dependiente se


denomina "eliminación de entidades huérfanas".

TIP
La eliminación en cascada y la eliminación de entidades huérfanas están estrechamente relacionadas. Ambas dan como
resultado la eliminación de entidades dependientes o secundarias cuando se interrumpe su relación con la entidad de
seguridad o primaria requerida. En el caso de la eliminación en cascada, esta interrupción de la relación tiene lugar porque
se elimina la propia entidad de seguridad o primaria. En el caso de las entidades huérfanas, la entidad de seguridad o
primaria sigue existiendo, pero ya no está relacionada con las entidades dependientes o secundarias.

Dónde se producen los comportamientos en cascada


Los comportamientos en cascada se pueden aplicar a:
Entidades de las que la clase DbContext actual realiza un seguimiento
Entidades de la base de datos que no se han cargado en el contexto
Eliminación en cascada de entidades de las que se realiza un seguimiento
EF Core aplica siempre los comportamientos en cascada configurados a las entidades sometidas a seguimiento.
Esto significa que, si la aplicación carga todas las entidades dependientes o secundarias relevantes en la
instancia de DbContext (como se muestra en los ejemplos anteriores), los comportamientos en cascada se
aplicarán correctamente independientemente de cómo esté configurada la base de datos.

TIP
El momento exacto en que se producen los comportamientos en cascada para las entidades sometidas a seguimiento se
puede controlar mediante las propiedades [Link] y [Link].
Para obtener más información, vea el artículo Cambio de las claves externas y las navegaciones.

Eliminaciones en cascada en la base de datos


Muchos sistemas de base de datos también ofrecen comportamientos en cascada, los cuales se desencadenan
cuando se elimina una entidad en la base de datos. EF Core configura estos comportamientos en función del
comportamiento de eliminación en cascada del modelo de EF Core cuando se crea una base de datos con
migraciones de EnsureCreated o EF Core. Por ejemplo, con el modelo anterior, se crea la siguiente tabla para las
publicaciones cuando se usa SQL Server:

CREATE TABLE [Posts] (


[Id] int NOT NULL IDENTITY,
[Title] nvarchar(max) NULL,
[Content] nvarchar(max) NULL,
[BlogId] int NOT NULL,
CONSTRAINT [PK_Posts] PRIMARY KEY ([Id]),
CONSTRAINT [FK_Posts_Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [Blogs] ([Id]) ON DELETE CASCADE
);

Fíjese en que la restricción de clave externa que define la relación entre los blogs y las publicaciones está
configurada con ON DELETE CASCADE .
Si sabemos que la base de datos está configurada de esta manera, podemos eliminar un blog sin cargar primero
las publicaciones, ya que la base de datos se encargará de eliminar todas las publicaciones relacionadas con ese
blog. Por ejemplo:

using var context = new BlogsContext();

var blog = [Link](e => [Link]).First();

[Link](blog);

[Link]();

Observe que no hay ningún método Include para las publicaciones, por lo que no se cargan. En este caso,
SaveChanges eliminará solo el blog, ya que es la única entidad de la que se realiza un seguimiento:

-- Executed DbCommand (6ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

Esto produciría una excepción si la restricción de clave externa de la base de datos no estuviera configurada para
las eliminaciones en cascada. Pero, en este caso, la base de datos elimina las publicaciones porque se ha
configurado con ON DELETE CASCADE en el momento de su creación.
NOTE
Las bases de datos no suelen tener ninguna forma de eliminar automáticamente las entidades huérfanas. Esto se debe a
que EF Core representa las relaciones mediante navegaciones y claves externas, pero las bases de datos solo tienen claves
externas. Lo que significa que normalmente no es posible interrumpir una relación sin cargar ambos lados en la instancia
de DbContext.

NOTE
Actualmente, la base de datos en memoria de EF Core no admite las eliminaciones en cascada en la base de datos.

WARNING
No configure la eliminación en cascada en la base de datos cuando quiera eliminar temporalmente las entidades. Esto
podría eliminar por completo las entidades accidentalmente en lugar de eliminarlas temporalmente.

Limitaciones de los comportamientos en cascada en las bases de datos


Algunas bases de datos, especialmente las de SQL Server, tienen limitaciones en cuanto a los comportamientos
en cascada que forman ciclos. Por ejemplo, tomemos el siguiente modelo:

public class Blog


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

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

public int OwnerId { get; set; }


public Person Owner { get; set; }
}

public class Post


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

public int BlogId { get; set; }


public Blog Blog { get; set; }

public int AuthorId { get; set; }


public Person Author { get; set; }
}

public class Person


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

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

public Blog OwnedBlog { get; set; }


}

Este modelo tiene tres relaciones, todas necesarias y, por tanto, están configuradas para la eliminación en
cascada por convención:
Al eliminar un blog, se eliminarán en cascada todas las publicaciones relacionadas.
Al eliminar al autor de las publicaciones, se eliminarán en cascada sus publicaciones creadas.
Al eliminar al propietario de un blog, el blog se eliminará en cascada.
Esto es todo razonable (aunque las directivas de administración de blogs son un poco severas), pero al intentar
crear una base de datos de SQL Server con estas eliminaciones en cascada configuradas, se produce la siguiente
excepción:

[Link] (0x80131904): Si especifica la restricción FOREIGN KEY


"FK_Posts_Person_AuthorId" en la tabla "Posts", podrían producirse ciclos o múltiples rutas en cascada.
Especifique ON DELETE NO ACTION o UPDATE NO ACTION, o bien modifique otras restricciones FOREIGN
KEY.

Hay dos formas de abordar esta situación:


1. Cambiar una o varias relaciones para que no se eliminen en cascada.
2. Configurar la base de datos sin una o varias de estas eliminaciones en cascada y asegurarse de que se cargan
todas las entidades dependientes para que EF Core pueda realizar el comportamiento en cascada.
Tomando el primer enfoque con nuestro ejemplo, podríamos hacer que la relación entre el propietario y el blog
fuera opcional asignándole una propiedad de clave externa que acepte valores NULL:

public int? BlogId { get; set; }

Una relación opcional permite que el blog exista sin un propietario, lo que significa que la eliminación en
cascada ya no se configurará de manera predeterminada. También significa que ya no hay un ciclo en las
acciones en cascada y, por tanto, la base de datos se puede crear sin errores en SQL Server.
Si tomamos el segundo enfoque, podemos mantener la relación entre el propietario y el blog como obligatoria,
configurarla para la eliminación en cascada y hacer que esta configuración solo se aplique a las entidades
sometidas a seguimiento, no a la base de datos:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<Blog>()
.HasOne(e => [Link])
.WithOne(e => [Link])
.OnDelete([Link]);
}

¿Qué ocurre si se cargan tanto un propietario como el blog que posee y luego se elimina al propietario?

using var context = new BlogsContext();

var owner = [Link](e => [Link] == "ajcvickers");


var blog = [Link](e => [Link] == owner);

[Link](owner);

[Link]();

EF Core eliminará en cascada al propietario para que también se elimine el blog:


-- Executed DbCommand (8ms) [Parameters=[@p0='1'], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p0;
SELECT @@ROWCOUNT;

-- Executed DbCommand (2ms) [Parameters=[@p1='1'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [People]
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Pero el blog puede no cargarse cuando se elimina al propietario:

using var context = new BlogsContext();

var owner = [Link](e => [Link] == "ajcvickers");

[Link](owner);

[Link]();

Esto generará una excepción debido a una infracción de la restricción de clave externa en la base de datos:

[Link]: Instrucción DELETE en conflicto con la restricción REFERENCE


"FK_Blogs_People_OwnerId". El conflicto ha aparecido en la base de datos "Scratch", tabla "[Link]",
columna "OwnerId". Se terminó la instrucción.

Establecimiento de valores NULL en cascada


Las relaciones opcionales tienen propiedades de clave externa que aceptan valores NULL asignadas a columnas
de base de datos que aceptan valores NULL. Esto significa que el valor de clave externa se puede establecer en
NULL cuando la entidad de seguridad o primaria actual se elimina o cuando se interrumpe su relación con la
entidad dependiente o secundaria.
Echemos un vistazo de nuevo a los ejemplos del apartado Cuándo se producen comportamientos en cascada,
pero esta vez con una relación opcional representada por una propiedad de clave externa [Link] que
acepta valores NULL:

public int? BlogId { get; set; }

Esta propiedad de clave externa se establecerá en NULL para cada publicación cuando se elimine su blog
relacionado. Por ejemplo, tenemos este código (que es el mismo que antes):

using var context = new BlogsContext();

var blog = [Link](e => [Link]).Include(e => [Link]).First();

[Link](blog);

[Link]();

Y este código ahora dará como resultado las siguientes actualizaciones de la base de datos cuando se llame a
SaveChanges:
-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text',
CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p1='2', @p0=NULL (DbType = Int32)], CommandType='Text',


CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (1ms) [Parameters=[@p2='1'], CommandType='Text', CommandTimeout='30']


SET NOCOUNT ON;
DELETE FROM [Blogs]
WHERE [Id] = @p2;
SELECT @@ROWCOUNT;

Del mismo modo, si la relación se interrumpe con cualquiera de los ejemplos anteriores, como el siguiente:

using var context = new BlogsContext();

var blog = [Link](e => [Link]).Include(e => [Link]).First();

foreach (var post in [Link])


{
[Link] = null;
}

[Link]();

O:

using var context = new BlogsContext();

var blog = [Link](e => [Link]).Include(e => [Link]).First();

[Link]();

[Link]();

En estos casos, se actualizan las publicaciones con valores de clave externa NULL cuando se llama a
SaveChanges:

-- Executed DbCommand (2ms) [Parameters=[@p1='1', @p0=NULL (DbType = Int32)], CommandType='Text',


CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

-- Executed DbCommand (0ms) [Parameters=[@p1='2', @p0=NULL (DbType = Int32)], CommandType='Text',


CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;

Consulte el artículo Cambio de las claves externas y las navegaciones para obtener más información sobre
cómo EF Core administra las claves externas y las navegaciones a medida que se cambian sus valores.

NOTE
La corrección de relaciones como esta es el comportamiento predeterminado de Entity Framework desde la primera
versión en 2008. Antes de EF Core, este comportamiento no tenía nombre y no era posible cambiarlo. Ahora se conoce
como ClientSetNull , tal y como se describe en la sección siguiente.

Las bases de datos también se pueden configurar para establecer valores NULL en cascada de esta forma
cuando se elimina una entidad de seguridad o primaria en una relación opcional, aunque esto es mucho menos
común que el uso de eliminaciones en cascada en las bases de datos. El uso de eliminaciones en cascada y del
establecimiento de valores NULL en cascada en la base de datos al mismo tiempo producirá casi siempre ciclos
de relación cuando se use SQL Server. Consulte la sección siguiente para obtener más información sobre la
configuración de valores NULL en cascada.

Configuración de comportamientos en cascada


TIP
Asegúrese de leer las secciones anteriores antes de llegar aquí. Es posible que las opciones de configuración no tengan
sentido si no se comprende el material anterior.

Los comportamientos en cascada se configuran por relación mediante el método OnDelete en


OnModelCreating. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<Blog>()
.HasOne(e => [Link])
.WithOne(e => [Link])
.OnDelete([Link]);
}

Consulte el artículo Relaciones para obtener más información sobre la configuración de relaciones entre tipos
de entidad.
OnDelete acepta un valor de la enumeración DeleteBehavior (que es ciertamente confusa). Esta enumeración
define el comportamiento de EF Core en las entidades sometidas a seguimiento y también la configuración de
eliminación en cascada en la base de datos cuando se usa EF para crear el esquema.
Impacto en el esquema de la base de datos
En la siguiente tabla se muestra el resultado de cada valor OnDelete en la restricción de clave externa creada
por migraciones de EF Core o EnsureCreated.

DEL ET EB EH AVIO R IM PA C TO EN EL ESQ UEM A DE L A B A SE DE DATO S

Cascade ON DELETE CASCADE

Restringir ON DELETE NO ACTION

NoAction database default


DEL ET EB EH AVIO R IM PA C TO EN EL ESQ UEM A DE L A B A SE DE DATO S

SetNull ON DELETE SET NULL

ClientSetNull ON DELETE NO ACTION

ClientCascade ON DELETE NO ACTION

ClientNoAction database default

NOTE
Esta tabla es confusa y tenemos previsto revisarla en una versión futura. Consulte la incidencia n.º 21252 de GitHub.

Los comportamientos de ON DELETE NO ACTION y ON DELETE RESTRICT en las bases de datos relacionales suelen
ser idénticos o muy parecidos. A pesar de lo que puede implicar NO ACTION , ambas opciones hacen que se
apliquen las restricciones referenciales. La diferencia, si la hay, es cuándo comprueba las restricciones la base de
datos. Consulte la documentación de su base de datos para conocer las diferencias específicas entre
ON DELETE NO ACTION y ON DELETE RESTRICT en su sistema de base de datos.

Los únicos valores que generarán comportamientos en cascada en la base de datos son Cascade y SetNull . El
resto de valores configurarán la base de datos para que no se realice ningún cambio en cascada.
Impacto en el comportamiento de SaveChanges
En las tablas de las siguientes secciones se describe lo que sucede con las entidades dependientes o secundarias
cuando se elimina la entidad de seguridad o primaria, así como cuando se interrumpe su relación con las
entidades dependientes o secundarias. Cada tabla abarca uno de los siguientes casos:
Relaciones opcionales (CE que admiten valores NULL) y obligatorias (CE que no aceptan valores NULL)
Cuando la instancia de DbContext carga y realiza el seguimiento de las entidades dependientes o
secundarias, y cuando estas solo existen en la base de datos
Relación obligatoria con las entidades dependientes o secundarias cargadas

IM PA C TO A L IN T ERRUM P IR L A
IM PA C TO A L EL IM IN A R UN A EN T IDA D REL A C IÓ N C O N L A EN T IDA D DE
DEL ET EB EH AVIO R DE SEGURIDA D O P RIM A RIA SEGURIDA D O P RIM A RIA

Cascade EF Core elimina las entidades EF Core elimina las entidades


dependientes dependientes

Restringir InvalidOperationException InvalidOperationException

NoAction InvalidOperationException InvalidOperationException

SetNull Se produce una excepción Se produce una excepción


SqlException al crear la base de SqlException al crear la base de
datos datos

ClientSetNull InvalidOperationException InvalidOperationException

ClientCascade EF Core elimina las entidades EF Core elimina las entidades


dependientes dependientes
IM PA C TO A L IN T ERRUM P IR L A
IM PA C TO A L EL IM IN A R UN A EN T IDA D REL A C IÓ N C O N L A EN T IDA D DE
DEL ET EB EH AVIO R DE SEGURIDA D O P RIM A RIA SEGURIDA D O P RIM A RIA

ClientNoAction DbUpdateException InvalidOperationException

Notas:
El valor predeterminado para las relaciones obligatorias como esta es Cascade .
El uso de cualquier cosa que no sea la eliminación en cascada para las relaciones obligatorias producirá una
excepción cuando se llame a SaveChanges.
Normalmente, se trata de una excepción InvalidOperationException de EF Core, puesto que el estado
no válido se detecta en las entidades secundarias o dependientes cargadas.
ClientNoAction obliga a EF Core a no comprobar la corrección de las entidades dependientes antes
de enviarlas a la base de datos, por lo que en este caso la base de datos produce una excepción, y
SaveChanges la encapsula en una excepción DbUpdateException .
El método SetNull se rechaza al crear la base de datos, ya que la columna de clave externa no admite
valores NULL.
Como las entidades dependientes o secundarias están cargadas, EF Core siempre las elimina y nunca se
dejan para que la base de datos las elimine.
Relación obligatoria con las entidades dependientes o secundarias no cargadas

IM PA C TO A L IN T ERRUM P IR L A
IM PA C TO A L EL IM IN A R UN A EN T IDA D REL A C IÓ N C O N L A EN T IDA D DE
DEL ET EB EH AVIO R DE SEGURIDA D O P RIM A RIA SEGURIDA D O P RIM A RIA

Cascade La base de datos elimina las entidades N/D


dependientes

Restringir DbUpdateException N/D

NoAction DbUpdateException N/D

SetNull Se produce una excepción N/D


SqlException al crear la base de
datos

ClientSetNull DbUpdateException N/D

ClientCascade DbUpdateException N/D

ClientNoAction DbUpdateException N/D

Notas:
No es posible interrumpir la relación en este caso, ya que las entidades dependientes o secundarias no están
cargadas.
El valor predeterminado para las relaciones obligatorias como esta es Cascade .
El uso de cualquier cosa que no sea la eliminación en cascada para las relaciones obligatorias producirá una
excepción cuando se llame a SaveChanges.
Normalmente, se trata de una excepción DbUpdateException porque las entidades dependientes o
secundarias no están cargadas y, por lo tanto, el estado no válido solo puede detectarlo la base de
datos. Después, SaveChanges encapsula la excepción de la base de datos en una excepción
DbUpdateException .
El método SetNull se rechaza al crear la base de datos, ya que la columna de clave externa no admite
valores NULL.
Relación opcional con las entidades dependientes o secundarias cargadas

IM PA C TO A L IN T ERRUM P IR L A
IM PA C TO A L EL IM IN A R UN A EN T IDA D REL A C IÓ N C O N L A EN T IDA D DE
DEL ET EB EH AVIO R DE SEGURIDA D O P RIM A RIA SEGURIDA D O P RIM A RIA

Cascade EF Core elimina las entidades EF Core elimina las entidades


dependientes dependientes

Restringir EF Core establece las CE de las EF Core establece las CE de las


entidades dependientes en NULL entidades dependientes en NULL

NoAction EF Core establece las CE de las EF Core establece las CE de las


entidades dependientes en NULL entidades dependientes en NULL

SetNull EF Core establece las CE de las EF Core establece las CE de las


entidades dependientes en NULL entidades dependientes en NULL

ClientSetNull EF Core establece las CE de las EF Core establece las CE de las


entidades dependientes en NULL entidades dependientes en NULL

ClientCascade EF Core elimina las entidades EF Core elimina las entidades


dependientes dependientes

ClientNoAction DbUpdateException EF Core establece las CE de las


entidades dependientes en NULL

Notas:
El valor predeterminado para las relaciones opcionales como esta es ClientSetNull .
Las entidades dependientes o secundarias nunca se eliminan, a menos que se configuren los
comportamientos Cascade o ClientCascade .
El resto de valores hacen que EF Core establezca las claves externas de las entidades dependientes en NULL,
excepto el siguiente valor:
ClientNoAction indica a EF Core que no toque las claves externas de las entidades dependientes o
secundarias cuando se elimina la entidad de seguridad. En este caso, la base de datos produce una
excepción, que SaveChanges encapsula como DbUpdateException .
Relación opcional con las entidades dependientes o secundarias no cargadas

IM PA C TO A L IN T ERRUM P IR L A
IM PA C TO A L EL IM IN A R UN A EN T IDA D REL A C IÓ N C O N L A EN T IDA D DE
DEL ET EB EH AVIO R DE SEGURIDA D O P RIM A RIA SEGURIDA D O P RIM A RIA

Cascade La base de datos elimina las entidades N/D


dependientes

Restringir DbUpdateException N/D

NoAction DbUpdateException N/D

SetNull La base de datos establece las CE de N/D


las entidades dependientes en NULL
IM PA C TO A L IN T ERRUM P IR L A
IM PA C TO A L EL IM IN A R UN A EN T IDA D REL A C IÓ N C O N L A EN T IDA D DE
DEL ET EB EH AVIO R DE SEGURIDA D O P RIM A RIA SEGURIDA D O P RIM A RIA

ClientSetNull DbUpdateException N/D

ClientCascade DbUpdateException N/D

ClientNoAction DbUpdateException N/D

Notas:
No es posible interrumpir la relación en este caso, ya que las entidades dependientes o secundarias no están
cargadas.
El valor predeterminado para las relaciones opcionales como esta es ClientSetNull .
Las entidades dependientes o secundarias se deben cargar para evitar una excepción de base de datos, a
menos que la base de datos se haya configurado para realizar eliminaciones en cascada o para establecer
valores NULL en cascada.
Administrar los conflictos de simultaneidad
07/04/2021 • 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
07/04/2021 • 10 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:

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
}

Si bien todos los proveedores de bases de datos relacionales admiten transacciones, otros tipos de proveedores
pueden generar errores o no operar cuando se llama a las API de transacciones.
Puntos de retorno
NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

Cuando se invoca a SaveChanges y ya hay una transacción en curso en el contexto, EF crea automáticamente un
punto de retorno antes de guardar los datos. Los puntos de retorno son puntos dentro de una transacción de
base de datos a los que se puede revertir más tarde en caso de que ocurra un error o por cualquier otro motivo.
Si SaveChanges encuentra algún error, revierte automáticamente la transacción al punto de retorno, y la
transacción se mantiene en el mismo estado que si nunca se hubiera iniciado. Esto le permite posiblemente
corregir problemas y volver a intentar guardar, en particular cuando ocurren problemas de simultaneidad
optimista.

WARNING
Los puntos de retorno son incompatibles con los conjuntos de resultados activos múltiples de SQL Server y no se usan. Si
se produce un error durante SaveChanges , es posible que la transacción se quede en un estado desconocido.

También es posible administrar los puntos de retorno de forma manual, del mismo modo que con las
transacciones. En el ejemplo siguiente se crea un punto de retorno dentro de una transacción y se revierte
cuando se produce un error:

using var context = new BloggingContext();


using var transaction = [Link]();

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

[Link]("BeforeMoreBlogs");

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


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

[Link]();
}
catch (Exception)
{
// If a failure occurred, we rollback to the savepoint and can continue the transaction
[Link]("BeforeMoreBlogs");

// TODO: Handle failure, possibly retry inserting blogs


}

Transacción entre contextos


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.
using var connection = new SqlConnection(connectionString);
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.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 (Exception)
{
// TODO: Handle failure
}

Utilizar [Link]
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 (Exception)
{
// 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 (Exception)
{
// 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 un proveedor no implementa la compatibilidad con [Link], es
posible que las llamadas a estas API se omitan completamente. SqlClient lo admite.

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 .NET Core 2.1, la implementación de [Link] en no incluye compatibilidad con


transacciones distribuidas, por lo que no puede usar TransactionScope ni CommittableTransaction para
coordinar las transacciones entre varios administradores de recursos.
Entidades desconectadas
07/04/2021 • 14 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.
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.
Herramienta de seguimiento de cambios en
EF Core
07/04/2021 • 14 minutes to read • Edit Online

Cada instancia de DbContext realiza un seguimiento de los cambios realizados en las entidades. Estas entidades
de las que se realiza un seguimiento, a su vez, impulsan los cambios en la base de datos cuando se llama a
SaveChanges.
En este documento se presenta información general sobre el seguimiento de cambios de Entity Framework Core
(EF Core) y cómo se relaciona con las consultas y actualizaciones.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

TIP
Para simplificar, este documento utiliza métodos sincrónicos como SaveChanges, y hace referencia a ellos, en lugar de sus
equivalentes asincrónicos como SaveChangesAsync. La llamada al método asincrónico y su espera pueden sustituirse a
menos que se indique lo contrario.

Seguimiento de entidades
Se realiza un seguimiento de las instancias de entidad cuando se dan estos casos:
Se devuelven de una consulta ejecutada en la base de datos.
Se asocian explícitamente a DbContext por Add , Attach , Update o métodos similares.
Se detecta como nuevas entidades conectadas a las entidades sometidas a seguimiento existentes
El seguimiento de las instancias de entidad deja de realizarse cuando se dan estos casos:
DbContext se desecha.
La herramienta de seguimiento de cambios está desactivada (EF Core 5.0 y versiones posteriores).
Las entidades se desasocian explícitamente.
DbContext está diseñado para representar una unidad de trabajo de corta duración, como se describe en
Inicialización y configuración de DbContext. Esto significa que desechar DbContext es la forma normal detener el
seguimiento de las entidades. En otras palabras, la vigencia de DbContext debe pasar por estos pasos:
1. Creación de la instancia de DbContext
2. Seguimiento de algunas entidades
3. Realización de algunos cambios en las entidades
4. Llamada a SaveChanges para actualizar la base de datos
5. Eliminación de la instancia de DbContext
TIP
No es necesario borrar la herramienta de seguimiento de cambios ni desasociar explícitamente las instancias de la entidad
al adoptar este enfoque. Sin embargo, si necesita desasociar entidades, llamar a [Link] es más eficaz que
separar las entidades una por una.

Estados de entidad
Cada entidad está asociada a un elemento EntityState determinado:
DbContext ya no realiza el seguimiento de las entidades Detached .
Las entidades Added son nuevas y aún no se han insertado en la base de datos. Esto significa que se
insertarán cuando se llame a SaveChanges.
Las entidades Unchanged no han cambiado desde que se consultaron desde la base de datos. Todas las
entidades devueltas de las consultas se encuentran inicialmente en este estado.
Las entidades Modified han cambiado desde que se consultaron desde la base de datos. Esto significa que
se actualizarán cuando se llame a SaveChanges.
Existen entidades Deleted en la base de datos, pero se marcan para eliminarse cuando se llama a
SaveChanges.
EF Core realiza un seguimiento de los cambios en el nivel de propiedad. Por ejemplo, si solo se modifica un
valor de propiedad único, una actualización de base de datos solo cambiará ese valor. Sin embargo, las
propiedades solo se pueden marcar como modificadas cuando la propia entidad está en el estado de
modificación. (O, desde una perspectiva alternativa, el estado de modificación significa que al menos un valor de
propiedad se ha marcado como modificado).
En la tabla siguiente se resumen los diferentes estados:

ESTA DO DE L A SEGUIDO P O R EXIST E EN L A B A SE P RO P IEDA DES A C C IÓ N EN


EN T IDA D DB C O N T EXT DE DATO S M O DIF IC A DA S SAVEC H A N GES

Detached No - - -

Added Sí No - Insertar

Unchanged Sí Sí No -

Modified Sí Sí Sí Actualizar

Deleted Sí Sí - Eliminar

NOTE
Este texto utiliza términos de base de datos relacional para mayor claridad. Las bases de datos NoSQL suelen admitir
operaciones similares pero posiblemente con nombres diferentes. Consulte la documentación del proveedor de bases de
datos para obtener más información.

Seguimiento de consultas
El seguimiento de cambios de EF Core funciona mejor cuando se utiliza la misma instancia de DbContext para
consultar las entidades y actualizarlas mediante una llamada a SaveChanges. Esto se debe a que EF Core realiza
un seguimiento automático del estado de las entidades consultadas y, a continuación, detecta los cambios
realizados en estas entidades cuando se llama a SaveChanges.
Este enfoque tiene varias ventajas con respecto a realizar un seguimiento explícito de las instancias de entidad:
Es sencilla. Los estados de la entidad rara vez tienen que manipularse explícitamente: EF Core se encarga de
los cambios de estado.
Las actualizaciones solo se limitan a aquellos valores que realmente han cambiado.
Los valores de las propiedades reemplazadas se conservan y se usan según sea necesario. Esto es
especialmente importante cuando las claves externas se almacenan en el estado reemplazado.
Los valores originales de las propiedades se conservan automáticamente y se usan para mejorar la eficacia
de las actualizaciones.

Consulta y actualización simples


Por ejemplo, considere un modelo de blog o publicaciones sencillo:

public class Blog


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

public IList<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 int? BlogId { get; set; }


public Blog Blog { get; set; }
}

Podemos usar este modelo para consultar blogs y publicaciones y luego realizar algunas actualizaciones en la
base de datos:

using var context = new BlogsContext();

var blog = [Link](e => [Link]).First(e => [Link] == ".NET Blog");

[Link] = ".NET Blog (Updated!)";

foreach (var post in [Link](e => ![Link]("5.0")))


{
[Link] = [Link]("5", "5.0");
}

[Link]();

La llamada a SaveChanges da como resultado las siguientes actualizaciones de base de datos, con SQLite como
base de datos de ejemplo:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog (Updated!)' (Size = 20)],
CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p1='2' (DbType = String), @p0='Announcing F# 5.0' (Size = 17)],
CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "Title" = @p0
WHERE "Id" = @p1;
SELECT changes();

La vista de depuración de la herramienta de seguimiento de cambios es una excelente manera de visualizar las
entidades de las que se realiza el seguimiento y cuáles son sus estados. Por ejemplo, la inserción del código
siguiente en el ejemplo anterior antes de llamar a SaveChanges:

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

Se genera el siguiente código resultado:

Blog {Id: 1} Modified


Id: 1 PK
Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, {Id: 3}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Modified
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5.0' Modified Originally 'Announcing F# 5'
Blog: {Id: 1}

Observe específicamente:
La propiedad [Link] se marca como modificada (
Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog' ) y esto hace que el blog esté en el estado
Modified.
La propiedad [Link] de la publicación 2 se marca como modificada (
Title: 'Announcing F# 5.0' Modified Originally 'Announcing F# 5' ) y esto hace que esta publicación esté en
el estado Modified .
Los demás valores de propiedad de la publicación 2 no han cambiado y, por lo tanto, no se marcan como
modificados. Este es el motivo por el cual estos valores no se incluyen en la actualización de la base de datos.
La otra publicación no se modificó de ningún modo. Esta es la razón por la que todavía está en el estado
Unchanged y no se incluye en la actualización de la base de datos.

Consulta y luego inserción, actualización y eliminación


Las actualizaciones como las del ejemplo anterior se pueden combinar con inserciones y eliminaciones en la
misma unidad de trabajo. Por ejemplo:
using var context = new BlogsContext();

var blog = [Link](e => [Link]).First(e => [Link] == ".NET Blog");

// Modify property values


[Link] = ".NET Blog (Updated!)";

// Insert a new Post


[Link](
new Post
{
Title = "What’s next for [Link]?", Content = ".NET 5.0 was released recently and has come
with many..."
});

// Mark an existing Post as Deleted


var postToDelete = [Link](e => [Link] == "Announcing F# 5");
[Link](postToDelete);

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

[Link]();

En este ejemplo:
Se consultan un blog y publicaciones relacionadas desde la base de datos y se realiza su seguimiento.
Se cambia la propiedad [Link] .
Se agrega una nueva publicación a la colección de publicaciones existentes para el blog.
Una publicación existente se marca para su eliminación mediante una llamada a [Link].
Al mirar de nuevo en la vista de depuración de la herramienta de seguimiento de cambios antes de llamar a
SaveChanges se muestra cómo EF Core está realizando el seguimiento de estos cambios:

Blog {Id: 1} Modified


Id: 1 PK
Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, {Id: 3}, {Id: -2147482638}]
Post {Id: -2147482638} Added
Id: -2147482638 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 was released recently and has come with many...'
Title: 'What's next for [Link]?'
Blog: {Id: 1}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Deleted
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

Tenga en lo siguiente:
El blog se marca como Modified . Se generará una actualización de base de datos.
La publicación 2 se marca como Deleted . Se generará una eliminación de base de datos.
Una nueva publicación con un identificador temporal se asocia al blog 1 y se marca como Added . Se
generará una inserción de base de datos.
Esto da como resultado los siguientes comandos de base de datos (con SQLite) cuando se llama a SaveChanges:

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog (Updated!)' (Size = 20)],
CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 was released recently and
has come with many...' (Size = 56), @p2='What's next for [Link]?' (Size = 33)],
CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Vea Seguimiento explícito de las entidades para obtener más información sobre cómo insertar y eliminar
entidades. Consulte Detección y notificaciones de cambios para obtener más información sobre cómo EF Core
detecta automáticamente los cambios como este.

TIP
Llame a [Link]() para determinar si se han realizado cambios que provocarán que SaveChanges
realice actualizaciones en la base de datos. Si HasChanges devuelve false, SaveChanges será una operación inefectiva.
Seguimiento explícito de entidades
12/03/2021 • 46 minutes to read • Edit Online

Cada instancia de DbContext realiza un seguimiento de los cambios realizados en las entidades. Estas entidades
de las que se realiza un seguimiento, a su vez, impulsan los cambios en la base de datos cuando se llama a
SaveChanges.
El seguimiento de cambios de Entity Framework Core (EF Core) funciona mejor cuando DbContext se utiliza la
misma instancia para consultar las entidades y actualizarlas mediante una llamada a SaveChanges . Esto se debe
a que EF Core realiza un seguimiento automático del estado de las entidades consultadas y, a continuación,
detecta los cambios realizados en estas entidades cuando se llama a SaveChanges. Este enfoque se trata en
Change Tracking en EF Core.

TIP
En este documento se da por supuesto que se entienden los Estados de las entidades y los aspectos básicos del
seguimiento de cambios de EF Core. Consulte Change Tracking en EF Core para obtener más información sobre estos
temas.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

TIP
Para simplificar, en este documento se usan métodos sincrónicos y referencias, como SaveChanges en lugar de sus
equivalentes asincrónicos como SaveChangesAsync . La llamada al método asincrónico y su espera pueden sustituirse a
menos que se indique lo contrario.

Introducción
Las entidades se pueden "asociar" explícitamente a un de DbContext tal forma que el contexto realice un
seguimiento de esas entidades. Esto es especialmente útil cuando:
1. Crear nuevas entidades que se van a insertar en la base de datos.
2. Volver a adjuntar entidades desconectadas que se consultaron previamente mediante una instancia de
DbContext diferente .
La primera de ellas será necesaria para la mayoría de las aplicaciones y la principal la controlan los
[Link] métodos.
La segunda solo es necesaria para las aplicaciones que cambian entidades o sus relaciones mientras no se
realiza el seguimiento de las entidades . Por ejemplo, una aplicación web puede enviar entidades al cliente
web donde el usuario realiza cambios y envía las entidades de nuevo. Estas entidades se conocen como
"desconectadas", ya que originalmente se consultaron desde DbContext, pero se desconectaron desde ese
contexto cuando se enviaron al cliente.
La aplicación web ahora debe volver a adjuntar estas entidades para que vuelvan a realizar el seguimiento e
indiquen los cambios que se han realizado de forma que SaveChanges puedan realizar las actualizaciones
adecuadas en la base de datos. Lo controlan principalmente los [Link] métodos y [Link] .

TIP
Normalmente no es necesario adjuntar entidades a la misma instancia de DbContext de la que se han consultado. No
realice habitualmente una consulta sin seguimiento y, a continuación, asocie las entidades devueltas al mismo contexto.
Esto será más lento que el uso de una consulta de seguimiento y puede dar lugar a problemas como los valores de las
propiedades de sombra que faltan, lo que dificulta la obtención de derechos.

Valores de clave generados frente a explícitos


De forma predeterminada, las propiedades Integer y de clave GUID están configuradas para utilizar valores de
clave generados automáticamente. Esto tiene una ventaja impor tante para el seguimiento de cambios:
un valor de clave no establecido indica que la entidad es "nueva" . Por "nuevo", nos referimos a que
todavía no se ha insertado en la base de datos.
En las secciones siguientes se usan dos modelos. El primero se configura para no usar los valores de clave
generados:

public class Blog


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

public string Name { get; set; }

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


}

public class Post


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

public string Title { get; set; }


public string Content { get; set; }

public int? BlogId { get; set; }


public Blog Blog { get; set; }
}

Los valores de clave no generados (es decir, establecidos explícitamente) se muestran en primer lugar en cada
ejemplo porque todo es muy explícito y fácil de seguir. A continuación, se sigue un ejemplo en el que se usan los
valores de clave generados:
public class Blog
{
public int Id { get; set; }
public string Name { get; set; }

public IList<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 int? BlogId { get; set; }


public Blog Blog { get; set; }
}

Tenga en cuenta que las propiedades clave de este modelo no necesitan ninguna configuración adicional, ya que
el uso de los valores de clave generados es el valor predeterminado para las claves enteras simples.

Insertar nuevas entidades


Valores de clave explícitos
Se debe realizar un seguimiento de una entidad en el Added Estado que va a insertar SaveChanges . Las
entidades normalmente se colocan en el estado agregado mediante una llamada a uno de los [Link]
[Link] métodos equivalentes de,,, [Link] [Link] o
DbSet<TEntity> .

TIP
Estos métodos funcionan de la misma manera en el contexto del seguimiento de cambios. Consulte características
adicionales de Change Tracking para obtener más información.

Por ejemplo, para empezar a realizar el seguimiento de un nuevo blog:

[Link](
new Blog { Id = 1, Name = ".NET Blog", });

Al inspeccionar la vista de depuración del seguimiento de cambios después de esta llamada, se muestra que el
contexto está realizando el seguimiento de la nueva entidad en el Added Estado:

Blog {Id: 1} Added


Id: 1 PK
Name: '.NET Blog'
Posts: []

Sin embargo, los métodos Add no solo funcionan en una entidad individual. En realidad, empiezan a realizar el
seguimiento de un gráfico completo de entidades relacionadas, poniéndolos al Added Estado. Por ejemplo, para
insertar un nuevo blog y las nuevas entradas asociadas:
[Link](
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});

Ahora el contexto está realizando el seguimiento de todas estas entidades como Added :

Blog {Id: 1} Added


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Added
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Added
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

Observe que se han establecido valores explícitos para las Id propiedades de clave en los ejemplos anteriores.
Esto se debe a que el modelo se ha configurado para utilizar valores de clave establecidos explícitamente, en
lugar de valores de clave generados automáticamente. Cuando no se usan claves generadas, las propiedades de
clave se deben establecer explícitamente antes de llamar a Add . Estos valores de clave se insertan después
cuando se llama a SaveChanges. Por ejemplo, al usar SQLite:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET Blog' (Size = 9)],
CommandType='Text', CommandTimeout='30']
INSERT INTO "Blogs" ("Id", "Name")
VALUES (@p0, @p1);

-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String), @p3='1' (DbType = String),


@p4='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p5='Announcing
the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p2, @p3, @p4, @p5);

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String), @p1='1' (DbType = String), @p2='F# 5 is
the latest version of F#, the functional programming language...' (Size = 72), @p3='Announcing F# 5' (Size =
15)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("Id", "BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2, @p3);

Se realiza un seguimiento de todas estas entidades en el Unchanged estado después de que se Complete
SaveChanges, ya que estas entidades ahora existen en la base de datos:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

Valores de clave generados


Como se mencionó anteriormente, las propiedades de clave de GUID y de entero se configuran para utilizar los
valores de clave generados automáticamente de forma predeterminada. Esto significa que la aplicación no debe
establecer ningún valor de clave explícitamente. Por ejemplo, para insertar un nuevo blog y exponer todos los
valores de clave generados:

[Link](
new Blog
{
Name = ".NET Blog",
Posts =
{
new Post
{
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});
Al igual que con los valores de clave explícitos, el contexto está realizando el seguimiento de todas estas
entidades como Added :

Blog {Id: -2147482644} Added


Id: -2147482644 PK Temporary
Name: '.NET Blog'
Posts: [{Id: -2147482637}, {Id: -2147482636}]
Post {Id: -2147482637} Added
Id: -2147482637 PK Temporary
BlogId: -2147482644 FK Temporary
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: -2147482644}
Post {Id: -2147482636} Added
Id: -2147482636 PK Temporary
BlogId: -2147482644 FK Temporary
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: -2147482644}

Observe que, en este caso, se han generado valores de clave temporales para cada entidad. EF Core utilizan
estos valores hasta que se llama a SaveChanges, en cuyo punto se leen los valores de clave reales de la base de
datos. Por ejemplo, al usar SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='.NET Blog' (Size = 9)], CommandType='Text',


CommandTimeout='30']
INSERT INTO "Blogs" ("Name")
VALUES (@p0);
SELECT "Id"
FROM "Blogs"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p2='Announcing the release of EF Core
5.0, a full featured cross-platform...' (Size = 72), @p3='Announcing the Release of EF Core 5.0' (Size =
37)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p1, @p2, @p3);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='F# 5 is the latest version of F#,
the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size = 15)], CommandType='Text',
CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Una vez completado SaveChanges, todas las entidades se han actualizado con sus valores de clave reales y se
realiza el seguimiento en el Unchanged Estado, ya que ahora coinciden con el estado de la base de datos:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

Este es exactamente el mismo estado final que el ejemplo anterior que usaba valores de clave explícitos.

TIP
Todavía se puede establecer un valor de clave explícito incluso cuando se usan valores de clave generados. A continuación,
EF Core intentará insertar con este valor de clave. Algunas configuraciones de base de datos, incluidas SQL Server con
columnas de identidad, no admiten estas inserciones y se producirán (vea estos documentos para obtener una solución
alternativa).

Asociar entidades existentes


Valores de clave explícitos
Se realiza un seguimiento de las entidades devueltas de las consultas en el Unchanged Estado. El Unchanged
Estado significa que la entidad no se ha modificado desde que se realizó la consulta. Una entidad desconectada,
tal vez que se devuelve desde un cliente web en una solicitud HTTP, se puede poner en este estado mediante
[Link] , [Link] o los métodos equivalentes en DbSet<TEntity> . Por ejemplo, para
empezar a realizar el seguimiento de un blog existente:

[Link](
new Blog { Id = 1, Name = ".NET Blog", });

NOTE
Los ejemplos siguientes son crear entidades explícitamente con new para simplificar. Normalmente, las instancias de la
entidad procederán de otro origen, como la deserialización de un cliente o la creación a partir de datos en un HTTP POST.

Al inspeccionar la vista de depuración del seguimiento de cambios después de esta llamada, se muestra que se
realiza un seguimiento de la entidad en el Unchanged Estado:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Posts: []

Del mismo modo Add , Attach establece realmente un grafo completo de entidades conectadas en el
Unchanged Estado. Por ejemplo, para adjuntar un blog existente y las publicaciones existentes asociadas:
[Link](
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});

Ahora el contexto está realizando el seguimiento de todas estas entidades como Unchanged :

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

La llamada a SaveChanges en este momento no tendrá ningún efecto. Todas las entidades se marcan como
Unchanged , por lo que no hay nada que actualizar en la base de datos.

Valores de clave generados


Como se mencionó anteriormente, las propiedades de clave de GUID y de entero se configuran para utilizar los
valores de clave generados automáticamente de forma predeterminada. Esto tiene una ventaja importante al
trabajar con entidades desconectadas: un valor de clave no establecido indica que la entidad no se ha insertado
todavía en la base de datos. Esto permite al seguimiento de cambios detectar automáticamente nuevas
entidades y ponerlas en el Added Estado. Por ejemplo, considere la posibilidad de adjuntar este gráfico de un
blog y publicaciones:
[Link](
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
},
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
},
}
});

El blog tiene un valor de clave de 1, que indica que ya existe en la base de datos. Dos de las publicaciones
también tienen valores de clave establecidos, pero el tercero no los tiene. EF Core verá este valor de clave como
0, el valor predeterminado de CLR para un entero. Esto da como resultado EF Core marcar la nueva entidad
como Added en lugar de Unchanged :

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482636}]
Post {Id: -2147482636} Added
Id: -2147482636 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 includes many enhancements, including single file a...'
Title: 'Announcing .NET 5.0'
Blog: {Id: 1}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'

La llamada a SaveChanges en este momento no hace nada con las Unchanged entidades, sino que inserta la
nueva entidad en la base de datos. Por ejemplo, al usar SQLite:
-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many
enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size =
19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Lo importante que se debe tener en cuenta es que, con los valores de clave generados, EF Core es capaz de
distinguir automáticamente los nuevos de las entidades existentes en un gráfico desconectado . En
pocas palabras, cuando se usan claves generadas, EF Core siempre insertará una entidad cuando esa entidad no
tenga establecido ningún valor de clave.

Actualización de entidades existentes


Valores de clave explícitos
[Link], [Link] y los métodos equivalentes en DbSet<TEntity> se comportan
exactamente igual que los Attach métodos descritos anteriormente, salvo que las entidades se colocan en
Modfied en lugar de en el Unchanged Estado. Por ejemplo, para empezar a realizar el seguimiento de un blog
existente como Modified :

[Link](
new Blog { Id = 1, Name = ".NET Blog", });

Al inspeccionar la vista de depuración del seguimiento de cambios después de esta llamada, se muestra que el
contexto está realizando el seguimiento de esta entidad en el Modified Estado:

Blog {Id: 1} Modified


Id: 1 PK
Name: '.NET Blog' Modified
Posts: []

Al igual Add que con y Attach , en Update realidad marca un grafo completo de entidades relacionadas como
Modified . Por ejemplo, para adjuntar un blog existente y los envíos existentes asociados como Modified :
[Link](
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
}
}
});

Ahora el contexto está realizando el seguimiento de todas estas entidades como Modified :

Blog {Id: 1} Modified


Id: 1 PK
Name: '.NET Blog' Modified
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
Id: 1 PK
BlogId: 1 FK Modified Originally <null>
Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
Title: 'Announcing the Release of EF Core 5.0' Modified
Blog: {Id: 1}
Post {Id: 2} Modified
Id: 2 PK
BlogId: 1 FK Modified Originally <null>
Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
Title: 'Announcing F# 5' Modified
Blog: {Id: 1}

La llamada a SaveChanges en este punto hará que las actualizaciones se envíen a la base de datos para todas
estas entidades. Por ejemplo, al usar SQLite:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)],
CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String),


@p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing
the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is
the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size =
15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

Valores de clave generados


Al igual que con Attach , los valores de clave generados tienen la misma ventaja principal para Update : un
valor de clave no establecido indica que la entidad es nueva y aún no se ha insertado en la base de datos. Como
con Attach , esto permite que DbContext detecte automáticamente las nuevas entidades y las ponga en el
Added Estado. Por ejemplo, considere la posibilidad de llamar a Update con este gráfico de un blog y
publicaciones:

[Link](
new Blog
{
Id = 1,
Name = ".NET Blog",
Posts =
{
new Post
{
Id = 1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = 2,
Title = "Announcing F# 5",
Content = "F# 5 is the latest version of F#, the functional programming language..."
},
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
},
}
});

Como en el Attach ejemplo, la publicación sin valor de clave se detecta como nueva y se establece en el Added
Estado. Las otras entidades se marcan como Modified :
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog' Modified
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482633}]
Post {Id: -2147482633} Added
Id: -2147482633 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 includes many enhancements, including single file a...'
Title: 'Announcing .NET 5.0'
Blog: {Id: 1}
Post {Id: 1} Modified
Id: 1 PK
BlogId: 1 FK Modified Originally <null>
Content: 'Announcing the release of EF Core 5.0, a full featured cross...' Modified
Title: 'Announcing the Release of EF Core 5.0' Modified
Blog: {Id: 1}
Post {Id: 2} Modified
Id: 2 PK
BlogId: 1 FK Modified Originally <null>
Content: 'F# 5 is the latest version of F#, the functional programming...' Modified
Title: 'Announcing F# 5' Modified
Blog: {Id: 1}

Llamar a SaveChanges en este punto hará que se envíen actualizaciones a la base de datos para todas las
entidades existentes, mientras se inserta la nueva entidad. Por ejemplo, al usar SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0='.NET Blog' (Size = 9)],
CommandType='Text', CommandTimeout='30']
UPDATE "Blogs" SET "Name" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='1' (DbType = String), @p0='1' (DbType = String),


@p1='Announcing the release of EF Core 5.0, a full featured cross-platform...' (Size = 72), @p2='Announcing
the Release of EF Core 5.0' (Size = 37)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p3='2' (DbType = String), @p0='1' (DbType = String), @p1='F# 5 is
the latest version of F#, the functional programming language...' (Size = 72), @p2='Announcing F# 5' (Size =
15)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0, "Content" = @p1, "Title" = @p2
WHERE "Id" = @p3;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String), @p1='.NET 5.0 includes many
enhancements, including single file applications, more...' (Size = 80), @p2='Announcing .NET 5.0' (Size =
19)], CommandType='Text', CommandTimeout='30']
INSERT INTO "Posts" ("BlogId", "Content", "Title")
VALUES (@p0, @p1, @p2);
SELECT "Id"
FROM "Posts"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Se trata de una forma muy sencilla de generar actualizaciones e inserciones desde un grafo desconectado. Sin
embargo, se generan actualizaciones o inserciones en la base de datos para cada propiedad de cada entidad de
la que se realiza el seguimiento, incluso cuando es posible que no se hayan cambiado algunos valores de
propiedad. Esto no es demasiado mezclarlas; para muchas aplicaciones con gráficos pequeños, esto puede ser
una forma sencilla y pragmática de generar actualizaciones. Dicho esto, a veces otros patrones más complejos
pueden dar lugar a actualizaciones más eficaces, como se describe en resolución de identidades en EF Core.
Eliminar entidades existentes
Para que SaveChanges elimine una entidad, debe realizar el seguimiento en el Deleted Estado. Las entidades
normalmente se colocan en el Deleted estado llamando a uno de [Link] [Link]
los métodos equivalentes en DbSet<TEntity> . Por ejemplo, para marcar un post existente como Deleted :

[Link](
new Post { Id = 2 });

Al inspeccionar la vista de depuración del seguimiento de cambios después de esta llamada, se muestra que el
contexto está realizando el seguimiento de la entidad en el Deleted Estado:

Post {Id: 2} Deleted


Id: 2 PK
BlogId: <null> FK
Content: <null>
Title: <null>
Blog: <null>

Esta entidad se eliminará cuando se llame a SaveChanges. Por ejemplo, al usar SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

Una vez completado SaveChanges, la entidad eliminada se desasocia de DbContext, ya que ya no existe en la
base de datos. Por lo tanto, la vista de depuración está vacía porque no se realiza el seguimiento de ninguna
entidad.

Eliminar entidades dependientes o secundarias


La eliminación de entidades dependientes o secundarias de un gráfico es más sencilla que la eliminación de
entidades principales/primarias. Vea la sección siguiente y el cambio de claves externas y navegaciones para
obtener más información.
No es habitual llamar a Remove en una entidad creada con new . Además, a diferencia Add de Attach y
Update , no es habitual llamar a Remove en una entidad de la que aún no se ha realizado un seguimiento en el
Unchanged Modified Estado o. En su lugar, es habitual realizar un seguimiento de una única entidad o gráfico de
entidades relacionadas y, a continuación, llamar a Remove en las entidades que deben eliminarse. Este gráfico de
entidades de las que se ha realizado un seguimiento se crea normalmente mediante:
1. Ejecutar una consulta para las entidades
2. Usar los Attach Update métodos o en un gráfico de entidades desconectadas, tal y como se describe en las
secciones anteriores.
Por ejemplo, el código de la sección anterior es más probable que obtenga una publicación de un cliente y, a
continuación, haga algo parecido a esto:

[Link](post);
[Link](post);

Esto se comporta exactamente de la misma manera que en el ejemplo anterior, ya que llamar a Remove en una
entidad sin seguimiento hace que se adjunte primero y se marque como Deleted .
En ejemplos más realistas, un grafo de entidades se adjunta primero y, a continuación, algunas de esas
entidades se marcan como eliminadas. Por ejemplo:

// Attach a blog and associated posts


[Link](blog);

// Mark one post as Deleted


[Link]([Link][1]);

Todas las entidades se marcan como Unchanged , excepto aquella en la que Remove se llamó:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Deleted
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

Esta entidad se eliminará cuando se llame a SaveChanges. Por ejemplo, al usar SQLite:

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

Una vez completado SaveChanges, la entidad eliminada se desasocia de DbContext, ya que ya no existe en la
base de datos. Otras entidades permanecen en el Unchanged Estado:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}

Eliminar entidades principales/primarias


Cada relación que conecta dos tipos de entidad tiene un extremo principal o primario, y un extremo dependiente
o secundario. La entidad dependiente/secundaria es la que tiene la propiedad de clave externa. En una relación
de uno a varios, la entidad de seguridad principal se encuentra en el lado "uno" y el dependiente/secundario
está en el lado "varios". Vea relaciones para obtener más información.
En los ejemplos anteriores se eliminaba una publicación, que es una entidad dependiente/secundaria en el blog,
que publica una relación de uno a varios. Esto es relativamente sencillo, ya que la eliminación de una entidad
dependiente o secundaria no afecta a otras entidades. Por otro lado, la eliminación de una entidad
principal/primaria también debe afectar a las entidades dependientes o secundarias. Si no lo hace, dejará un
valor de clave externa que haga referencia a un valor de clave principal que ya no existe. Se trata de un estado
de modelo no válido y genera un error de restricción referencial en la mayoría de las bases de datos.
Este estado de modelo no válido se puede controlar de dos maneras:
1. Estableciendo valores de FK en NULL. Esto indica que los elementos secundarios o dependientes ya no están
relacionados con ninguna entidad de seguridad o elemento primario. Este es el valor predeterminado para
las relaciones opcionales en las que la clave externa debe admitir valores NULL. Establecer el valor de FK en
NULL no es válido para las relaciones necesarias, donde la clave externa normalmente no acepta valores
NULL.
2. Eliminar los elementos dependientes o secundarios. Este es el valor predeterminado para las relaciones
necesarias y también es válido para las relaciones opcionales.
Consulte cambio de las claves externas y las navegaciones para obtener información detallada sobre el
seguimiento de cambios y las relaciones.
Relaciones opcionales
La [Link] propiedad de clave externa admite valores NULL en el modelo que se ha usado. Esto significa
que la relación es opcional y, por lo tanto, el comportamiento predeterminado de EF Core consiste en establecer
BlogId las propiedades de clave externa en NULL cuando se elimine el blog. Por ejemplo:

// Attach a blog and associated posts


[Link](blog);

// Mark the blog as deleted


[Link](blog);

Al inspeccionar la vista de depuración del seguimiento de cambios después de la llamada a Remove , se muestra
que, como se esperaba, el blog ahora está marcado como Deleted :

Blog {Id: 1} Deleted


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Modified
Id: 1 PK
BlogId: <null> FK Modified Originally 1
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: <null>
Post {Id: 2} Modified
Id: 2 PK
BlogId: <null> FK Modified Originally 1
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>

Lo más interesante es que todos los comentarios relacionados se marcan ahora como Modified . Esto se debe a
que la propiedad de clave externa de cada entidad se ha establecido en NULL. La llamada a SaveChanges
actualiza el valor de clave externa de cada post a NULL en la base de datos, antes de eliminar el blog:
-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text',
CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p1='2' (DbType = String), @p0=NULL], CommandType='Text',


CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p2='1' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p2;
SELECT changes();

Una vez completado SaveChanges, la entidad eliminada se desasocia de DbContext, ya que ya no existe en la
base de datos. Otras entidades se marcan ahora como Unchanged con valores de clave externa null, que coincide
con el estado de la base de datos:

Post {Id: 1} Unchanged


Id: 1 PK
BlogId: <null> FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: <null>
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: <null> FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>

Relaciones obligatorias
Si la [Link] propiedad de clave externa no acepta valores NULL, la relación entre blogs y entradas se
convierte en "Required". En esta situación, EF Core eliminará, de forma predeterminada, las entidades
dependientes o secundarias cuando se elimine la entidad de seguridad/primaria. Por ejemplo, la eliminación de
un blog con publicaciones relacionadas como en el ejemplo anterior:

// Attach a blog and associated posts


[Link](blog);

// Mark the blog as deleted


[Link](blog);

Al inspeccionar la vista de depuración del seguimiento de cambios después de la llamada a Remove , se muestra
que, como se esperaba, el blog se marca de nuevo como Deleted :
Blog {Id: 1} Deleted
Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}, {Id: 2}]
Post {Id: 1} Deleted
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Deleted
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

En este caso, lo más interesante es que todos los comentarios relacionados también se han marcado como
Deleted . La llamada a SaveChanges hace que el blog y todos los envíos relacionados se eliminen de la base de
datos:

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Blogs"
WHERE "Id" = @p1;

Una vez completado SaveChanges, todas las entidades eliminadas se desasocian de DbContext, ya que ya no
existen en la base de datos. Por lo tanto, la salida de la vista de depuración está vacía.

NOTE
Este documento solo borra la superficie del trabajo con relaciones en EF Core. Vea relaciones para obtener más
información sobre las relaciones de modelado y el cambio de las claves externas y las navegaciones para obtener más
información sobre cómo actualizar o eliminar entidades dependientes o secundarias al llamar a SaveChanges.

Seguimiento personalizado con TrackGraph


[Link] funciona como Add Attach y, Update salvo que genera una devolución de llamada
para cada instancia de entidad antes de realizar el seguimiento. Esto permite usar la lógica personalizada para
determinar cómo realizar un seguimiento de las entidades individuales de un gráfico.
Por ejemplo, considere la regla EF Core usa al realizar el seguimiento de las entidades con valores de clave
generados: Si el valor de clave es cero, la entidad es nueva y debe insertarse. Vamos a ampliar esta regla para
indicar si el valor de clave es negativo, se debe eliminar la entidad. Esto nos permite cambiar los valores de la
clave principal en las entidades de un grafo desconectado para marcar las entidades eliminadas:
[Link](
new Post
{
Title = "Announcing .NET 5.0",
Content = ".NET 5.0 includes many enhancements, including single file applications, more..."
}
);

var toDelete = [Link](e => [Link] == "Announcing F# 5");


[Link] = -[Link];

A continuación, se puede realizar el seguimiento de este gráfico desconectado mediante TrackGraph:

public static void UpdateBlog(Blog blog)


{
using var context = new BlogsContext();

[Link](
blog, node =>
{
var propertyEntry = [Link]("Id");
var keyValue = (int)[Link];

if (keyValue == 0)
{
[Link] = [Link];
}
else if (keyValue < 0)
{
[Link] = -keyValue;
[Link] = [Link];
}
else
{
[Link] = [Link];
}

[Link]($"Tracking {[Link]()} with key value {keyValue} as


{[Link]}");
});

[Link]();
}

Para cada entidad del gráfico, el código anterior comprueba el valor de la clave principal antes de realizar el
seguimiento de la entidad. En el caso de los valores de clave ununset (cero), el código hace lo que EF Core haría
normalmente. Es decir, si no se establece la clave, la entidad se marca como Added . Si se establece la clave y el
valor no es negativo, la entidad se marca como Modified . Sin embargo, si se encuentra un valor de clave
negativo, se restaura su valor real, no negativo y se realiza el seguimiento de la entidad como Deleted .
La salida de la ejecución de este código es:

Tracking Blog with key value 1 as Modified


Tracking Post with key value 1 as Modified
Tracking Post with key value -2 as Deleted
Tracking Post with key value 0 as Added
NOTE
Para simplificar, en este código se supone que cada entidad tiene una propiedad de clave principal de entero denominada
Id . Esto se podría codificar en una interfaz o clase base abstracta. Como alternativa, la propiedad o propiedades de la
clave principal se pueden obtener a partir de los IEntityType metadatos de modo que este código funcione con cualquier
tipo de entidad.

TrackGraph tiene dos sobrecargas. En la sobrecarga simple utilizada anteriormente, EF Core determina cuándo
detener el recorrido del gráfico. En concreto, deja de visitar nuevas entidades relacionadas de una entidad
determinada cuando ya se ha realizado el seguimiento de esa entidad o cuando la devolución de llamada no
inicia el seguimiento de la entidad.
La sobrecarga avanzada, [Link]<TState>(Object, TState,
Func<EntityEntryGraphNode<TState>,Boolean>) , tiene una devolución de llamada que devuelve un booleano.
Si la devolución de llamada devuelve false, el recorrido del gráfico se detiene; en caso contrario, continúa. Se
debe tener cuidado para evitar bucles infinitos al utilizar esta sobrecarga.
La sobrecarga avanzada también permite proporcionar el estado a TrackGraph y, a continuación, este estado se
pasa a cada devolución de llamada.
Acceso a entidades sometidas a seguimiento
12/03/2021 • 34 minutes to read • Edit Online

Hay cuatro API principales para tener acceso a las entidades a las que realiza un seguimiento DbContext :
[Link] Devuelve una EntityEntry<TEntity> instancia de para una instancia de entidad determinada.
[Link] Devuelve EntityEntry<TEntity> instancias para todas las entidades de las que se ha
realizado un seguimiento o para todas las entidades de las que se ha realizado un seguimiento de un tipo
determinado.
[Link], [Link] , DbSet<TEntity>.Find y buscan DbSet<TEntity>.FindAsync una sola
entidad por clave principal, buscando primero las entidades de las que se realiza un seguimiento y, a
continuación, consultando la base de datos si es necesario.
DbSet<TEntity>.Local Devuelve las entidades reales (no las instancias de EntityEntry) para las entidades del
tipo de entidad representadas por DbSet.
Cada una de ellas se describe con más detalle en las secciones siguientes.

TIP
En este documento se da por supuesto que se entienden los Estados de las entidades y los aspectos básicos del
seguimiento de cambios de EF Core. Consulte Change Tracking en EF Core para obtener más información sobre estos
temas.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Usar instancias de DbContext. entry e EntityEntry


Para cada entidad de la que se realiza un seguimiento, Entity Framework Core (EF Core) realiza un seguimiento
de:
El estado general de la entidad. Este es uno de Unchanged , Modified , Added o Deleted ; consulte Change
Tracking en EF Core para obtener más información.
Las relaciones entre las entidades de las que se realiza un seguimiento. Por ejemplo, el blog al que pertenece
un envío.
"Valores actuales" de las propiedades.
Los "valores originales" de las propiedades, cuando esta información está disponible. Los valores originales
son los valores de propiedad que existían al consultar la entidad desde la base de datos.
Los valores de propiedad que se han modificado desde que se consultaron.
Otra información sobre los valores de propiedad, como si el valor es temporalo no.
Al pasar una instancia de entidad a, se obtiene [Link] un que EntityEntry<TEntity> proporciona acceso
a esta información para la entidad determinada. Por ejemplo:
using var context = new BlogsContext();

var blog = [Link](e => [Link] == 1);


var entityEntry = [Link](blog);

En las secciones siguientes se muestra cómo usar EntityEntry para tener acceso y manipular el estado de la
entidad, así como el estado de las propiedades y las navegaciones de la entidad.
Trabajar con la entidad
El uso más común de EntityEntry<TEntity> es el acceso al actual EntityState de una entidad. Por ejemplo:

var currentState = [Link](blog).State;


if (currentState == [Link])
{
[Link](blog).State = [Link];
}

El método entry también se puede usar en entidades que todavía no se han controlado. Esto no inicia el
seguimiento de la entidad; el estado de la entidad sigue siendo Detatched . Sin embargo, el EntityEntry devuelto
se puede usar para cambiar el estado de la entidad, momento en el que se realizará el seguimiento de la entidad
en el estado especificado. Por ejemplo, el código siguiente comenzará el seguimiento de una instancia de blog
como Added :

var newBlog = new Blog();


[Link]([Link](newBlog).State == [Link]);

[Link](newBlog).State = [Link];
[Link]([Link](newBlog).State == [Link]);

TIP
A diferencia de EF6, establecer el estado de una entidad individual no hará que se realice el seguimiento de todas las
entidades conectadas. Esto hace que se establezca el estado de esta manera como una operación de nivel inferior que
llamar a Add , Attach o Update , que operan en un grafo completo de entidades.

En la tabla siguiente se resumen las maneras de usar una EntityEntry para trabajar con una entidad completa:

M IEM B RO EN T IT Y EN T RY DESC RIP C IÓ N

[Link] Obtiene y establece el EntityState de la entidad.

[Link] Obtiene la instancia de la entidad.

[Link] DbContextQue está realizando el seguimiento de esta


entidad.

[Link] IEntityType metadatos para el tipo de entidad.

[Link] Indica si se ha establecido o no el valor de clave de la


entidad.

[Link]() Sobrescribe los valores de propiedad con los valores leídos


de la base de datos.
M IEM B RO EN T IT Y EN T RY DESC RIP C IÓ N

[Link]() Fuerza la detección de cambios solo para esta entidad;


consulte detección y notificaciones de cambios.

Trabajar con una sola propiedad


Varias sobrecargas de EntityEntry<TEntity>.Property permiten el acceso a información sobre una propiedad
individual de una entidad. Por ejemplo, mediante una API de tipo "fuertemente tipada":

PropertyEntry<Blog, string> propertyEntry = [Link](blog).Property(e => [Link]);

En su lugar, se puede pasar el nombre de la propiedad como una cadena. Por ejemplo:

PropertyEntry<Blog, string> propertyEntry = [Link](blog).Property<string>("Name");

A continuación, el devuelto PropertyEntry<TEntity,TProperty> se puede utilizar para tener acceso a la


información sobre la propiedad. Por ejemplo, se puede usar para obtener y establecer el valor actual de la
propiedad en esta entidad:

string currentValue = [Link](blog).Property(e => [Link]).CurrentValue;


[Link](blog).Property(e => [Link]).CurrentValue = "1unicorn2";

Los dos métodos de propiedad usados anteriormente devuelven una instancia genérica fuertemente tipada
PropertyEntry<TEntity,TProperty> . Se prefiere el uso de este tipo genérico porque permite el acceso a los
valores de propiedad sin tipos de valor de conversión boxing. Sin embargo, si no se conoce el tipo de entidad o
propiedad en tiempo de compilación, en PropertyEntry su lugar se puede obtener un no genérico:

PropertyEntry propertyEntry = [Link](blog).Property("Name");

Esto permite el acceso a la información de propiedades de cualquier propiedad, independientemente de su tipo,


a costa de los tipos de valor de conversión boxing. Por ejemplo:

object blog = [Link](e => [Link] == 1);

object currentValue = [Link](blog).Property("Name").CurrentValue;


[Link](blog).Property("Name").CurrentValue = "1unicorn2";

En la tabla siguiente se resume la información de propiedades expuesta por PropertyEntry:

M IEM B RO P RO P ERT Y EN T RY DESC RIP C IÓ N

PropertyEntry<TEntity,TProperty>.CurrentValue Obtiene y establece el valor actual de la propiedad.

PropertyEntry<TEntity,TProperty>.OriginalValue Obtiene y establece el valor original de la propiedad, si está


disponible.

PropertyEntry<TEntity,TProperty>.EntityEntry Referencia inversa a la EntityEntry<TEntity> de la entidad.

[Link] IProperty metadatos de la propiedad.


M IEM B RO P RO P ERT Y EN T RY DESC RIP C IÓ N

[Link] Indica si esta propiedad está marcada como modificada y


permite cambiar este estado.

[Link] Indica si esta propiedad está marcada como temporaly


permite cambiar este estado.

Notas:
El valor original de una propiedad es el valor que tenía la propiedad cuando se realizó una consulta a la
entidad desde la base de datos. Sin embargo, los valores originales no están disponibles si la entidad se
desconectó y, a continuación, se asocia explícitamente a otro DbContext, por ejemplo, con Attach o Update .
En este caso, el valor original devuelto será el mismo que el valor actual.
SaveChanges solo actualizará las propiedades marcadas como modificadas. Establézcalo en IsModified true
para forzar que EF Core actualice un valor de propiedad determinado, o establézcalo en false para impedir
que EF Core actualice el valor de propiedad.
Los generadoresde valores EF Core generan normalmente valores temporales . Al establecer el valor actual
de una propiedad, se reemplazará el valor temporal por el valor especificado y se marcará la propiedad
como no temporal. Establézcalo IsTemporary en true para forzar que un valor sea temporal incluso después
de que se haya establecido explícitamente.
Trabajar con una sola navegación
Varias sobrecargas de EntityEntry<TEntity>.Reference , EntityEntry<TEntity>.Collection y [Link]
permiten el acceso a información sobre una navegación individual.
Se tiene acceso a las navegaciones de referencia a una sola entidad relacionada a través de los Reference
métodos. Las navegaciones de referencia apuntan a los lados "uno" de las relaciones uno a varios y ambos lados
de las relaciones uno a uno. Por ejemplo:

ReferenceEntry<Post, Blog> referenceEntry1 = [Link](post).Reference(e => [Link]);


ReferenceEntry<Post, Blog> referenceEntry2 = [Link](post).Reference<Blog>("Blog");
ReferenceEntry referenceEntry3 = [Link](post).Reference("Blog");

Las navegaciones también pueden ser colecciones de entidades relacionadas cuando se usan para los lados
"varios" de las relaciones uno a varios y varios a varios. Los Collection métodos se usan para tener acceso a las
navegaciones de la colección. Por ejemplo:

CollectionEntry<Blog, Post> collectionEntry1 = [Link](blog).Collection(e => [Link]);


CollectionEntry<Blog, Post> collectionEntry2 = [Link](blog).Collection<Post>("Posts");
CollectionEntry collectionEntry3 = [Link](blog).Collection("Posts");

Algunas operaciones son comunes para todas las navegaciones. Se puede tener acceso a estas para las
referencias y a las navegaciones de la colección mediante el [Link] método. Tenga en cuenta que
solo está disponible el acceso no genérico cuando se tiene acceso a todas las navegaciones juntas. Por ejemplo:

NavigationEntry navigationEntry = [Link](blog).Navigation("Posts");

En la tabla siguiente se resumen las distintas formas de usar ReferenceEntry<TEntity,TProperty> ,


CollectionEntry<TEntity,TRelatedEntity> y NavigationEntry :
M IEM B RO N AVIGAT IO N EN T RY DESC RIP C IÓ N

[Link] Obtiene y establece el valor actual de la navegación. Se trata


de la colección completa para las navegaciones de la
colección.

[Link] INavigationBase metadatos para la navegación.

[Link] Obtiene o establece un valor que indica si la entidad o


colección relacionada se ha cargado completamente desde la
base de datos.

[Link]() Carga la entidad o colección relacionada desde la base de


datos; Vea la carga explícita de datos relacionados.

[Link]() La consulta EF Core utilizaría para cargar esta navegación


como un IQueryable que se puede componer más; vea la
carga explícita de datos relacionados.

Trabajar con todas las propiedades de una entidad


[Link] Devuelve un IEnumerable<T> de PropertyEntry para cada propiedad de la entidad. Se
puede usar para realizar una acción para cada propiedad de la entidad. Por ejemplo, para establecer cualquier
propiedad DateTime en [Link] :

foreach (var propertyEntry in [Link](blog).Properties)


{
if ([Link] == typeof(DateTime))
{
[Link] = [Link];
}
}

Además, EntityEntry contiene varios métodos para obtener y establecer todos los valores de propiedad al
mismo tiempo. Estos métodos usan la PropertyValues clase, que representa una colección de propiedades y sus
valores. PropertyValues se puede obtener para los valores actuales o originales, o para los valores que están
almacenados actualmente en la base de datos. Por ejemplo:

var currentValues = [Link](blog).CurrentValues;


var originalValues = [Link](blog).OriginalValues;
var databaseValues = [Link](blog).GetDatabaseValues();

Estos objetos PropertyValues no son muy útiles por sí solos. Sin embargo, se pueden combinar para realizar
operaciones comunes necesarias al manipular entidades. Esto resulta útil cuando se trabaja con objetos de
transferencia de datos y cuando se resuelven conflictos de simultaneidad optimista. En las secciones siguientes
se muestran algunos ejemplos.
Establecimiento de los valores actuales o originales de una entidad o DTO
Los valores actuales o originales de una entidad se pueden actualizar mediante la copia de los valores de otro
objeto. Por ejemplo, considere un BlogDto objeto de transferencia de datos (DTO) con las mismas propiedades
que el tipo de entidad:
public class BlogDto
{
public int Id { get; set; }
public string Name { get; set; }
}

Se puede usar para establecer los valores actuales de una entidad de la que se ha realizado un seguimiento
mediante [Link] :

var blogDto = new BlogDto { Id = 1, Name = "1unicorn2" };

[Link](blog).[Link](blogDto);

Esta técnica se usa a veces al actualizar una entidad con valores obtenidos de una llamada de servicio o un
cliente en una aplicación de n niveles. Tenga en cuenta que el objeto utilizado no tiene que ser del mismo tipo
que la entidad, siempre y cuando tenga propiedades cuyos nombres coincidan con los de la entidad. En el
ejemplo anterior, se usa una instancia de DTO BlogDto para establecer los valores actuales de una entidad de la
que se ha realizado un seguimiento Blog .
Tenga en cuenta que las propiedades solo se marcarán como modificadas si el conjunto de valores difiere del
valor actual.
Establecer los valores actuales o originales de un diccionario
En el ejemplo anterior se establecen valores de una instancia de Entity o DTO. El mismo comportamiento está
disponible cuando los valores de propiedad se almacenan como pares de nombre y valor en un diccionario. Por
ejemplo:

var blogDictionary = new Dictionary<string, object> { ["Id"] = 1, ["Name"] = "1unicorn2" };

[Link](blog).[Link](blogDictionary);

Establecer valores actuales o originales de la base de datos


Los valores actuales o originales de una entidad se pueden actualizar con los valores más recientes de la base de
datos llamando a GetDatabaseValues() o GetDatabaseValuesAsync y usando el objeto devuelto para establecer
los valores actuales o originales, o ambos. Por ejemplo:

var databaseValues = [Link](blog).GetDatabaseValues();


[Link](blog).[Link](databaseValues);
[Link](blog).[Link](databaseValues);

Crear un objeto clonado que contenga valores actual, original o de base de datos
El objeto PropertyValues devuelto de CurrentValues, OriginalValues o GetDatabaseValues se puede usar para
crear un clon de la entidad mediante [Link]() . Por ejemplo:

var clonedBlog = [Link](blog).GetDatabaseValues().ToObject();

Tenga en cuenta que ToObject devuelve una nueva instancia de la que el DbContext no realiza un seguimiento.
El objeto devuelto tampoco tiene ninguna relación establecida con otras entidades.
El objeto clonado puede ser útil para resolver problemas relacionados con las actualizaciones simultáneas en la
base de datos, especialmente cuando se enlazan datos a objetos de un tipo determinado. Vea simultaneidad
optimista para obtener más información.
Trabajar con todas las navegaciones de una entidad
[Link] Devuelve un IEnumerable<T> de NavigationEntry para cada navegación de la entidad.
[Link] y [Link] hacen lo mismo, pero se restringen a las navegaciones de
referencia o de colección, respectivamente. Se puede usar para realizar una acción para cada navegación de la
entidad. Por ejemplo, para forzar la carga de todas las entidades relacionadas:

foreach (var navigationEntry in [Link](blog).Navigations)


{
[Link]();
}

Trabajar con todos los miembros de una entidad


Las propiedades normales y las propiedades de navegación tienen un estado y un comportamiento diferentes.
Por lo tanto, es habitual procesar las navegaciones y las no navegaciones por separado, tal como se muestra en
las secciones anteriores. Sin embargo, a veces puede resultar útil hacer algo con cualquier miembro de la
entidad, independientemente de si se trata de una propiedad o navegación normal. [Link] y
[Link] se proporcionan para este fin. Por ejemplo:

foreach (var memberEntry in [Link](blog).Members)


{
[Link](
$"Member {[Link]} is of type {[Link]()}
and has value {[Link]}");
}

La ejecución de este código en un blog del ejemplo genera el siguiente resultado:

Member Id is of type int and has value 1


Member Name is of type string and has value .NET Blog
Member Posts is of type IList<Post> and has value [Link]`1[Post]

TIP
La vista de depuración del seguimiento de cambios muestra información como esta. La vista de depuración para todo el
seguimiento de cambios se genera a partir del individuo [Link] de cada entidad de la que se realiza el
seguimiento.

Buscar y FindAsync
[Link], [Link] , DbSet<TEntity>.Find y DbSet<TEntity>.FindAsync están diseñados para
una búsqueda eficaz de una sola entidad cuando se conoce su clave principal. Buscar primero comprueba si ya
se ha realizado el seguimiento de la entidad y, si es así, devuelve la entidad inmediatamente. Solo se realiza una
consulta de base de datos si no se realiza un seguimiento de la entidad localmente. Por ejemplo, considere este
código que llama a buscar dos veces para la misma entidad:
using var context = new BlogsContext();

[Link]("First call to Find...");


var blog1 = [Link](1);

[Link]($"...found blog {[Link]}");

[Link]();
[Link]("Second call to Find...");
var blog2 = [Link](1);
[Link](blog1 == blog2);

[Link]("...returned the same instance without executing a query.");

La salida de este código (incluido el registro de EF Core) cuando se usa SQLite es la siguiente:

First call to Find...


info: 12/29/2020 07:45:53.682 [Link][20101]
([Link])
Executed DbCommand (1ms) [Parameters=[@__p_0='1' (DbType = String)], CommandType='Text',
CommandTimeout='30']
SELECT "b"."Id", "b"."Name"
FROM "Blogs" AS "b"
WHERE "b"."Id" = @__p_0
LIMIT 1
...found blog .NET Blog

Second call to Find...


...returned the same instance without executing a query.

Observe que la primera llamada no encuentra la entidad localmente y ejecuta una consulta de base de datos.
Por el contrario, la segunda llamada devuelve la misma instancia sin consultar la base de datos porque ya se
está realizando el seguimiento.
Find devuelve NULL si no se realiza un seguimiento de una entidad con la clave especificada localmente y no
existe en la base de datos.
Claves compuestas
La búsqueda también se puede usar con claves compuestas. Por ejemplo, considere una OrderLine entidad con
una clave compuesta que consta del identificador de pedido y el ID. de producto:

public class OrderLine


{
public int OrderId { get; set; }
public int ProductId { get; set; }

//...
}

La clave compuesta debe estar configurada en [Link] para definir las partes clave y su
orden. Por ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.Entity<OrderLine>()
.HasKey(e => new { [Link], [Link] });
}
Observe que OrderId es la primera parte de la clave y ProductId es la segunda parte de la clave. Este orden
debe utilizarse al pasar los valores de clave que se van a buscar. Por ejemplo:

var orderline = [Link](orderId, productId);

Usar ChangeTracker. Entries para tener acceso a todas las entidades


sometidas a seguimiento
Hasta ahora solo hemos tenido acceso a una sola vez EntityEntry . [Link]() Devuelve un
EntityEntry para cada entidad de la que el DbContext realiza un seguimiento actualmente. Por ejemplo:

using var context = new BlogsContext();


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

foreach (var entityEntry in [Link]())


{
[Link]($"Found {[Link]} entity with ID
{[Link]("Id").CurrentValue}");
}

Este código genera el siguiente resultado:

Found Blog entity with ID 1


Found Post entity with ID 1
Found Post entity with ID 2

Observe que se devuelven entradas para blogs y publicaciones. En su lugar, los resultados se pueden filtrar a un
tipo de entidad específico mediante la [Link]<TEntity>() sobrecarga genérica:

foreach (var entityEntry in [Link]<Post>())


{
[Link](
$"Found {[Link]} entity with ID {[Link](e => [Link]).CurrentValue}");
}

La salida de este código muestra que solo se devuelven publicaciones:

Found Post entity with ID 1


Found Post entity with ID 2

Además, el uso de la sobrecarga genérica devuelve instancias genéricas EntityEntry<TEntity> . Esto es lo que
permite el acceso de tipo fluida a la Id propiedad en este ejemplo.
No es necesario que el tipo genérico usado para filtrar sea un tipo de entidad asignado; en su lugar, se puede
usar un tipo base o una interfaz sin asignar. Por ejemplo, si todos los tipos de entidad del modelo implementan
una interfaz que define su propiedad de clave:

public interface IEntityWithKey


{
int Id { get; set; }
}

Después, esta interfaz se puede usar para trabajar con la clave de cualquier entidad de la que se ha realizado un
seguimiento de forma fuertemente tipada. Por ejemplo:

foreach (var entityEntry in [Link]<IEntityWithKey>())


{
[Link](
$"Found {[Link]} entity with ID {[Link](e => [Link]).CurrentValue}");
}

Usar DbSet. local para consultar las entidades sometidas a


seguimiento
Las consultas de EF Core se ejecutan siempre en la base de datos y solo devuelven las entidades que se han
guardado en la base de datos. DbSet<TEntity>.Local proporciona un mecanismo para consultar a DbContext
para entidades de las que se ha realizado un seguimiento local.
Dado que [Link] se utiliza para consultar las entidades de las que se realiza un seguimiento, es habitual
cargar entidades en DbContext y, después, trabajar con esas entidades cargadas. Esto es especialmente cierto
para el enlace de datos, pero también puede ser útil en otras situaciones. Por ejemplo, en el código siguiente, la
base de datos se consulta primero para todos los blogs y publicaciones. El Load método de extensión se utiliza
para ejecutar esta consulta con los resultados seguidos por el contexto sin devolverse directamente a la
aplicación. (El uso de ToList o similar tiene el mismo efecto, pero con la sobrecarga de crear la lista devuelta,
que no se necesita aquí). A continuación, en el ejemplo [Link] se usa para acceder a las entidades de las
que se realiza un seguimiento local:

using var context = new BlogsContext();

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

foreach (var blog in [Link])


{
[Link]($"Blog: {[Link]}");
}

foreach (var post in [Link])


{
[Link]($"Post: {[Link]}");
}

Tenga en cuenta que, a diferencia [Link]() de, [Link] devuelve directamente las instancias
de la entidad. Por supuesto, una EntityEntry puede obtenerse siempre para la entidad devuelta mediante una
llamada a [Link] .
La vista local
DbSet<TEntity>.Local Devuelve una vista de entidades de las que se realiza un seguimiento local que refleja el
actual EntityState de esas entidades. En concreto, esto significa que:
Added se incluyen las entidades. Tenga en cuenta que este no es el caso de las consultas de EF Core
normales, ya que las Added entidades aún no existen en la base de datos y, por tanto, nunca se devuelven en
una consulta de base de datos.
Deleted se excluyen las entidades. Tenga en cuenta que esto no es el caso de las consultas de EF Core
normales, ya que las Deleted entidades todavía existen en la base de datos y , por lo tanto, las consultas de
base de datos las devuelven.
Todo esto significa que [Link] es una vista sobre los datos que refleja el estado conceptual actual del
gráfico de entidades, con las Added entidades incluidas y las Deleted entidades excluidas. Esto coincide con el
estado de la base de datos que se espera que sea después de llamar a SaveChanges.
Esta suele ser la vista ideal para el enlace de datos, ya que presenta al usuario los datos a medida que los
entienden en función de los cambios realizados por la aplicación.
En el código siguiente se muestra el marcado de una publicación como Deleted y, a continuación, la adición de
una nueva publicación, que se marca como Added :

using var context = new BlogsContext();

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

[Link]("Local view after loading posts:");

foreach (var post in [Link])


{
[Link]($" Post: {[Link]}");
}

[Link](posts[1]);

[Link](
new Post
{
Title = "What’s next for [Link]?",
Content = ".NET 5.0 was released recently and has come with many...",
Blog = posts[0].Blog
});

[Link]("Local view after adding and deleting posts:");

foreach (var post in [Link])


{
[Link]($" Post: {[Link]}");
}

El resultado de este código es:

Local view after loading posts:


Post: Announcing the Release of EF Core 5.0
Post: Announcing F# 5
Post: Announcing .NET 5.0
Local view after adding and deleting posts:
Post: What’s next for [Link]?
Post: Announcing the Release of EF Core 5.0
Post: Announcing .NET 5.0

Tenga en cuenta que la publicación eliminada se quita de la vista local y se incluye la publicación agregada.
Usar local para agregar y quitar entidades
DbSet<TEntity>.Local devuelve una instancia de LocalView<TEntity>. Se trata de una implementación de
ICollection<T> que genera y responde a las notificaciones cuando se agregan y quitan entidades de la colección.
(Este es el mismo concepto que ObservableCollection<T> , pero se implementa como una proyección sobre las
entradas de seguimiento de cambios de EF Core existentes, en lugar de como una colección independiente).
Las notificaciones de la vista local se enlazan al seguimiento de cambios de DbContext de modo que la vista
local permanece sincronizada con DbContext. De manera específica:
Agregar una nueva entidad a [Link] hace que el DbContext realice el seguimiento, normalmente en el
Added Estado. (Si la entidad ya tiene un valor de clave generado, se realiza el seguimiento como Unchanged
en su lugar).
Al quitar una entidad de [Link] , se marca como Deleted .
Una entidad que se convierte en el seguimiento por DbContext aparecerá automáticamente en la
[Link] colección. Por ejemplo, la ejecución de una consulta para incorporar más entidades
automáticamente hace que se actualice la vista local.
Una entidad marcada como se Deleted quitará automáticamente de la colección local.

Esto significa que la vista local se puede usar para manipular las entidades de las que se ha realizado un
seguimiento agregando y quitando de la colección. Por ejemplo, permite modificar el código de ejemplo
anterior para agregar y quitar entradas de la colección local:

using var context = new BlogsContext();

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

[Link]("Local view after loading posts:");

foreach (var post in [Link])


{
[Link]($" Post: {[Link]}");
}

[Link](posts[1]);

[Link](
new Post
{
Title = "What’s next for [Link]?",
Content = ".NET 5.0 was released recently and has come with many...",
Blog = posts[0].Blog
});

[Link]("Local view after adding and deleting posts:");

foreach (var post in [Link])


{
[Link]($" Post: {[Link]}");
}

La salida permanece sin cambios en el ejemplo anterior porque los cambios realizados en la vista local se
sincronizan con DbContext.
Usar la vista local para el enlace de datos de Windows Forms o WPF
DbSet<TEntity>.Local constituye la base para el enlace de datos a entidades EF Core. Sin embargo, tanto
Windows Forms como WPF funcionan mejor cuando se usan con el tipo específico de colección de
notificaciones que esperan. La vista local admite la creación de estos tipos de colección específicos:
LocalView<TEntity>.ToObservableCollection() Devuelve un ObservableCollection<T> para el enlace de datos
de WPF.
LocalView<TEntity>.ToBindingList() Devuelve un BindingList<T> para el enlace de datos Windows Forms.
Por ejemplo:

ObservableCollection<Post> observableCollection = [Link]();


BindingList<Post> bindingList = [Link]();

Vea Introducción a WPF para obtener más información sobre el enlace de datos de wpf con EF Core.
TIP
La vista local de una instancia de DbSet determinada se crea de forma diferida cuando se obtiene acceso por primera vez
y después se almacena en caché. La creación de LocalView es rápida y no usa mucha memoria. Sin embargo, llama a
DetectChanges, que puede ser lento para un gran número de entidades. Las colecciones creadas por
ToObservableCollection y ToBindingList también se crean de forma diferida y, a continuación, se almacenan en
caché. Ambos métodos crean colecciones nuevas, que pueden ser lentas y usar una gran cantidad de memoria cuando se
usan miles de entidades.
Cambiar las claves externas y las navegaciones
12/03/2021 • 60 minutes to read • Edit Online

Información general sobre las claves externas y las navegaciones


Las relaciones en un modelo de Entity Framework Core (EF Core) se representan mediante claves externas
(claves externas). Un FK consta de una o más propiedades en la entidad dependiente o secundaria de la relación.
Esta entidad dependiente/secundaria se asocia a una entidad de entidad de seguridad o primaria determinada
cuando los valores de las propiedades de clave externa del objeto dependiente o secundario coinciden con los
valores de las propiedades de clave principal o alternativa (PK) de la entidad de seguridad.
Las claves externas son una buena manera de almacenar y manipular relaciones en la base de datos, pero no
son muy fáciles de usar cuando se trabaja con varias entidades relacionadas en el código de la aplicación. Por lo
tanto, la mayoría de los modelos de EF Core también exponen "navegaciones" sobre la representación de FK. Las
navegaciones forman referencias de C#/.NET entre instancias de la entidad que reflejan las asociaciones
encontradas mediante la coincidencia de valores de clave externa con valores de clave principales o alternativos.
Las navegaciones se pueden usar en ambos lados de la relación, solo en un lado o, no solo en la propiedad FK.
La propiedad FK se puede ocultar convirtiéndola en una propiedad Shadow. Vea relaciones para obtener más
información sobre las relaciones de modelado.

TIP
En este documento se da por supuesto que se entienden los Estados de las entidades y los aspectos básicos del
seguimiento de cambios de EF Core. Consulte Change Tracking en EF Core para obtener más información sobre estos
temas.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Modelo de ejemplo
El siguiente modelo contiene cuatro tipos de entidad con relaciones entre ellos. Los comentarios en el código
indican qué propiedades son las claves externas, las claves principales y las navegaciones.
public class Blog
{
public int Id { get; set; } // Primary key
public string Name { get; set; }

public IList<Post> Posts { get; } = new List<Post>(); // Collection navigation


public BlogAssets Assets { get; set; } // Reference navigation
}

public class BlogAssets


{
public int Id { get; set; } // Primary key
public byte[] Banner { get; set; }

public int? BlogId { get; set; } // Foreign key


public Blog Blog { get; set; } // Reference navigation
}

public class Post


{
public int Id { get; set; } // Primary key
public string Title { get; set; }
public string Content { get; set; }

public int? BlogId { get; set; } // Foreign key


public Blog Blog { get; set; } // Reference navigation

public IList<Tag> Tags { get; } = new List<Tag>(); // Skip collection navigation


}

public class Tag


{
public int Id { get; set; } // Primary key
public string Text { get; set; }

public IList<Post> Posts { get; } = new List<Post>(); // Skip collection navigation


}

Las tres relaciones de este modelo son:


Cada blog puede tener muchas publicaciones (de uno a varios):
Blog es la entidad de seguridad principal.
Post es el dependiente o el elemento secundario. Contiene la propiedad FK [Link] , cuyo valor
debe coincidir con el [Link] valor PK del blog relacionado.
[Link] es una navegación de referencia desde una publicación al blog asociado. [Link] es la
navegación inversa para [Link] .
[Link] es una navegación de colección de un blog a todas las publicaciones asociadas.
[Link] es la navegación inversa para [Link] .
Cada blog puede tener un recurso (uno a uno):
Blog es la entidad de seguridad principal.
BlogAssets es el dependiente o el elemento secundario. Contiene la propiedad FK [Link]
, cuyo valor debe coincidir con el [Link] valor PK del blog relacionado.
[Link] es una navegación de referencia entre los activos y el blog asociado.
[Link] es la navegación inversa para [Link] .
[Link] es una navegación de referencia desde el blog hasta los recursos asociados.
[Link] es la navegación inversa para [Link] .
Cada publicación puede tener muchas etiquetas y cada etiqueta puede tener muchas entradas (varios a
varios):
Las relaciones de varios a varios son una capa adicional sobre las relaciones de 2 1 a muchos. Las
relaciones de varios a varios se describen más adelante en este documento.
[Link] es una navegación de colección de una entrada a todas las etiquetas asociadas. [Link]
es la navegación inversa para [Link] .
[Link] es una navegación de colección de una etiqueta a todas las publicaciones asociadas.
[Link] es la navegación inversa para [Link] .

Vea relaciones para obtener más información sobre cómo modelar y configurar relaciones.

Corrección de relación
EF Core mantiene las navegaciones en la alineación con valores de clave externa y viceversa. Es decir, si un valor
de clave externa cambia de modo que ahora hace referencia a otra entidad principal/primaria, se actualizan las
navegaciones para reflejar este cambio. Del mismo modo, si se cambia una navegación, los valores de clave
externa de las entidades implicadas se actualizan para reflejar este cambio. Esto se denomina "corrección de
relación".
Corrección por consulta
La corrección se produce primero cuando se consultan las entidades desde la base de datos. La base de datos
solo tiene valores de clave externa, por lo que cuando EF Core crea una instancia de entidad a partir de la base
de datos, usa los valores de clave externa para establecer las navegaciones de referencia y agregar entidades a
las navegaciones de la colección según corresponda. Por ejemplo, considere una consulta para los blogs y sus
entradas y recursos asociados:

using var context = new BlogsContext();

var blogs = [Link]


.Include(e => [Link])
.Include(e => [Link])
.ToList();

[Link]([Link]);

En cada blog, EF Core creará una instancia en primer lugar Blog . Después, a medida que cada publicación se
carga desde la base de datos, su [Link] navegación de referencia se establece para que apunte al blog
asociado. Del mismo modo, la publicación se agrega a la navegación de la [Link] colección. Lo mismo
sucede con BlogAssets , salvo que en este caso ambas son referencias. La [Link] navegación se establece
para que apunte a la instancia de activos y la [Link] navegación se establece para que apunte a la
instancia de blog.
Si se examina la vista de depuración de Change Tracker después de que esta consulta muestre dos blogs, cada
uno con un recurso y dos publicaciones en las que se realiza el seguimiento:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: {Id: 1}
Posts: [{Id: 1}, {Id: 2}]
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 1} Unchanged
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 2} Unchanged
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Tags: []
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Tags: []
Post {Id: 4} Unchanged
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []

La vista de depuración muestra tanto los valores de clave como las navegaciones. Las navegaciones se muestran
mediante los valores de clave principal de las entidades relacionadas. Por ejemplo, Posts: [{Id: 1}, {Id: 2}]
en la salida anterior indica que la [Link] navegación de la colección contiene dos entradas relacionadas
con las claves principales 1 y 2, respectivamente. Del mismo modo, para cada publicación asociada con el
primer blog, la Blog: {Id: 1} línea indica que la [Link] navegación hace referencia al blog con la clave
principal 1.
Corrección para entidades de las que se realiza un seguimiento local
La corrección de la relación también se produce entre las entidades devueltas desde una consulta de
seguimiento y las entidades a las que el DbContext ya realiza un seguimiento. Por ejemplo, considere la
posibilidad de ejecutar tres consultas independientes para blogs, publicaciones y recursos:
using var context = new BlogsContext();

var blogs = [Link]();


[Link]([Link]);

var assets = [Link]();


[Link]([Link]);

var posts = [Link]();


[Link]([Link]);

Al mirar de nuevo las vistas de depuración, después de la primera consulta, solo se realiza el seguimiento de los
dos blogs:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: []
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: <null>
Posts: []

Las [Link] navegaciones de referencia son NULL y las [Link] navegaciones de la colección están
vacías porque el contexto no está realizando actualmente un seguimiento de entidades asociadas.
Después de la segunda consulta, [Link] se han corregido las navegaciones de referencia para que
apunten a las instancias recién controladas BlogAsset . Del mismo modo, las [Link] navegaciones de
referencia se establecen para que apunten a la instancia de ya sometida a seguimiento adecuada Blog .

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Assets: {Id: 1}
Posts: []
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: []
BlogAssets {Id: 1} Unchanged
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 2} Unchanged
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}

Por último, después de la tercera consulta, las [Link] navegaciones de la colección contienen ahora todos
los envíos relacionados y las [Link] referencias apuntan a la Blog instancia adecuada:
Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: {Id: 1}
Posts: [{Id: 1}, {Id: 2}]
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 1} Unchanged
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 2} Unchanged
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Tags: []
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Tags: []
Post {Id: 4} Unchanged
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []

Este es el mismo estado final que se ha logrado con la consulta única original, ya que EF Core las navegaciones
fijas a medida que se realiza el seguimiento de las entidades, incluso cuando proceden de varias consultas
diferentes.

NOTE
La corrección nunca hace que se devuelvan más datos de la base de datos. Solo se conectan las entidades que ya
devuelve la consulta o a las que el DbContext ya realiza un seguimiento. Vea resolución de identidades en EF Core para
obtener información sobre cómo administrar duplicados al serializar entidades.

Cambiar relaciones mediante navegación


La forma más fácil de cambiar la relación entre dos entidades es mediante la manipulación de una navegación, a
la vez que se mantiene EF Core para corregir la navegación inversa y los valores de FK adecuadamente. Esto se
puede hacer de la forma siguiente:
Adición o eliminación de una entidad de una navegación de colección.
Cambiar una navegación de referencia para apuntar a otra entidad o establecerla en NULL.
Agregar o quitar de las navegaciones de la colección
Por ejemplo, vamos a migrar una de las entradas del blog de Visual Studio al blog de .NET. Esto requiere cargar
primero los blogs y entradas y, a continuación, mover la entrada desde la colección de navegación de un blog a
la colección de navegación del otro blog:

using var context = new BlogsContext();

var dotNetBlog = [Link](e => [Link]).Single(e => [Link] == ".NET Blog");


var vsBlog = [Link](e => [Link]).Single(e => [Link] == "Visual Studio Blog");

[Link]([Link]);

var post = [Link](e => [Link]("Disassembly improvements"));


[Link](post);
[Link](post);

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

[Link]();

TIP
[Link]()Aquí se necesita una llamada a porque el acceso a la vista de depuración no provoca la
detección automática de los cambios.

Esta es la vista de depuración impresa después de ejecutar el código anterior:


Blog {Id: 1} Unchanged
Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: [{Id: 1}, {Id: 2}, {Id: 3}]
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: <null>
Posts: [{Id: 4}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Tags: []
Post {Id: 3} Modified
Id: 3 PK
BlogId: 1 FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 1}
Tags: []
Post {Id: 4} Unchanged
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []

La [Link] navegación en el blog de .net tiene ahora tres publicaciones (


Posts: [{Id: 1}, {Id: 2}, {Id: 3}] ). Del mismo modo, la [Link] navegación en el blog de Visual Studio
solo tiene una publicación ( Posts: [{Id: 4}] ). Esto se debe esperar, ya que el código cambió explícitamente
estas colecciones.
Más Curiosamente, aunque el código no cambiara explícitamente la [Link] navegación, se ha corregido
para que apunte al blog de Visual Studio ( Blog: {Id: 1} ). Además, el [Link] valor de clave externa se ha
actualizado para que coincida con el valor de clave principal del blog de .net. Este cambio en el valor de FK en se
conserva en la base de datos cuando se llama a SaveChanges:

-- Executed DbCommand (0ms) [Parameters=[@p1='3' (DbType = String), @p0='1' (Nullable = true) (DbType =
String)], CommandType='Text', CommandTimeout='30']
UPDATE "Posts" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();

Cambiar las navegaciones de referencia


En el ejemplo anterior, una publicación se ha desplazado de un blog a otro manipulando la navegación por la
colección de publicaciones en cada blog. Lo mismo se puede lograr si se cambia la navegación de [Link]
referencia para que apunte al nuevo blog. Por ejemplo:
var post = [Link](e => [Link]("Disassembly improvements"));
[Link] = dotNetBlog;

La vista de depuración después de este cambio es exactamente la misma que en el ejemplo anterior. Esto se
debe a que EF Core detectó el cambio de navegación de referencia y, a continuación, corrigió las navegaciones
de la colección y el valor de FK para que coincidan.

Cambiar relaciones mediante valores de clave externa


En la sección anterior, las relaciones se manipulaban mediante navegación y los valores de clave externa se
actualizaban automáticamente. Esta es la manera recomendada de manipular relaciones en EF Core. Sin
embargo, también es posible manipular los valores de FK directamente. Por ejemplo, podemos trasladar una
entrada de un blog a otro cambiando el valor de la [Link] clave externa:

var post = [Link](e => [Link]("Disassembly improvements"));


[Link] = [Link];

Observe cómo esto es muy similar al cambio de la navegación de referencia, tal como se muestra en el ejemplo
anterior.
La vista de depuración después de este cambio vuelve a ser exactamente igual que en el caso de los dos
ejemplos anteriores. Esto se debe a que EF Core detectó el cambio de valor de FK y, a continuación, corrigió las
navegaciones de referencia y de colección para que coincidan.

TIP
No Escriba código para manipular todos los valores de navegación y FK cada vez que cambie una relación. Este código es
más complicado y debe garantizar cambios coherentes en las claves externas y navegaciones en todos los casos. Si es
posible, basta con manipular una sola navegación, o quizás ambas navegaciones. Si es necesario, basta con manipular los
valores de FK. Evite manipular los valores de navegación y de FK.

Corrección para entidades agregadas o eliminadas


Agregar a una navegación de colección
EF Core realiza las siguientes acciones cuando detecta que se ha agregado una nueva entidad dependiente o
secundaria a una navegación de colección:
Si no se realiza el seguimiento de la entidad, se realiza el seguimiento. (La entidad normalmente estará en el
Added Estado. Sin embargo, si el tipo de entidad está configurado para usar claves generadas y se establece
el valor de clave principal, se realiza un seguimiento de la entidad en el Unchanged Estado.)
Si la entidad está asociada a otra entidad de seguridad o primaria, se rompe la relación.
La entidad se asocia con el principal/primario que posee la navegación de la colección.
Las navegaciones y los valores de clave externa se corrigen para todas las entidades implicadas.
En función de esto, podemos ver que para trasladar una entrada de un blog a otro, realmente no es necesario
quitarla de la antigua navegación de la colección antes de agregarla al nuevo. Por lo tanto, el código del ejemplo
anterior se puede cambiar de:

var post = [Link](e => [Link]("Disassembly improvements"));


[Link](post);
[Link](post);
A:

var post = [Link](e => [Link]("Disassembly improvements"));


[Link](post);

EF Core observa que la publicación se ha agregado a un nuevo blog y la quita automáticamente de la colección
en el primer blog.
Quitar de una navegación de colección
Al quitar una entidad dependiente o secundaria de la navegación por la colección de la entidad de seguridad o
primaria, se produce el control de la relación con esa entidad de seguridad o primaria. Lo que sucede después
depende de si la relación es opcional u obligatoria.
Relaciones opcionales
De forma predeterminada, para las relaciones opcionales, el valor de clave externa se establece en NULL. Esto
significa que el dependiente o el elemento secundario ya no está asociado con ninguna entidad de seguridad o
elemento primario. Por ejemplo, vamos a cargar un blog y a publicar y, después, quitar una de las entradas de la
navegación por la [Link] colección:

var post = [Link](e => [Link] == "Announcing F# 5");


[Link](post);

Al examinar la vista de depuración del seguimiento de cambios después de este cambio, se muestra lo siguiente:
El valor de FK se ha [Link] establecido en null ( BlogId: <null> FK Modified Originally 1 )
La [Link] navegación de referencia se ha establecido en null ( Blog: <null> )
La publicación se ha quitado de la [Link] navegación de colección ( Posts: [{Id: 1}] )

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: [{Id: 1}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Modified
Id: 2 PK
BlogId: <null> FK Modified Originally 1
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>
Tags: []

Observe que la publicación no está marcada como Deleted . Está marcado como Modified para que el valor de
FK de la base de datos se establezca en NULL cuando se llame a SaveChanges.
Relaciones obligatorias
No se permite establecer el valor de FK en null (y normalmente no es posible) para las relaciones necesarias. Por
lo tanto, el servidor de una relación necesaria significa que la entidad dependiente o secundaria debe volver a
ser primaria para una nueva entidad de seguridad principal o quitarse de la base de datos cuando se llama a
SaveChanges para evitar una infracción de la restricción referencial. Esto se conoce como "eliminación de
huérfanos" y es el comportamiento predeterminado en EF Core para las relaciones necesarias.
Por ejemplo, vamos a cambiar la relación entre blog y publicaciones para que sea necesaria y, a continuación,
ejecutar el mismo código que en el ejemplo anterior:

var post = [Link](e => [Link] == "Announcing F# 5");


[Link](post);

Al examinar la vista de depuración después de este cambio, se muestra lo siguiente:


La publicación se ha marcado como tal Deleted que se eliminará de la base de datos cuando se llame a
SaveChanges.
La [Link] navegación de referencia se ha establecido en null ( Blog: <null> ).
La publicación se ha quitado de la [Link] navegación de colección ( Posts: [{Id: 1}] ).

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Assets: <null>
Posts: [{Id: 1}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Deleted
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: <null>
Tags: []

Tenga en cuenta que el [Link] permanece sin cambios desde que para una relación requerida no se puede
establecer en NULL.
La llamada a SaveChanges da como resultado la eliminación de la publicación huérfana:

-- Executed DbCommand (0ms) [Parameters=[@p0='2' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Posts"
WHERE "Id" = @p0;
SELECT changes();

Eliminar el control de tiempo y volver a crear un elemento huérfano


De forma predeterminada, marcar huérfanos como Deleted sucede en cuanto se detectael cambio de relación.
Sin embargo, este proceso se puede retrasar hasta que se llame a SaveChanges. Esto puede ser útil para evitar
la creación de huérfanos de entidades que se han quitado de una entidad de seguridad o un elemento primario,
pero que se volverán a crear con una nueva entidad de seguridad o primaria antes de que se llame a
SaveChanges. [Link] se usa para establecer este tiempo. Por ejemplo:
[Link] = [Link];

var post = [Link](e => [Link]("Disassembly improvements"));


[Link](post);

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

[Link](post);

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

[Link]();

Después de quitar la entrada de la primera colección, el objeto no está marcado como Deleted en el ejemplo
anterior. En su lugar, EF Core está realizando un seguimiento de que la relación se rompe, aunque se trata de una
relación requerida. (El valor de FK se considera null en EF Core aunque no sea realmente nulo porque el tipo no
admite valores NULL. Esto se conoce como "null conceptual").

Post {Id: 3} Modified


Id: 3 PK
BlogId: <null> FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: []

La llamada a SaveChanges en este momento daría lugar a la eliminación de la publicación huérfana. Sin
embargo, si, como en el ejemplo anterior, post está asociado a un nuevo blog antes de que se llame a
SaveChanges, se corregirá correctamente en ese nuevo blog y ya no se considerará huérfano:

Post {Id: 3} Modified


Id: 3 PK
BlogId: 1 FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 1}
Tags: []

El SaveChanges llamado en este punto actualizará la publicación en la base de datos en lugar de eliminarla.
También es posible desactivar la eliminación automática de huérfanos. Esto producirá una excepción si se llama
a SaveChanges mientras se realiza el seguimiento de un huérfano. Por ejemplo, este código:

var dotNetBlog = [Link](e => [Link]).Single(e => [Link] == ".NET Blog");

[Link] = [Link];

var post = [Link](e => [Link] == "Announcing F# 5");


[Link](post);

[Link](); // Throws

Producirá esta excepción:

System. InvalidOperationException: se ha interrumpido la asociación entre las entidades ' blog ' y ' post ' con
el valor de clave ' {BlogId: 1} ', pero la relación está marcada como requerida o es implícitamente necesaria
porque la clave externa no admite valores NULL. Si se debe eliminar la entidad dependiente o secundaria
cuando se interrumpe una relación requerida, configure la relación para utilizar las eliminaciones en
cascada.

La eliminación de huérfanos, así como las eliminaciones en cascada, se puede forzar en cualquier momento
llamando a [Link]() . La combinación de esto con el establecimiento del tiempo de
eliminación de huérfanos en Never garantizará que los huérfanos nunca se eliminan a menos que se indique
explícitamente EF Core.
Cambiar una navegación de referencia
Cambiar la navegación de referencia de una relación de uno a varios tiene el mismo efecto que cambiar la
navegación por la colección en el otro extremo de la relación. Establecer la navegación de referencia de
dependent/Child a NULL equivale a quitar la entidad de la navegación de la colección de la entidad de seguridad
principal. Todos los cambios de corrección y base de datos se realizan como se describe en la sección anterior, lo
que incluye convertir la entidad en huérfana si se requiere la relación.
Relaciones de uno a uno opcionales
En el caso de las relaciones uno a uno, el cambio de una navegación de referencia hace que cualquier relación
anterior se haga más grave. En el caso de las relaciones opcionales, esto significa que el valor de FK del
dependiente o elemento secundario relacionado anteriormente se establece en NULL. Por ejemplo:

using var context = new BlogsContext();

var dotNetBlog = [Link](e => [Link]).Single(e => [Link] == ".NET Blog");


[Link] = new BlogAssets();

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

[Link]();

La vista de depuración antes de llamar a SaveChanges muestra que los recursos nuevos han reemplazado a los
activos existentes, que ahora está marcado como Modified con un [Link] valor de FK nulo:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Assets: {Id: -2147482629}
Posts: []
BlogAssets {Id: -2147482629} Added
Id: -2147482629 PK Temporary
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 1} Modified
Id: 1 PK
Banner: <null>
BlogId: <null> FK Modified Originally 1
Blog: <null>

Esto da como resultado una actualización e inserción cuando se llama a SaveChanges:


-- Executed DbCommand (0ms) [Parameters=[@p1='1' (DbType = String), @p0=NULL], CommandType='Text',
CommandTimeout='30']
UPDATE "Assets" SET "BlogId" = @p0
WHERE "Id" = @p1;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p2=NULL, @p3='1' (Nullable = true) (DbType = String)],


CommandType='Text', CommandTimeout='30']
INSERT INTO "Assets" ("Banner", "BlogId")
VALUES (@p2, @p3);
SELECT "Id"
FROM "Assets"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

Relaciones uno a uno necesarios


Si se ejecuta el mismo código que en el ejemplo anterior, pero esta vez con una relación uno a uno necesaria, se
muestra que el objeto asociado anteriormente BlogAssets ahora está marcado como Deleted , ya que se
convierte en huérfano cuando el nuevo BlogAssets ocupa su lugar:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Assets: {Id: -2147482639}
Posts: []
BlogAssets {Id: -2147482639} Added
Id: -2147482639 PK Temporary
Banner: <null>
BlogId: 1 FK
Blog: {Id: 1}
BlogAssets {Id: 1} Deleted
Id: 1 PK
Banner: <null>
BlogId: 1 FK
Blog: <null>

Esto da como resultado una eliminación de e Insert cuando se llama a SaveChanges:

-- Executed DbCommand (0ms) [Parameters=[@p0='1' (DbType = String)], CommandType='Text',


CommandTimeout='30']
DELETE FROM "Assets"
WHERE "Id" = @p0;
SELECT changes();

-- Executed DbCommand (0ms) [Parameters=[@p1=NULL, @p2='1' (DbType = String)], CommandType='Text',


CommandTimeout='30']
INSERT INTO "Assets" ("Banner", "BlogId")
VALUES (@p1, @p2);
SELECT "Id"
FROM "Assets"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

La temporización de marcar huérfanos como eliminados se puede cambiar de la misma manera que se muestra
para las navegaciones de la colección y tiene los mismos efectos.
Eliminar una entidad
Relaciones opcionales
Cuando una entidad se marca como Deleted , por ejemplo llamando a [Link] , se quitan las
referencias a la entidad eliminada de las navegaciones de otras entidades. En el caso de las relaciones
opcionales, los valores de FK de entidades dependientes se establecen en NULL.
Por ejemplo, vamos a marcar el blog de Visual Studio como Deleted :

using var context = new BlogsContext();

var vsBlog = [Link]


.Include(e => [Link])
.Include(e => [Link])
.Single(e => [Link] == "Visual Studio Blog");

[Link](vsBlog);

[Link]([Link]);

[Link]();

Al examinar la vista de depuración de Change Tracker antes de llamar a SaveChanges, se muestra:

Blog {Id: 2} Deleted


Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 2} Modified
Id: 2 PK
Banner: <null>
BlogId: <null> FK Modified Originally 2
Blog: <null>
Post {Id: 3} Modified
Id: 3 PK
BlogId: <null> FK Modified Originally 2
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: []
Post {Id: 4} Modified
Id: 4 PK
BlogId: <null> FK Modified Originally 2
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: <null>
Tags: []

Tenga en lo siguiente:
El blog se marca como Deleted .
Los recursos relacionados con el blog eliminado tienen un valor de FK null (
BlogId: <null> FK Modified Originally 2 ) y una navegación de referencia nula ( Blog: <null> )
Cada publicación relacionada con el blog eliminado tiene un valor de FK null (
BlogId: <null> FK Modified Originally 2 ) y una navegación de referencia nula ( Blog: <null> )
Relaciones obligatorias
El comportamiento de corrección para las relaciones necesarias es el mismo que para las relaciones opcionales,
salvo que las entidades dependientes o secundarias se marcan como Deleted porque no pueden existir sin una
entidad de seguridad o un elemento primario y se deben quitar de la base de datos cuando se llama a
SaveChanges para evitar una excepción de restricción referencial. Esto se conoce como "eliminación en cascada"
y es el comportamiento predeterminado en EF Core para las relaciones necesarias. Por ejemplo, si se ejecuta el
mismo código que en el ejemplo anterior, pero con una relación requerida, se obtiene la siguiente vista de
depuración antes de que se llame a SaveChanges:
Blog {Id: 2} Deleted
Id: 2 PK
Name: 'Visual Studio Blog'
Assets: {Id: 2}
Posts: [{Id: 3}, {Id: 4}]
BlogAssets {Id: 2} Deleted
Id: 2 PK
Banner: <null>
BlogId: 2 FK
Blog: {Id: 2}
Post {Id: 3} Deleted
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Tags: []
Post {Id: 4} Deleted
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: {Id: 2}
Tags: []

Como se esperaba, los elementos secundarios o dependientes se marcan ahora como Deleted . Sin embargo,
tenga en cuenta que las navegaciones en las entidades eliminadas no han cambiado. Esto puede parecer
extraño, pero evita la fragmentación completa de un gráfico de entidades eliminado borrando todas las
navegaciones. Es decir, el blog, el recurso y las entradas siguen conformando un gráfico de entidades incluso
después de haberse eliminado. Esto hace que sea mucho más fácil anular la eliminación de un gráfico de
entidades que en el caso de EF6 en el que se ha purgado el gráfico.
Temporización de eliminación en cascada y Realquiler
De forma predeterminada, la eliminación en cascada se produce tan pronto como la entidad de seguridad
principal se marca como Deleted . Esto es lo mismo que para eliminar huérfanos, como se ha descrito
anteriormente. Al igual que con la eliminación de huérfanos, este proceso se puede retrasar hasta que se llame a
SaveChanges, o incluso deshabilitado por completo, estableciendo la configuración
[Link] apropiada. Esto resulta útil de la misma manera que para eliminar
huérfanos, lo que incluye la reutilización de los elementos secundarios o dependientes de la eliminación de una
entidad de seguridad principal.
Las eliminaciones en cascada, así como la eliminación de huérfanos, se pueden forzar en cualquier momento
llamando a [Link]() . Al combinarlo con el establecimiento del control de tiempo de
eliminación en cascada en Never se garantizará que las eliminaciones en cascada no se produzcan a menos que
se indique explícitamente EF Core.

TIP
La eliminación en cascada y la eliminación de entidades huérfanas están estrechamente relacionadas. Ambas dan como
resultado la eliminación de entidades dependientes o secundarias cuando se interrumpe su relación con la entidad de
seguridad o primaria requerida. En el caso de la eliminación en cascada, esta interrupción de la relación tiene lugar porque
se elimina la propia entidad de seguridad o primaria. En el caso de las entidades huérfanas, la entidad de seguridad o
primaria sigue existiendo, pero ya no está relacionada con las entidades dependientes o secundarias.

Relaciones de varios a varios


Las relaciones de varios a varios en EF Core se implementan mediante una entidad join. Cada lado, la relación de
varios a varios está relacionada con esta entidad de combinación con una relación de uno a varios. Antes de EF
Core 5,0, esta entidad join tenía que definirse y asignarse explícitamente. A partir de EF Core 5,0, se puede crear
de forma implícita y oculta. Sin embargo, en ambos casos, el comportamiento subyacente es el mismo. En
primer lugar, veremos este comportamiento subyacente para comprender cómo funciona el seguimiento de
relaciones de varios a varios.
Cómo funcionan las relaciones varios a varios
Tenga en cuenta este modelo de EF Core que crea una relación de varios a varios entre entradas y etiquetas
mediante un tipo de entidad de combinación definido explícitamente:

public class Post


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

public int? BlogId { get; set; }


public Blog Blog { get; set; }

public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation


}

public class Tag


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

public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation


}

public class PostTag


{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag

public Post Post { get; set; } // Reference navigation


public Tag Tag { get; set; } // Reference navigation
}

Observe que el PostTag tipo de entidad de combinación contiene dos propiedades de clave externa. En este
modelo, para que una publicación esté relacionada con una etiqueta, debe haber una entidad de combinación
PostTag en la que el [Link] valor de clave externa coincida con el [Link] valor de clave principal y
donde el [Link] valor de clave externa coincida con el [Link] valor de clave principal. Por ejemplo:

using var context = new BlogsContext();

var post = [Link](e => [Link] == 3);


var tag = [Link](e => [Link] == 1);

[Link](new PostTag { PostId = [Link], TagId = [Link] });

[Link]([Link]);

Al examinar la vista de depuración de Change Tracker después de ejecutar este código, se muestra que la
entrada y la etiqueta están relacionadas con la nueva PostTag entidad de combinación:
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
PostTags: [{PostId: 3, TagId: 1}]
PostTag {PostId: 3, TagId: 1} Added
PostId: 3 PK FK
TagId: 1 PK FK
Post: {Id: 3}
Tag: {Id: 1}
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
PostTags: [{PostId: 3, TagId: 1}]

Observe que las navegaciones de la colección en Post y Tag se han corregido, al igual que las navegaciones
de referencia en PostTag . Estas relaciones se pueden manipular mediante navegaciones en lugar de valores de
FK, al igual que en todos los ejemplos anteriores. Por ejemplo, el código anterior se puede modificar para
agregar la relación estableciendo las navegaciones de referencia en la entidad de combinación:

[Link](new PostTag { Post = post, Tag = tag });

Esto produce exactamente el mismo cambio en claves externas y en las navegaciones que en el ejemplo anterior.
Omitir navegaciones

NOTE
Omitir las navegaciones se introdujeron en EF Core 5,0.

Manipular la tabla de combinación manualmente puede resultar complicado. A partir de EF Core 5,0, las
relaciones varios a varios se pueden manipular directamente mediante las navegaciones de colección especiales
que "omiten" la entidad de combinación. Por ejemplo, se pueden agregar dos navegaciones de omisión al
modelo anterior; uno de las etiquetas post a y el otro de la etiqueta a posts:
public class Post
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }

public int? BlogId { get; set; }


public Blog Blog { get; set; }

public IList<Tag> Tags { get; } = new List<Tag>(); // Skip collection navigation


public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation
}

public class Tag


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

public IList<Post> Posts { get; } = new List<Post>(); // Skip collection navigation


public IList<PostTag> PostTags { get; } = new List<PostTag>(); // Collection navigation
}

public class PostTag


{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag

public Post Post { get; set; } // Reference navigation


public Tag Tag { get; set; } // Reference navigation
}

Esta relación de varios a varios requiere la siguiente configuración para garantizar que las navegaciones de
omisión y las navegaciones normales se usan para la misma relación de varios a varios:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasMany(p => [Link])
.WithMany(p => [Link])
.UsingEntity<PostTag>(
j => [Link](t => [Link]).WithMany(p => [Link]),
j => [Link](t => [Link]).WithMany(p => [Link]));
}

Vea relaciones para obtener más información sobre la asignación de relaciones varios a varios.
Omitir las navegaciones tienen el aspecto y se comportan como navegación de colección normal. Sin embargo,
el modo en que trabajan con valores de clave externa es diferente. Vamos a asociar una publicación con una
etiqueta, pero esta vez mediante una omisión de navegación:

using var context = new BlogsContext();

var post = [Link](e => [Link] == 3);


var tag = [Link](e => [Link] == 1);

[Link](tag);

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

Tenga en cuenta que este código no usa la entidad join. En su lugar, simplemente agrega una entidad a una
colección de navegación de la misma manera que si se tratara de una relación de uno a varios. La vista de
depuración resultante es esencialmente la misma que antes:

Post {Id: 3} Unchanged


Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
PostTags: [{PostId: 3, TagId: 1}]
Tags: [{Id: 1}]
PostTag {PostId: 3, TagId: 1} Added
PostId: 3 PK FK
TagId: 1 PK FK
Post: {Id: 3}
Tag: {Id: 1}
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
PostTags: [{PostId: 3, TagId: 1}]
Posts: [{Id: 3}]

Observe que PostTag se ha creado automáticamente una instancia de la entidad de combinación con los
valores de FK establecidos en los valores PK de la etiqueta y la publicación que ahora están asociadas. Todas las
referencias normales y las navegaciones de colección se han corregido para que coincidan con estos valores de
FK. Además, dado que este modelo contiene saltos de navegación, también se han corregido. En concreto,
aunque agregamos la etiqueta a la [Link] navegación de omitir, la [Link] navegación inversa omitir en
el otro lado de esta relación también se ha corregido para que contenga la publicación asociada.
Merece la pena tener en cuenta que las relaciones de varios a varios subyacentes todavía se pueden manipular
directamente aunque se hayan superpuesto en la parte superior las navegaciones de omitir. Por ejemplo, la
etiqueta y la publicación pueden asociarse como hicimos antes de introducir omitir navegación:

[Link](new PostTag { Post = post, Tag = tag });

O usando valores de FK:

[Link](new PostTag { PostId = [Link], TagId = [Link] });

Esto hará que las navegaciones de omisión se solucionen correctamente, lo que da lugar a la misma salida de la
vista de depuración que en el ejemplo anterior.
Omitir solo navegación
En la sección anterior hemos agregado omitir las navegaciones, además de definir totalmente las dos relaciones
uno a varios subyacentes. Esto resulta útil para ilustrar lo que sucede con los valores de FK, pero a menudo no
es necesario. En su lugar, se puede definir la relación de varios a varios usando solo las navegaciones de
omisión. Así es como se define la relación de varios a varios en el modelo en la parte superior de este
documento. Con este modelo, se puede volver a asociar una publicación y una etiqueta agregando un
comentario a la [Link] navegación por omitir (o, como alternativa, agregando una etiqueta a la [Link]
navegación omitida):
using var context = new BlogsContext();

var post = [Link](e => [Link] == 3);


var tag = [Link](e => [Link] == 1);

[Link](tag);

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

Al examinar la vista de depuración después de hacer este cambio, se revela que EF Core ha creado una instancia
de Dictionary<string, object> para representar la entidad de combinación. Esta entidad join contiene PostsId
TagsId las propiedades de clave externa y que se han establecido para que coincidan con los valores PK de la
entrada y la etiqueta que están asociados.

Post {Id: 3} Unchanged


Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: [{Id: 1}]
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
Posts: [{Id: 3}]
PostTag (Dictionary<string, object>) {PostsId: 3, TagsId: 1} Added
PostsId: 3 PK FK
TagsId: 1 PK FK

Vea relaciones para obtener más información sobre las entidades de combinación implícitas y el uso de
Dictionary<string, object> tipos de entidad.

IMPORTANT
El tipo CLR que se usa para los tipos de entidad de combinación por Convención puede cambiar en futuras versiones para
mejorar el rendimiento. No dependa del tipo de combinación a menos que se haya Dictionary<string, object>
configurado explícitamente.

Unir entidades con cargas


Hasta ahora todos los ejemplos han utilizado un tipo de entidad de combinación (ya sea explícito o implícito)
que solo contiene las dos propiedades de clave externa necesarias para la relación de varios a varios. Ninguno
de estos valores de FK debe establecerse explícitamente por la aplicación al manipular las relaciones, ya que sus
valores proceden de las propiedades de clave principal de las entidades relacionadas. Esto permite a EF Core
crear instancias de la entidad de combinación sin datos que faltan.
Cargas con valores generados
EF Core admite la adición de propiedades adicionales al tipo de entidad de combinación. Esto se conoce como la
asignación de una "carga" a la entidad de combinación. Por ejemplo, vamos a agregar TaggedOn la propiedad a
la PostTag entidad de combinación:
public class PostTag
{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag

public DateTime TaggedOn { get; set; } // Payload


}

Esta propiedad de carga no se establecerá cuando EF Core crea una instancia de entidad de combinación. La
manera más común de tratar esto es usar propiedades de carga con valores generados automáticamente. Por
ejemplo, la TaggedOn propiedad se puede configurar para usar una marca de tiempo generada por el almacén
cuando se inserta cada nueva entidad:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Post>()
.HasMany(p => [Link])
.WithMany(p => [Link])
.UsingEntity<PostTag>(
j => [Link]<Tag>().WithMany(),
j => [Link]<Post>().WithMany(),
j => [Link](e => [Link]).HasDefaultValueSql("CURRENT_TIMESTAMP"));
}

Ahora se puede etiquetar una publicación de la misma manera que antes:

using var context = new BlogsContext();

var post = [Link](e => [Link] == 3);


var tag = [Link](e => [Link] == 1);

[Link](tag);

[Link]();

[Link]([Link]);

Al examinar la vista de depuración de Change Tracker después de llamar a SaveChanges, se muestra que la
propiedad payload se ha establecido correctamente:

Post {Id: 3} Unchanged


Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: <null>
Tags: [{Id: 1}]
PostTag {PostId: 3, TagId: 1} Unchanged
PostId: 3 PK FK
TagId: 1 PK FK
TaggedOn: '12/29/2020 8:13:21 PM'
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
Posts: [{Id: 3}]

Establecer explícitamente los valores de carga


Después de en el ejemplo anterior, vamos a agregar una propiedad de carga que no usa un valor generado
automáticamente:
public class PostTag
{
public int PostId { get; set; } // First part of composite PK; FK to Post
public int TagId { get; set; } // Second part of composite PK; FK to Tag

public DateTime TaggedOn { get; set; } // Auto-generated payload property


public string TaggedBy { get; set; } // Not-generated payload property
}

Ahora se puede etiquetar una publicación de la misma manera que antes, y la entidad de combinación se creará
automáticamente. A continuación, se puede tener acceso a esta entidad mediante uno de los mecanismos
descritos en acceso a entidades de las que se ha realizado un seguimiento. Por ejemplo, el código siguiente
utiliza DbSet<TEntity>.Find para tener acceso a la instancia de la entidad de combinación:

using var context = new BlogsContext();

var post = [Link](e => [Link] == 3);


var tag = [Link](e => [Link] == 1);

[Link](tag);

[Link]();

var joinEntity = [Link]<PostTag>().Find([Link], [Link]);

[Link] = "ajcvickers";

[Link]();

[Link]([Link]);

Una vez que se encuentra la entidad de combinación, se puede manipular de la manera normal; en este ejemplo,
para establecer la TaggedBy propiedad de carga antes de llamar a SaveChanges.

NOTE
Tenga en cuenta que [Link]() aquí se requiere una llamada a para proporcionar a EF Core una
oportunidad de detectar el cambio de propiedad de navegación y crear la instancia de la entidad de combinación antes de
que Find se use. Consulte detección y notificaciones de cambios para obtener más información.

Como alternativa, la entidad de combinación se puede crear explícitamente para asociar una publicación con
una etiqueta. Por ejemplo:

using var context = new BlogsContext();

var post = [Link](e => [Link] == 3);


var tag = [Link](e => [Link] == 1);

[Link](
new PostTag { PostId = [Link], TagId = [Link], TaggedBy = "ajcvickers" });

[Link]();

[Link]([Link]);

Por último, otra manera de establecer los datos de carga es reemplazar SaveChanges o usar el
[Link] evento para procesar entidades antes de actualizar la base de datos. Por ejemplo:
public override int SaveChanges()
{
foreach (var entityEntry in [Link]<PostTag>())
{
if ([Link] == [Link])
{
[Link] = "ajcvickers";
}
}

return [Link]();
}
Detección y notificaciones de cambios
12/03/2021 • 24 minutes to read • Edit Online

Cada instancia de DbContext realiza un seguimiento de los cambios realizados en las entidades. Estas entidades
de las que se realiza un seguimiento, a su vez, impulsan los cambios en la base de datos cuando se llama a
SaveChanges. Esto se trata en Change Tracking en EF Core, y en este documento se da por supuesto que se
entienden los Estados de las entidades y los aspectos básicos del seguimiento de cambios de Entity Framework
Core (EF Core).
El seguimiento de los cambios de propiedades y relaciones requiere que DbContext pueda detectar estos
cambios. En este documento se explica cómo se produce esta detección y cómo usar las notificaciones de
propiedad o los proxies de seguimiento de cambios para forzar la detección inmediata de los cambios.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Seguimiento de cambios de instantánea


De forma predeterminada, EF Core crea una instantánea de los valores de propiedad de cada entidad cuando se
realiza un seguimiento por primera instancia de DbContext. Los valores almacenados en esta instantánea se
comparan con los valores actuales de la entidad para determinar qué valores de propiedad han cambiado.
Esta detección de cambios se produce cuando se llama a SaveChanges para asegurarse de que se detectan
todos los valores cambiados antes de enviar actualizaciones a la base de datos. Sin embargo, la detección de
cambios también se produce en otros momentos para asegurarse de que la aplicación funciona con información
de seguimiento actualizada. La detección de cambios se puede forzar en cualquier momento mediante una
llamada a [Link]() .
Cuando se necesita la detección de cambios
La detección de cambios es necesaria cuando se ha cambiado una propiedad o navegación sin utilizar EF Core
para realizar este cambio. Por ejemplo, considere la posibilidad de cargar blogs y entradas y, a continuación,
realizar cambios en estas entidades:

using var context = new BlogsContext();


var blog = [Link](e => [Link]).First(e => [Link] == ".NET Blog");

// Change a property value


[Link] = ".NET Blog (Updated!)";

// Add a new entity to a navigation


[Link](
new Post
{
Title = "What’s next for [Link]?", Content = ".NET 5.0 was released recently and has come
with many..."
});

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

Al examinar la vista de depuración de Change Tracker antes de llamar a [Link]() , se


muestra que los cambios realizados no se han detectado y, por lo tanto, no se reflejan en los Estados de la
entidad y en los datos de propiedad modificados

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog (Updated!)' Originally '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, <not found>]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

En concreto, el estado de la entrada de blog sigue siendo Unchanged y la nueva publicación no aparece como
entidad sometida a seguimiento. (El astutos observará que las propiedades notifican sus nuevos valores, aunque
EF Core no hayan detectado todavía estos cambios. Esto se debe a que la vista de depuración Lee los valores
actuales directamente de la instancia de la entidad).
Compare esto con la vista de depuración después de llamar a DetectChanges:

Blog {Id: 1} Modified


Id: 1 PK
Name: '.NET Blog (Updated!)' Modified Originally '.NET Blog'
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}]
Post {Id: -2147482643} Added
Id: -2147482643 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 was released recently and has come with many...'
Title: 'What's next for [Link]?'
Blog: {Id: 1}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

Ahora el blog está marcado correctamente como Modified y se ha detectado la nueva publicación y se realiza
su seguimiento como Added .
Al principio de esta sección, se indica que se necesita la detección de cambios cuando no se usa EF Core para
realizar el cambio. Esto es lo que sucede en el código anterior. Es decir, los cambios en la propiedad y la
navegación se realizan directamente en las instancias de la entidad y no mediante métodos de EF Core.
Compare esto con el siguiente código que modifica las entidades de la misma manera, pero esta vez mediante
métodos EF Core:
using var context = new BlogsContext();
var blog = [Link](e => [Link]).First(e => [Link] == ".NET Blog");

// Change a property value


[Link](blog).Property(e => [Link]).CurrentValue = ".NET Blog (Updated!)";

// Add a new entity to the DbContext


[Link](
new Post
{
Blog = blog,
Title = "What’s next for [Link]?",
Content = ".NET 5.0 was released recently and has come with many..."
});

[Link]([Link]);

En este caso, la vista de depuración del seguimiento de cambios muestra que se conocen todos los Estados de
las entidades y las modificaciones de propiedades, aunque no se ha producido la detección de cambios. Esto se
debe a [Link] que es un método EF Core, lo que significa que EF Core conoce
inmediatamente el cambio realizado por este método. Del mismo modo, la llamada a [Link] permite a
EF Core saber de inmediato la nueva entidad y realizar su seguimiento de forma adecuada.

TIP
No intente evitar la detección de cambios mediante el uso de métodos EF Core para realizar cambios en la entidad. Esto
suele ser más complicado y se comporta menos bien que realizar cambios en las entidades de la manera normal. La
intención de este documento es informar de Cuándo se necesita detectar cambios y cuándo no lo está. La intención es no
fomentar la prevención de la detección de cambios.

Métodos que detectan automáticamente los cambios


DetectChanges() se llama automáticamente a los métodos en los que es probable que esto afecte a los
resultados. Estos métodos son:
[Link] y [Link] , para asegurarse de que se detectan todos los
cambios antes de actualizar la base de datos.
[Link]() y [Link]<TEntity>() , para asegurarse de que los Estados de entidad y
las propiedades modificadas están actualizados.
[Link](), para asegurarse de que el resultado es preciso.
[Link](), para garantizar los Estados de entidad correctos para las entidades
principal y primaria antes de la cascada.
DbSet<TEntity>.Local, para asegurarse de que el gráfico sometido a seguimiento esté actualizado.
También hay algunos lugares donde se produce la detección de cambios solo en una única instancia de entidad,
en lugar de en el gráfico completo de entidades de las que se ha realizado un seguimiento. Estos lugares son:
Al utilizar [Link] , para asegurarse de que el estado y las propiedades modificadas de la entidad
están actualizados.
Cuando EntityEntry se usan métodos como Property , Collection Reference o Member para garantizar que
las modificaciones de propiedades, los valores actuales, etc. están actualizados.
Cuando una entidad dependiente/secundaria se va a eliminar porque se ha roto una relación requerida. Esto
detecta cuándo no se debe eliminar una entidad porque se ha vuelto a crear un elemento primario.
La detección local de los cambios de una sola entidad se puede desencadenar explícitamente mediante una
llamada a [Link]() .
NOTE
Los cambios en la detección local pueden pasar por alto algunos cambios que encontraría una detección completa. Esto
sucede cuando las acciones en cascada resultantes de cambios no detectados en otras entidades afectan a la entidad en
cuestión. En tales casos, la aplicación puede necesitar forzar un examen completo de todas las entidades mediante una
llamada explícita a [Link]() .

Deshabilitar la detección automática de cambios


El rendimiento de la detección de cambios no es un cuello de botella para la mayoría de las aplicaciones. Sin
embargo, la detección de cambios puede suponer un problema de rendimiento para algunas aplicaciones que
realizan el seguimiento de miles de entidades. (El número exacto dependerá de muchas cosas, como el número
de propiedades de la entidad). Por este motivo, se puede deshabilitar la detección automática de cambios
mediante [Link] . Por ejemplo, considere el procesamiento de entidades de
combinación en una relación de varios a varios con cargas:

public override int SaveChanges()


{
foreach (var entityEntry in [Link]<PostTag>()) // Detects changes automatically
{
if ([Link] == [Link])
{
[Link] = "ajcvickers";
[Link] = [Link];
}
}

try
{
[Link] = false;
return [Link](); // Avoid automatically detecting changes again here
}
finally
{
[Link] = true;
}
}

Como sabemos de la sección anterior, [Link]<TEntity>() y [Link] detectan


automáticamente los cambios. Sin embargo, después de llamar a las entradas, el código no realiza ningún
cambio en el estado de la entidad o propiedad. (Establecer valores de propiedades normales en entidades
agregadas no produce cambios de estado). Por lo tanto, el código deshabilita la detección de cambios
automática innecesaria al llamar al método SaveChanges de base. El código también usa un bloque try/finally
para asegurarse de que se restaure la configuración predeterminada incluso si se produce un error en
SaveChanges.

TIP
No asuma que el código debe deshabilitar la detección automática de cambios para que funcione correctamente. Esto solo
es necesario cuando la generación de perfiles de una aplicación que realiza un seguimiento de muchas entidades indica
que el rendimiento de la detección de cambios es un problema.

Detección de cambios y conversiones de valores


Para usar el seguimiento de cambios de instantánea con un tipo de entidad, EF Core debe ser capaz de:
Crear una instantánea de cada valor de propiedad cuando se realiza el seguimiento de la entidad
Compara este valor con el valor actual de la propiedad.
Generar un código hash para el valor
Esto lo controla automáticamente EF Core para los tipos que se pueden asignar directamente a la base de datos.
Sin embargo, cuando se utiliza un convertidor de valores para asignar una propiedad, dicho convertidor debe
especificar cómo realizar estas acciones. Esto se logra con un comparador de valores y se describe en detalle en
la documentación de los comparadores de valores .

Entidades de notificación
El seguimiento de cambios de instantánea se recomienda para la mayoría de las aplicaciones. Sin embargo, las
aplicaciones que realizan el seguimiento de muchas entidades o realizan muchos cambios en esas entidades
pueden beneficiarse de la implementación de entidades que notifican automáticamente EF Core cuando
cambian sus valores de propiedad y navegación. Estos se conocen como "entidades de notificación".
Implementación de entidades de notificación
Las entidades de notificación hacen uso de las INotifyPropertyChanging INotifyPropertyChanged interfaces e,
que forman parte de la biblioteca de clases base (BCL) de .net. Estas interfaces definen eventos que se deben
desencadenar antes y después de cambiar un valor de propiedad. Por ejemplo:

public class Blog : INotifyPropertyChanging, INotifyPropertyChanged


{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;

private int _id;

public int Id
{
get => _id;
set
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Id)));
_id = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Id)));
}
}

private string _name;

public string Name


{
get => _name;
set
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(nameof(Name)));
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}

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


}

Además, las navegaciones de las colecciones deben implementar INotifyCollectionChanged ; en el ejemplo


anterior, esto se cumple mediante el uso ObservableCollection<T> de un de posts. EF Core también se
suministra con una ObservableHashSet<T> implementación que tiene búsquedas más eficaces a costa de una
ordenación estable.
Normalmente, la mayor parte de este código de notificación se mueve a una clase base no asignada. Por
ejemplo:
public class Blog : NotifyingEntity
{
private int _id;

public int Id
{
get => _id;
set => SetWithNotify(value, out _id);
}

private string _name;

public string Name


{
get => _name;
set => SetWithNotify(value, out _name);
}

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


}

public abstract class NotifyingEntity : INotifyPropertyChanging, INotifyPropertyChanged


{
protected void SetWithNotify<T>(T value, out T field, [CallerMemberName] string propertyName = "")
{
NotifyChanging(propertyName);
field = value;
NotifyChanged(propertyName);
}

public event PropertyChangingEventHandler PropertyChanging;


public event PropertyChangedEventHandler PropertyChanged;

private void NotifyChanged(string propertyName)


=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

private void NotifyChanging(string propertyName)


=> PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}

Configuración de entidades de notificación


No hay ninguna manera de que EF Core valide que INotifyPropertyChanging o INotifyPropertyChanged estén
totalmente implementados para su uso con EF Core. En concreto, algunos usos de estas interfaces lo hacen con
las notificaciones solo en determinadas propiedades, en lugar de en todas las propiedades (incluidas las
navegaciones) según lo requiera EF Core. Por esta razón, EF Core no se enlaza automáticamente a estos eventos.
En su lugar, EF Core debe estar configurado para usar estas entidades de notificación. Normalmente, esto se
hace para todos los tipos de entidad mediante una llamada a [Link] . Por
ejemplo:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]([Link]);
}

(La estrategia también se puede establecer de manera diferente para los distintos tipos de entidad mediante,
pero suele ser un valor de [Link] contraproducción, ya que
DetectChanges sigue siendo necesario para los tipos que no son entidades de notificación).
El seguimiento de cambios de notificación completo requiere que tanto INotifyPropertyChanging como
INotifyPropertyChanged estén implementados. Esto permite guardar los valores originales justo antes de
cambiar el valor de la propiedad, evitando la necesidad de EF Core para crear una instantánea al realizar el
seguimiento de la entidad. Los tipos de entidad que implementan solo INotifyPropertyChanged se pueden usar
con EF Core. En este caso, EF todavía crea una instantánea al realizar el seguimiento de una entidad para realizar
un seguimiento de los valores originales, pero usa las notificaciones para detectar los cambios inmediatamente,
en lugar de tener que llamar a DetectChanges.
Los diferentes ChangeTrackingStrategy valores se resumen en la tabla siguiente.

C H A N GET RA C K IN GST RAT E VA LO RES O RIGIN A L ES DE


GY IN T ERFA C ES N EC ESA RIA S N EC ESITA DET EC TC H A N GES IN STA N TÁ N EA S

Instantánea Ninguno Sí Sí

ChangedNotifications INotifyPropertyChanged No Sí

ChangingAndChangedNotifi INotifyPropertyChanged y No No
cations INotifyPropertyChanging

ChangingAndChangedNotifi INotifyPropertyChanged y No Sí
cationsWithOriginalValues INotifyPropertyChanging

Usar entidades de notificación


Las entidades de notificación se comportan como cualquier otra entidad, salvo que realizar cambios en las
instancias de la entidad no requiere una llamada a [Link]() para detectar estos cambios.
Por ejemplo:

using var context = new BlogsContext();


var blog = [Link](e => [Link]).First(e => [Link] == ".NET Blog");

// Change a property value


[Link] = ".NET Blog (Updated!)";

// Add a new entity to a navigation


[Link](
new Post
{
Title = "What’s next for [Link]?", Content = ".NET 5.0 was released recently and has come
with many..."
});

[Link]([Link]);

Con las entidades normales, la vista de depuración de Change Tracker mostró que estos cambios no se
detectaron hasta que se llamó a DetectChanges. Al examinar la vista de depuración cuando se usan las
entidades de notificación, se muestra que estos cambios se han detectado inmediatamente:
Blog {Id: 1} Modified
Id: 1 PK
Name: '.NET Blog (Updated!)' Modified
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}]
Post {Id: -2147482643} Added
Id: -2147482643 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 was released recently and has come with many...'
Title: 'What's next for [Link]?'
Blog: {Id: 1}
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}

Proxies de seguimiento de cambios


NOTE
Los proxies de seguimiento de cambios se introdujeron en EF Core 5,0.

EF Core pueden generar dinámicamente tipos de proxy que implementan INotifyPropertyChanging y


INotifyPropertyChanged . Esto requiere la instalación del paquete NuGet Microsoft. EntityFrameworkCore.
Proxies y la habilitación de los proxies de seguimiento de cambios con, UseChangeTrackingProxies por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link]();

La creación de un proxy dinámico implica la creación de un nuevo tipo .net dinámico (mediante la
implementación de los proxies de . Core ), que hereda del tipo de entidad y, a continuación, invalida todos los
establecedores de propiedad. Por tanto, los tipos de entidad de los servidores proxy deben ser tipos que se
pueden heredar de y deben tener propiedades que se puedan invalidar. Además, las navegaciones de la
colección creadas explícitamente deben implementar, INotifyCollectionChanged por ejemplo:
public class Blog
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }

public virtual IList<Post> Posts { get; } = new ObservableCollection<Post>();


}

public class Post


{
public virtual int Id { get; set; }
public virtual string Title { get; set; }
public virtual string Content { get; set; }

public virtual int BlogId { get; set; }


public virtual Blog Blog { get; set; }
}

Una desventaja importante de los proxies de seguimiento de cambios es que EF Core siempre debe realizar un
seguimiento de las instancias del proxy, nunca instancias del tipo de entidad subyacente. Esto se debe a que las
instancias del tipo de entidad subyacente no generarán notificaciones, lo que significa que se omitirán los
cambios realizados en estas entidades.
EF Core crea instancias de proxy automáticamente al consultar la base de datos, por lo que generalmente se
limita a realizar el seguimiento de nuevas instancias de entidad. Estas instancias deben crearse mediante los
CreateProxy métodos de extensión y no de la manera normal mediante new . Esto significa que el código de los
ejemplos anteriores ahora debe hacer uso de CreateProxy :

using var context = new BlogsContext();


var blog = [Link](e => [Link]).First(e => [Link] == ".NET Blog");

// Change a property value


[Link] = ".NET Blog (Updated!)";

// Add a new entity to a navigation


[Link](
[Link]<Post>(
p =>
{
[Link] = "What’s next for [Link]?";
[Link] = ".NET 5.0 was released recently and has come with many...";
}));

[Link]([Link]);

Eventos de seguimiento de cambios


EF Core activa el [Link] evento cuando se realiza un seguimiento de una entidad por primera
vez. Los cambios futuros de estado de la entidad producen [Link] eventos. Consulte
Eventos de .NET en EF Core para obtener más información.

NOTE
El StateChanged evento no se desencadena cuando se realiza el seguimiento de una entidad por primera vez, aunque el
Estado haya cambiado de Detached a uno de los otros Estados. Asegúrese de escuchar los StateChanged Tracked
eventos y para obtener todas las notificaciones pertinentes.
Resolución de identidades en EF Core
12/03/2021 • 31 minutes to read • Edit Online

Un DbContext solo puede realizar el seguimiento de una instancia de entidad con cualquier valor de clave
principal determinado. Esto significa que se deben resolver varias instancias de una entidad con el mismo valor
de clave en una sola instancia. Esto se denomina "resolución de identidad". La resolución de identidad garantiza
Entity Framework Core (EF Core) realiza el seguimiento de un gráfico coherente sin ambigüedades sobre las
relaciones o los valores de propiedad de las entidades.

TIP
En este documento se da por supuesto que se entienden los Estados de las entidades y los aspectos básicos del
seguimiento de cambios de EF Core. Consulte Change Tracking en EF Core para obtener más información sobre estos
temas.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Introducción
En el código siguiente se consulta una entidad y, a continuación, se intenta adjuntar una instancia diferente con
el mismo valor de clave principal:

using var context = new BlogsContext();

var blogA = [Link](e => [Link] == 1);


var blogB = new Blog { Id = 1, Name = ".NET Blog (All new!)" };

try
{
[Link](blogB); // This will throw
}
catch (Exception e)
{
[Link]($"{[Link]().FullName}: {[Link]}");
}

La ejecución de este código produce la siguiente excepción:

System. InvalidOperationException: no se puede realizar el seguimiento de la instancia del tipo de entidad '
blog ' porque ya se está realizando el seguimiento de otra instancia con el valor de clave ' {ID: 1} '. Al
adjuntar entidades existentes, asegúrese de que solo se adjunta una instancia de entidad con un valor de
clave determinado.

EF Core requiere una única instancia porque:


Los valores de propiedad pueden ser diferentes en varias instancias. Al actualizar la base de datos, EF Core
necesita saber qué valores de propiedad se deben usar.
Las relaciones con otras entidades pueden ser diferentes entre varias instancias. Por ejemplo, "Bloga" puede
estar relacionado con una colección diferente de entradas que "blogB".
La excepción anterior se encuentra normalmente en estas situaciones:
Al intentar actualizar una entidad
Al intentar realizar el seguimiento de un gráfico serializado de entidades
Cuando no se puede establecer un valor de clave que no se genera automáticamente
Cuando se reutiliza una instancia de DbContext para varias unidades de trabajo
En las secciones siguientes se describe cada una de estas situaciones.

Actualización de una entidad


Hay varios enfoques diferentes para actualizar una entidad con nuevos valores, como se describe en Change
Tracking en EF Core y realizar un seguimiento explícitode las entidades. Estos enfoques se describen a
continuación en el contexto de la resolución de identidades. Un aspecto importante que se debe tener en cuenta
es que cada uno de los enfoques utiliza una consulta o una llamada a uno de Update o Attach , pero nunca a
ambos .
Actualizar llamada
A menudo, la entidad que se va a actualizar no procede de una consulta en DbContext que vamos a usar para
SaveChanges. Por ejemplo, en una aplicación Web, se puede crear una instancia de la entidad a partir de la
información de una solicitud POST. La manera más sencilla de controlarlo es usar [Link] o
DbSet<TEntity>.Update . Por ejemplo:

public static void UpdateFromHttpPost1(Blog blog)


{
using var context = new BlogsContext();

[Link](blog);

[Link]();
}

En este caso:
Solo se crea una instancia de la entidad.
La instancia de la entidad no se consulta desde la base de datos como parte de la realización de la
actualización.
Todos los valores de propiedad se actualizarán en la base de datos, independientemente de si realmente han
cambiado o no.
Se realiza una operación de ida y vuelta de base de datos.
Después, la consulta aplica los cambios
Normalmente, no se sabe qué valores de propiedad se han cambiado realmente cuando se crea una entidad a
partir de información en una solicitud POST o similar. A menudo, basta con actualizar todos los valores de la
base de datos, como hicimos en el ejemplo anterior. Sin embargo, si la aplicación administra muchas entidades y
solo un número pequeño de ellos tiene cambios reales, puede ser útil limitar las actualizaciones enviadas. Esto
puede lograrse mediante la ejecución de una consulta para realizar el seguimiento de las entidades tal como
existen actualmente en la base de datos y, a continuación, aplicar los cambios a estas entidades sometidas a
seguimiento. Por ejemplo:
public static void UpdateFromHttpPost2(Blog blog)
{
using var context = new BlogsContext();

var trackedBlog = [Link]([Link]);

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

[Link]();
}

En este caso:
Solo se realiza el seguimiento de una única instancia de la entidad; el devuelto por la consulta por la base de
datos Find .
Update , Attach ,, etc. no se usan.
En la base de datos solo se actualizan los valores de propiedad que han cambiado realmente.
Se realizan dos viajes de ida y vuelta de base de datos.
EF Core tiene algunas aplicaciones auxiliares para transferir valores de propiedad como esta. Por ejemplo,
[Link] copiará todos los valores del objeto especificado y los establecerá en el objeto
sometido a seguimiento:

public static void UpdateFromHttpPost3(Blog blog)


{
using var context = new BlogsContext();

var trackedBlog = [Link]([Link]);

[Link](trackedBlog).[Link](blog);

[Link]();
}

SetValues acepta varios tipos de objetos, incluidos los objetos de transferencia de datos (DTO) con nombres de
propiedad que coinciden con las propiedades del tipo de entidad. Por ejemplo:

public static void UpdateFromHttpPost4(BlogDto dto)


{
using var context = new BlogsContext();

var trackedBlog = [Link]([Link]);

[Link](trackedBlog).[Link](dto);

[Link]();
}

O un diccionario con entradas de nombre/valor para los valores de propiedad:


public static void UpdateFromHttpPost5(Dictionary<string, object> propertyValues)
{
using var context = new BlogsContext();

var trackedBlog = [Link](propertyValues["Id"]);

[Link](trackedBlog).[Link](propertyValues);

[Link]();
}

Vea obtener acceso a entidades de las que se ha realizado un seguimiento para obtener más información sobre
cómo trabajar con valores de propiedad como este.
Usar valores originales
Hasta ahora, cada enfoque ha ejecutado una consulta antes de realizar la actualización o ha actualizado todos
los valores de propiedad independientemente de si han cambiado o no. Para actualizar solo los valores que han
cambiado sin realizar consultas como parte de la actualización, se requiere información específica sobre los
valores de propiedad que han cambiado. Una forma habitual de obtener esta información es devolver los
valores actuales y originales en HTTP POST o similar. Por ejemplo:

public static void UpdateFromHttpPost6(Blog blog, Dictionary<string, object> originalValues)


{
using var context = new BlogsContext();

[Link](blog);
[Link](blog).[Link](originalValues);

[Link]();
}

En este código, la entidad con valores modificados se adjunta primero. Esto hace que EF Core realice el
seguimiento de la entidad en el Unchanged Estado; es decir, sin valores de propiedad marcados como
modificados. A continuación, el Diccionario de valores originales se aplica a esta entidad sometida a
seguimiento. Esto marcará como propiedades modificadas con distintos valores actuales y originales. Las
propiedades que tienen los mismos valores actual y original no se marcan como modificadas.
En este caso:
Solo se realiza el seguimiento de una sola instancia de la entidad, mediante Attach.
La instancia de la entidad no se consulta desde la base de datos como parte de la realización de la
actualización.
La aplicación de los valores originales garantiza que solo se actualicen en la base de datos los valores de
propiedad que hayan cambiado realmente.
Se realiza una operación de ida y vuelta de base de datos.
Al igual que con los ejemplos de la sección anterior, los valores originales no tienen que pasarse como un
diccionario; también funcionará una instancia de entidad o DTO.

TIP
Aunque este enfoque tiene características atractivas, requiere el envío de los valores originales de la entidad al cliente web
y desde él. Considere detenidamente si esta complejidad adicional merece las ventajas; para muchas aplicaciones, uno de
los enfoques más sencillos es más pragmático.
Asociar un gráfico serializado
EF Core funciona con gráficos de entidades conectadas a través de claves externas y propiedades de navegación,
como se describe en cambiar las claves externas y las navegaciones. Si estos gráficos se crean fuera de EF Core
mediante, por ejemplo, de un archivo JSON, pueden tener varias instancias de la misma entidad. Estos
duplicados deben resolverse en instancias únicas antes de que se pueda realizar el seguimiento del gráfico.
Gráficos sin duplicados
Antes de continuar, es importante reconocer que:
Los serializadores suelen tener opciones para administrar bucles e instancias duplicadas en el gráfico.
La elección del objeto que se usa como la raíz del gráfico puede ayudar a reducir o quitar los duplicados.
Si es posible, use las opciones de serialización y elija raíces que no produzcan duplicados. Por ejemplo, el código
siguiente usa [Link] para serializar una lista de blogs cada uno con sus entradas asociadas:

using var context = new BlogsContext();

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

var serialized = [Link](


blogs,
new JsonSerializerSettings { ReferenceLoopHandling = [Link], Formatting =
[Link] });

[Link](serialized);

El JSON generado a partir de este código es:


[
{
"Id": 1,
"Name": ".NET Blog",
"Summary": "Posts about .NET",
"Posts": [
{
"Id": 1,
"Title": "Announcing the Release of EF Core 5.0",
"Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
"BlogId": 1
},
{
"Id": 2,
"Title": "Announcing F# 5",
"Content": "F# 5 is the latest version of F#, the functional programming language...",
"BlogId": 1
}
]
},
{
"Id": 2,
"Name": "Visual Studio Blog",
"Summary": "Posts about Visual Studio",
"Posts": [
{
"Id": 3,
"Title": "Disassembly improvements for optimized managed debugging",
"Content": "If you are focused on squeezing out the last bits of performance for your .NET service
or...",
"BlogId": 2
},
{
"Id": 4,
"Title": "Database Profiling with Visual Studio",
"Content": "Examine when database queries were executed and measure how long the take using...",
"BlogId": 2
}
]
}
]

Tenga en cuenta que no hay ningún blog o publicación duplicados en el archivo JSON. Esto significa que las
llamadas simples a Update funcionarán para actualizar estas entidades en la base de datos:

public static void UpdateBlogsFromJson(string json)


{
using var context = new BlogsContext();

var blogs = [Link]<List<Blog>>(json);

foreach (var blog in blogs)


{
[Link](blog);
}

[Link]();
}

Administrar duplicados
El código del ejemplo anterior serializaba cada blog con sus entradas asociadas. Si se cambia para serializar
cada publicación con su blog asociado, los duplicados se introducen en el JSON serializado. Por ejemplo:
using var context = new BlogsContext();

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

var serialized = [Link](


posts,
new JsonSerializerSettings { ReferenceLoopHandling = [Link], Formatting =
[Link] });

[Link](serialized);

El JSON serializado ahora tiene el siguiente aspecto:

[
{
"Id": 1,
"Title": "Announcing the Release of EF Core 5.0",
"Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
"BlogId": 1,
"Blog": {
"Id": 1,
"Name": ".NET Blog",
"Summary": "Posts about .NET",
"Posts": [
{
"Id": 2,
"Title": "Announcing F# 5",
"Content": "F# 5 is the latest version of F#, the functional programming language...",
"BlogId": 1
}
]
}
},
{
"Id": 2,
"Title": "Announcing F# 5",
"Content": "F# 5 is the latest version of F#, the functional programming language...",
"BlogId": 1,
"Blog": {
"Id": 1,
"Name": ".NET Blog",
"Summary": "Posts about .NET",
"Posts": [
{
"Id": 1,
"Title": "Announcing the Release of EF Core 5.0",
"Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
"BlogId": 1
}
]
}
},
{
"Id": 3,
"Title": "Disassembly improvements for optimized managed debugging",
"Content": "If you are focused on squeezing out the last bits of performance for your .NET service
or...",
"BlogId": 2,
"Blog": {
"Id": 2,
"Name": "Visual Studio Blog",
"Summary": "Posts about Visual Studio",
"Posts": [
{
"Id": 4,
"Title": "Database Profiling with Visual Studio",
"Content": "Examine when database queries were executed and measure how long the take using...",
"BlogId": 2
}
]
}
},
{
"Id": 4,
"Title": "Database Profiling with Visual Studio",
"Content": "Examine when database queries were executed and measure how long the take using...",
"BlogId": 2,
"Blog": {
"Id": 2,
"Name": "Visual Studio Blog",
"Summary": "Posts about Visual Studio",
"Posts": [
{
"Id": 3,
"Title": "Disassembly improvements for optimized managed debugging",
"Content": "If you are focused on squeezing out the last bits of performance for your .NET service
or...",
"BlogId": 2
}
]
}
}
]

Observe que el gráfico ahora incluye varias instancias de blog con el mismo valor de clave, así como varias
instancias posteriores con el mismo valor de clave. Al intentar realizar un seguimiento de este gráfico como
hicimos en el ejemplo anterior, se producirá lo siguiente:

System. InvalidOperationException: no se puede realizar el seguimiento de la instancia del tipo de entidad '
post ' porque ya se está realizando el seguimiento de otra instancia con el valor de clave ' {ID: 2} '. Al
adjuntar entidades existentes, asegúrese de que solo se adjunta una instancia de entidad con un valor de
clave determinado.

Podemos corregir esto de dos maneras:


Usar opciones de serialización de JSON que conserven las referencias
Realizar la resolución de identidad mientras se realiza el seguimiento del gráfico
Conservación de las referencias
[Link] ofrece la PreserveReferencesHandling opción de controlar esto. Por ejemplo:

var serialized = [Link](


posts,
new JsonSerializerSettings
{
PreserveReferencesHandling = [Link], Formatting = [Link]
});

El JSON resultante tiene ahora el siguiente aspecto:

{
"$id": "1",
"$values": [
{
"$id": "2",
"Id": 1,
"Title": "Announcing the Release of EF Core 5.0",
"Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
"Content": "Announcing the release of EF Core 5.0, a full featured cross-platform...",
"BlogId": 1,
"Blog": {
"$id": "3",
"Id": 1,
"Name": ".NET Blog",
"Summary": "Posts about .NET",
"Posts": [
{
"$ref": "2"
},
{
"$id": "4",
"Id": 2,
"Title": "Announcing F# 5",
"Content": "F# 5 is the latest version of F#, the functional programming language...",
"BlogId": 1,
"Blog": {
"$ref": "3"
}
}
]
}
},
{
"$ref": "4"
},
{
"$id": "5",
"Id": 3,
"Title": "Disassembly improvements for optimized managed debugging",
"Content": "If you are focused on squeezing out the last bits of performance for your .NET service
or...",
"BlogId": 2,
"Blog": {
"$id": "6",
"Id": 2,
"Name": "Visual Studio Blog",
"Summary": "Posts about Visual Studio",
"Posts": [
{
"$ref": "5"
},
{
"$id": "7",
"Id": 4,
"Title": "Database Profiling with Visual Studio",
"Content": "Examine when database queries were executed and measure how long the take using...",
"BlogId": 2,
"Blog": {
"$ref": "6"
}
}
]
}
},
{
"$ref": "7"
}
]
}

Tenga en cuenta que este JSON ha reemplazado duplicados con referencias como "$ref": "5" que hacen
referencia a la instancia ya existente en el gráfico. Se puede realizar un seguimiento de este gráfico mediante las
llamadas simples a Update , como se muestra anteriormente.
La [Link] compatibilidad de las bibliotecas de clases base (BCL) de .net tiene una opción similar que
produce el mismo resultado. Por ejemplo:

var serialized = [Link](


posts, new JsonSerializerOptions { ReferenceHandler = [Link], WriteIndented = true
});

Resolver duplicados
Si no es posible eliminar duplicados en el proceso de serialización, [Link] proporciona una
manera de controlar esto. TrackGraph funciona como Add Attach y, Update salvo que genera una devolución
de llamada para cada instancia de entidad antes de realizar el seguimiento. Esta devolución de llamada se puede
usar para realizar un seguimiento de la entidad u omitirla. Por ejemplo:

public static void UpdatePostsFromJsonWithIdentityResolution(string json)


{
using var context = new BlogsContext();

var posts = [Link]<List<Post>>(json);

foreach (var post in posts)


{
[Link](
post, node =>
{
var keyValue = [Link]("Id").CurrentValue;
var entityType = [Link];

var existingEntity = [Link]()


.FirstOrDefault(
e => Equals([Link], entityType)
&& Equals([Link]("Id").CurrentValue, keyValue));

if (existingEntity == null)
{
[Link]($"Tracking {[Link]()} entity with key value
{keyValue}");

[Link] = [Link];
}
else
{
[Link]($"Discarding duplicate {[Link]()} entity with key
value {keyValue}");
}
});
}

[Link]();
}

Para cada entidad del gráfico, este código hará lo siguiente:


Buscar el tipo de entidad y el valor de clave de la entidad
Buscar la entidad con esta clave en el seguimiento de cambios
Si se encuentra la entidad, no se realiza ninguna otra acción, ya que la entidad es un duplicado.
Si no se encuentra la entidad, se realiza el seguimiento estableciendo el estado en Modified

La salida de la ejecución de este código es:


Tracking EntityType: Post entity with key value 1
Tracking EntityType: Blog entity with key value 1
Tracking EntityType: Post entity with key value 2
Discarding duplicate EntityType: Post entity with key value 2
Tracking EntityType: Post entity with key value 3
Tracking EntityType: Blog entity with key value 2
Tracking EntityType: Post entity with key value 4
Discarding duplicate EntityType: Post entity with key value 4

IMPORTANT
En este código se supone que todos los duplicados son idénticos . Esto hace que sea seguro elegir arbitrariamente
uno de los duplicados para realizar el seguimiento mientras se descartan los demás. Si los duplicados pueden diferir, el
código deberá decidir cómo determinar cuál usar y cómo combinar los valores de propiedad y navegación juntos.

NOTE
Para simplificar, en este código se supone que cada entidad tiene una propiedad de clave principal denominada Id . Esto
se podría codificar en una interfaz o clase base abstracta. Como alternativa, la propiedad o propiedades de la clave
principal se pueden obtener a partir de los IEntityType metadatos de modo que este código funcione con cualquier tipo
de entidad.

No se pueden establecer los valores de clave


Los tipos de entidad se configuran a menudo para usar valores de clave generados automáticamente. Este es el
valor predeterminado para las propiedades Integer y GUID de las claves no compuestas. Sin embargo, si el tipo
de entidad no está configurado para usar los valores de clave generados automáticamente, debe establecerse
un valor de clave explícito antes de realizar el seguimiento de la entidad. Por ejemplo, con el siguiente tipo de
entidad:

public class Pet


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

public string Name { get; set; }


}

Considere el código que intenta rastrear dos nuevas instancias de entidad sin establecer los valores de clave:

using var context = new BlogsContext();

[Link](new Pet { Name = "Smokey" });

try
{
[Link](new Pet { Name = "Clippy" }); // This will throw
}
catch (Exception e)
{
[Link]($"{[Link]().FullName}: {[Link]}");
}

Este código producirá:


System. InvalidOperationException: no se puede realizar el seguimiento de la instancia del tipo de entidad '
PET ' porque ya se está realizando el seguimiento de otra instancia con el valor de clave ' {ID: 0} '. Al adjuntar
entidades existentes, asegúrese de que solo se adjunta una instancia de entidad con un valor de clave
determinado.

La solución para esto es para establecer los valores de clave explícitamente o configurar la propiedad clave para
usar los valores de clave generados. Vea valores generados para obtener más información.

Sobreutilización de una única instancia de DbContext


DbContext está diseñado para representar una unidad de trabajo de corta duración, como se describe en la
configuración y la inicialización de DbContext, y se ha elaborado en Change Tracking en EF Core. No seguir esta
guía facilita la ejecución en situaciones en las que se realiza un intento de realizar un seguimiento de varias
instancias de la misma entidad. Los ejemplos comunes son:
Usar la misma instancia de DbContext para configurar el estado de prueba y, a continuación, ejecutar la
prueba. Esto suele hacer que DbContext siga realizando el seguimiento de una instancia de la entidad a partir
de la configuración de pruebas, mientras que después intenta adjuntar una nueva instancia de la prueba
adecuada. En su lugar, use una instancia de DbContext diferente para configurar el estado de prueba y el
código de prueba adecuado.
Usar una instancia compartida DbContext en un repositorio o código similar. En su lugar, asegúrese de que el
repositorio usa una única instancia de DbContext para cada unidad de trabajo.

Resolución y consultas de identidad


La resolución de identidad se produce automáticamente cuando se realiza un seguimiento de las entidades
desde una consulta. Esto significa que, si ya se ha realizado un seguimiento de una instancia de una entidad con
un valor de clave determinado, se usará esta instancia de seguimiento existente en lugar de crear una nueva
instancia. Esto tiene una consecuencia importante: si los datos han cambiado en la base de datos, no se
reflejarán en los resultados de la consulta. Esta es una buena razón para usar una nueva instancia de DbContext
para cada unidad de trabajo, como se describe en la configuración y la inicialización de dbcontext, y se ha
elaborado en Change Tracking en EF Core.

IMPORTANT
Es importante entender que EF Core siempre ejecuta una consulta LINQ en un DbSet en la base de datos y solo devuelve
resultados en función de lo que hay en la base de datos. Sin embargo, para una consulta de seguimiento, si ya se realiza el
seguimiento de las entidades devueltas, se utilizan las instancias de las que se ha realizado un seguimiento en lugar de
crear instancias a partir de los datos de la base de datos.

Reload() o GetDatabaseValues() se puede utilizar cuando es necesario actualizar las entidades de las que se
realiza un seguimiento con los datos más recientes de la base de datos. Consulte acceso a entidades de las que
se ha realizado un seguimiento para obtener más información.
A diferencia de las consultas de seguimiento, las consultas sin seguimiento no realizan la resolución de
identidades. Esto significa que las consultas sin seguimiento pueden devolver duplicados como en el caso de
serialización de JSON descrito anteriormente. Esto no suele ser un problema si los resultados de la consulta se
van a serializar y enviar al cliente.
TIP
No realice habitualmente una consulta sin seguimiento y, a continuación, asocie las entidades devueltas al mismo
contexto. Esto será más lento y más difícil de conseguir que el uso de una consulta de seguimiento.

Las consultas sin seguimiento no realizan la resolución de identidad porque esto afecta al rendimiento de la
transmisión por secuencias de un gran número de entidades de una consulta. Esto se debe a que la resolución
de identidad requiere realizar un seguimiento de cada instancia devuelta para que se pueda usar en lugar de
crear posteriormente un duplicado.
A partir de EF Core 5,0, las consultas sin seguimiento se pueden forzar para realizar la resolución de identidades
mediante AsNoTrackingWithIdentityResolution<TEntity>(IQueryable<TEntity>) . La consulta realizará un
seguimiento de las instancias devueltas (sin realizar el seguimiento de forma normal) y garantiza que no se
crean duplicados en los resultados de la consulta.

Reemplazar la igualdad de objetos


EF Core usa la igualdad de referencia al comparar instancias de entidad. Este es el caso incluso si los tipos de
entidad invalidan [Link](Object) o cambian la igualdad de objetos. Sin embargo, hay un lugar donde la
igualdad de invalidación puede afectar al comportamiento de EF Core: cuando las navegaciones de la colección
usan la igualdad invalidada en lugar de la igualdad de la referencia y, por lo tanto, notifican varias instancias
como iguales.
Debido a esto, se recomienda evitar la igualdad de la entidad. Si se usa, asegúrese de crear navegaciones de
colección que fuercen la igualdad de referencia. Por ejemplo, cree un comparador de igualdad que use la
igualdad de referencia:

public sealed class ReferenceEqualityComparer : IEqualityComparer<object>


{
private ReferenceEqualityComparer()
{
}

public static ReferenceEqualityComparer Instance { get; } = new ReferenceEqualityComparer();

bool IEqualityComparer<object>.Equals(object x, object y) => x == y;

int IEqualityComparer<object>.GetHashCode(object obj) => [Link](obj);


}

(A partir de .NET 5, esto se incluye en la BCL como ReferenceEqualityComparer ).


Este comparador se puede usar al crear navegaciones de colección. Por ejemplo:

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


= new HashSet<Order>([Link]);

Comparar propiedades de clave


Además de las comparaciones de igualdad, también es necesario ordenar los valores de clave. Esto es
importante para evitar los interbloqueos al actualizar varias entidades en una única llamada a SaveChanges.
Todos los tipos que se usan para las propiedades principales, alternativas o de clave externa, así como los que se
usan para los índices únicos, deben implementar IComparable<T> y IEquatable<T> . Los tipos que
normalmente se usan como claves (int, GUID, String, etc.) ya admiten estas interfaces. Los tipos de clave
personalizados pueden agregar estas interfaces.
Características de Change Tracking adicionales
12/03/2021 • 25 minutes to read • Edit Online

En este documento se tratan varias características y escenarios que implican el seguimiento de cambios.

TIP
En este documento se da por supuesto que se entienden los Estados de las entidades y los aspectos básicos del
seguimiento de cambios de EF Core. Consulte Change Tracking en EF Core para obtener más información sobre estos
temas.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Diferencias entre Add y AddAsync


Entity Framework Core (EF Core) proporciona métodos asincrónicos cada vez que se usa ese método, se puede
producir una interacción con la base de datos. También se proporcionan métodos sincrónicos para evitar la
sobrecarga cuando se usan bases de datos que no admiten el acceso asincrónico de alto rendimiento.
[Link] y DbSet<TEntity>.Add normalmente no tienen acceso a la base de datos, ya que estos métodos
solo inician el seguimiento de las entidades. Sin embargo, algunas formas de generación de valores pueden
tener acceso a la base de datos para generar un valor de clave. El único generador de valores que realiza esta y
se distribuye con EF Core es HiLoValueGenerator<TValue> . No es habitual utilizar este generador. nunca se
configura de forma predeterminada. Esto significa que la mayoría de las aplicaciones deben usar Add , y no
AddAsync .

Otros métodos similares como Update , Attach y Remove no tienen sobrecargas asincrónicas porque nunca
generan nuevos valores de clave y, por lo tanto, no necesitan tener acceso a la base de datos.

AddRange , UpdateRange , AttachRange y RemoveRange .


DbSet<TEntity> y DbContext proporcionan versiones alternativas de Add , Update , Attach y Remove que
aceptan varias instancias en una sola llamada. Estos métodos son AddRange , UpdateRange , AttachRange y,
RemoveRange respectivamente.
Estos métodos se proporcionan por comodidad. El uso de un método "Range" tiene la misma funcionalidad que
varias llamadas al método equivalente que no es de intervalo. No hay ninguna diferencia de rendimiento
significativa entre los dos enfoques.

NOTE
Esto es diferente de EF6, donde se AddRange llama a y a Add ambos automáticamente DetectChanges , pero la
llamada Add varias veces hizo que DetectChanges se llamara varias veces en lugar de una vez. Esto hace que sea
AddRange más eficaz en EF6. En EF Core, ninguno de estos métodos llama automáticamente a DetectChanges .

DbContext frente a métodos DbSet


Muchos métodos, incluidos Add , Update , Attach y Remove , tienen implementaciones en DbSet<TEntity> y
DbContext . Estos métodos tienen exactamente el mismo comportamiento para los tipos de entidad normales.
Esto se debe a que el tipo CLR de la entidad se asigna a un solo tipo de entidad del modelo de EF Core. Por lo
tanto, el tipo CLR define totalmente Dónde encaja la entidad en el modelo, por lo que el DbSet que se va a usar
se puede determinar de forma implícita.
La excepción a esta regla es cuando se usan tipos de entidad de tipo compartido, que se introdujeron en EF Core
5,0, principalmente para entidades de combinación de varios a varios. Cuando se usa un tipo de entidad de tipo
compartido, primero se debe crear un DbSet para el tipo de modelo de EF Core que se está usando. Los
métodos como Add , Update , Attach y Remove se pueden usar en DbSet sin ninguna ambigüedad en cuanto
a qué tipo de modelo de EF Core se está usando.
Los tipos de entidad de tipo compartido se usan de forma predeterminada para las entidades de combinación
en las relaciones de varios a varios. Un tipo de entidad de tipo compartido también puede configurarse
explícitamente para su uso en una relación de varios a varios. Por ejemplo, el código siguiente configura
Dictionary<string, int> como un tipo de entidad de combinación:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
modelBuilder
.SharedTypeEntity<Dictionary<string, int>>(
"PostTag",
b =>
{
[Link]<int>("TagId");
[Link]<int>("PostId");
});

[Link]<Post>()
.HasMany(p => [Link])
.WithMany(p => [Link])
.UsingEntity<Dictionary<string, int>>(
"PostTag",
j => [Link]<Tag>().WithMany(),
j => [Link]<Post>().WithMany());
}

Al cambiar las claves externas y las navegaciones , se muestra cómo asociar dos entidades mediante el
seguimiento de una nueva instancia de entidad join. El código siguiente hace esto para el
Dictionary<string, int> tipo de entidad de tipo compartido que se usa para la entidad de combinación:

using var context = new BlogsContext();

var post = [Link](e => [Link] == 3);


var tag = [Link](e => [Link] == 1);

var joinEntitySet = [Link]<Dictionary<string, int>>("PostTag");


var joinEntity = new Dictionary<string, int> { ["PostId"] = [Link], ["TagId"] = [Link] };
[Link](joinEntity);

[Link]([Link]);

[Link]();

Observe que [Link]<TEntity>(String) se usa para crear un DbSet para el PostTag tipo de entidad. Este
DbSet se puede usar para llamar a Add con la nueva instancia de la entidad join.
IMPORTANT
El tipo CLR que se usa para los tipos de entidad de combinación por Convención puede cambiar en futuras versiones para
mejorar el rendimiento. No dependa de ningún tipo de entidad de combinación concreta, a menos que se haya
configurado explícitamente como se hace Dictionary<string, int> en el código anterior.

Propiedad frente al acceso a campos


A partir de EF Core 3,0, el acceso a las propiedades de la entidad usa el campo de respaldo de la propiedad de
forma predeterminada. Esto es eficaz y evita que se desencadenen efectos secundarios al llamar a captadores y
establecedores de propiedad. Por ejemplo, este es el modo en que la carga diferida puede evitar desencadenar
bucles infinitos. Vea campos de respaldo para obtener más información sobre cómo configurar los campos de
respaldo en el modelo.
A veces puede ser deseable que EF Core genere efectos secundarios al modificar los valores de propiedad. Por
ejemplo, cuando se enlazan datos a entidades, el establecimiento de una propiedad puede generar
notificaciones al U.I. que no se producen al establecer el campo directamente. Esto se puede lograr cambiando el
PropertyAccessMode para:
Todos los tipos de entidad del modelo mediante [Link]
Todas las propiedades y navegaciones de un tipo de entidad específico mediante
EntityTypeBuilder<TEntity>.UsePropertyAccessMode
Una propiedad específica mediante [Link]
Una navegación específica mediante [Link]
Los modos de acceso de propiedad y harán que Field PreferField EF Core tenga acceso al valor de propiedad
a través de su campo de respaldo. Del mismo modo, y hará que Property PreferProperty EF Core tenga acceso
al valor de propiedad a través de su captador y establecedor.
Si Field Property se usan o y EF Core no pueden tener acceso al valor mediante el campo o
captador/establecedor de propiedad respectivamente, EF Core producirá una excepción. Esto garantiza que EF
Core siempre use el acceso de campo o propiedad cuando cree que es.
Por otro lado, los PreferField modos y revertirán PreferProperty al uso de la propiedad o el campo de
respaldo, respectivamente, si no es posible usar el acceso preferido. PreferField es el valor predeterminado de
EF Core 3,0 en adelante. Esto significa que EF Core usará los campos siempre que pueda, pero no producirá un
error si se debe tener acceso a una propiedad a través de su captador o establecedor en su lugar.
FieldDuringConstruction y PreferFieldDuringConstruction configure EF Core para usar los campos de respaldo
solo al crear instancias de entidad. Esto permite que las consultas se ejecuten sin efectos secundarios de
captador y establecedor, mientras que los cambios de propiedad posteriores de EF Core provocarán estos
efectos secundarios. PreferFieldDuringConstruction era el valor predeterminado antes de EF Core 3,0.
Los diferentes modos de acceso a propiedades se resumen en la tabla siguiente:

P REF EREN C IA DE C REA C IÓ N DE


P RO P ERT YA C C ESSM C REA C IÓ N DE EN T IDA DES DE
O DE REF EREN C IA EN T IDA DES RESERVA RESERVA

Field Campo Campo Produce Produce

Property Propiedad Propiedad Produce Produce

PreferField Campo Campo Propiedad Propiedad


P REF EREN C IA DE C REA C IÓ N DE
P RO P ERT YA C C ESSM C REA C IÓ N DE EN T IDA DES DE
O DE REF EREN C IA EN T IDA DES RESERVA RESERVA

PreferProperty Propiedad Propiedad Campo Campo

FieldDuringConstructionPropiedad Campo Campo Produce

Propiedad
PreferFieldDuringConstruction Campo Campo Propiedad

Valores temporales
EF Core crea valores de clave temporales al realizar el seguimiento de nuevas entidades que tendrán valores de
clave reales generados por la base de datos cuando se llame a SaveChanges. Consulte Change Tracking en EF
Core para obtener información general sobre cómo se usan estos valores temporales.
Obtener acceso a valores temporales
A partir de EF Core 3,0, los valores temporales se almacenan en el seguimiento de cambios y no se establecen
directamente en instancias de entidad. Sin embargo, estos valores temporales se exponen al usar los distintos
mecanismos para tener acceso a las entidadesde las que se ha realizado un seguimiento. Por ejemplo, el código
siguiente obtiene acceso a un valor temporal mediante [Link] :

using var context = new BlogsContext();

var blog = new Blog { Name = ".NET Blog" };

[Link](blog);

[Link]($"[Link] set on entity is {[Link]}");


[Link]($"[Link] tracked by EF is {[Link](blog).Property(e => [Link]).CurrentValue}");

El resultado de este código es:

[Link] set on entity is 0


[Link] tracked by EF is -2147482643

[Link] se puede usar para comprobar los valores temporales.


Manipular valores temporales
A veces resulta útil trabajar explícitamente con valores temporales. Por ejemplo, se puede crear una colección de
nuevas entidades en un cliente web y, a continuación, volver a serializarse en el servidor. Los valores de clave
externa son una manera de configurar relaciones entre estas entidades. En el código siguiente se usa este
enfoque para asociar un gráfico de nuevas entidades por clave externa, a la vez que se permite que se generen
valores de clave reales cuando se llama a SaveChanges.
var blogs = new List<Blog> { new Blog { Id = -1, Name = ".NET Blog" }, new Blog { Id = -2, Name = "Visual
Studio Blog" } };

var posts = new List<Post>


{
new Post
{
Id = -1,
BlogId = -1,
Title = "Announcing the Release of EF Core 5.0",
Content = "Announcing the release of EF Core 5.0, a full featured cross-platform..."
},
new Post
{
Id = -2,
BlogId = -2,
Title = "Disassembly improvements for optimized managed debugging",
Content = "If you are focused on squeezing out the last bits of performance for your .NET service
or..."
}
};

using var context = new BlogsContext();

foreach (var blog in blogs)


{
[Link](blog).Property(e => [Link]).IsTemporary = true;
}

foreach (var post in posts)


{
[Link](post).Property(e => [Link]).IsTemporary = true;
}

[Link]([Link]);

[Link]();

[Link]([Link]);

Tenga en lo siguiente:
Los números negativos se utilizan como valores de clave temporales; Esto no es necesario, pero es una
Convención común para evitar conflictos de claves.
La [Link] propiedad FK tiene asignado el mismo valor negativo que la clave principal del blog
asociado.
Los valores PK se marcan como temporales estableciendo IsTemporary una vez que se realiza el seguimiento
de cada entidad. Esto es necesario porque se supone que cualquier valor de clave proporcionado por la
aplicación es un valor de clave real.
Al examinar la vista de depuración de Change Tracker antes de llamar a SaveChanges, se muestra que los
valores PK están marcados como temporales y que las publicaciones están asociadas a los blogs correctos,
incluida la corrección de las navegaciones:
Blog {Id: -2} Added
Id: -2 PK Temporary
Name: 'Visual Studio Blog'
Posts: [{Id: -2}]
Blog {Id: -1} Added
Id: -1 PK Temporary
Name: '.NET Blog'
Posts: [{Id: -1}]
Post {Id: -2} Added
Id: -2 PK Temporary
BlogId: -2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: -2}
Tags: []
Post {Id: -1} Added
Id: -1 PK Temporary
BlogId: -1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: -1}

Después de llamar a SaveChanges , estos valores temporales se han reemplazado por valores reales generados
por la base de datos:

Blog {Id: 1} Unchanged


Id: 1 PK
Name: '.NET Blog'
Posts: [{Id: 1}]
Blog {Id: 2} Unchanged
Id: 2 PK
Name: 'Visual Studio Blog'
Posts: [{Id: 2}]
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: []
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Tags: []

Trabajar con valores predeterminados


EF Core permite que una propiedad obtenga su valor predeterminado de la base de datos cuando SaveChanges
se llama a. Al igual que con los valores de clave generados, EF Core solo usará un valor predeterminado de la
base de datos si no se ha establecido explícitamente ningún valor. Por ejemplo, considere el siguiente tipo de
entidad:
public class Token
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime ValidFrom { get; set; }
}

La ValidFrom propiedad está configurada para obtener un valor predeterminado de la base de datos:

modelBuilder
.Entity<Token>()
.Property(e => [Link])
.HasDefaultValueSql("CURRENT_TIMESTAMP");

Al insertar una entidad de este tipo, EF Core permitirá a la base de datos generar el valor a menos que se haya
establecido un valor explícito en su lugar. Por ejemplo:

using var context = new BlogsContext();

[Link](
new Token { Name = "A" },
new Token { Name = "B", ValidFrom = new DateTime(1111, 11, 11, 11, 11, 11) });

[Link]();

[Link]([Link]);

En la vista de depuración del seguimiento de cambios se muestra que el primer token fue ValidFrom generado
por la base de datos, mientras que el segundo token usó el valor establecido explícitamente:

Token {Id: 1} Unchanged


Id: 1 PK
Name: 'A'
ValidFrom: '12/30/2020 6:36:06 PM'
Token {Id: 2} Unchanged
Id: 2 PK
Name: 'B'
ValidFrom: '11/11/1111 11:11:11 AM'

NOTE
El uso de valores predeterminados de base de datos requiere que la columna de base de datos tenga una restricción de
valor predeterminado. Esto lo hace automáticamente EF Core migraciones cuando se usa HasDefaultValueSql o
HasDefaultValue . Asegúrese de crear la restricción predeterminada en la columna de otra manera cuando no use
migraciones de EF Core.

Usar propiedades que aceptan valores NULL


EF Core puede determinar si se ha establecido una propiedad comparando el valor de la propiedad con el valor
predeterminado de CLR para ese tipo. Esto funciona bien en la mayoría de los casos, pero significa que el valor
predeterminado de CLR no se puede insertar explícitamente en la base de datos. Por ejemplo, considere una
entidad con una propiedad de entero:
public class Foo1
{
public int Id { get; set; }
public int Count { get; set; }
}

Donde esa propiedad está configurada para tener una base de datos predeterminada de-1:

modelBuilder
.Entity<Foo1>()
.Property(e => [Link])
.HasDefaultValue(-1);

La intención es que se use el valor predeterminado de-1 siempre que no se establezca un valor explícito. Sin
embargo, no se puede distinguir si se establece el valor en 0 (el valor predeterminado de CLR para enteros) en
EF Core no se establece ningún valor, lo que significa que no es posible insertar 0 para esta propiedad. Por
ejemplo:

using var context = new BlogsContext();

var fooA = new Foo1 { Count = 10 };


var fooB = new Foo1 { Count = 0 };
var fooC = new Foo1();

[Link](fooA, fooB, fooC);


[Link]();

[Link]([Link] == 10);
[Link]([Link] == -1); // Not what we want!
[Link]([Link] == -1);

Tenga en cuenta que la instancia en la que Count se estableció explícitamente en 0 todavía obtiene el valor
predeterminado de la base de datos, que no es lo que se pretendía. Una manera fácil de solucionarlo es hacer
que la Count propiedad acepte valores NULL:

public class Foo2


{
public int Id { get; set; }
public int? Count { get; set; }
}

Esto hace que el valor predeterminado de CLR sea NULL, en lugar de 0, lo que significa que se insertará 0
cuando se establezca explícitamente:

using var context = new BlogsContext();

var fooA = new Foo2 { Count = 10 };


var fooB = new Foo2 { Count = 0 };
var fooC = new Foo2();

[Link](fooA, fooB, fooC);


[Link]();

[Link]([Link] == 10);
[Link]([Link] == 0);
[Link]([Link] == -1);
Usar campos de respaldo que aceptan valores NULL

NOTE
Este patrón de campo de respaldo que acepta valores NULL es compatible con EF Core 5,0 y versiones posteriores.

El problema de hacer que la propiedad acepte valores NULL, por lo que puede que no sea conceptualmente
aceptable en el modelo de dominio. Forzar que la propiedad acepte valores NULL, por tanto, pone en peligro el
modelo.
A partir de EF Core 5,0, la propiedad puede dejarse que no admita valores NULL, y solo el campo de respaldo
acepta valores NULL. Por ejemplo:

public class Foo3


{
public int Id { get; set; }

private int? _count;

public int Count


{
get => _count ?? -1;
set => _count = value;
}
}

Esto permite que se inserte el valor predeterminado de CLR (0) si la propiedad se establece explícitamente en 0,
aunque no es necesario exponer la propiedad como que acepta valores NULL en el modelo de dominio. Por
ejemplo:

using var context = new BlogsContext();

var fooA = new Foo3 { Count = 10 };


var fooB = new Foo3 { Count = 0 };
var fooC = new Foo3();

[Link](fooA, fooB, fooC);


[Link]();

[Link]([Link] == 10);
[Link]([Link] == 0);
[Link]([Link] == -1);

Campos de respaldo que aceptan valores NULL para las propiedades bool
Este patrón es especialmente útil cuando se usan propiedades bool con valores predeterminados generados por
el almacén. Dado que el valor predeterminado de CLR para bool es "false", significa que "false" no se puede
insertar explícitamente mediante el patrón normal. Por ejemplo, considere un User tipo de entidad:
public class User
{
public int Id { get; set; }
public string Name { get; set; }

private bool? _isAuthorized;

public bool IsAuthorized


{
get => _isAuthorized ?? true;
set => _isAuthorized = value;
}
}

La IsAuthorized propiedad se configura con un valor predeterminado de la base de datos "true":

modelBuilder
.Entity<User>()
.Property(e => [Link])
.HasDefaultValue(true);

La IsAuthorized propiedad se puede establecer en "true" o "false" explícitamente antes de la inserción, o bien se
puede dejar sin establecer, en cuyo caso se usará el valor predeterminado de la base de datos:

using var context = new BlogsContext();

var userA = new User { Name = "Mac" };


var userB = new User { Name = "Alice", IsAuthorized = true };
var userC = new User { Name = "Baxter", IsAuthorized = false }; // Always deny Baxter access!

[Link](userA, userB, userC);

[Link]();

La salida de SaveChanges cuando se usa SQLite muestra que el valor predeterminado de la base de datos se usa
para Mac, mientras que los valores explícitos están establecidos para Alice y Baxter:

-- Executed DbCommand (0ms) [Parameters=[@p0='Mac' (Size = 3)], CommandType='Text', CommandTimeout='30']


INSERT INTO "User" ("Name")
VALUES (@p0);
SELECT "Id", "IsAuthorized"
FROM "User"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p0='True' (DbType = String), @p1='Alice' (Size = 5)],


CommandType='Text', CommandTimeout='30']
INSERT INTO "User" ("IsAuthorized", "Name")
VALUES (@p0, @p1);
SELECT "Id"
FROM "User"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

-- Executed DbCommand (0ms) [Parameters=[@p0='False' (DbType = String), @p1='Baxter' (Size = 6)],


CommandType='Text', CommandTimeout='30']
INSERT INTO "User" ("IsAuthorized", "Name")
VALUES (@p0, @p1);
SELECT "Id"
FROM "User"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
Solo valores predeterminados de esquema
A veces resulta útil tener valores predeterminados en el esquema de la base de datos creado por EF Core
migraciones sin EF Core que nunca use estos valores para las inserciones. Esto puede lograrse configurando la
propiedad como, [Link] por ejemplo:

modelBuilder
.Entity<Bar>()
.Property(e => [Link])
.HasDefaultValue(-1)
.ValueGeneratedNever();
Depuración de Change Tracker
12/03/2021 • 13 minutes to read • Edit Online

El seguimiento de cambios de Entity Framework Core (EF Core) genera dos tipos de resultados para ayudar con
la depuración:
[Link] una vista legible de todas las entidades de las que se realiza un
seguimiento.
Los mensajes de registro de nivel de depuración se generan cuando el seguimiento de cambios detecta el
estado y corrige las relaciones

TIP
En este documento se da por supuesto que se entienden los Estados de las entidades y los aspectos básicos del
seguimiento de cambios de EF Core. Consulte Change Tracking en EF Core para obtener más información sobre estos
temas.

TIP
Puede ejecutar y depurar en todo el código de este documento descargando el código de ejemplo de GitHub.

Vista de depuración de Change Tracker


Se puede tener acceso a la vista de depuración Change Tracker en el depurador del IDE. Por ejemplo, con Visual
Studio:

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

[Link]([Link]);

La vista de depuración tiene una forma abreviada y un formato largo. La forma abreviada muestra las entidades
sometidas a seguimiento, su estado y sus valores de clave. El formato largo también incluye todos los valores de
propiedad y navegación y el estado.
Vista corta
Echemos un vistazo a un ejemplo de vista de depuración con el modelo que se muestra al final de este
documento. En primer lugar, realizaremos un seguimiento de algunas entidades y las colocaremos en algunos
Estados diferentes, por lo que tenemos buenos datos de seguimiento de cambios para ver:

using var context = new BlogsContext();

var blogs = [Link]


.Include(e => [Link]).ThenInclude(e => [Link])
.Include(e => [Link])
.ToList();

// Mark something Added


blogs[0].[Link](
new Post
{
Title = "What’s next for [Link]?",
Content = ".NET 5.0 was released recently and has come with many new features and..."
});

// Mark something Deleted


blogs[1].[Link](blogs[1].Posts[1]);

// Make something Modified


blogs[0].Name = ".NET Blog (All new!)";

[Link]();

La impresión de la vista corta en este punto, como se muestra arriba, da como resultado el siguiente resultado:

Blog {Id: 1} Modified AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0}


Blog {Id: 2} Unchanged AK {AssetsId: 3a54b880-2b9d-486b-9403-dc2e52d36d65}
BlogAssets {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} Unchanged FK {Id: 3a54b880-2b9d-486b-9403-
dc2e52d36d65}
BlogAssets {Id: ed727978-1ffe-4709-baee-73913e8e44a0} Unchanged FK {Id: ed727978-1ffe-4709-baee-
73913e8e44a0}
Post {Id: -2147482643} Added FK {BlogId: 1}
Post {Id: 1} Unchanged FK {BlogId: 1}
Post {Id: 2} Unchanged FK {BlogId: 1}
Post {Id: 3} Unchanged FK {BlogId: 2}
Post {Id: 4} Deleted FK {BlogId: 2}
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 1} Unchanged FK {PostsId: 1} FK {TagsId: 1}
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 3} Unchanged FK {PostsId: 1} FK {TagsId: 3}
PostTag (Dictionary<string, object>) {PostsId: 2, TagsId: 1} Unchanged FK {PostsId: 2} FK {TagsId: 1}
PostTag (Dictionary<string, object>) {PostsId: 3, TagsId: 2} Unchanged FK {PostsId: 3} FK {TagsId: 2}
PostTag (Dictionary<string, object>) {PostsId: 4, TagsId: 2} Deleted FK {PostsId: 4} FK {TagsId: 2}
Tag {Id: 1} Unchanged
Tag {Id: 2} Unchanged
Tag {Id: 3} Unchanged

Aviso:
Cada entidad de la que se realiza un seguimiento se muestra con su valor de clave principal (PK). Por
ejemplo, Blog {Id: 1} .
Si la entidad es un tipo de entidad de tipo compartido, también se muestra el tipo CLR. Por ejemplo,
PostTag (Dictionary<string, object>) .
EntityStateSe muestra a continuación. Será uno de los de Unchanged , Added , Modified o Deleted .
A continuación se muestran los valores de cualquier clave alternativa (AKs). Por ejemplo,
AK {AssetsId: ed727978-1ffe-4709-baee-73913e8e44a0} .
Por último, se muestran los valores de cualquier clave externa (claves externas). Por ejemplo,
FK {PostsId: 4} FK {TagsId: 2} .

Vista larga
La vista larga se puede enviar a la consola de la misma manera que la vista corta:

[Link]([Link]);

La salida para el mismo estado que la vista abreviada anterior es:

Blog {Id: 1} Modified


Id: 1 PK
AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK
Name: '.NET Blog (All new!)' Modified Originally '.NET Blog'
Assets: {Id: ed727978-1ffe-4709-baee-73913e8e44a0}
Posts: [{Id: 1}, {Id: 2}, {Id: -2147482643}]
Blog {Id: 2} Unchanged
Id: 2 PK
AssetsId: '3a54b880-2b9d-486b-9403-dc2e52d36d65' AK
Name: 'Visual Studio Blog'
Assets: {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65}
Posts: [{Id: 3}]
BlogAssets {Id: 3a54b880-2b9d-486b-9403-dc2e52d36d65} Unchanged
Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK
Banner: <null>
Blog: {Id: 2}
BlogAssets {Id: ed727978-1ffe-4709-baee-73913e8e44a0} Unchanged
Id: 'ed727978-1ffe-4709-baee-73913e8e44a0' PK FK
Banner: <null>
Blog: {Id: 1}
Post {Id: -2147482643} Added
Id: -2147482643 PK Temporary
BlogId: 1 FK
Content: '.NET 5.0 was released recently and has come with many new fe...'
Title: 'What's next for [Link]?'
Blog: {Id: 1}
Tags: []
Post {Id: 1} Unchanged
Id: 1 PK
BlogId: 1 FK
Content: 'Announcing the release of EF Core 5.0, a full featured cross...'
Title: 'Announcing the Release of EF Core 5.0'
Blog: {Id: 1}
Tags: [{Id: 1}, {Id: 3}]
Post {Id: 2} Unchanged
Id: 2 PK
BlogId: 1 FK
Content: 'F# 5 is the latest version of F#, the functional programming...'
Title: 'Announcing F# 5'
Blog: {Id: 1}
Tags: [{Id: 1}]
Post {Id: 3} Unchanged
Id: 3 PK
BlogId: 2 FK
Content: 'If you are focused on squeezing out the last bits of perform...'
Title: 'Disassembly improvements for optimized managed debugging'
Blog: {Id: 2}
Blog: {Id: 2}
Tags: [{Id: 2}]
Post {Id: 4} Deleted
Id: 4 PK
BlogId: 2 FK
Content: 'Examine when database queries were executed and measure how ...'
Title: 'Database Profiling with Visual Studio'
Blog: <null>
Tags: [{Id: 2}]
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 1} Unchanged
PostsId: 1 PK FK
TagsId: 1 PK FK
PostTag (Dictionary<string, object>) {PostsId: 1, TagsId: 3} Unchanged
PostsId: 1 PK FK
TagsId: 3 PK FK
PostTag (Dictionary<string, object>) {PostsId: 2, TagsId: 1} Unchanged
PostsId: 2 PK FK
TagsId: 1 PK FK
PostTag (Dictionary<string, object>) {PostsId: 3, TagsId: 2} Unchanged
PostsId: 3 PK FK
TagsId: 2 PK FK
PostTag (Dictionary<string, object>) {PostsId: 4, TagsId: 2} Deleted
PostsId: 4 PK FK
TagsId: 2 PK FK
Tag {Id: 1} Unchanged
Id: 1 PK
Text: '.NET'
Posts: [{Id: 1}, {Id: 2}]
Tag {Id: 2} Unchanged
Id: 2 PK
Text: 'Visual Studio'
Posts: [{Id: 3}, {Id: 4}]
Tag {Id: 3} Unchanged
Id: 3 PK
Text: 'EF Core'
Posts: [{Id: 1}]

Cada entidad de la que se realiza el seguimiento y su estado se muestra como antes. Sin embargo, la vista larga
también muestra los valores de propiedad y de navegación.
Valores de propiedad
Para cada propiedad, la vista larga muestra si la propiedad forma parte de una clave principal (PK), una clave
alternativa (AK) o una clave externa (FK). Por ejemplo:
[Link] es una propiedad de clave principal: Id: 1 PK
[Link] es una propiedad de clave alternativa: AssetsId: 'ed727978-1ffe-4709-baee-73913e8e44a0' AK
[Link] es una propiedad de clave externa: BlogId: 2 FK
[Link] es tanto una clave principal como una propiedad de clave externa:
Id: '3a54b880-2b9d-486b-9403-dc2e52d36d65' PK FK

Los valores de propiedad que se han modificado se marcan como tales y también se muestra el valor original de
la propiedad. Por ejemplo, Name: '.NET Blog (All new!)' Modified Originally '.NET Blog' .
Por último, Added las entidades con valores de clave temporales indican que el valor es temporal. Por ejemplo,
Id: -2147482643 PK Temporary .
Valores de navegación
Los valores de navegación se muestran mediante los valores de clave principal de las entidades a las que hace
referencia la navegación. Por ejemplo, en la salida anterior, post 3 está relacionado con el blog 2. Esto significa
que la [Link] navegación señala a la Blog instancia de con el identificador 2. Esto se muestra como
Blog: {Id: 2} .

Lo mismo sucede con las navegaciones de la colección, salvo que en este caso puede haber varias entidades
relacionadas. Por ejemplo, la navegación de colección [Link] contiene tres entidades, con los valores de
clave 1, 2 y-2147482643, respectivamente. Esto se muestra como [{Id: 1}, {Id: 2}, {Id: -2147482643}] .

Registro de seguimiento de cambios


El seguimiento de cambios registra los mensajes en el Debug LogLevel cuando detecta cambios de propiedad o
navegación. Por ejemplo, cuando [Link]() se llama a en el código en la parte superior de
este documento y el registro de depuración está habilitado, se generan los registros siguientes:

dbug: 12/30/2020 13:52:44.815 [Link][10800]


([Link])
DetectChanges starting for 'BlogsContext'.
dbug: 12/30/2020 13:52:44.818 [Link][10802]
([Link])
The unchanged property '[Link]' was detected as changed from '.NET Blog' to '.NET Blog (All new!)'
and will be marked as modified for entity with key '{Id: 1}'.
dbug: 12/30/2020 13:52:44.820 [Link][10807] ([Link])
The 'Blog' entity with key '{Id: 1}' tracked by 'BlogsContext' changed state from 'Unchanged' to
'Modified'.
dbug: 12/30/2020 13:52:44.821 [Link][10804]
([Link])
1 entities were added and 0 entities were removed from navigation '[Link]' on entity with key
'{Id: 1}'.
dbug: 12/30/2020 13:52:44.822 [Link][10808]
([Link])
'BlogsContext' generated temporary value '-2147482638' for the property '[Link]'.
dbug: 12/30/2020 13:52:44.822 [Link][10806]
([Link])
Context 'BlogsContext' started tracking 'Post' entity with key '{Id: -2147482638}'.
dbug: 12/30/2020 13:52:44.827 [Link][10804]
([Link])
0 entities were added and 1 entities were removed from navigation '[Link]' on entity with key
'{Id: 2}'.
dbug: 12/30/2020 13:52:44.827 [Link][10807] ([Link])
The 'Post' entity with key '{Id: 4}' tracked by 'BlogsContext' changed state from 'Unchanged' to
'Modified'.
dbug: 12/30/2020 13:52:44.829 [Link][10003] ([Link])
An entity of type 'Post' with key '{Id: 4}' changed to 'Deleted' state due to severed required
relationship to its parent entity of type 'Blog'.
dbug: 12/30/2020 13:52:44.829 [Link][10807] ([Link])
The 'Post' entity with key '{Id: 4}' tracked by 'BlogsContext' changed state from 'Modified' to
'Deleted'.
dbug: 12/30/2020 13:52:44.829 [Link][10804]
([Link])
0 entities were added and 1 entities were removed from navigation '[Link]' on entity with key
'{Id: 2}'.
dbug: 12/30/2020 13:52:44.831 [Link][10002] ([Link])
A cascade state change of an entity of type 'PostTag' with key '{PostsId: 4, TagsId: 2}' to 'Deleted'
occurred due to the deletion of its parent entity of type 'Post' with key '{Id: 4}'.
dbug: 12/30/2020 13:52:44.831 [Link][10807] ([Link])
The 'PostTag' entity with key '{PostsId: 4, TagsId: 2}' tracked by 'BlogsContext' changed state from
'Unchanged' to 'Deleted'.
dbug: 12/30/2020 13:52:44.831 [Link][10801]
([Link])
DetectChanges completed for 'BlogsContext'.

En la tabla siguiente se resumen los mensajes de registro de seguimiento de cambios:

ID. DE EVEN TO DESC RIP C IÓ N

[Link] DetectChanges() se está iniciando


ID. DE EVEN TO DESC RIP C IÓ N

[Link] DetectChanges() ha finalizado

[Link] Ha cambiado un valor de propiedad normal

[Link] Ha cambiado un valor de propiedad de clave externa

[Link] Se ha agregado o quitado entidades relacionadas con una


navegación de colección que no omite.

[Link] Una navegación de referencia se ha cambiado para apuntar


a otra entidad o debe establecerse en NULL.

[Link] EF Core inició el seguimiento de una entidad

[Link] La EntityState de una entidad ha cambiado

[Link] Se generó un valor para una propiedad

[Link] Se ha agregado o quitado entidades relacionadas con la


navegación por omitir una colección

El modelo
El modelo que se usa para los ejemplos anteriores contiene los siguientes tipos de entidad:
public class Blog
{
public int Id { get; set; } // Primary key
public Guid AssetsId { get; set; } // Alternate key
public string Name { get; set; }

public IList<Post> Posts { get; } = new List<Post>(); // Collection navigation


public BlogAssets Assets { get; set; } // Reference navigation
}

public class BlogAssets


{
public Guid Id { get; set; } // Primary key and foreign key
public byte[] Banner { get; set; }

public Blog Blog { get; set; } // Reference navigation


}

public class Post


{
public int Id { get; set; } // Primary key
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; } // Foreign key


public Blog Blog { get; set; } // Reference navigation

public IList<Tag> Tags { get; } = new List<Tag>(); // Skip collection navigation


}

public class Tag


{
public int Id { get; set; } // Primary key
public string Text { get; set; }

public IList<Post> Posts { get; } = new List<Post>(); // Skip collection navigation


}

El modelo se configura principalmente por Convención, con solo unas pocas líneas en OnModelCreating:

protected override void OnModelCreating(ModelBuilder modelBuilder)


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

modelBuilder
.Entity<BlogAssets>()
.HasOne(e => [Link])
.WithOne(e => [Link])
.HasForeignKey<BlogAssets>(e => [Link])
.HasPrincipalKey<Blog>(e => [Link]);
}
Información general sobre el registro y la
interceptación
12/03/2021 • 6 minutes to read • Edit Online

Entity Framework Core (EF Core) contiene varios mecanismos para generar registros, responder a eventos y
obtener diagnósticos. Cada uno de ellos se adapta a diferentes situaciones, y es importante seleccionar el
mecanismo adecuado para cada tarea, incluso cuando puedan funcionar varios. Por ejemplo, un interceptor de
base de datos se podría usar para registrar SQL, pero sería más adecuado usar en este caso alguno de los
mecanismos adaptados al registro. En esta página, se presenta información general de cada uno de estos
mecanismos y se describe en qué casos se debe usar cada uno de ellos.

Referencia rápida
En la siguiente tabla se proporciona una referencia rápida para las diferencias entre los mecanismos descritos
aquí.

M EC H A N ISM A SY N C Á M B ITO REGIST RA DO USO P REVISTO

Registro sencillo No Por contexto Configuración en Registro del tiempo


contexto de desarrollo

[Link]. No Por contexto* D.I. o configuración Registro de


Logging en contexto producción

Eventos No Por contexto Cualquier momento Reacción a eventos


de EF

Interceptores Sí Por contexto Configuración en Manipulación de


contexto operaciones EF

Escuchas de No Proceso Globalmente Diagnósticos de


diagnóstico aplicaciones

*Normalmente [Link] se configura por aplicación a través de la inserción de


dependencias. Sin embargo, en el nivel de EF, cada contexto se puede configurar con un registrador diferente en
caso necesario.

Registro sencillo
NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

Se puede acceder a los registros de EF Core desde cualquier tipo de aplicación mediante el uso de LogTo al
configurar una instancia de DbContext. Esta configuración se realiza normalmente en una invalidación de
[Link]. Por ejemplo:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> [Link]([Link]);

Este concepto es similar a [Link] en EF6.


Vea Registro simple para más información.

[Link]
[Link] es un mecanismo de registro extensible con proveedores de complementos para
muchos sistemas de registro comunes. EF Core se integra totalmente con [Link] y esta
forma de registro se usa de forma predeterminada para las aplicaciones [Link] Core.
Consulte Usar [Link] en EF Core para obtener más información.

Eventos
NOTE
Se incluyeron eventos adicionales en EF Core 5.0.

EF Core expone eventos de .NET para que actúen como devoluciones de llamada cuando ocurran ciertas cosas
en el código EF Core. Los eventos son más sencillos que los interceptores y permiten un registro más flexible.
Sin embargo, solo son sincrónicos y, por tanto, no pueden realizar operaciones de E/S asincrónicas sin bloqueo.
Los eventos se registran por instancia de DbContext y este registro se puede realizar en cualquier momento. Use
una escucha de diagnóstico para obtener la misma información, pero para todas las instancias de DbContext del
proceso.
Consulte Eventos de .NET en EF Core para obtener más información.

Interception
NOTE
Esta característica se incluyó por primera vez en EF Core 3.0. Los interceptores adicionales se incluyeron por primera vez
en EF Core 5.0.

Los interceptores de EF Core habilitan la intercepción, la modificación o la supresión de operaciones de EF Core.


Esto incluye operaciones de base de datos de bajo nivel tales como ejecutar un comando, así como operaciones
de nivel superior tales como llamadas a SaveChanges.
Los interceptores son distintos del registro y el diagnóstico en que permiten la modificación o supresión de la
operación que se intercepta. El registro sencillo o [Link] son mejores opciones de
registro.
Los interceptores se registran por instancia de DbContext al configurarse el contexto. Use una escucha de
diagnóstico para obtener la misma información, pero para todas las instancias de DbContext del proceso.
Consulte Interceptación para obtener más información.

Escuchas de diagnóstico
Las escuchas de diagnóstico permiten escuchar cualquier evento de EF Core que se produzca en el proceso de
.NET actual.
Las escuchas de diagnóstico no son adecuadas para obtener eventos de una sola instancia de DbContext. Los
interceptores de EF Core proporcionan acceso a los mismos eventos con el registro por contexto.
Las escuchas de diagnóstico no están diseñadas para el registro. El registro sencillo o
[Link] son mejores opciones de registro.
Consulte el artículo sobre cómo Usar escuchas de diagnóstico en EF Core para obtener más información.
Registro sencillo
12/03/2021 • 16 minutes to read • Edit Online

NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

TIP
Puede descargar el ejemplo de este artículo en github.

Se puede usar el registro simple de Entity Framework Core (EF Core) para obtener fácilmente los registros
durante el desarrollo y la depuración de aplicaciones. Esta forma de registro requiere una configuración mínima
y ningún paquete de NuGet adicional.

TIP
EF Core también se integra con Microsoft. Extensions. Logging, que requiere más configuración, pero suele ser más
adecuado para el registro en aplicaciones de producción.

Configuración
Se puede acceder a los registros de EF Core desde cualquier tipo de aplicación mediante el uso de LogTo al
configurar una instancia de DbContext. Esta configuración se realiza normalmente en una invalidación de
[Link]. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link]([Link]);

Como alternativa, LogTo se puede llamar a como parte de AddDbContext o al crear una instancia de que se va a
DbContextOptions pasar al DbContext constructor.

TIP
La configuración se sigue llamando cuando se usa AddDbContext o se pasa una instancia de DbContextOptions al
constructor DbContext. Esto hace que sea el lugar ideal para aplicar la configuración de contexto con independencia de
cómo se construya el DbContext.

Dirigir los registros


Registro en la consola
LogTo requiere un Action<T> delegado que acepte una cadena. EF Core llamará a este delegado con una
cadena para cada mensaje de registro generado. Después, el delegado debe hacer algo con el mensaje
especificado.
El [Link] método se utiliza a menudo para este delegado, como se muestra anteriormente. Esto hace
que cada mensaje de registro se escriba en la consola.
Registro en la ventana de depuración
[Link] se puede usar para enviar la salida a la ventana de depuración en Visual Studio o en otros IDE.
En este caso, se debe usar la Sintaxis lambda porque la Debug clase se compila fuera de las compilaciones de
versión. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


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

Registro en un archivo
Para escribir en un archivo, es necesario crear un StreamWriter o similar para el archivo. El WriteLine método se
puede usar como en los otros ejemplos anteriores. Recuerde asegurarse de que el archivo se cierra limpiamente
eliminando el escritor cuando se desecha el contexto. Por ejemplo:

private readonly StreamWriter _logStream = new StreamWriter("[Link]", append: true);

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link](_logStream.WriteLine);

public override void Dispose()


{
[Link]();
_logStream.Dispose();
}

public override async ValueTask DisposeAsync()


{
await [Link]();
await _logStream.DisposeAsync();
}

TIP
Considere la posibilidad de usar Microsoft. Extensions. Logging para registrar archivos en aplicaciones de producción.

Obtención de mensajes detallados


Información confidencial
De forma predeterminada, EF Core no incluirá los valores de los datos en los mensajes de excepción. Esto se
debe a que estos datos pueden ser confidenciales y se pueden revelar en el uso de producción si no se controla
una excepción.
Sin embargo, conocer los valores de datos, especialmente para las claves, puede ser muy útil al depurar. Esto se
puede habilitar en EF Core mediante una llamada a EnableSensitiveDataLogging() . Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.LogTo([Link])
.EnableSensitiveDataLogging();

Excepciones de consulta detalladas


Por motivos de rendimiento, EF Core no encapsula cada llamada para leer un valor del proveedor de base de
datos en un bloque try-catch. Sin embargo, en ocasiones se producen excepciones que son difíciles de
diagnosticar, especialmente cuando la base de datos devuelve un valor NULL cuando el modelo no lo permite.
La activación de hará que EnableDetailedErrors EF introduzca estos bloques try-catch y, por tanto, proporcione
errores más detallados. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.LogTo([Link])
.EnableDetailedErrors();

Filtrado
Niveles de registro
Cada mensaje de registro de EF Core se asigna a un nivel definido por la LogLevel enumeración. De forma
predeterminada, EF Core registro simple incluye todos los mensajes en el Debug nivel o superior. LogTo se
puede pasar un nivel mínimo más alto para filtrar algunos mensajes. Por ejemplo, si Information se pasa el
resultado en un conjunto mínimo de registros limitado al acceso a la base de datos y a algunos mensajes de
mantenimiento.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


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

Mensajes específicos
A todos los mensajes de registro se les asigna un EventId . Se puede tener acceso a estos identificadores desde
la CoreEventId clase o la RelationalEventId clase para mensajes específicos relacionales. Un proveedor de bases
de datos también puede tener identificadores específicos del proveedor en una clase similar. Por ejemplo,
SqlServerEventId para el proveedor de SQL Server.
LogTo se puede configurar para registrar solo los mensajes asociados a uno o varios identificadores de eventos.
Por ejemplo, para registrar solo los mensajes para el contexto que se está inicializando o eliminando:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.LogTo([Link], new[] { [Link], [Link] });

Categorías de mensajes
Cada mensaje de registro se asigna a una categoría de registrador jerárquico con nombre. Las categorías son:

C AT EGO RY ERRO R DE H A DO O P

[Link] Todos los mensajes de EF Core

Microsoft. EntityFrameworkCore. Database Todas las interacciones de base de datos

Microsoft. EntityFrameworkCore. Database. Connection Usos de una conexión de base de datos

Microsoft. EntityFrameworkCore. Database. Command Usos de un comando de base de datos

Microsoft. EntityFrameworkCore. Database. Transaction Usos de una transacción de base de datos

Microsoft. EntityFrameworkCore. Update Guardar entidades, excluidas las interacciones de base de


datos
C AT EGO RY ERRO R DE H A DO O P

Microsoft. EntityFrameworkCore. Model Todas las interacciones entre modelos y metadatos

Microsoft. EntityFrameworkCore. Model. Validation Validación de modelos

Microsoft. EntityFrameworkCore. Query Consultas, excluidas las interacciones de base de datos

Microsoft. EntityFrameworkCore. Infrastructure Eventos generales, como la creación de contexto

Microsoft. EntityFrameworkCore. scaffolding Ingeniería inversa de base de datos

Microsoft. EntityFrameworkCore. Migrations Migraciones

Microsoft. EntityFrameworkCore. ChangeTracking Interacciones de seguimiento de cambios

LogTo se puede configurar para registrar solo los mensajes de una o varias categorías. Por ejemplo, para
registrar solo interacciones de base de datos:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.LogTo([Link], new[] { [Link] });

Tenga en cuenta que la DbLoggerCategory clase proporciona una API jerárquica para buscar una categoría y
evita la necesidad de codificar cadenas de forma rígida.
Puesto que las categorías son jerárquicas, este ejemplo con la Database categoría incluirá todos los mensajes
para las subcategorías [Link] , [Link] y [Link] .
Filtros personalizados
LogTo permite usar un filtro personalizado para los casos en los que ninguna de las opciones de filtrado
anteriores sean suficientes. Por ejemplo, para registrar cualquier mensaje en Information el nivel o superior, así
como mensajes para abrir y cerrar una conexión:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.LogTo(
[Link],
(eventId, logLevel) => logLevel >= [Link]
|| eventId == [Link]
|| eventId == [Link]);

TIP
El filtrado mediante filtros personalizados o el uso de cualquiera de las otras opciones que se muestran aquí es más eficaz
que el filtrado del LogTo delegado. Esto se debe a que, si el filtro determina que el mensaje no se debe registrar, no se
crea ni siquiera el mensaje de registro.

Configuración de mensajes específicos


La API de EF Core ConfigureWarnings permite a las aplicaciones cambiar lo que ocurre cuando se encuentra un
evento específico. Se puede usar para:
Cambiar el nivel de registro en el que se registra el evento
Omitir el registro del evento por completo
Producir una excepción cuando se produce el evento
Cambiar el nivel de registro de un evento
En el ejemplo anterior se usaba un filtro personalizado para registrar todos [Link] los mensajes
en, así como dos eventos definidos para [Link] . Lo mismo se puede lograr cambiando el nivel de
registro de los dos Debug eventos a Information :

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.ConfigureWarnings(
b => [Link](
([Link], [Link]),
([Link], [Link])))
.LogTo([Link], [Link]);

Suprimir el registro de un evento


De forma similar, se puede suprimir un evento individual del registro. Esto es especialmente útil para omitir una
advertencia que se ha revisado y comprendido. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.ConfigureWarnings(b => [Link]([Link]))
.LogTo([Link]);

Throw para un evento


Por último, EF Core se pueden configurar para que se inicien para un evento determinado. Esto es especialmente
útil para cambiar una advertencia a un error. (De hecho, este era el propósito original del ConfigureWarnings
método, es decir, el nombre). Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.ConfigureWarnings(b => [Link]([Link]))
.LogTo([Link]);

Contenido y formato del mensaje


El contenido predeterminado de LogTo se formatea en varias líneas. La primera línea contiene metadatos de
mensaje:
LogLevelComo prefijo de cuatro caracteres
Marca de tiempo local, con formato para la referencia cultural actual
EventIdEn el formulario que se puede copiar y pegar para obtener el miembro de CoreEventId o una de las
otras EventId clases, más el valor de identificador sin formato
La categoría de eventos, tal y como se ha descrito anteriormente.
Por ejemplo:
info: 10/6/2020 10:52:45.581 [Link][20101]
([Link])
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE "Blogs" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
"Name" INTEGER NOT NULL
);
dbug: 10/6/2020 10:52:45.582 [Link][20210]
([Link])
Committing transaction.
dbug: 10/6/2020 10:52:45.585 [Link][20202]
([Link])
Committed transaction.

Este contenido se puede personalizar pasando valores de DbContextLoggerOptions , como se muestra en las
secciones siguientes.

TIP
Considere la posibilidad de usar Microsoft. Extensions. Logging para obtener más control sobre el formato del registro.

Usar la hora UTC


De forma predeterminada, las marcas de tiempo están diseñadas para el consumo local durante la depuración.
Use [Link] para usar marcas de tiempo UTC independientes de la
referencia cultural en su lugar, pero mantenga todo lo demás. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


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

En este ejemplo se da como resultado el siguiente formato de registro:

info: 2020-10-06T17:55:39.0333701Z [Link][20101]


([Link])
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE "Blogs" (
"Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY AUTOINCREMENT,
"Name" INTEGER NOT NULL
);
dbug: 2020-10-06T17:55:39.0333892Z [Link][20210]
([Link])
Committing transaction.
dbug: 2020-10-06T17:55:39.0351684Z [Link][20202]
([Link])
Committed transaction.

Registro de una sola línea


A veces resulta útil obtener exactamente una línea por cada mensaje de registro. Esto puede habilitarse
mediante [Link] . Por ejemplo:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> [Link](
[Link],
[Link],
[Link] | [Link]);

En este ejemplo se da como resultado el siguiente formato de registro:

info: 10/6/2020 10:52:45.723 [Link][20101]


([Link]) -> Executed DbCommand (0ms) [Parameters=[],
CommandType='Text', CommandTimeout='30']CREATE TABLE "Blogs" ( "Id" INTEGER NOT NULL CONSTRAINT
"PK_Blogs" PRIMARY KEY AUTOINCREMENT, "Name" INTEGER NOT NULL);
dbug: 10/6/2020 10:52:45.723 [Link][20210]
([Link]) -> Committing transaction.
dbug: 10/6/2020 10:52:45.725 [Link][20202]
([Link]) -> Committed transaction.

Otras opciones de contenido


Otras marcas de DbContextLoggerOptions se pueden usar para reducir la cantidad de metadatos que se
incluyen en el registro. Esto puede ser útil junto con el registro de una sola línea. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link](
[Link],
[Link],
[Link] | [Link]);

En este ejemplo se da como resultado el siguiente formato de registro:

2020-10-06T17:52:45.7320362Z -> Executed DbCommand (0ms) [Parameters=[], CommandType='Text',


CommandTimeout='30']CREATE TABLE "Blogs" ( "Id" INTEGER NOT NULL CONSTRAINT "PK_Blogs" PRIMARY KEY
AUTOINCREMENT, "Name" INTEGER NOT NULL);
2020-10-06T17:52:45.7320531Z -> Committing transaction.
2020-10-06T17:52:45.7339441Z -> Committed transaction.

Pasar de EF6
EF Core registro simple difiere de [Link] en EF6 en dos aspectos importantes:
Los mensajes de registro no se limitan únicamente a interacciones de base de datos
El registro se debe configurar en el momento de inicialización del contexto.
En la primera diferencia, el filtrado descrito anteriormente se puede usar para limitar los mensajes que se
registran.
La segunda diferencia es un cambio intencionado para mejorar el rendimiento, ya que no genera mensajes de
registro cuando no es necesario. Sin embargo, todavía es posible obtener un comportamiento similar a EF6
mediante la creación de una Log propiedad en DbContext y, a continuación, usarla solo cuando se haya
establecido. Por ejemplo:

public Action<string> Log { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link](s => Log?.Invoke(s));
Usar Microsoft. Extensions. Logging en EF Core
12/03/2021 • 7 minutes to read • Edit Online

Microsoft. Extensions. Logging es un mecanismo de registro extensible con proveedores de complementos para
muchos sistemas de registro comunes. Tanto los complementos suministrados por Microsoft (por ejemplo,
Microsoft. Extensions. Logging. Console) como los complementos de terceros (por ejemplo, Serilog. Extensions.
Logging) están disponibles como paquetes NuGet.
Entity Framework Core (EF Core) se integra totalmente con [Link] . Sin embargo,
considere la posibilidad de usar un registro simple para una forma más sencilla de registrar, especialmente en el
caso de las aplicaciones que no usan la inserción de dependencias.

Aplicaciones de [Link] Core


se utiliza de forma predeterminada en las aplicaciones [Link] Core. Llamar a
[Link]
AddDbContext o AddDbContextPool .

Otros tipos de aplicaciones


Otros tipos de aplicaciones pueden usar GenericHost para obtener los mismos patrones de inserción de
dependencias que se usan en [Link] Core. AddDbContext o AddDbContextPool se pueden usar de la misma
manera que en las aplicaciones [Link] Core.
[Link] también se puede usar para aplicaciones que no usan la inserción de
dependencias, aunque el registro sencillo puede ser más fácil de configurar.
[Link] requiere la creación de un LoggerFactory . Este generador debe almacenarse
como una instancia estática o global en algún lugar y usarse cada vez que se crea un DbContext. Por ejemplo, es
habitual almacenar el generador del registrador como una propiedad estática en DbContext.
EF Core 3,0 y versiones posteriores
EF Core 2.1

public static readonly ILoggerFactory MyLoggerFactory


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

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

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.UseLoggerFactory(MyLoggerFactory)
.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFLogging;ConnectRetryCount=0");

Obtención de mensajes detallados


TIP
La configuración se sigue llamando cuando se usa AddDbContext o se pasa una instancia de DbContextOptions al
constructor DbContext. Esto hace que sea el lugar ideal para aplicar la configuración de contexto con independencia de
cómo se construya el DbContext.

Información confidencial
De forma predeterminada, EF Core no incluirá los valores de los datos en los mensajes de excepción. Esto se
debe a que estos datos pueden ser confidenciales y se pueden revelar en el uso de producción si no se controla
una excepción.
Sin embargo, conocer los valores de datos, especialmente para las claves, puede ser muy útil al depurar. Esto se
puede habilitar en EF Core mediante una llamada a EnableSensitiveDataLogging() . Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link]();

Excepciones de consulta detalladas


Por motivos de rendimiento, EF Core no encapsula cada llamada para leer un valor del proveedor de base de
datos en un bloque try-catch. Sin embargo, en ocasiones se producen excepciones que son difíciles de
diagnosticar, especialmente cuando la base de datos devuelve un valor NULL cuando el modelo no lo permite.
La activación de hará que EnableDetailedErrors EF introduzca estos bloques try-catch y, por tanto, proporcione
errores más detallados. Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link]();

Configuración de mensajes específicos


La API de EF Core ConfigureWarnings permite a las aplicaciones cambiar lo que ocurre cuando se encuentra un
evento específico. Se puede usar para:
Cambiar el nivel de registro en el que se registra el evento
Omitir el registro del evento por completo
Producir una excepción cuando se produce el evento
Cambiar el nivel de registro de un evento
A veces puede resultar útil cambiar el nivel de registro predefinido para un evento. Por ejemplo, se puede usar
para promover dos eventos adicionales de [Link] a [Link] :

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.ConfigureWarnings(
b => [Link](
([Link], [Link]),
([Link], [Link])));

Suprimir el registro de un evento


De forma similar, se puede suprimir un evento individual del registro. Esto es especialmente útil para omitir una
advertencia que se ha revisado y comprendido. Por ejemplo:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.ConfigureWarnings(b => [Link]([Link]));

Throw para un evento


Por último, EF Core se pueden configurar para que se inicien para un evento determinado. Esto es especialmente
útil para cambiar una advertencia a un error. (De hecho, este era el propósito original del ConfigureWarnings
método, es decir, el nombre). Por ejemplo:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.ConfigureWarnings(b => [Link]([Link]));

Filtrado y otra configuración


Consulte Inicio de sesión en .net para obtener instrucciones sobre el filtrado de registros y otras
configuraciones.
EF Core eventos de registro se definen en una de las siguientes:
CoreEventId para eventos comunes a todos los proveedores de bases de datos de EF Core
RelationalEventId para eventos comunes a todos los proveedores de bases de datos relacionales
Clase similar para los eventos específicos del proveedor de base de datos actual. Por ejemplo,
SqlServerEventId para el proveedor de SQL Server.
Estas definiciones contienen los identificadores de evento, el nivel de registro y la categoría de cada evento, tal y
como usa [Link] .
Eventos .NET en EF Core
12/03/2021 • 5 minutes to read • Edit Online

TIP
Puede descargar el ejemplo de eventos de github.

Entity Framework Core (EF Core) expone eventos .net para que actúen como devoluciones de llamada cuando se
producen ciertas cosas en el código EF Core. Los eventos son más sencillos que los interceptores y permiten un
registro más flexible. Sin embargo, solo son sincrónicos y, por tanto, no pueden realizar operaciones de E/S
asincrónicas sin bloqueo.
Los eventos se registran por DbContext instancia. Use una escucha de diagnóstico para obtener la misma
información, pero para todas las instancias de DbContext del proceso.

Eventos generados por EF Core


Los eventos siguientes los genera EF Core:

EVEN TO VERSIÓ N IN T RO DUC IDA C UA N DO SE P RO DUC E

[Link] 5.0 Al principio de SaveChanges o


SaveChangesAsync

[Link] 5.0 Al final de una operación correcta


SaveChanges o SaveChangesAsync

[Link] 5.0 Al final de un error SaveChanges o


SaveChangesAsync

[Link] 2.1 Cuando el contexto realiza un


seguimiento de una entidad

[Link] 2.1 Cuando una entidad de la que se


realiza un seguimiento cambia su
estado

Ejemplo: cambios de estado de marca de tiempo


Cada entidad de la que un DbContext realiza un seguimiento tiene un EntityState . Por ejemplo, el Added Estado
indica que la entidad se insertará en la base de datos.
En este ejemplo se usan los Tracked StateChanged eventos y para detectar cuándo cambia el estado de una
entidad. A continuación, marca la entidad con la hora actual que indica el momento en que se produjo este
cambio. Esto da como resultado marcas de tiempo que indican cuándo se insertó, eliminó o actualizó por última
vez la entidad.
Los tipos de entidad de este ejemplo implementan una interfaz que define las propiedades de marca de tiempo:
public interface IHasTimestamps
{
DateTime? Added { get; set; }
DateTime? Deleted { get; set; }
DateTime? Modified { get; set; }
}

Un método en DbContext de la aplicación puede establecer marcas de tiempo para cualquier entidad que
implemente esta interfaz:

private static void UpdateTimestamps(object sender, EntityEntryEventArgs e)


{
if ([Link] is IHasTimestamps entityWithTimestamps)
{
switch ([Link])
{
case [Link]:
[Link] = [Link];
[Link]($"Stamped for delete: {[Link]}");
break;
case [Link]:
[Link] = [Link];
[Link]($"Stamped for update: {[Link]}");
break;
case [Link]:
[Link] = [Link];
[Link]($"Stamped for insert: {[Link]}");
break;
}
}
}

Este método tiene la firma adecuada que se va a usar como controlador de eventos para los Tracked
StateChanged eventos y. El controlador se registra para ambos eventos en el constructor DbContext. Tenga en
cuenta que los eventos se pueden adjuntar a DbContext en cualquier momento; no es necesario que esto suceda
en el constructor de contexto.

public BlogsContext()
{
[Link] += UpdateTimestamps;
[Link] += UpdateTimestamps;
}

Ambos eventos son necesarios porque las nuevas entidades desencadenan Tracked eventos cuando se realiza
el seguimiento por primera vez. StateChanged los eventos solo se activan para las entidades que cambian de
estado mientras ya se están realizando el seguimiento.
El ejemplo de este ejemplo contiene una aplicación de consola simple que realiza cambios en la base de datos
de blogs:
using (var context = new BlogsContext())
{
[Link]();
[Link]();

[Link](
new Blog
{
Id = 1,
Name = "EF Blog",
Posts = { new Post { Id = 1, Title = "EF Core 3.1!" }, new Post { Id = 2, Title = "EF Core 5.0!"
} }
});

[Link]();
}

using (var context = new BlogsContext())


{
var blog = [Link](e => [Link]).Single();

[Link] = "EF Core Blog";


[Link]([Link]());
[Link](new Post { Id = 3, Title = "EF Core 6.0!" });

[Link]();
}

La salida de este código muestra los cambios de estado que se producen y las marcas de tiempo que se aplican:

Stamped for insert: Blog 1 Added on: 10/15/2020 11:01:26 PM


Stamped for insert: Post 1 Added on: 10/15/2020 11:01:26 PM
Stamped for insert: Post 2 Added on: 10/15/2020 11:01:26 PM
Stamped for delete: Post 1 Added on: 10/15/2020 11:01:26 PM Deleted on: 10/15/2020 11:01:26 PM
Stamped for update: Blog 1 Added on: 10/15/2020 11:01:26 PM Modified on: 10/15/2020 11:01:26 PM
Stamped for insert: Post 3 Added on: 10/15/2020 11:01:26 PM
Interceptores
07/04/2021 • 27 minutes to read • Edit Online

Los interceptores de Entity Framework Core (EF Core) permiten la interceptación, modificación y/o supresión de
operaciones de EF Core. Esto incluye operaciones de base de datos de bajo nivel tales como ejecutar un
comando, así como operaciones de nivel superior tales como llamadas a SaveChanges.
Los interceptores son distintos del registro y el diagnóstico en que permiten la modificación o supresión de la
operación que se intercepta. El registro sencillo o [Link] son mejores opciones de
registro.
Los interceptores se registran por instancia de DbContext al configurarse el contexto. Use una escucha de
diagnóstico para obtener la misma información, pero para todas las instancias de DbContext del proceso.

Registro de interceptores
Los interceptores se registran mediante AddInterceptors al configurar una instancia de DbContext. Esto se
realiza normalmente en una invalidación de [Link] . Por ejemplo:

public class ExampleContext : BlogsContext


{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> [Link](new TaggedQueryCommandInterceptor());
}

Como alternativa, AddInterceptors se puede llamar a como parte de AddDbContext o al crear una instancia de
que se va a DbContextOptions pasar al constructor DbContext.

TIP
La configuración se sigue llamando cuando se usa AddDbContext o se pasa una instancia de DbContextOptions al
constructor DbContext. Esto hace que sea el lugar ideal para aplicar la configuración de contexto con independencia de
cómo se construya el DbContext.

A menudo, los interceptores no tienen estado, lo que significa que se puede usar una única instancia de
interceptor para todas las instancias de DbContext. Por ejemplo:

public class TaggedQueryCommandInterceptorContext : BlogsContext


{
private static readonly TaggedQueryCommandInterceptor _interceptor
= new TaggedQueryCommandInterceptor();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link](_interceptor);
}

Cada instancia de interceptor debe implementar una o más interfaces derivadas de IInterceptor . Cada instancia
solo debe registrarse una vez incluso si implementa varias interfaces de interceptación; EF Core enrutará los
eventos de cada interfaz según corresponda.

Interceptación de base de datos


NOTE
La intercepción de bases de datos se presentó en EF Core 3,0 y solo está disponible para los proveedores de bases de
datos relacionales. La compatibilidad con el punto de retorno se presentó en EF Core 5,0.

La interceptación de base de datos de bajo nivel se divide en las tres interfaces que se muestran en la tabla
siguiente.

IN T ERC EP TO R O P ERA C IO N ES DE B A SE DE DATO S IN T ERC EP TA DA S

IDbCommandInterceptor Crear comandos


Ejecutar comandos
Errores de comando
Desechar el DbDataReader del comando

IDbConnectionInterceptor Abrir y cerrar conexiones


Errores de conexión

IDbTransactionInterceptor Crear transacciones


Uso de transacciones existentes
Confirmar transacciones
Revertir transacciones
Crear y usar puntos de retorno
Errores de transacción

Las clases base DbCommandInterceptor , DbConnectionInterceptor y DbTransactionInterceptor contienen


implementaciones no operativas para cada método de la interfaz correspondiente. Utilice las clases base para
evitar la necesidad de implementar métodos de intercepción no utilizados.
Los métodos de cada tipo de interceptor se encuentran en pares, con el primero que se llama antes de que se
inicie la operación de base de datos, y el segundo después de que se haya completado la operación. Por
ejemplo. Por ejemplo, [Link] se llama a antes de que se ejecute una consulta
y [Link] se llama después de que se haya enviado la consulta a la base de
datos.
Cada par de métodos tiene dos variaciones sincrónicas y asincrónicas. Esto permite que se produzca la e/s
asincrónica, como la solicitud de un token de acceso, como parte de la interceptación de una operación de base
de datos asincrónica.
Ejemplo: intercepción de comandos para agregar sugerencias de consulta

TIP
Puede descargar el ejemplo de interceptor de comandos de github.

IDbCommandInterceptorSe puede utilizar para modificar SQL antes de enviarlo a la base de datos. En este
ejemplo se muestra cómo modificar el SQL para incluir una sugerencia de consulta.
A menudo, la parte más complicada de la interceptación es determinar si el comando corresponde a la consulta
que debe modificarse. Analizar el SQL es una opción, pero tiende a ser frágil. Otra opción consiste en usar EF
Core etiquetas de consulta para etiquetar cada consulta que se debe modificar. Por ejemplo:

var blogs1 = [Link]("Use hint: robust plan").ToList();

Esta etiqueta se puede detectar en el interceptor ya que siempre se incluirá como comentario en la primera línea
del texto del comando. Al detectar la etiqueta, se modifica el SQL de la consulta para agregar la sugerencia
adecuada:

public class TaggedQueryCommandInterceptor : DbCommandInterceptor


{
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result)
{
ManipulateCommand(command);

return result;
}

public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(


DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = default)
{
ManipulateCommand(command);

return new ValueTask<InterceptionResult<DbDataReader>>(result);


}

private static void ManipulateCommand(DbCommand command)


{
if ([Link]("-- Use hint: robust plan", [Link]))
{
[Link] += " OPTION (ROBUST PLAN)";
}
}
}

Aviso:
El interceptor hereda de DbCommandInterceptor para evitar tener que implementar todos los métodos en la
interfaz del interceptor.
El interceptor implementa métodos de sincronización y asincrónicos. Esto garantiza que se aplica la misma
sugerencia de consulta a las consultas asincrónicas y de sincronización.
El interceptor implementa los Executing métodos a los que llama EF Core con el SQL generado antes de que
se envíe a la base de datos. Compare esto con los Executed métodos, a los que se llama después de que se
devuelva la llamada a la base de datos.
Al ejecutar el código de este ejemplo, se genera lo siguiente cuando se etiqueta una consulta:

-- Use hint: robust plan

SELECT [b].[Id], [b].[Name]


FROM [Blogs] AS [b] OPTION (ROBUST PLAN)

Por otro lado, cuando una consulta no se etiqueta, se envía a la base de datos sin modificar:

SELECT [b].[Id], [b].[Name]


FROM [Blogs] AS [b]

Ejemplo: intercepción de conexión para la autenticación de SQL Azure mediante AAD


TIP
Puede descargar el ejemplo de interceptor de conexión desde github.

IDbConnectionInterceptorSe puede utilizar para manipular el antes de que DbConnection se utilice para
conectarse a la base de datos. Se puede usar para obtener un token de acceso Azure Active Directory (AAD). Por
ejemplo:

public class AadAuthenticationInterceptor : DbConnectionInterceptor


{
public override InterceptionResult ConnectionOpening(
DbConnection connection,
ConnectionEventData eventData,
InterceptionResult result)
=> throw new InvalidOperationException("Open connections asynchronously when using AAD
authentication.");

public override async ValueTask<InterceptionResult> ConnectionOpeningAsync(


DbConnection connection,
ConnectionEventData eventData,
InterceptionResult result,
CancellationToken cancellationToken = default)
{
var sqlConnection = (SqlConnection)connection;

var provider = new AzureServiceTokenProvider();


// Note: in some situations the access token may not be cached automatically the Azure Token
Provider.
// Depending on the kind of token requested, you may need to implement your own caching here.
[Link] = await [Link]("[Link]
null, cancellationToken);

return result;
}
}

TIP
Microsoft. Data. SqlClient ahora admite la autenticación de AAD a través de la cadena de conexión. Consulte
SqlAuthenticationMethod para obtener más información.

WARNING
Observe que el interceptor produce si se realiza una llamada de sincronización para abrir la conexión. Esto se debe a que
no hay ningún método no asincrónico para obtener el token de acceso y no hay ninguna manera universal y sencilla de
llamar a un método asincrónico desde el contexto no asincrónico sin arriesgarse al interbloqueo.

WARNING
en algunas situaciones, es posible que el token de acceso no se almacene en caché automáticamente en el proveedor de
tokens de Azure. Según el tipo de token solicitado, puede que tenga que implementar su propio almacenamiento en
caché aquí.

Ejemplo: intercepción de comandos avanzada para el almacenamiento en caché


TIP
Puede descargar el ejemplo de interceptor de comandos avanzado de github.

Los interceptores de EF Core pueden:


Indicar EF Core para suprimir la ejecución de la operación que se va a interceptar
Cambiar el resultado de la operación devuelto a EF Core
En este ejemplo se muestra un interceptor que usa estas características para comportarse como una memoria
caché de segundo nivel primitiva. Se devuelven resultados de consulta en caché para una consulta específica,
evitando un ida y vuelta de base de datos.

WARNING
Tenga cuidado al cambiar el comportamiento predeterminado de EF Core de esta manera. EF Core pueden comportarse
de maneras inesperadas si obtiene un resultado anómalo que no puede procesar correctamente. Además, este ejemplo
muestra los conceptos del interceptor; no se ha diseñado como plantilla para una implementación de caché de segundo
nivel sólida.

En este ejemplo, la aplicación ejecuta con frecuencia una consulta para obtener el "mensaje diario" más reciente:

async Task<string> GetDailyMessage(DailyMessageContext context)


=> (await [Link]("Get_Daily_Message").OrderBy(e => [Link]).LastAsync()).Message;

Esta consulta se etiqueta para que se pueda detectar fácilmente en el interceptor. La idea es consultar solo la
base de datos de un mensaje nuevo cada día. En otras ocasiones, la aplicación usará un resultado almacenado
en caché. (El ejemplo utiliza el retraso de 10 segundos en el ejemplo para simular un nuevo día).
Estado del interceptor
Este interceptor es con estado: almacena el identificador y el texto del mensaje del mensaje diario más reciente
consultado, más la hora a la que se ejecutó la consulta. Debido a este estado, también se necesita un bloqueo ,
ya que el almacenamiento en caché requiere que varias instancias de contexto utilicen el mismo interceptor.

private readonly object _lock = new object();


private int _id;
private string _message;
private DateTime _queriedAt;

Antes de la ejecución
En el Executing método (es decir, antes de realizar una llamada a la base de datos), el interceptor detecta la
consulta etiquetada y, a continuación, comprueba si hay un resultado almacenado en caché. Si se encuentra un
resultado de este tipo, se suprime la consulta y se usan en su lugar los resultados almacenados en caché.
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
DbCommand command,
CommandEventData eventData,
InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = default)
{
if ([Link]("-- Get_Daily_Message", [Link]))
{
lock (_lock)
{
if (_message != null
&& [Link] < _queriedAt + new TimeSpan(0, 0, 10))
{
[Link] = "-- Get_Daily_Message: Skipping DB call; using cache.";
result = InterceptionResult<DbDataReader>.SuppressWithResult(new
CachedDailyMessageDataReader(_id, _message));
}
}
}

return new ValueTask<InterceptionResult<DbDataReader>>(result);


}

Observe cómo el código llama a InterceptionResult<TResult>.SuppressWithResult y pasa un reemplazo


DbDataReader que contiene los datos almacenados en caché. A continuación, se devuelve este
InterceptionResult, lo que provoca la supresión de la ejecución de la consulta. En su lugar, EF Core usa el lector
de reemplazo como los resultados de la consulta.
Este interceptor también manipula el texto del comando. Esta manipulación no es necesaria, pero mejora la
claridad en los mensajes de registro. No es necesario que el texto del comando sea un SQL válido, ya que la
consulta no se va a ejecutar.
Después de la ejecución
Si no hay ningún mensaje en caché disponible, o si ha expirado, el código anterior no suprime el resultado. Por
tanto, EF Core ejecutará la consulta de la manera habitual. Después se devolverá al método del interceptor
Executed después de la ejecución. En este punto, si el resultado no es ya un lector almacenado en memoria
caché, el nuevo identificador de mensaje y la cadena se exfieren del lector real y se almacenan en caché listos
para el siguiente uso de esta consulta.
public override async ValueTask<DbDataReader> ReaderExecutedAsync(
DbCommand command,
CommandExecutedEventData eventData,
DbDataReader result,
CancellationToken cancellationToken = default)
{
if ([Link]("-- Get_Daily_Message", [Link])
&& !(result is CachedDailyMessageDataReader))
{
try
{
await [Link](cancellationToken);

lock (_lock)
{
_id = result.GetInt32(0);
_message = [Link](1);
_queriedAt = [Link];
return new CachedDailyMessageDataReader(_id, _message);
}
}
finally
{
await [Link]();
}
}

return result;
}

Demostración
El ejemplo de interceptor de almacenamiento en caché contiene una aplicación de consola simple que consulta
los mensajes diarios para probar el almacenamiento en caché:
// 1. Initialize the database with some daily messages.
using (var context = new DailyMessageContext())
{
await [Link]();
await [Link]();

[Link](
new DailyMessage { Message = "Remember: All builds are GA; no builds are RTM." },
new DailyMessage { Message = "Keep calm and drink tea" });

await [Link]();
}

// 2. Query for the most recent daily message. It will be cached for 10 seconds.
using (var context = new DailyMessageContext())
{
[Link](await GetDailyMessage(context));
}

// 3. Insert a new daily message.


using (var context = new DailyMessageContext())
{
[Link](new DailyMessage { Message = "Free beer for unicorns" });

await [Link]();
}

// 4. Cached message is used until cache expires.


using (var context = new DailyMessageContext())
{
[Link](await GetDailyMessage(context));
}

// 5. Pretend it's the next day.


[Link](10000);

// 6. Cache is expired, so the last message will noe be queried again.


using (var context = new DailyMessageContext())
{
[Link](await GetDailyMessage(context));
}

async Task<string> GetDailyMessage(DailyMessageContext context)


=> (await [Link]("Get_Daily_Message").OrderBy(e => [Link]).LastAsync()).Message;

Esta acción devuelve la siguiente salida:


info: 10/15/2020 12:32:11.801 [Link][20101]
([Link])
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
-- Get_Daily_Message

SELECT "d"."Id", "d"."Message"


FROM "DailyMessages" AS "d"
ORDER BY "d"."Id" DESC
LIMIT 1

Keep calm and drink tea

info: 10/15/2020 12:32:11.821 [Link][20101]


([Link])
Executed DbCommand (0ms) [Parameters=[@p0='Free beer for unicorns' (Size = 22)], CommandType='Text',
CommandTimeout='30']
INSERT INTO "DailyMessages" ("Message")
VALUES (@p0);
SELECT "Id"
FROM "DailyMessages"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();

info: 10/15/2020 12:32:11.826 [Link][20101]


([Link])
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
-- Get_Daily_Message: Skipping DB call; using cache.

Keep calm and drink tea

info: 10/15/2020 12:32:21.833 [Link][20101]


([Link])
Executed DbCommand (0ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
-- Get_Daily_Message

SELECT "d"."Id", "d"."Message"


FROM "DailyMessages" AS "d"
ORDER BY "d"."Id" DESC
LIMIT 1

Free beer for unicorns

Tenga en cuenta la salida del registro de que la aplicación sigue utilizando el mensaje almacenado en caché
hasta que expire el tiempo de espera, momento en el que se vuelve a consultar la base de datos para cualquier
mensaje nuevo.

Interceptación de SaveChanges
NOTE
La intercepción de SaveChanges se presentó en EF Core 5,0.

TIP
Puede descargar el ejemplo de interceptor de SaveChanges desde github.

SaveChangesSaveChangesAsynclos puntos de interceptación y se definen mediante la ISaveChangesInterceptor


interfaz. En lo que se refiere a otros interceptores, la SaveChangesInterceptor clase base con métodos no-OP se
proporciona por comodidad.
TIP
Los interceptores son eficaces. Sin embargo, en muchos casos puede ser más fácil invalidar el método SaveChanges o usar
los eventos .net para SaveChanges expuesto en DbContext.

Ejemplo: intercepción de SaveChanges para la auditoría


SaveChanges se puede interceptar para crear un registro de auditoría independiente de los cambios realizados.

NOTE
No pretende ser una solución de auditoría sólida. En su lugar, es un ejemplo sencillo que se usa para mostrar las
características de la interceptación.

El contexto de la aplicación
El ejemplo de auditoría usa un DbContext sencillo con blogs y publicaciones.

public class BlogsContext : DbContext


{
private readonly AuditingInterceptor _auditingInterceptor = new
AuditingInterceptor("DataSource=[Link]");

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> optionsBuilder
.AddInterceptors(_auditingInterceptor)
.UseSqlite("DataSource=[Link]");

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


}

public class Blog


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

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


}

public class Post


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

public Blog Blog { get; set; }


}

Observe que se registra una nueva instancia del interceptor para cada instancia de DbContext. Esto se debe a
que el interceptor de auditoría contiene el estado vinculado a la instancia de contexto actual.
Contexto de auditoría
El ejemplo también contiene un segundo DbContext y el modelo que se usa para la base de datos de auditoría.
public class AuditContext : DbContext
{
private readonly string _connectionString;

public AuditContext(string connectionString)


{
_connectionString = connectionString;
}

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link](_connectionString);

public DbSet<SaveChangesAudit> SaveChangesAudits { get; set; }


}

public class SaveChangesAudit


{
public int Id { get; set; }
public Guid AuditId { get; set; }
public DateTime StartTime { get; set; }
public DateTime EndTime { get; set; }
public bool Succeeded { get; set; }
public string ErrorMessage { get; set; }

public ICollection<EntityAudit> Entities { get; } = new List<EntityAudit>();


}

public class EntityAudit


{
public int Id { get; set; }
public EntityState State { get; set; }
public string AuditMessage { get; set; }

public SaveChangesAudit SaveChangesAudit { get; set; }


}

El interceptor
La idea general para la auditoría con el interceptor es:
Se crea un mensaje de auditoría al principio de SaveChanges y se escribe en la base de datos de auditoría.
Se permite que SaveChanges continúe
Si SaveChanges se realiza correctamente, se actualiza el mensaje de auditoría para indicar que la operación
se ha realizado correctamente.
Si se produce un error en SaveChanges, el mensaje de auditoría se actualiza para indicar el error.
La primera fase se controla antes de que los cambios se envíen a la base de datos mediante invalidaciones de
[Link] y [Link] .
public async ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = default)
{
_audit = CreateAudit([Link]);

using (var auditContext = new AuditContext(_connectionString))


{
[Link](_audit);
await [Link]();
}

return result;
}

public InterceptionResult<int> SavingChanges(


DbContextEventData eventData,
InterceptionResult<int> result)
{
_audit = CreateAudit([Link]);

using (var auditContext = new AuditContext(_connectionString))


{
[Link](_audit);
[Link]();
}

return result;
}

La invalidación de los métodos de sincronización y Async garantiza que la auditoría se realizará


independientemente de si SaveChanges SaveChangesAsync se llama a o. Observe también que la sobrecarga
asincrónica es capaz de realizar la e/s asincrónica sin bloqueo en la base de datos de auditoría. Puede que desee
iniciar desde el método Sync SavingChanges para asegurarse de que toda la e/s de la base de datos sea
asincrónica. Esto requiere que la aplicación siempre llame a SaveChangesAsync y nunca SaveChanges .
Mensaje de auditoría
Cada método de interceptor tiene un eventData parámetro que proporciona información contextual sobre el
evento que se va a interceptar. En este caso, el DbContext de la aplicación actual se incluye en los datos de
evento, que se utiliza a continuación para crear un mensaje de auditoría.
private static SaveChangesAudit CreateAudit(DbContext context)
{
[Link]();

var audit = new SaveChangesAudit { AuditId = [Link](), StartTime = [Link] };

foreach (var entry in [Link]())


{
var auditMessage = [Link] switch
{
[Link] => CreateDeletedMessage(entry),
[Link] => CreateModifiedMessage(entry),
[Link] => CreateAddedMessage(entry),
_ => null
};

if (auditMessage != null)
{
[Link](new EntityAudit { State = [Link], AuditMessage = auditMessage });
}
}

return audit;

string CreateAddedMessage(EntityEntry entry)


=> [Link](
$"Inserting {[Link]()} with ",
(auditString, property) => auditString + $"{[Link]}: '{[Link]}'
");

string CreateModifiedMessage(EntityEntry entry)


=> [Link](property => [Link] ||
[Link]()).Aggregate(
$"Updating {[Link]()} with ",
(auditString, property) => auditString + $"{[Link]}: '{[Link]}'
");

string CreateDeletedMessage(EntityEntry entry)


=> [Link](property => [Link]()).Aggregate(
$"Deleting {[Link]()} with ",
(auditString, property) => auditString + $"{[Link]}: '{[Link]}'
");
}

El resultado es una SaveChangesAudit entidad con una colección de EntityAudit entidades, una para cada
inserción, actualización o eliminación. A continuación, el interceptor inserta estas entidades en la base de datos
de auditoría.

TIP
ToString se invalida en cada EF Core clase de datos de evento para generar el mensaje de registro equivalente para el
evento. Por ejemplo, al llamar a se [Link] genera "Entity Framework Core 5.0.0
inicializado ' BlogsContext ' con el proveedor ' Microsoft. EntityFrameworkCore. SQLite ' con Options: None".

Detección correcta
La entidad Audit se almacena en el interceptor para que se pueda tener acceso a ella de nuevo una vez que
SaveChanges se realiza correctamente o se produce un error. Es si se realiza correctamente
[Link] o [Link] se llama a.
public int SavedChanges(SaveChangesCompletedEventData eventData, int result)
{
using (var auditContext = new AuditContext(_connectionString))
{
[Link](_audit);
_audit.Succeeded = true;
_audit.EndTime = [Link];

[Link]();
}

return result;
}

public async ValueTask<int> SavedChangesAsync(


SaveChangesCompletedEventData eventData,
int result,
CancellationToken cancellationToken = default)
{
using (var auditContext = new AuditContext(_connectionString))
{
[Link](_audit);
_audit.Succeeded = true;
_audit.EndTime = [Link];

await [Link](cancellationToken);
}

return result;
}

La entidad de auditoría está asociada al contexto de auditoría, ya que ya existe en la base de datos y debe
actualizarse. A continuación, establecemos Succeeded y EndTime , que marca estas propiedades como
modificadas, por lo que SaveChanges enviará una actualización a la base de datos de auditoría.
Detección de errores
El error se administra de forma muy similar a como se realiza correctamente, pero en el
[Link] [Link] método o. Los
datos del evento contienen la excepción que se produjo.
public void SaveChangesFailed(DbContextErrorEventData eventData)
{
using (var auditContext = new AuditContext(_connectionString))
{
[Link](_audit);
_audit.Succeeded = false;
_audit.EndTime = [Link];
_audit.ErrorMessage = [Link];

[Link]();
}
}

public async Task SaveChangesFailedAsync(


DbContextErrorEventData eventData,
CancellationToken cancellationToken = default)
{
using (var auditContext = new AuditContext(_connectionString))
{
[Link](_audit);
_audit.Succeeded = false;
_audit.EndTime = [Link];
_audit.ErrorMessage = [Link]?.Message;

await [Link](cancellationToken);
}
}

Demostración
El ejemplo de auditoría contiene una aplicación de consola simple que realiza cambios en la base de datos de
blogs y, a continuación, muestra la auditoría que se ha creado.
// Insert, update, and delete some entities

using (var context = new BlogsContext())


{
[Link](
new Blog { Name = "EF Blog", Posts = { new Post { Title = "EF Core 3.1!" }, new Post { Title = "EF
Core 5.0!" } } });

await [Link]();
}

using (var context = new BlogsContext())


{
var blog = [Link](e => [Link]).Single();

[Link] = "EF Core Blog";


[Link]([Link]());
[Link](new Post { Title = "EF Core 6.0!" });

[Link]();
}

// Do an insert that will fail

using (var context = new BlogsContext())


{
try
{
[Link](new Post { Id = 3, Title = "EF Core 3.1!" });

await [Link]();
}
catch (DbUpdateException)
{
}
}

// Look at the audit trail

using (var context = new AuditContext("DataSource=[Link]"))


{
foreach (var audit in [Link](e => [Link]).ToList())
{
[Link](
$"Audit {[Link]} from {[Link]} to {[Link]} was{([Link] ? "" : "
not")} successful.");

foreach (var entity in [Link])


{
[Link]($" {[Link]}");
}

if (![Link])
{
[Link]($" Error: {[Link]}");
}
}
}

El resultado muestra el contenido de la base de datos de auditoría:


Audit 52e94327-1767-4046-a3ca-4c6b1eecbca6 from 10/14/2020 9:10:17 PM to 10/14/2020 9:10:17 PM was
successful.
Inserting Blog with Id: '-2147482647' Name: 'EF Blog'
Inserting Post with Id: '-2147482647' BlogId: '-2147482647' Title: 'EF Core 3.1!'
Inserting Post with Id: '-2147482646' BlogId: '-2147482647' Title: 'EF Core 5.0!'
Audit 8450f57a-5030-4211-a534-eb66b8da7040 from 10/14/2020 9:10:17 PM to 10/14/2020 9:10:17 PM was
successful.
Inserting Post with Id: '-2147482645' BlogId: '1' Title: 'EF Core 6.0!'
Updating Blog with Id: '1' Name: 'EF Core Blog'
Deleting Post with Id: '1'
Audit 201fef4d-66a7-43ad-b9b6-b57e9d3f37b3 from 10/14/2020 9:10:17 PM to 10/14/2020 9:10:17 PM was not
successful.
Inserting Post with Id: '3' BlogId: '' Title: 'EF Core 3.1!'
Error: SQLite Error 19: 'UNIQUE constraint failed: [Link]'.
Uso de agentes de escucha de diagnóstico en EF
Core
12/03/2021 • 5 minutes to read • Edit Online

TIP
Puede descargar el ejemplo de este artículo en github.

Las escuchas de diagnóstico permiten escuchar cualquier evento de EF Core que se produzca en el proceso de
.NET actual. La DiagnosticListener clase forma parte de un mecanismo común en .net para obtener información
de diagnóstico de aplicaciones en ejecución.
Las escuchas de diagnóstico no son adecuadas para obtener eventos de una sola instancia de DbContext. Los
interceptores de EF Core proporcionan acceso a los mismos eventos con registro por contexto.
Las escuchas de diagnóstico no están diseñadas para el registro. Considere la posibilidad de usar un registro
simple o Microsoft. Extensions. Logging para el registro.

Ejemplo: observación de eventos de diagnóstico


La resolución de eventos EF Core es un proceso de dos pasos. En primer lugar , DiagnosticListener se debe
crear un observador para sí mismo:

public class DiagnosticObserver : IObserver<DiagnosticListener>


{
public void OnCompleted()
=> throw new NotImplementedException();

public void OnError(Exception error)


=> throw new NotImplementedException();

public void OnNext(DiagnosticListener value)


{
if ([Link] == [Link]) // "[Link]"
{
[Link](new KeyValueObserver());
}
}
}

El OnNext método busca el DiagnosticListener que procede de EF Core. Este agente de escucha tiene el nombre
"Microsoft. EntityFrameworkCore", que se puede obtener de la DbLoggerCategory clase tal y como se muestra.
Este observador debe registrarse en todo el mundo, por ejemplo, en el método de la aplicación Main :

[Link](new DiagnosticObserver());

En segundo lugar, una vez que se encuentra el EF Core DiagnosticListener, se crea un nuevo observador de
clave-valor para suscribirse a los eventos de EF Core reales. Por ejemplo:
public class KeyValueObserver : IObserver<KeyValuePair<string, object>>
{
public void OnCompleted()
=> throw new NotImplementedException();

public void OnError(Exception error)


=> throw new NotImplementedException();

public void OnNext(KeyValuePair<string, object> value)


{
if ([Link] == [Link])
{
var payload = (ContextInitializedEventData)[Link];
[Link]($"EF is initializing {[Link]().Name} ");
}

if ([Link] == [Link])
{
var payload = (ConnectionEventData)[Link];
[Link]($"EF is opening a connection to {[Link]} ");
}
}
}

El OnNext método es esta hora llamada con un par clave-valor para cada evento de EF Core. La clave es el
nombre del evento, que se puede obtener de uno de los siguientes:
CoreEventId para eventos comunes a todos los proveedores de bases de datos de EF Core
RelationalEventId para eventos comunes a todos los proveedores de bases de datos relacionales
Clase similar para los eventos específicos del proveedor de base de datos actual. Por ejemplo,
SqlServerEventId para el proveedor de SQL Server.
El valor del par clave-valor es un tipo de carga específico para el evento. El tipo de carga que se espera se
documenta en cada evento definido en estas clases de eventos.
Por ejemplo, el código anterior controla los ContextInitialized eventos y ConnectionOpening . Para el primero de
ellos, la carga es ContextInitializedEventData . En el segundo caso, es ConnectionEventData .

TIP
ToString se invalida en cada EF Core clase de datos de evento para generar el mensaje de registro equivalente para el
evento. Por ejemplo, al llamar a se [Link] genera "Entity Framework Core 5.0.0
inicializado ' BlogsContext ' con el proveedor ' Microsoft. EntityFrameworkCore. SQLite ' con Options: None".

El ejemplo contiene una aplicación de consola simple que realiza cambios en la base de datos de blogs e
imprime los eventos de diagnóstico encontrados.
public static void Main()
{
[Link](new DiagnosticObserver());

using (var context = new BlogsContext())


{
[Link]();
[Link]();

[Link](
new Blog { Name = "EF Blog", Posts = { new Post { Title = "EF Core 3.1!" }, new Post { Title =
"EF Core 5.0!" } } });

[Link]();
}

using (var context = new BlogsContext())


{
var blog = [Link](e => [Link]).Single();

[Link] = "EF Core Blog";


[Link]([Link]());
[Link](new Post { Title = "EF Core 6.0!" });

[Link]();
}

La salida de este código muestra los eventos detectados:

EF is initializing BlogsContext
EF is opening a connection to Data Source=[Link];Mode=ReadOnly
EF is opening a connection to DataSource=[Link]
EF is opening a connection to Data Source=[Link];Mode=ReadOnly
EF is opening a connection to DataSource=[Link]
EF is opening a connection to DataSource=[Link]
EF is opening a connection to DataSource=[Link]
EF is initializing BlogsContext
EF is opening a connection to DataSource=[Link]
EF is opening a connection to DataSource=[Link]
Contadores de eventos
12/03/2021 • 6 minutes to read • Edit Online

NOTE
Esta característica se agregó en EF Core 5.0.

Entity Framework Core (EF Core) expone métricas numéricas continuas que pueden proporcionar una buena
indicación del estado del programa. Estas métricas se pueden usar para los siguientes fines:
Seguimiento de la carga de base de datos general en tiempo real cuando se ejecuta la aplicación
Exponga prácticas de codificación problemáticas que puedan provocar un rendimiento degradado
Seguimiento y aislamiento del comportamiento anómalo del programa
EF Core informa de las métricas a través de la característica de contadores de eventos estándar de .NET; se
recomienda leer esta entrada de blog para obtener una visión general rápida de cómo funcionan los contadores.

Asociar a un proceso mediante dotnet-counters


La herramienta dotnet-counters se puede utilizar para asociar a un proceso en ejecución y notificar EF Core los
contadores de eventos con regularidad. no es necesario hacer nada especial en el programa para que estos
contadores estén disponibles.
En primer lugar, instale la dotnet-counters herramienta: dotnet tool install --global dotnet-counters .
A continuación, busque el ID. de proceso (PID) del proceso .NET que ejecuta la aplicación EF Core:
Windows
Linux o macOS

1. Abra el administrador de tareas de Windows; para ello, haga clic con el botón secundario en la barra de
tareas y seleccione "Administrador de tareas".
2. Asegúrese de que la opción "más detalles" esté seleccionada en la parte inferior de la ventana.
3. En la pestaña procesos, haga clic con el botón secundario en una columna y asegúrese de que la columna
PID esté habilitada.
4. Busque la aplicación en la lista de procesos y obtenga su identificador de proceso de la columna PID.
Dentro de la aplicación .NET, el ID. de proceso está disponible como [Link]().Id ; esto
puede ser útil para imprimir el PID al iniciarse.
Por último, inicie dotnet-counters como se indica a continuación:

dotnet counters monitor [Link] -p <PID>

dotnet-counters ahora se asociará al proceso en ejecución y comenzará a notificar datos de contador continuos:
Press p to pause, r to resume, q to quit.
Status: Running

[[Link]]
Active DbContexts 1
Execution Strategy Operation Failures (Count / 1 sec) 0
Execution Strategy Operation Failures (Total) 0
Optimistic Concurrency Failures (Count / 1 sec) 0
Optimistic Concurrency Failures (Total) 0
Queries (Count / 1 sec) 1
Queries (Total) 189
Query Cache Hit Rate (%) 100
SaveChanges (Count / 1 sec) 0
SaveChanges (Total) 0

Contadores y su significado
N O M B RE DEL C O N TA DO R DESC RIP C IÓ N

Active DbContexts El número de instancias de DbContext activas y no


desechadas actualmente en la aplicación. Si este número
crece continuamente, puede tener una fuga porque las
instancias de DbContext no se desechan correctamente.
Tenga en cuenta que si está habilitada la agrupación de
contexto , este número incluye instancias de DbContext
agrupadas que no se usan actualmente.

Errores de operación de la estrategia de ejecución Número de veces que una operación de base de datos no se
pudo ejecutar. Si se habilita una estrategia de ejecución de
reintento, esto incluye cada error individual en varios
intentos en la misma operación. Se puede usar para detectar
problemas transitorios con la infraestructura.

Errores de simultaneidad optimista Número de veces SaveChanges que se produjo un error de


simultaneidad optimista porque los datos del almacén de
datos se cambiaron desde que el código lo cargó. Esto
corresponde a un DbUpdateConcurrencyException que se
está iniciando.

Consultas El número de consultas ejecutadas.

Tasa de aciertos de caché de consultas (%) La proporción de aciertos de caché de consultas. La primera
vez que se ejecuta una consulta LINQ determinada por EF
Core (excepto los parámetros), se debe compilar en qué es
un proceso relativamente pesado. En una aplicación normal,
se reutilizan todas las consultas y la tasa de aciertos de
caché de consulta debe ser estable al 100% después de un
período de preparación inicial. Si este número es inferior al
100% a lo largo del tiempo, puede experimentar un
rendimiento degradado debido a compilaciones repetidas, lo
que podría ser el resultado de una generación de consultas
dinámicas que no es óptima.

SaveChanges Número de veces que se ha SaveChanges llamado a. Tenga


en cuenta que SaveChanges guarda varios cambios en un
único lote, por lo que no representa necesariamente cada
actualización individual realizada en una sola entidad.
Recursos adicionales
Documentación de .NET sobre los contadores de eventos
Pruebas de código que usa EF Core
12/03/2021 • 11 minutes to read • Edit Online

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 y los inconvenientes de cada una de estas opciones, y se muestra
cómo usar EF Core con cada método.

TIP
Eche un vistazo al ejemplo de prueba de EF Core para ver código que muestra los conceptos descritos aquí.

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 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 sistemas de 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.
LocalDB también presenta algunos inconvenientes:
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. Con todo, LocalDB puede ser adecuado
para algunas personas, especialmente en equipos de desarrollo menos potentes.
Otra manera de evitar que el sistema de base de datos se ejecute directamente en el equipo de desarrollo es
ejecutar SQL Server (o cualquier otro sistema de base de datos) en un contenedor de Docker (o similar).

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 para probar las aplicaciones que usan EF Core . De manera
específica:
No es una base de datos relacional.
No admite transacciones.
No puede ejecutar consultas SQL sin formato.
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 de EF siempre que se realizan pruebas unitarias de algo que use
DbContext. En este caso, el uso de la base de datos en memoria de EF 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.
En el ejemplo de prueba de EF Core puede ver pruebas que usan la base de datos en memoria de EF, así como
SQL Server y SQLite.
Ejemplo de prueba de EF Core
12/03/2021 • 16 minutes to read • Edit Online

TIP
El código de este documento se puede encontrar en GitHub como un ejemplo ejecutable. Tenga en cuenta que se espera
que algunas de estas pruebas produzcan un error . Los motivos para ello se explican a continuación.

Este documento le guía a través de un ejemplo para probar el código que usa EF Core.

Aplicación
El ejemplo contiene dos proyectos:
ItemsWebApi: una API Web muy sencilla respaldada por [Link] Core con un solo controlador
Pruebas: un proyecto de prueba de xUnit para probar el controlador
El modelo y las reglas de negocios
El modelo de respaldo de esta API tiene dos tipos Items de entidad: y Tags .
Items tienen un nombre que distingue entre mayúsculas y minúsculas y una colección de Tags .
Cada Tag una tiene una etiqueta y un recuento que representa el número de veces que se ha aplicado a Item .
Cada Item una de ellas solo debe tener una Tag con una etiqueta determinada.
Si un elemento se etiqueta con la misma etiqueta más de una vez, se incrementa el recuento de la
etiqueta existente con esa etiqueta en lugar de crear una nueva etiqueta.
La eliminación de Item debe eliminar todos los asociados Tags .
El Item tipo de entidad
El Item tipo de entidad:
public class Item
{
private readonly int _id;
private readonly List<Tag> _tags = new List<Tag>();

private Item(int id, string name)


{
_id = id;
Name = name;
}

public Item(string name)


{
Name = name;
}

public Tag AddTag(string label)


{
var tag = _tags.FirstOrDefault(t => [Link] == label);

if (tag == null)
{
tag = new Tag(label);
_tags.Add(tag);
}

[Link]++;

return tag;
}

public string Name { get; }

public IReadOnlyList<Tag> Tags => _tags;


}

Y su configuración en [Link] :

[Link]<Item>(
b =>
{
[Link]("_id");
[Link]("_id");
[Link](e => [Link]);
[Link](e => [Link]).WithOne().IsRequired();
});

Observe que el tipo de entidad restringe la manera en que se puede usar para reflejar el modelo de dominio y
las reglas de negocios. En concreto:
La clave principal se asigna directamente al _id campo y no se expone públicamente.
EF detecta y usa el constructor privado que acepta el valor y el nombre de la clave principal.
La Name propiedad es de solo lectura y se establece solo en el constructor.
Tags se exponen como IReadOnlyList<Tag> para evitar modificaciones arbitrarias.
EF asocia el Tags nombre de la propiedad con el _tags campo de respaldo.
El AddTag método toma una etiqueta de etiqueta e implementa la regla de negocios que se ha
descrito anteriormente. Es decir, solo se agrega una etiqueta para las etiquetas nuevas. En caso
contrario, se incrementa el recuento de una etiqueta existente.
La Tags propiedad de navegación se configura para una relación de varios a uno
No es necesario que una propiedad de navegación de Tag a Item , por lo que no se incluye.
Además, no Tag define una propiedad de clave externa. En su lugar, EF creará y administrará una
propiedad en el estado de sombra.
El Tag tipo de entidad
El Tag tipo de entidad:

public class Tag


{
private readonly int _id;

private Tag(int id, string label)


{
_id = id;
Label = label;
}

public Tag(string label) => Label = label;

public string Label { get; }

public int Count { get; set; }


}

Y su configuración en [Link] :

[Link]<Tag>(
b =>
{
[Link]("_id");
[Link]("_id");
[Link](e => [Link]);
});

De forma similar a Item , Tag oculta su clave principal y hace que la Label propiedad sea de solo lectura.
El Items controlador
El controlador de API Web es bastante básico. Obtiene un DbContext del contenedor de inserción de
dependencias a través de la inserción de constructores:

private readonly ItemsContext _context;

public ItemsController(ItemsContext context)


=> _context = context;

Tiene métodos para obtener todos Items o un Item con un nombre determinado:

[HttpGet]
public IEnumerable<Item> Get()
=> _context.Set<Item>().Include(e => [Link]).OrderBy(e => [Link]);

[HttpGet]
public Item Get(string itemName)
=> _context.Set<Item>().Include(e => [Link]).FirstOrDefault(e => [Link] == itemName);

Tiene un método para agregar un nuevo Item :


[HttpPost]
public ActionResult<Item> PostItem(string itemName)
{
var item = _context.Add(new Item(itemName)).Entity;

_context.SaveChanges();

return item;
}

Un método para etiquetar un Item con una etiqueta:

[HttpPost]
public ActionResult<Tag> PostTag(string itemName, string tagLabel)
{
var tag = _context
.Set<Item>()
.Include(e => [Link])
.Single(e => [Link] == itemName)
.AddTag(tagLabel);

_context.SaveChanges();

return tag;
}

Y un método para eliminar un Item y todos los asociados Tags :

[HttpDelete("{itemName}")]
public ActionResult<Item> DeleteItem(string itemName)
{
var item = _context
.Set<Item>()
.SingleOrDefault(e => [Link] == itemName);

if (item == null)
{
return NotFound();
}

_context.Remove(item);
_context.SaveChanges();

return item;
}

La mayoría de la validación y el control de errores se han quitado para reducir la confusión.

Las pruebas
Las pruebas se organizan para ejecutarse con varias configuraciones de proveedor de base de datos:
Proveedor de SQL Server, que es el proveedor que usa la aplicación.
Proveedor de SQLite
Proveedor de SQLite que usa bases de datos de SQLite en memoria
El proveedor de base de datos de EF en memoria
Esto se logra colocando todas las pruebas en una clase base y, a continuación, heredando de esta para probar
con cada proveedor.
TIP
Tendrá que cambiar el SQL Server cadena de conexión si no está usando LocalDB. Consulte pruebas con SQLite para
obtener instrucciones sobre el uso de SQLite para realizar pruebas en memoria.

Se espera que se produzcan errores en las dos pruebas siguientes:


Can_remove_item_and_all_associated_tags al ejecutar con el proveedor de base de datos de EF en memoria
Can_add_item_differing_only_by_case al ejecutar con el proveedor de SQL Server

Esto se describe con más detalle a continuación.


Configurar y propagar la base de datos
XUnit, al igual que la mayoría de los marcos de pruebas, creará una nueva instancia de clase de prueba para
cada serie de pruebas. Además, XUnit no ejecutará en paralelo las pruebas de una clase de prueba determinada.
Esto significa que se puede instalar y configurar la base de datos en el constructor de prueba y que estará en un
estado conocido para cada prueba.

TIP
En este ejemplo se vuelve a crear la base de datos para cada prueba. Esto funciona bien para las pruebas de base de datos
en memoria de SQLite y EF, pero puede suponer una sobrecarga significativa con otros sistemas de base de datos, como
SQL Server. Los enfoques para reducir esta sobrecarga se abordan en el uso compartido de bases de datos entre pruebas.

Cuando se ejecuta cada prueba:


DbContextOptions se configuran para el proveedor en uso y se pasan al constructor de clase base
Estas opciones se almacenan en una propiedad y se usan en las pruebas para la creación de instancias
de DbContext.
Se llama a un método de inicialización para crear e inicializar la base de datos
El método de inicialización garantiza que la base de datos está limpia al eliminarla y volver a crearla.
Algunas entidades de prueba conocidas se crean y se guardan en la base de datos.
protected ItemsControllerTest(DbContextOptions<ItemsContext> contextOptions)
{
ContextOptions = contextOptions;

Seed();
}

protected DbContextOptions<ItemsContext> ContextOptions { get; }

private void Seed()


{
using (var context = new ItemsContext(ContextOptions))
{
[Link]();
[Link]();

var one = new Item("ItemOne");


[Link]("Tag11");
[Link]("Tag12");
[Link]("Tag13");

var two = new Item("ItemTwo");

var three = new Item("ItemThree");


[Link]("Tag31");
[Link]("Tag31");
[Link]("Tag31");
[Link]("Tag32");
[Link]("Tag32");

[Link](one, two, three);

[Link]();
}
}

Cada clase de prueba concreta hereda de este. Por ejemplo:

public class SqliteItemsControllerTest : ItemsControllerTest


{
public SqliteItemsControllerTest()
: base(
new DbContextOptionsBuilder<ItemsContext>()
.UseSqlite("Filename=[Link]")
.Options)
{
}
}

Estructura de prueba
Aunque la aplicación usa la inserción de dependencias, las pruebas no lo hacen. Sería adecuado usar la inserción
de dependencias aquí, pero el código adicional que requiere tiene poco valor. En su lugar, se crea un DbContext
usando new y, a continuación, se pasa directamente como la dependencia al controlador.
Después, cada prueba ejecuta el método sometido a prueba en el controlador y valida que los resultados son los
esperados. Por ejemplo:
[Fact]
public void Can_get_items()
{
using (var context = new ItemsContext(ContextOptions))
{
var controller = new ItemsController(context);

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

[Link](3, [Link]);
[Link]("ItemOne", items[0].Name);
[Link]("ItemThree", items[1].Name);
[Link]("ItemTwo", items[2].Name);
}
}

Observe que se usan diferentes instancias de DbContext para inicializar la base de datos y ejecutar las pruebas.
Esto garantiza que la prueba no esté usando (o pasando por) entidades cuyo seguimiento realiza el contexto al
realizar la propagación. También mejor coincide con lo que ocurre en servicios y aplicaciones Web.
Las pruebas que mutan la base de datos crean una segunda instancia de DbContext en la prueba por motivos
similares. Es decir, crear un nuevo contexto, limpiar y, a continuación, leerlo desde la base de datos para
asegurarse de que los cambios se guardaron en la base de datos. Por ejemplo:

[Fact]
public void Can_add_item()
{
using (var context = new ItemsContext(ContextOptions))
{
var controller = new ItemsController(context);

var item = [Link]("ItemFour").Value;

[Link]("ItemFour", [Link]);
}

using (var context = new ItemsContext(ContextOptions))


{
var item = [Link]<Item>().Single(e => [Link] == "ItemFour");

[Link]("ItemFour", [Link]);
[Link](0, [Link]);
}
}

Dos pruebas ligeramente más complicadas cubren la lógica de negocios en torno a la adición tags .
[Fact]
public void Can_add_tag()
{
using (var context = new ItemsContext(ContextOptions))
{
var controller = new ItemsController(context);

var tag = [Link]("ItemTwo", "Tag21").Value;

[Link]("Tag21", [Link]);
[Link](1, [Link]);
}

using (var context = new ItemsContext(ContextOptions))


{
var item = [Link]<Item>().Include(e => [Link]).Single(e => [Link] == "ItemTwo");

[Link](1, [Link]);
[Link]("Tag21", [Link][0].Label);
[Link](1, [Link][0].Count);
}
}

[Fact]
public void Can_add_tag_when_already_existing_tag()
{
using (var context = new ItemsContext(ContextOptions))
{
var controller = new ItemsController(context);

var tag = [Link]("ItemThree", "Tag32").Value;

[Link]("Tag32", [Link]);
[Link](3, [Link]);
}

using (var context = new ItemsContext(ContextOptions))


{
var item = [Link]<Item>().Include(e => [Link]).Single(e => [Link] == "ItemThree");

[Link](2, [Link]);
[Link]("Tag31", [Link][0].Label);
[Link](3, [Link][0].Count);
[Link]("Tag32", [Link][1].Label);
[Link](3, [Link][1].Count);
}
}

Problemas con diferentes proveedores de bases de datos


La prueba con un sistema de base de datos diferente al que se usa en la aplicación de producción puede
provocar problemas. Estos se describen en el nivel conceptual del código de prueba que usa EF Core. En las
secciones siguientes se incluyen dos ejemplos de estos problemas que se muestran en las pruebas de este
ejemplo.
La prueba se supera cuando se interrumpe la aplicación
Uno de los requisitos de nuestra aplicación es que " Items tiene un nombre que distingue entre mayúsculas y
minúsculas y una colección de Tags ". Esto es bastante sencillo de probar:
[Fact]
public void Can_add_item_differing_only_by_case()
{
using (var context = new ItemsContext(ContextOptions))
{
var controller = new ItemsController(context);

var item = [Link]("itemtwo").Value;

[Link]("itemtwo", [Link]);
}

using (var context = new ItemsContext(ContextOptions))


{
var item = [Link]<Item>().Single(e => [Link] == "itemtwo");

[Link](0, [Link]);
}
}

La ejecución de esta prueba en la base de datos en memoria de EF indica que todo está bien. Todo sigue
teniendo el aspecto correcto al usar SQLite. Pero se produce un error en la prueba cuando se ejecuta en SQL
Server.

[Link] : Sequence contains more than one element


at [Link]()
at [Link][TSource](IEnumerable`1 source)
at [Link][TResult](Expression query)
at [Link][TResult](Expression
expression)
at [Link][TSource](IQueryable`1 source, Expression`1 predicate)
at [Link].Can_add_item_differing_only_by_case()

Esto se debe a que la base de datos de EF en memoria y la base de datos de SQLite distinguen mayúsculas de
minúsculas de forma predeterminada. SQL Server, por otro lado, no distingue entre mayúsculas y minúsculas.
EF Core, por diseño, no cambia estos comportamientos porque forzar un cambio en la distinción de mayúsculas
y minúsculas puede tener un gran impacto en el rendimiento.
Una vez que sabemos que se trata de un problema, podemos corregir la aplicación y compensar las pruebas. Sin
embargo, el punto aquí es que este error podría perderse si solo se prueba con la base de datos de EF en
memoria o con proveedores de SQLite.
Se produce un error en la prueba cuando la aplicación es correcta
Otro de los requisitos para nuestra aplicación es que "la eliminación de un Item debe eliminar todos los
asociados Tags ". De nuevo, fácil de probar:
[Fact]
public void Can_remove_item_and_all_associated_tags()
{
using (var context = new ItemsContext(ContextOptions))
{
var controller = new ItemsController(context);

var item = [Link]("ItemThree").Value;

[Link]("ItemThree", [Link]);
}

using (var context = new ItemsContext(ContextOptions))


{
[Link]([Link]<Item>().Any(e => [Link] == "ItemThree"));
[Link]([Link]<Tag>().Any(e => [Link]("Tag3")));
}
}

Esta prueba se supera en SQL Server y SQLite, pero produce un error con la base de datos de EF en memoria.

[Link]() Failure
Expected: False
Actual: True
at [Link].Can_remove_item_and_all_associated_tags()

En este caso, la aplicación funciona correctamente porque SQL Server admite eliminaciones en cascada. SQLite
también admite eliminaciones en cascada, al igual que la mayoría de las bases de datos relacionales, por lo que
la prueba en SQLite funciona. Por otro lado, la base de datos en memoria de EF no admite eliminaciones en
cascada. Esto significa que esta parte de la aplicación no se puede probar con el proveedor de base de datos de
EF en memoria.
Compartir bases de datos entre pruebas
12/03/2021 • 9 minutes to read • Edit Online

En el ejemplo de pruebas de EF Core se ha mostrado cómo probar aplicaciones en diferentes sistemas de base
de datos. Para ese ejemplo, cada prueba ha creado una nueva base de datos. Este es un buen patrón al usar
SQLite o la base de datos en memoria de EF, pero puede suponer una sobrecarga importante al usar otros
sistemas de base de datos.
Este ejemplo se basa en el ejemplo anterior moviendo la creación de la base de datos a un accesorio de prueba.
Esto permite que una sola base de datos de SQL Server se cree y se inicialice solo una vez para todas las
pruebas.

TIP
Asegúrese de trabajar en el ejemplo de prueba de EF Core antes de continuar.

No es difícil escribir varias pruebas en la misma base de datos. El truco lo está haciendo de manera que las
pruebas no viajen entre sí mientras se ejecutan. Esto requiere comprender lo siguiente:
Cómo compartir de forma segura objetos entre pruebas
Cuando el marco de pruebas ejecuta pruebas en paralelo
Cómo mantener la base de datos en un estado limpio para cada prueba

El accesorio
Usaremos un accesorio de prueba para compartir objetos entre pruebas. La documentación de xUnit indica que
se debe usar un accesorio "Si desea crear un único contexto de prueba y compartirlo entre todas las pruebas de
la clase y hacer que se limpie una vez finalizadas todas las pruebas de la clase".

TIP
En este ejemplo se usa xUnit, pero existen conceptos similares en otros marcos de pruebas, incluido NUnit.

Esto significa que se debe trasladar la creación y propagación de la base de datos a una clase de accesorio. Este
es su aspecto:

public class SharedDatabaseFixture : IDisposable


{
private static readonly object _lock = new object();
private static bool _databaseInitialized;

public SharedDatabaseFixture()
{
Connection = new SqlConnection(@"Server=
(localdb)\mssqllocaldb;Database=EFTestSample;ConnectRetryCount=0");

Seed();

[Link]();
}

public DbConnection Connection { get; }


public ItemsContext CreateContext(DbTransaction transaction = null)
{
var context = new ItemsContext(new DbContextOptionsBuilder<ItemsContext>
().UseSqlServer(Connection).Options);

if (transaction != null)
{
[Link](transaction);
}

return context;
}

private void Seed()


{
lock (_lock)
{
if (!_databaseInitialized)
{
using (var context = CreateContext())
{
[Link]();
[Link]();

var one = new Item("ItemOne");


[Link]("Tag11");
[Link]("Tag12");
[Link]("Tag13");

var two = new Item("ItemTwo");

var three = new Item("ItemThree");


[Link]("Tag31");
[Link]("Tag31");
[Link]("Tag31");
[Link]("Tag32");
[Link]("Tag32");

[Link](one, two, three);

[Link]();
}

_databaseInitialized = true;
}
}
}

public void Dispose() => [Link]();


}

Por ahora, observe cómo el constructor:


Crea una conexión de base de datos única para la duración del accesorio.
Crea y inicializa la base de datos llamando al Seed método.
Omitir el bloqueo por ahora; volveremos a él más adelante.

TIP
No es necesario que el código de creación y propagación sea asincrónico. Si se convierte en Async, se complicará el código
y no se mejorará el rendimiento ni el rendimiento de las pruebas.

La base de datos se crea mediante la eliminación de cualquier base de datos existente y, a continuación, la
creación de una nueva base de datos. Esto garantiza que la base de datos coincida con el modelo EF actual
incluso si se ha cambiado desde la última ejecución de pruebas.

TIP
Puede ser más rápido "limpiar" la base de datos existente usando algo como regenerar en lugar de volver a crearla cada
vez. Sin embargo, debe tener cuidado para asegurarse de que el esquema de la base de datos esté actualizado con el
modelo de EF al hacerlo.

La conexión a la base de datos se elimina cuando se desecha el accesorio. También puede considerar la
posibilidad de eliminar la base de datos de prueba en este momento. Sin embargo, esto requerirá un recuento
de referencias y bloqueo adicional si el accesorio se comparte con varias clases de prueba. Además, a menudo
resulta útil tener la base de datos de prueba todavía disponible para la depuración de pruebas con errores.

Uso del accesorio


XUnit tiene un patrón común para asociar un accesorio de prueba con una clase de pruebas:

public class SharedDatabaseTest : IClassFixture<SharedDatabaseFixture>


{
public SharedDatabaseTest(SharedDatabaseFixture fixture) => Fixture = fixture;

public SharedDatabaseFixture Fixture { get; }

XUnit creará ahora una instancia de accesorio única y la pasará a cada instancia de la clase de prueba. (Recuerde
en el primer ejemplo de prueba que xUnit crea una nueva instancia de clase de prueba cada vez que ejecuta una
prueba). Esto significa que la base de datos se creará y se inicializará una vez y cada prueba utilizará esta base
de datos.
Tenga en cuenta que las pruebas dentro de una sola clase no se ejecutarán en paralelo. Esto significa que es
seguro que cada prueba use la misma conexión de base de datos, aunque el DbConnection objeto no sea seguro
para subprocesos.

Mantenimiento de estado de base de datos


Las pruebas a menudo necesitan mutar los datos de prueba con inserciones, actualizaciones y eliminaciones. Sin
embargo, estos cambios afectarán a otras pruebas que esperan una base de datos limpia y propagada.
Esto se puede solucionar mediante la ejecución de pruebas mutadas dentro de una transacción. Por ejemplo:
[Fact]
public void Can_add_item()
{
using (var transaction = [Link]())
{
using (var context = [Link](transaction))
{
var controller = new ItemsController(context);

var item = [Link]("ItemFour").Value;

[Link]("ItemFour", [Link]);
}

using (var context = [Link](transaction))


{
var item = [Link]<Item>().Single(e => [Link] == "ItemFour");

[Link]("ItemFour", [Link]);
[Link](0, [Link]);
}
}
}

Observe que la transacción se crea cuando la prueba se inicia y se desecha cuando finaliza. Al desechar la
transacción, ésta se revierte, por lo que ninguna otra prueba verá ninguno de los cambios.
El método auxiliar para crear un contexto (consulte el código de accesorio anterior) acepta esta transacción y
opta por que el DbContext la use.

Uso compartido del accesorio


Es posible que haya observado el bloqueo del código en torno a la creación y propagación de bases de datos.
Esto no es necesario para este ejemplo, ya que solo una clase de pruebas usa el accesorio, por lo que solo se
crea una instancia de accesorio.
Sin embargo, puede que desee usar el mismo accesorio con varias clases de pruebas. XUnit creará una instancia
de accesorio para cada una de estas clases. Estos pueden ser usados por diferentes subprocesos que ejecutan
pruebas en paralelo. Por lo tanto, es importante tener un bloqueo adecuado para asegurarse de que solo un
subproceso realiza la creación y propagación de la base de datos.

TIP
lock Aquí está bien un sencillo. No es necesario intentar nada más complejo, como los patrones sin bloqueos.
Uso de SQLite para probar una aplicación EF Core
12/03/2021 • 2 minutes to read • Edit Online

WARNING
El uso de SQLite puede ser una manera eficaz de probar una aplicación EF Core. Sin embargo, pueden surgir problemas en
los que SQLite se comporta de forma diferente a otros sistemas de base de datos. Vea código de prueba que usa EF Core
para obtener una explicación de los problemas y las ventajas.

Este documento se basa en los conceptos presentados en el ejemplo que muestra cómo probar las aplicaciones
que usan EF Core. Los ejemplos de código que se muestran aquí provienen de este ejemplo.

Usar bases de datos en memoria de SQLite


Normalmente, SQLite crea bases de datos como archivos simples y accede al archivo en proceso con la
aplicación. Esto es muy rápido, especialmente cuando se usa una SSDrápida.
SQLite también puede usar bases de datos creadas exclusivamente en memoria. Esto es fácil de usar con EF
Core siempre que comprenda la duración de la base de datos en memoria:
La base de datos se crea cuando se abre la conexión con ella
La base de datos se elimina cuando se cierra la conexión con ella.
EF Core usará una conexión que ya está abierta cuando se le proporcione una y nunca intentará cerrarla. Por lo
tanto, la clave para usar EF Core con una base de datos SQLite en memoria es abrir la conexión antes de pasarla
a EF.
En el ejemplo se consigue con el código siguiente:

public class SqliteInMemoryItemsControllerTest : ItemsControllerTest, IDisposable


{
private readonly DbConnection _connection;

public SqliteInMemoryItemsControllerTest()
: base(
new DbContextOptionsBuilder<ItemsContext>()
.UseSqlite(CreateInMemoryDatabase())
.Options)
{
_connection = [Link](ContextOptions).Connection;
}

private static DbConnection CreateInMemoryDatabase()


{
var connection = new SqliteConnection("Filename=:memory:");

[Link]();

return connection;
}

public void Dispose() => _connection.Dispose();


}

Aviso:
El CreateInMemoryDatabase método crea una base de datos en memoria de SQLite y abre la conexión con ella.
El creado DbConnection se extrae de ContextOptions y se guarda.
La conexión se elimina cuando se elimina la prueba para que no se pierdan los recursos.

NOTE
Problema #16103 es el seguimiento de las formas de facilitar esta administración de conexiones.
Pruebas con la base de datos de EF In-Memory
12/03/2021 • 2 minutes to read • Edit Online

WARNING
La base de datos en memoria de EF a menudo se comporta de forma diferente a las bases de datos relacionales. Use la
base de datos en memoria de EF solo después de comprender totalmente los problemas y las ventajas y desventajas que
conlleva, como se describe en probar el código que usa EF Core.

TIP
SQLite es un proveedor relacional y también puede usar bases de datos en memoria. Considere la posibilidad de utilizarlo
para realizar pruebas con el fin de encontrar más coincidencia con los comportamientos comunes de la base de datos Esto
se trata en el uso de SQLite para probar una aplicación EF Core.

La información de esta página se encuentra ahora en otras ubicaciones:


Vea código de prueba que usa EF Core para obtener información general sobre las pruebas con la base de
datos en memoria de EF.
Vea el ejemplo que muestra cómo probar las aplicaciones que usan EF Core para obtener un ejemplo que
usa la base de datos en memoria de EF.
Consulte el proveedor de base de datos de EF en memoria para obtener información general sobre la base
de datos en memoria de EF.
Introducción al rendimiento
12/03/2021 • 9 minutes to read • Edit Online

El rendimiento de la base de datos es un tema amplio y complejo que abarca toda una pila de componentes: la
base de datos, la red, el controlador de base de datos y los niveles de acceso a datos, como EF Core. Aunque los
niveles generales y los O/RM como EF Core simplifican considerablemente el desarrollo de aplicaciones y
mejoran la facilidad de mantenimiento, en ocasiones pueden ser opacos y ocultar detalles internos críticos para
el rendimiento, como el SQL que se ejecuta. En esta sección se intenta proporcionar información general sobre
cómo conseguir un buen rendimiento con EF Core y cómo evitar errores comunes que pueden degradar el
rendimiento de la aplicación.

Identificación de cuellos de botella y medidas continuadas


Como sucede siempre con el rendimiento, es importante no apresurarse en la optimización sin contar con datos
que muestren un problema; como el gran Donald Knuth afirmó, "La optimización prematura es la raíz de todos
los males". En la sección de diagnóstico del rendimiento se describen varias maneras de entender dónde dedica
tiempo la aplicación en la lógica de base de datos y cómo identificar áreas problemáticas concretas. Una vez que
se ha identificado una consulta lenta, se pueden barajar las soluciones: ¿falta un índice en la base de datos? ¿Se
deben probar otros modelos de consulta?
Siempre debe someter a un banco de pruebas el código y las posibles alternativas personalmente: la sección de
diagnóstico del rendimiento contiene un banco de pruebas de ejemplo con BenchmarkDotNet, que puede usar
como plantilla para pruebas comparativas propias. No suponga que los bancos de pruebas públicos y generales
se aplican tal cual a cada caso de uso concreto; una gran variedad de factores, como la latencia de la base de
datos, la complejidad de las consultas y las cantidades de datos reales de las tablas, pueden tener un impacto
profundo en la solución más adecuada. Por ejemplo, muchos bancos de pruebas públicos se usan bajo
condiciones de red idóneas, donde la latencia para la base de datos es casi cero y con consultas
extremadamente ligeras que apenas requieren procesamiento (o E/S de disco) en la base de datos. Aunque son
valiosos para comparar las sobrecargas en tiempo de ejecución de los diferentes niveles de acceso a datos, las
diferencias que revelan suelen ser insignificantes en una aplicación real, donde la base de datos realiza el trabajo
real y la latencia en la base de datos es un factor de rendimiento importante.

Aspectos del rendimiento de acceso a datos


El rendimiento general del acceso a datos se puede dividir en las siguientes categorías generales:
Rendimiento de base de datos puro . Con las bases de datos relacionales, EF traduce las consultas LINQ
de la aplicación en las instrucciones SQL que ejecuta la base de datos; estas instrucciones SQL se pueden
ejecutar de forma más o menos eficaz. El índice adecuado en el lugar adecuado puede marcar
considerablemente las diferencias para el rendimiento de SQL, o bien volver a escribir la consulta LINQ
puede hacer que EF genere una consulta SQL mejor.
Transferencia de datos entre redes . Como sucede con cualquier sistema de redes, es importante limitar
la cantidad de datos que se transmiten por la conexión. Esto implica asegurarse de que solo se envían y
cargan los datos que se van a necesitar, pero que también se evita el efecto denominado "explosión
cartesiana" al cargar entidades relacionadas.
Recorridos de ida y vuelta de red . Más allá de la cantidad de datos que se transmiten, los recorridos de
ida y vuelta de red, ya que el tiempo que se tarda en ejecutar una consulta en la base de datos puede verse
reducido por el tiempo de desplazamiento de los paquetes entre la aplicación y la base de datos. La
sobrecarga de los recorridos de ida y vuelta depende en gran medida del entorno; cuanto más lejos esté el
servidor de base de datos, mayor será la latencia y más costoso cada recorrido de ida y vuelta. Con la llegada
de la nube, las aplicaciones se encuentran cada vez más lejos de la base de datos y las más activas que
realizan demasiados recorridos de ida y vuelta sufren un rendimiento degradado. Por tanto, es importante
comprender exactamente cuándo la aplicación se pone en contacto con la base de datos, cuántos recorridos
de ida y vuelta realiza, y si ese número se puede minimizar.
Sobrecarga de tiempo de ejecución de EF . Finalmente, EF agrega una sobrecarga en tiempo de
ejecución a las operaciones de base de datos: EF debe compilar las consultas desde LINQ to SQL (aunque
normalmente solo se debe hacer una vez), el seguimiento de los cambios agrega cierta sobrecarga (pero se
puede deshabilitar), etc. En la práctica, es probable que la sobrecarga de EF para las aplicaciones reales sea
insignificante en la mayoría de los casos, ya que el tiempo de ejecución de la consulta en la base de datos y la
latencia de red dominan el tiempo total; pero es importante comprender cuáles son las opciones y cómo
evitar algunos problemas.

Saber lo que ocurre en segundo plano


EF permite a los desarrolladores concentrarse en la lógica empresarial mediante la generación de SQL, la
materialización de resultados y la realización de otras tareas. Como sucede con cualquier nivel o abstracción,
también tiende a ocultar lo que sucede en segundo plano, como las consultas SQL reales que se ejecutan. El
rendimiento no es necesariamente un aspecto fundamental de todas las aplicaciones, sino que, en las
aplicaciones en las que lo es, es imprescindible que el desarrollador comprenda lo que hace EF: inspeccionar las
consultas SQL salientes, seguir los recorridos de ida y vuelta para asegurarse de que el problema N+1 no se
produzca, etc.

Almacenamiento en caché fuera de la base de datos


Por último, la manera más eficaz de interactuar con una base de datos es no interactuar con ella. Es decir, si el
acceso a la base de datos aparece como un cuello de botella de rendimiento en la aplicación, puede merecer la
pena almacenar en caché determinados resultados fuera de la base de datos, para minimizar las solicitudes.
Aunque el almacenamiento en caché aumenta la complejidad, es una parte fundamental de cualquier aplicación
escalable: mientras que la capa de aplicación se puede escalar fácilmente mediante la adición de más servidores
para controlar el aumento de la carga, el escalado del nivel de base de datos suele ser mucho más complicado.
Diagnóstico de rendimiento
12/03/2021 • 17 minutes to read • Edit Online

En esta sección se describen las formas de detectar problemas de rendimiento en la aplicación de EF y, una vez
que se ha identificado una área problemática, cómo analizarlos más adelante para identificar el problema raíz.
Es importante diagnosticar e investigar detenidamente cualquier problema antes de pasar a cualquier
conclusión y evitar que se asuma Dónde está la raíz del problema.

Identificación de comandos lentos de base de datos mediante


registro
Al final del día, EF prepara y ejecuta los comandos que se van a ejecutar en la base de datos. con bases de datos
relacionales, esto significa ejecutar instrucciones SQL a través de la API de [Link] Database. Si una
determinada consulta está tardando demasiado tiempo (por ejemplo, porque falta un índice), esto se puede
detectar detectando los registros de ejecución de comandos y observando cuánto tiempo tardan en
completarse.
EF hace que sea muy fácil capturar los tiempos de ejecución de los comandos, a través de un registro simple o
de Microsoft. Extensions. Logging:
Registro sencillo
[Link]

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


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

Cuando el nivel de registro se establece en [Link] , EF emite un mensaje de registro para cada
ejecución del comando con el tiempo necesario:

info: 06/12/2020 09:12:36.117 [Link][20101]


([Link])
Executed DbCommand (4ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [b].[Id], [b].[Name]
FROM [Blogs] AS [b]
WHERE [b].[Name] = N'foo'

El comando anterior tardó 4 milisegundos. Si un comando determinado tarda más de lo esperado, ha


encontrado una posible causa para un problema de rendimiento y ahora se puede centrar en él para
comprender por qué se ejecuta lentamente. El registro de comandos también puede revelar casos en los que se
están realizando interconexións de base de datos inesperadas. Esto se mostraría como varios comandos en los
que solo se esperaba uno.
WARNING
Mantener el registro de la ejecución de comandos habilitado en el entorno de producción suele ser una buena idea. El
registro reduce la velocidad de la aplicación y puede crear rápidamente archivos de registro enormes que pueden llenar el
disco del servidor. Se recomienda mantener solo el inicio de sesión durante un breve intervalo de tiempo para recopilar
datos y supervisar cuidadosamente la aplicación, o bien para capturar los datos de registro en un sistema de
preproducción.

Correlacionar comandos de base de datos en consultas LINQ


Un problema con el registro de la ejecución de comandos es que a veces es difícil correlacionar las consultas
SQL y las consultas LINQ: los comandos SQL que ejecuta EF pueden ser muy diferentes de las consultas LINQ
desde las que se generaron. Para ayudar a solucionar este problema, puede que desee usar la característica de
etiquetas de consulta de EF, que permite insertar un pequeño comentario de identificación en la consulta SQL:

var myLocation = new Point(1, 2);


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

La etiqueta se muestra en los registros:

-- This is my spatial query!

SELECT TOP(@__p_1) [p].[Id], [p].[Location]


FROM [People] AS [p]
ORDER BY [p].[Location].STDistance(@__myLocation_0) DESC

A menudo merece la pena etiquetar las consultas más importantes de una aplicación de esta manera, para que
los registros de ejecución de comandos sean más inmediatos y legibles.

Otras interfaces para capturar datos de rendimiento


Hay varias alternativas a la característica de registro de EF para capturar los tiempos de ejecución del comando,
lo que puede ser más eficaz. Las bases de datos suelen incluir sus propias herramientas de análisis de
rendimiento y seguimiento, que normalmente proporcionan información específica de la base de datos mucho
más enriquecida, más allá de los tiempos de ejecución simples. la configuración, las capacidades y el uso reales
varían considerablemente entre las bases de datos.
Por ejemplo, SQL Server Management Studio es un cliente eficaz que puede conectarse a su instancia de SQL
Server y proporcionar información valiosa sobre el rendimiento y la administración. Queda fuera del ámbito de
esta sección entrar en los detalles, pero dos funciones que merece la pena mencionar son el monitor de
actividad, que proporciona un panel activo de la actividad del servidor (incluidas las consultas más costosas) y la
característica eventos extendidos (XEvent) , que permite definir sesiones de captura de datos arbitrarias que se
pueden adaptar a sus necesidades exactas. La documentación SQL Server sobre supervisión proporciona más
información sobre estas características, así como otras.
Otro enfoque para capturar datos de rendimiento consiste en recopilar información emitida automáticamente
por EF o el controlador de base de datos a través de la DiagnosticSource interfaz y, a continuación, analizar los
datos o mostrarlos en un panel. Si usa Azure, aplicación de Azure Insights proporciona una excelente
supervisión de forma rápida, integrando el rendimiento de la base de datos y los tiempos de ejecución de las
consultas en el análisis de la rapidez con que se atienden las solicitudes Web. Puede encontrar más información
sobre esto en el tutorial de rendimiento de Application Insightsy en la Página de Azure SQL Analytics.
Inspeccionar planes de ejecución de consultas
Una vez que haya finalizado una consulta problemática que requiere optimización, el paso siguiente suele
analizar el plan de ejecución de la consulta. Cuando las bases de datos reciben una instrucción SQL,
normalmente producen un plan del modo en que se va a ejecutar el plan; en ocasiones, esto requiere la toma de
decisiones complicada en función de los índices que se han definido, la cantidad de datos que hay en las tablas,
etc. (casualmente, el propio plan normalmente se debe almacenar en caché en el servidor para obtener un
rendimiento óptimo). Las bases de datos relacionales proporcionan normalmente una manera para que los
usuarios vean el plan de consulta, junto con el costo calculado para distintas partes de la consulta. Esto es muy
útil para mejorar las consultas.
Para empezar a trabajar en SQL Server, consulte la documentación sobre los planes de ejecución de consultas. El
flujo de trabajo de análisis típico sería usar SQL Server Management Studio, pegar el SQL de una consulta lenta
identificada mediante uno de los medios anteriores y generar un plan de ejecución gráfico:

Aunque los planes de ejecución pueden parecer complicados al principio, merece la pena dedicar algo de
tiempo a familiarizarse con ellos. Es especialmente importante tener en cuenta los costos asociados a cada nodo
del plan y para identificar cómo se usan (o no) los índices en los distintos nodos.
Aunque la información anterior es específica de SQL Server, otras bases de datos normalmente proporcionan el
mismo tipo de herramientas con una visualización similar.

IMPORTANT
A veces, las bases de datos generan distintos planes de consulta en función de los datos reales de la base de datos. Por
ejemplo, si una tabla contiene solo unas pocas filas, una base de datos puede optar por no utilizar un índice en esa tabla,
pero para realizar un recorrido de tabla completo en su lugar. Si analiza los planes de consulta en una base de datos de
prueba, asegúrese siempre de que contenga datos similares a los del sistema de producción.

Contadores de eventos
Las secciones anteriores se centraban en cómo obtener información sobre los comandos y cómo se ejecutan
estos comandos en la base de datos. Además, EF expone un conjunto de contadores de eventos que
proporcionan información más detallada sobre lo que está ocurriendo en el propio EF y cómo lo usa la
aplicación. Estos contadores pueden ser muy útiles para diagnosticar problemas de rendimiento y anomalías de
rendimiento específicos, como los problemas de almacenamiento en caché de las consultas que causan una
recompilación constante, pérdidas de DbContext desechadas y otras.
Para obtener más información, consulte la página dedicada en los contadores de eventos de EF .
Pruebas comparativas con EF Core
Al final del día, a veces es necesario saber si una forma determinada de escribir o ejecutar una consulta es más
rápida que otra. Es importante no asumir ni especular la respuesta, y es muy fácil reunir una prueba
comparativa rápida para obtener la respuesta. Al escribir las pruebas comparativas, se recomienda
encarecidamente usar la biblioteca BenchmarkDotNet conocida, que controla muchos problemas que los
usuarios encuentran al intentar escribir sus propias pruebas comparativas: ¿ha realizado algunas iteraciones de
preparación? ¿Cuántas iteraciones ejecuta realmente la prueba comparativa y por qué? Echemos un vistazo a lo
que tiene un banco de pruebas con EF Core.

TIP
El proyecto de prueba comparativa completa para el origen siguiente está disponible aquí. Se recomienda copiarla y usarla
como plantilla para sus propias pruebas comparativas.

Como escenario de pruebas comparativas simples, vamos a comparar los siguientes métodos diferentes para
calcular el promedio de clasificación de todos los blogs de nuestra base de datos:
Cargar todas las entidades, sumar sus clasificaciones individuales y calcular el promedio.
Igual que antes, use solo una consulta sin seguimiento. Esto debería ser más rápido, ya que no se realiza la
resolución de identidades y las entidades no son instantáneas para los fines del seguimiento de cambios.
Evite la carga de todas las instancias de la entidad de blog, ya que solo tiene que proyectar la clasificación.
Nos evita transferir las demás columnas innecesarias del tipo de entidad de blog.
Calcule el promedio en la base de datos haciéndolo parte de la consulta. Esta es la forma más rápida, ya que
todo se calcula en la base de datos y solo el resultado se transfiere de nuevo al cliente.
Con BenchmarkDotNet, se escribe el código que se va a comparar como un método simple, al igual que una
prueba unitaria-y BenchmarkDotNet ejecuta automáticamente cada método para un número suficiente de
iteraciones, lo que mide de forma confiable cuánto tiempo tarda y cuánta memoria se asigna. Este es el método
diferente (el código de prueba comparativa completa se puede ver aquí):

Cargar entidades
Cargar entidades, sin seguimiento
Clasificación solo del proyecto
Calcular en la base de datos

[Benchmark]
public double LoadEntities()
{
var sum = 0;
var count = 0;
using var ctx = new BloggingContext();
foreach (var blog in [Link])
{
sum += [Link];
count++;
}

return (double)sum / count;


}

Los resultados se muestran a continuación, como se imprime en BenchmarkDotNet:


M ÉTO ST DDE M EDIA P RO P O RAT IO S A L LO C
DO M EDIA ERRO R V NA RC IÓ N D GEN . 0 GEN . 1 GEN . 2 AT ED

LoadE 2.860, 54,31 93,68 2.844, 4.55 0,33 210,93 70,312 - 1309,5
ntities 4 EE. EE. UU. EE. UU. 5 EE. 75 5 6 KB
UU. UU.

LoadE 1.353, 21,26 18,85 1.355, 2.10 0,14 87,890 3,9063 - 540,09
ntities 0 EE. EE. UU. EE. UU. 6 EE. 6 KB
NoTrac UU. UU.
king

Project 910,9 20,91 61,65 892,9 1,46 0,14 41,015 0,9766 - 252,08
OnlyRa EE. UU. EE. UU. EE. UU. EE. UU. 6 KB
nking

Calcula 627,1 14,58 42,54 626,4 1.00 0.00 4,8828 - - 33,27


teInDa EE. UU. EE. UU. EE. UU. EE. UU. KB
tabase

NOTE
Como los métodos crean una instancia y disposean el contexto dentro del método, estas operaciones se cuentan para la
prueba comparativa, aunque en realidad no forman parte del proceso de consulta. Esto no es importante si el objetivo es
comparar dos alternativas entre sí (dado que la creación de instancias del contexto y la eliminación son iguales) y
proporciona una medida más holística para toda la operación.

Una limitación de BenchmarkDotNet es que mide el rendimiento sencillo de un único subproceso de los
métodos proporcionados y, por tanto, no es adecuado para escenarios de pruebas comparativas.

IMPORTANT
Asegúrese siempre de que los datos de la base de datos son similares a los datos de producción al realizar pruebas
comparativas; de lo contrario, los resultados de las pruebas comparativas pueden no representar el rendimiento real en
producción.
Consultas eficaces
12/03/2021 • 30 minutes to read • Edit Online

Realizar consultas de forma eficaz es un gran asunto, que abarca temas como índices, estrategias de carga de
entidades relacionadas y muchas otras. En esta sección se detallan algunos temas comunes para agilizar las
consultas y los problemas que suelen tener los usuarios.

Usar índices correctamente


La principal decisión sobre si una consulta se ejecuta con rapidez o no es si se utilizarán correctamente los
índices cuando sea apropiado: las bases de datos se utilizan normalmente para almacenar grandes cantidades
de datos y las consultas que atraviesan tablas enteras suelen ser orígenes de graves problemas de rendimiento.
No es fácil detectar los problemas de indexación, ya que no es evidente de inmediato si una consulta
determinada va a utilizar un índice o no. Por ejemplo:

// Matches on start, so uses an index (on SQL Server)


var posts1 = [Link](p => [Link]("A")).ToList();
// Matches on end, so does not use the index
var posts2 = [Link](p => [Link]("A")).ToList();

Una buena manera de detectar problemas de indexación consiste en localizar primero una consulta lenta y, a
continuación, examinar su plan de consulta a través de la herramienta favorita de la base de datos. Consulte la
página de diagnóstico de rendimiento para obtener más información sobre cómo hacerlo. El plan de consulta
muestra si la consulta atraviesa toda la tabla o utiliza un índice.
Como norma general, no hay ningún conocimiento especial de EF para usar índices ni para diagnosticar
problemas de rendimiento relacionados con ellos. los conocimientos generales de base de datos relacionados
con los índices son tan relevantes para las aplicaciones EF como en las aplicaciones que no usan EF. A
continuación se enumeran algunas directrices generales que se deben tener en cuenta al usar índices:
Aunque los índices agilizan las consultas, también ralentizan las actualizaciones, ya que deben mantenerse
actualizadas. Evite definir índices que no sean necesarios y considere la posibilidad de usar filtros de índice
para limitar el índice a un subconjunto de las filas, lo que reduce esta sobrecarga.
Los índices compuestos pueden acelerar las consultas que filtran varias columnas, pero también pueden
acelerar las consultas que no filtran en todas las columnas del índice, en función de la ordenación. Por
ejemplo, un índice en las columnas A y B acelera las consultas que filtra a y B, así como las consultas que solo
se filtran por, pero no acelera el filtrado de consultas solo por B.
Si una consulta filtra por una expresión sobre una columna (por ejemplo price / 2 ,), no se puede usar un
índice simple. Sin embargo, puede definir una columna persistente almacenada para la expresión y crear un
índice sobre ella. Algunas bases de datos también admiten índices de expresión, que se pueden usar
directamente para acelerar las consultas que filtran cualquier expresión.
Las distintas bases de datos permiten configurar los índices de varias maneras y, en muchos casos EF Core
proveedores los exponen a través de la API fluida. Por ejemplo, el proveedor de SQL Server le permite
configurar si un índice está agrupadoo establecer su factor de relleno. Consulte la documentación del
proveedor para obtener más información.

Solo las propiedades necesarias del proyecto


EF Core hace que sea muy fácil consultar las instancias de la entidad y, a continuación, usar esas instancias en el
código. Sin embargo, la consulta de instancias de entidad puede extraer con frecuencia más datos de los
necesarios en la base de datos. Tenga en cuenta lo siguiente.

foreach (var blog in [Link])


{
[Link]("Blog: " + [Link]);
}

Aunque este código solo necesita realmente la propiedad de cada blog Url , se captura toda la entidad de blog
y las columnas innecesarias se transfieren de la base de datos:

SELECT [b].[BlogId], [b].[CreationDate], [b].[Name], [b].[Rating], [b].[Url]


FROM [Blogs] AS [b]

Esto se puede optimizar mediante el uso Select de para indicar a EF Qué columnas se deben proyectar:

foreach (var blogName in [Link](b => [Link]))


{
[Link]("Blog: " + blogName);
}

El SQL resultante extrae solo las columnas necesarias:

SELECT [b].[Url]
FROM [Blogs] AS [b]

Si necesita proyectar más de una columna, salga de un tipo anónimo de C# con las propiedades que desee.
Tenga en cuenta que esta técnica es muy útil para las consultas de solo lectura, pero las cosas son más
complicadas si necesita Actualizar los blogs capturados, ya que el seguimiento de cambios de EF solo funciona
con instancias de entidad. Es posible realizar actualizaciones sin cargar entidades enteras adjuntando una
instancia de blog modificada e indicando a EF qué propiedades han cambiado, pero es una técnica más
avanzada que puede no merecer la pena.

Limitar el tamaño del conjunto de resultados


De forma predeterminada, una consulta devuelve todas las filas que coincidan con sus filtros:

var blogsAll = [Link]


.Where(p => [Link]("A"))
.ToList();

Dado que el número de filas devueltas depende de los datos reales de la base de datos, es imposible saber
cuántos datos se cargarán desde la base de datos, cuánta memoria ocuparán los resultados y cuánta carga
adicional se generará al procesar estos resultados (por ejemplo, enviándolos a un explorador del usuario a
través de la red). En realidad, las bases de datos de prueba suelen contener poca información, por lo que todo
funciona bien durante las pruebas, pero los problemas de rendimiento aparecen repentinamente cuando la
consulta comienza a ejecutarse en datos reales y se devuelven muchas filas.
Como resultado, normalmente merece la pena pensar en limitar el número de resultados:
var blogs25 = [Link]
.Where(p => [Link]("A"))
.Take(25)
.ToList();

Como mínimo, la interfaz de usuario podría mostrar un mensaje que indica que pueden existir más filas en la
base de datos (y permitir que se recuperen de alguna otra manera). Una solución completa implementaría la
paginación, donde la interfaz de usuario solo muestra un número determinado de filas a la vez y permite a los
usuarios avanzar a la siguiente página según sea necesario. Normalmente, esto combina Take los Skip
operadores y para seleccionar un intervalo específico en el conjunto de resultados cada vez.

Evitar la explosión cartesiano al cargar entidades relacionadas


En las bases de datos relacionales, todas las entidades relacionadas se cargan mediante la introducción de
instrucciones JOIN en consultas únicas.

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 [Post] AS [p] ON [b].[BlogId] = [p].[BlogId]
ORDER BY [b].[BlogId], [p].[PostId]

Si un blog típico tiene varias entradas relacionadas, las filas de estas entradas duplicarán la información del
blog, lo que genera un problema conocido como "explosión cartesiana". A medida que se cargan más relaciones
uno a varios, la cantidad de datos duplicados puede crecer y afectar negativamente al rendimiento de la
aplicación.
EF permite evitar este efecto mediante el uso de "consultas divididas", que cargan las entidades relacionadas a
través de consultas independientes. Para obtener más información, lea la documentación sobre consultas
divididas y únicas.

NOTE
La implementación actual de consultas divididas ejecuta un viaje de ida y vuelta para cada consulta. Tenemos previsto
mejorar esto en el futuro y ejecutar todas las consultas en un solo viaje de ida y vuelta.

Cargar las entidades relacionadas concienzudamente cuando sea


posible
Se recomienda leer la página dedicada en entidades relacionadas antes de continuar con esta sección.
Cuando se trabaja con entidades relacionadas, normalmente sabemos de antemano lo que necesitamos cargar:
un ejemplo típico sería la carga de un determinado conjunto de blogs, junto con todas sus publicaciones. En
estos escenarios, siempre es mejor usar la carga diligente, de modo que EF pueda capturar todos los datos
necesarios en un viaje de ida y vuelta. La característica de inclusión filtrada , introducida en EF Core 5,0, también
le permite limitar las entidades relacionadas que le gustaría cargar, manteniendo el proceso de carga diligente y,
por lo tanto, factible en un único viaje de ida y vuelta:
using (var context = new BloggingContext())
{
var filteredBlogs = [Link]
.Include(
blog => [Link]
.Where(post => [Link] == 1)
.OrderByDescending(post => [Link])
.Take(5))
.ToList();
}

En otros escenarios, es posible que no sepa qué entidad relacionada vamos a necesitar antes de obtener su
entidad principal. Por ejemplo, al cargar un blog, es posible que necesitemos consultar algún otro origen de
datos, posiblemente un servicio WebService, para saber si estamos interesados en las publicaciones de ese blog.
En estos casos, la carga explícita o diferida se puede usar para capturar entidades relacionadas por separado y
rellenar la navegación de entradas del blog. Tenga en cuenta que, dado que estos métodos no son diligentes,
requieren viajes de ida y vuelta adicionales a la base de datos, que es el origen de la ralentización. en función de
su escenario específico, puede ser más eficaz cargar siempre todas las publicaciones, en lugar de ejecutar los
viajes de ida adicionales y obtener de forma selectiva solo las publicaciones que necesite.
Tenga cuidado con la carga diferida
La carga diferida suele parecer una manera muy útil de escribir la lógica de base de datos, ya que EF Core carga
automáticamente las entidades relacionadas de la base de datos, ya que el código tiene acceso a ellas. Esto evita
la carga de entidades relacionadas que no son necesarias (como la carga explícita) y, aparentemente, libera al
programador de tener que tratar las entidades relacionadas de forma conjunta. Sin embargo, la carga diferida es
especialmente propensa para producir viajes de ida y vuelta innecesarios, lo que puede ralentizar la aplicación.
Tenga en cuenta lo siguiente.

foreach (var blog in [Link]())


{
foreach (var post in [Link])
{
[Link]($"Blog {[Link]}, Post: {[Link]}");
}
}

Este fragmento de código aparentemente inocente recorre en iteración todos los blogs y sus publicaciones, para
imprimirlos. La activación del registro de instrucciones de EF Core revela lo siguiente:
info: [Link][20101]
Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [b].[BlogId], [b].[Rating], [b].[Url]
FROM [Blogs] AS [b]
info: [Link][20101]
Executed DbCommand (5ms) [Parameters=[@__p_0='1'], CommandType='Text', CommandTimeout='30']
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
FROM [Post] AS [p]
WHERE [p].[BlogId] = @__p_0
info: [Link][20101]
Executed DbCommand (1ms) [Parameters=[@__p_0='2'], CommandType='Text', CommandTimeout='30']
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
FROM [Post] AS [p]
WHERE [p].[BlogId] = @__p_0
info: [Link][20101]
Executed DbCommand (1ms) [Parameters=[@__p_0='3'], CommandType='Text', CommandTimeout='30']
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[Title]
FROM [Post] AS [p]
WHERE [p].[BlogId] = @__p_0

... and so on

¿Qué ocurre aquí? ¿Por qué se envían todas estas consultas para los bucles simples anteriores? Con la carga
diferida, las publicaciones de un blog solo se cargan (de forma diferida) cuando se tiene acceso a su propiedad
posts. como resultado, cada iteración de la instrucción foreach interna desencadena una consulta de base de
datos adicional, en su propio recorrido de ida y vuelta. Como resultado, después de que la consulta inicial
cargue todos los blogs, tendrá otra consulta por cada blog y cargará todas sus entradas; a veces, esto se
denomina el problema N + 1 y puede causar problemas de rendimiento muy significativos.
Suponiendo que vamos a necesitar todas las publicaciones de blogs, tiene sentido usar la carga diligente aquí en
su lugar. Se puede usar el operador include para realizar la carga, pero como solo se necesitan las direcciones
URL de los blogs (y solo se deben cargar los elementos necesarios). En su lugar, usaremos una proyección:

foreach (var blog in [Link](b => new { [Link], [Link] }).ToList())


{
foreach (var post in [Link])
{
[Link]($"Blog {[Link]}, Post: {[Link]}");
}
}

Esto hará que EF Core Capture todos los blogs, junto con sus posts, en una sola consulta. En algunos casos,
también puede resultar útil evitar efectos de explosión cartesiano mediante el uso de consultas divididas.

WARNING
Dado que la carga diferida facilita enormemente el desencadenamiento del problema N + 1, se recomienda evitarlo. La
carga diligente o explícita la hacen muy claras en el código fuente cuando se produce un viaje de base de datos.

Almacenamiento en búfer y transmisión por secuencias


El almacenamiento en búfer hace referencia a la carga de todos los resultados de la consulta en la memoria,
mientras que el streaming significa que EF trata a la aplicación como un resultado único cada vez, nunca con el
conjunto de resultados completo en la memoria. En principio, los requisitos de memoria de una consulta de
streaming son fijos, es decir, si la consulta devuelve 1 fila o 1000; por otro lado, una consulta de
almacenamiento en búfer requiere más memoria para devolver más filas. En el caso de las consultas que
generan conjuntos de resultados grandes, puede ser un factor de rendimiento importante.
El hecho de que un búfer de consulta o secuencias dependa de cómo se evalúa:

// ToList and ToArray cause the entire resultset to be buffered:


var blogsList = [Link](p => [Link]("A")).ToList();
var blogsArray = [Link](p => [Link]("A")).ToArray();

// Foreach streams, processing one row at a time:


foreach (var blog in [Link](p => [Link]("A")))
{
// ...
}

// AsEnumerable also streams, allowing you to execute LINQ operators on the client-side:
var doubleFilteredBlogs = [Link]
.Where(p => [Link]("A")) // Translated to SQL and executed in the database
.AsEnumerable()
.Where(p => SomeDotNetMethod(p)); // Executed at the client on all database results

Si las consultas devuelven solo unos cuantos resultados, probablemente no tenga que preocuparse de ello. Sin
embargo, si la consulta puede devolver un gran número de filas, merece la pena hacer streaming en lugar de
almacenar en búfer.

NOTE
Evite el uso de ToList o ToArray si piensa usar otro operador LINQ en el resultado; de esta forma, se almacenarán todos
los resultados en la memoria. En su lugar, use AsEnumerable.

Almacenamiento en búfer interno por EF


En determinadas situaciones, EF almacenará en búfer el conjunto de resultados internamente,
independientemente de cómo se evalúe la consulta. Los dos casos en los que ocurre esto son:
Cuando se implementa una estrategia de ejecución de reintento. Esto se hace para asegurarse de que se
devuelven los mismos resultados si la consulta se vuelve a intentar más tarde.
Cuando se usa la consulta Split , se almacenan en búfer los conjuntos de filas de todas las consultas, excepto
la última, a menos que se habilite MARS en SQL Server. Esto se debe a que normalmente no es posible tener
varios conjuntos de consultas de consulta activos al mismo tiempo.
Tenga en cuenta que este almacenamiento en búfer interno se produce además de cualquier almacenamiento en
búfer que se produzca mediante operadores LINQ. Por ejemplo, si se usa ToList en una consulta y se implementa
una estrategia de ejecución de reintento, el conjunto de resultados se carga en la memoria dos veces: una vez
internamente por EF y otra ToList .

Seguimiento, no seguimiento y resolución de identidad


Se recomienda leer la página dedicada en el seguimiento y sin seguimiento antes de continuar con esta sección.
EF realiza un seguimiento de las instancias de la entidad de forma predeterminada, de modo que los cambios en
ellas se detectan y se conservan cuando SaveChanges se llama a. Otro efecto de las consultas de seguimiento es
que EF detecta si ya se ha cargado una instancia de los datos y devolverá automáticamente la instancia de la que
se ha realizado un seguimiento en lugar de devolver una nueva. Esto se denomina resolución de identidad.
Desde la perspectiva del rendimiento, el seguimiento de cambios implica lo siguiente:
EF mantiene internamente un diccionario de instancias de las que se ha realizado un seguimiento. Cuando se
cargan nuevos datos, EF comprueba el diccionario para ver si ya se ha realizado un seguimiento de una
instancia para la clave de esa entidad (resolución de identidad). El mantenimiento y las búsquedas del
diccionario tardan un tiempo en cargar los resultados de la consulta.
Antes de entregar una instancia cargada a la aplicación, EF realiza instantáneas de la instancia y mantiene la
instantánea internamente. Cuando SaveChanges se llama a, la instancia de la aplicación se compara con la
instantánea para detectar los cambios que se van a conservar. La instantánea ocupa más memoria y el propio
proceso de la instantánea lleva tiempo; a veces es posible especificar un comportamiento de la instantánea
diferente, posiblemente más eficaz, a través de los comparadores de valores, o usar los proxies de
seguimiento de cambios para omitir el proceso de la instantánea (aunque esto incluye su propio conjunto de
desventajas).
En los escenarios de solo lectura donde los cambios no se guardan en la base de datos, se pueden evitar las
sobrecargas anteriores mediante el uso de consultas sin seguimiento. Sin embargo, puesto que las consultas sin
seguimiento no realizan la resolución de identidad, una fila de base de datos a la que hacen referencia varias
filas cargadas se materializará como instancias diferentes.
Para ilustrar, supongamos que estamos cargando un gran número de publicaciones de la base de datos, así
como el blog al que se hace referencia en cada publicación. Si se producen 100 publicaciones para hacer
referencia al mismo blog, una consulta de seguimiento lo detecta a través de la resolución de identidades y
todas las instancias de post hacen referencia a la misma instancia de blog desduplicada. Por el contrario, una
consulta sin seguimiento duplica el mismo blog 100 veces y el código de aplicación debe escribirse en
consecuencia.
Estos son los resultados de una prueba comparativa que compara el seguimiento con el comportamiento sin
seguimiento de una consulta que carga 10 blogs con 20 publicaciones cada una. El código fuente está
disponible aquí, no dude en usarlo como base para sus propias mediciones.

N UM
N UM P O ST P RO P A L LO
M ÉT B LO G SP ER M EDI ERRO ST DD M EDI O RC I RAT I GEN . GEN . GEN . C AT E
O DO S B LO G A R EV ANA ÓN O SD 0 1 2 D

Realiz 10 20 1.41 27,2 45,4 1.40 1.00 0.00 60,5 13,6 - 380,
ar un 4,7 0 EE. 4 EE. 5,5 469 719 11
segui EE. UU. UU. EE. KB
mien UU. UU.
to

AsNo 10 20 993, 24,0 65,4 966, 0.71 0,05 37,1 6,83 - 232,
Tracki 3 EE. 4 EE. 0 EE. 2 EE. 094 59 89
ng UU. UU. UU. UU. KB

Por último, es posible realizar actualizaciones sin la sobrecarga que supone el seguimiento de cambios, ya que
se usa una consulta sin seguimiento y, después, se asocia la instancia devuelta al contexto, lo que especifica los
cambios que se van a realizar. Esto transfiere la carga del seguimiento de cambios de EF al usuario y solo debe
intentarse si se ha demostrado que la sobrecarga del seguimiento de cambios es inaceptable a través de la
generación de perfiles o la prueba comparativa.

Uso de SQL sin formato


En algunos casos, existe un SQL más optimizado para la consulta, que EF no genera. Esto puede ocurrir cuando
la construcción de SQL es una extensión específica de la base de datos que no es compatible, o simplemente
porque EF no se traduce todavía en él. En estos casos, escribir SQL a mano puede proporcionar un aumento
considerable del rendimiento y EF admite varias maneras de hacerlo.
Use SQL sin formato directamente en la consulta, por ejemplo, a través de FromSqlRaw . EF incluso le
permite componer sobre el SQL sin formato con consultas LINQ normales, lo que le permite expresar solo
una parte de la consulta en SQL sin procesar. Esta es una buena técnica cuando el código SQL sin procesar
solo debe usarse en una sola consulta en el código base.
Definir una función definida por el usuario (UDF) y, a continuación, llamarla desde las consultas. Tenga en
cuenta que, dado que 5,0, EF permite que las UDF devuelvan conjuntos de resultados completos, que se
conocen como funciones con valores de tabla (TVF), y también permite asignar un DbSet a una función, lo
que hace que tenga el aspecto de solo otra tabla.
Defina una vista de base de datos y realice una consulta a partir de ella en las consultas. Tenga en cuenta que,
a diferencia de las funciones, las vistas no pueden aceptar parámetros.

NOTE
SQL sin procesar generalmente debe usarse como último recurso, después de asegurarse de que EF no puede generar el
SQL que desea y cuando el rendimiento es lo suficientemente importante para que la consulta determinada lo justifique.
El uso de SQL sin procesar aporta importantes desventajas de mantenimiento.

Programación asincrónica
Como norma general, para que la aplicación sea escalable, es importante usar siempre las API asincrónicas en
lugar de una sincrónica (por ejemplo, SaveChangesAsync en lugar de SaveChanges ). Las API sincrónicas
bloquean el subproceso mientras dure la e/s de la base de datos, lo que aumenta la necesidad de subprocesos y
el número de cambios de contexto de subprocesos que se deben producir.
Para obtener más información, vea la página sobre programación asincrónica.

WARNING
Evite mezclar código sincrónico y asincrónico en la misma aplicación: es muy fácil desencadenar problemas sutiles de
colapso de grupos de subprocesos.

Recursos adicionales
Vea la sección rendimiento de la página de documentación de comparación nula para ver algunas prácticas
recomendadas al comparar valores que aceptan valores NULL.
Actualización eficaz
12/03/2021 • 4 minutes to read • Edit Online

Lotes
EF Core ayuda a minimizar los viajes de ida y vuelta agrupando automáticamente todas las actualizaciones en
un solo viaje de ida y vuelta. Tenga en cuenta lo siguiente.

var blog = [Link](b => [Link] == "[Link]


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

Lo anterior carga un blog de la base de datos, cambia su dirección URL y, a continuación, agrega dos blogs
nuevos; para aplicar esto, se envían a la base de datos dos instrucciones INSERT de SQL y una instrucción
UPDATE. En lugar de enviarlos uno a uno, a medida que se agregan instancias de blog, EF Core realiza un
seguimiento de estos cambios internamente y los ejecuta en un solo ida y vuelta cuando SaveChanges se llama
a.
El número de instrucciones que EF procesa por lotes en un único viaje de ida y vuelta depende del proveedor de
base de datos utilizado. Por ejemplo, el análisis de rendimiento ha mostrado que el procesamiento por lotes sea
menos eficaz para SQL Server cuando hay menos de 4 instrucciones implicadas. Del mismo modo, las ventajas
del procesamiento por lotes se degradan después de alrededor de 40 instrucciones para SQL Server, por lo que
EF Core solo ejecutará de forma predeterminada hasta 42 instrucciones en un único lote y ejecutará
instrucciones adicionales en viajes de ida y vuelta independientes.
Los usuarios también pueden retocar estos umbrales para lograr un rendimiento potencialmente superior, pero
con cuidado antes de modificarlos:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
[Link](
@"Server=(localdb)\mssqllocaldb;Database=Blogging;Integrated Security=True",
o => o
.MinBatchSize(1)
.MaxBatchSize(100));
}

Actualizaciones masivas
Supongamos que desea dar una elevación a todos los empleados. Una implementación típica para esto en EF
Core sería similar a la siguiente:

foreach (var employee in [Link])


{
[Link] += 1000;
}

[Link]();

Aunque se trata de código perfectamente válido, vamos a analizar lo que hace desde la perspectiva del
rendimiento:
Se realiza un ida y vuelta de base de datos para cargar todos los empleados pertinentes. Tenga en cuenta que
esto pone todos los datos de fila de los empleados en el cliente, incluso si solo se necesita el salario.
El seguimiento de cambios de EF Core crea instantáneas al cargar las entidades y, a continuación, compara
esas instantáneas con las instancias para averiguar qué propiedades han cambiado.
Se realiza un segundo viaje de base de datos para guardar todos los cambios. Aunque todos los cambios se
realizan en un único viaje de ida y vuelta, el procesamiento por lotes EF Core sigue enviando una instrucción
UPDATE por empleado, que debe ser ejecutada por la base de datos.
Las bases de datos relacionales también admiten actualizaciones masivas, por lo que lo anterior podría volver a
escribirse como la siguiente instrucción SQL:

UPDATE [Employees] SET [Salary] = [Salary] + 1000;

Esto realiza la operación completa en un único ciclo de ida y vuelta, sin cargar ni enviar ningún dato real a la
base de datos, y sin hacer uso de la maquinaria de seguimiento de cambios de EF, que impone una sobrecarga
adicional.
Desafortunadamente, EF no proporciona actualmente las API para realizar actualizaciones masivas. Hasta que se
introduzcan, puede usar SQL sin procesar para realizar la operación en la que el rendimiento es sensible:

[Link]("UPDATE [Employees] SET [Salary] = [Salary] + 1000");


Modelado de rendimiento
12/03/2021 • 12 minutes to read • Edit Online

En muchos casos, la manera de modelar puede tener un impacto profundo en el rendimiento de la aplicación.
Aunque un modelo "correcto" y normalizado correctamente es normalmente un buen punto de partida, en las
aplicaciones reales, algunos peligros pragmáticos pueden llevar mucho tiempo para lograr un buen
rendimiento. Dado que es bastante difícil cambiar el modelo una vez que una aplicación se ejecuta en
producción, merece la pena tener en cuenta el rendimiento al crear el modelo inicial.

Desnormalización y almacenamiento en caché


La desnormalización es la práctica de agregar datos redundantes al esquema, normalmente con el fin de
eliminar combinaciones al realizar consultas. Por ejemplo, para un modelo con blogs y publicaciones, donde
cada publicación tiene una clasificación, es posible que se le pida que muestre con frecuencia la clasificación
promedio del blog. El enfoque sencillo para esto agruparía las entradas por su blog y calcularía el promedio
como parte de la consulta. pero esto requiere una combinación costosa entre las dos tablas. La
desnormalización agregaría el promedio calculado de todos los envíos a una nueva columna en el blog, de
modo que se pueda acceder a él de inmediato, sin unirse ni calcular.
Lo anterior se puede ver como una forma de información de agregación de almacenamiento en caché de las
publicaciones se almacena en caché en su blog. y, al igual que con cualquier almacenamiento en caché, el
problema es cómo mantener actualizado el valor almacenado en caché con los datos que almacena en memoria
caché. En muchos casos, es correcto que los datos almacenados en caché se retrasen un poco. por ejemplo, en el
ejemplo anterior, suele ser razonable que la puntuación media del blog no esté totalmente actualizada en un
momento dado. Si ese es el caso, puede hacer que se vuelva a calcular cada ahora y, después, a; de lo contrario,
se debe configurar un sistema más elaborado para mantener actualizados los valores almacenados en caché.
A continuación se detallan algunas técnicas para la desnormalización y el almacenamiento en caché en EF Core
y se apunta a las secciones pertinentes de la documentación.
Columnas calculadas almacenadas
Si los datos que se van a almacenar en caché son un producto de otras columnas de la misma tabla, una
columna calculada almacenada puede ser una solución perfecta. Por ejemplo, un Customer puede tener
FirstName LastName columnas y, pero es posible que sea necesario buscar por el nombre completo del cliente.
La base de datos mantiene automáticamente una columna calculada almacenada, que la vuelve a calcular cada
vez que se cambia la fila, e incluso puede definir un índice para acelerar las consultas.
Actualizar columnas de caché cuando cambien las entradas
Si la columna almacenada en caché necesita hacer referencia a entradas desde fuera de la fila de la tabla, no se
pueden usar columnas calculadas. Sin embargo, sigue siendo posible volver a calcular la columna cada vez que
cambie la entrada; por ejemplo, podría recalcular el promedio de clasificación del blog cada vez que se cambia,
agrega o quita una publicación. Asegúrese de identificar las condiciones exactas en las que es necesario volver a
calcular; de lo contrario, el valor almacenado en caché no se sincronizará.
Una forma de hacerlo consiste en realizar la actualización por su cuenta, a través de la API de EF Core normal.
SaveChanges Los eventos o los interceptores se pueden usar para comprobar automáticamente si se está
actualizando cualquier publicación y para realizar el recálculo de este modo. Tenga en cuenta que esto
normalmente conlleva viajes de ida de base de datos adicionales, ya que se deben enviar comandos adicionales.
En el caso de las aplicaciones más sensibles al rendimiento, se pueden definir desencadenadores de base de
datos para realizar automáticamente el recálculo en la base de datos. Esto guarda el interactivo de la base de
datos adicional, se produce automáticamente dentro de la misma transacción que la actualización principal y
puede ser más fácil de configurar. EF no proporciona ninguna API específica para crear o mantener
desencadenadores, pero es perfecta para crear una migración vacía y agregar la definición del desencadenador
a través de SQL sin procesar.
Vistas materializadas
Las vistas materializadas son similares a las vistas normales, excepto en que sus datos se almacenan en el disco
("materializado"), en lugar de calcularse cada vez que se consulta la vista. Esta herramienta es útil cuando no se
desea simplemente agregar una única columna de caché a una base de datos existente, sino que se desea
almacenar en caché todo el conjunto de resultados de los resultados de una consulta complicada y costosa,
como si fuera una tabla normal. Estos resultados se pueden consultar de forma muy barata sin que se produzca
ningún cálculo o combinación. A diferencia de las columnas calculadas, las vistas materializadas no se actualizan
automáticamente cuando se modifican sus tablas subyacentes, sino que se deben actualizar manualmente. Si los
datos almacenados en caché se pueden retrasar, la actualización de la vista se puede realizar a través de un
temporizador. otra opción consiste en configurar desencadenadores de base de datos para revisar una vista
materializada cuando se produzcan determinados eventos de base de datos.
EF no proporciona actualmente ninguna API específica para crear o mantener vistas, materializadas o de otro
tipo. pero es perfectamente adecuado crear una migración vacía y agregar la definición de la vista a través de
SQL sin procesar.

Asignación de herencia
Se recomienda leer la página dedicada en la herencia antes de continuar con esta sección.
EF Core admite actualmente dos técnicas para asignar un modelo de herencia a una base de datos relacional:
Tabla por jerarquía (TPH), en la que toda una jerarquía de clases de .net está asignada a una sola tabla de
base de datos
Tabla por tipo (TPT), en la que cada tipo de la jerarquía de .net se asigna a una tabla diferente en la base de
datos.
La elección de la técnica de asignación de herencia puede tener un impacto considerable en el rendimiento de la
aplicación; se recomienda medir cuidadosamente antes de confirmar una opción.
A veces, los usuarios eligen TPT porque parece ser la técnica de "limpiador"; una tabla independiente para cada
tipo .NET hace que el esquema de la base de datos sea similar a la jerarquía de tipos .NET. Además, dado que
TPH debe representar toda la jerarquía en una sola tabla, las filas tienen todas las columnas
independientemente del tipo que realmente se mantiene en la fila, y las columnas no relacionadas siempre
están vacías y no se utilizan. Además de parecerse a una técnica de asignación "sin limpieza", muchos
consideran que estas columnas vacías ocupan un espacio considerable en la base de datos y pueden afectar
también al rendimiento.
Sin embargo, la medición muestra que TPT en la mayoría de los casos es la técnica de asignación inferior desde
un punto de vista del rendimiento. Cuando todos los datos de TPH proceden de una sola tabla, las consultas de
TPT deben combinarse con varias tablas y las combinaciones son una de las principales fuentes de problemas
de rendimiento de las bases de datos relacionales. Las bases de datos también suelen tratar bien con columnas
vacías, y las características como SQL Server columnas dispersas pueden reducir aún más esta sobrecarga.
Para ver un ejemplo concreto, vea este criterio de referencia que configura un modelo simple con una jerarquía
de 7 tipos; 5000 las filas se inicializan para cada tipo: total de 35000 filas, y la prueba comparativa simplemente
carga todas las filas de la base de datos:
A L LO C AT E
M ÉTO DO M EDIA ERRO R ST DDEV GEN . 0 GEN . 1 GEN . 2 D

TPH 132,3 MS 2,29 MS 2,03 MS 8000,0000 3000,0000 1250,0000 44,49 MB

TPT 201,3 MS 3,32 ms 3,10 ms 9000,0000 4000,0000 - 61,84 MB

Como se puede ver, TPH es considerablemente más eficiente que TPT para este escenario. Tenga en cuenta que
los resultados reales siempre dependen de la consulta específica que se ejecuta y el número de tablas de la
jerarquía, por lo que otras consultas pueden mostrar un intervalo de rendimiento diferente. le recomendamos
que use este código de referencia comparativa como plantilla para probar otras consultas.
Temas de rendimiento avanzados
12/03/2021 • 11 minutes to read • Edit Online

Agrupación de DbContext
AddDbContextPool habilita la agrupación de DbContext instancias de. La agrupación de contexto puede
aumentar el rendimiento en escenarios de gran escala, como servidores Web, mediante la reutilización de
instancias de contexto, en lugar de crear nuevas instancias para cada solicitud.
El patrón típico en una aplicación [Link] Core con EF Core implica registrar un tipo personalizado en
DbContext el contenedor de inserción de dependencias y obtener instancias de ese tipo a través de parámetros
de constructor en controladores o Razor pages. Mediante la inserción de constructores, se crea una nueva
instancia de contexto para cada solicitud.
AddDbContextPool habilita un grupo de instancias de contexto reutilizables. Para usar la agrupación de contexto,
use el AddDbContextPool método en lugar de AddDbContext durante el registro del servicio:

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

Cuando AddDbContextPool se usa, en el momento en que se solicita una instancia de contexto, EF comprueba
primero si hay una instancia 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 parte del costo de inicialización de la instancia de contexto.
El poolSize parámetro de AddDbContextPool establece el número máximo de instancias retenidas por el grupo.
Una vez que poolSize se supera, las nuevas instancias de contexto no se almacenan en caché y EF recurre al
comportamiento de no agrupación de la creación de instancias a petición.
Limitaciones
Se debe crear un perfiles de las aplicaciones y probarlas para mostrar que la inicialización del contexto supone
un costo significativo.
AddDbContextPool tiene algunas limitaciones en lo que se puede hacer en el OnConfiguring método del
contexto.

WARNING
Evite el uso de la agrupación de contexto en aplicaciones que mantienen el estado. Por ejemplo, los campos privados en el
contexto que no se deberían compartir entre las solicitudes. EF Core solo restablece el estado que se conoce antes de
agregar una instancia de contexto al grupo.

La agrupación de contexto funciona mediante la reutilización de la misma instancia de contexto en todas las
solicitudes. Esto significa que se registra de forma eficaz como Singleton en lo que se refiere a la propia
instancia para que pueda persistir.
La agrupación de contexto está pensada para escenarios en los que la configuración de contexto, que incluye los
servicios resueltos, se fija entre solicitudes. En los casos en los que se requieren los servicios de ámbito o es
necesario cambiar la configuración, no utilice la agrupación. La ganancia de rendimiento de la agrupación suele
ser despreciable, excepto en escenarios muy optimizados.

Almacenamiento en caché y parametrización de consultas


Cuando EF recibe un árbol de consulta LINQ para su ejecución, primero debe "compilar" ese árbol en una
consulta SQL. Dado que se trata de un proceso intensivo, EF almacena en caché las consultas por la forma de
árbol de consulta: las consultas con la misma estructura reutilizan las salidas de compilación almacenadas en
caché internamente y pueden omitir la compilación repetida. Las distintas consultas pueden seguir haciendo
referencia a valores diferentes, pero siempre y cuando estos valores estén correctamente parametrizados, la
estructura será la misma y el almacenamiento en caché funcionará correctamente.
Tenga en cuenta las dos consultas siguientes:

var post1 = [Link](p => [Link] == "post1");


var post2 = [Link](p => [Link] == "post2");

Dado que los árboles de expresión contienen constantes diferentes, el árbol de expresión difiere y cada una de
estas consultas se compilará por separado mediante EF Core. Además, cada consulta genera un comando SQL
ligeramente diferente:

SELECT TOP(1) [b].[Id], [b].[Name]


FROM [Blogs] AS [b]
WHERE [b].[Name] = N'blog1'

SELECT TOP(1) [b].[Id], [b].[Name]


FROM [Blogs] AS [b]
WHERE [b].[Name] = N'blog2'

Dado que SQL es diferente, el servidor de base de datos probablemente también tendrá que generar un plan de
consulta para ambas consultas, en lugar de reutilizar el mismo plan.
Una pequeña modificación en las consultas puede cambiar considerablemente:

var postTitle = "post1";


var post1 = [Link](p => [Link] == postTitle);
postTitle = "post2";
var post2 = [Link](p => [Link] == postTitle);

Dado que el nombre del blog ahora tiene parámetros, ambas consultas tienen la misma forma de árbol y EF
solo debe compilarse una vez. El SQL generado también tiene parámetros, lo que permite que la base de datos
vuelva a usar el mismo plan de consulta:

SELECT TOP(1) [b].[Id], [b].[Name]


FROM [Blogs] AS [b]
WHERE [b].[Name] = @__blogName_0

Tenga en cuenta que no hay necesidad de parametrizar cada una de las consultas: es absolutamente preciso
tener algunas consultas con constantes y, en realidad, las bases de datos (y EF) pueden realizar algunas tareas de
optimización en torno a constantes que no son posibles cuando la consulta está parametrizada. Vea la sección
sobre consultas construidas dinámicamente para obtener un ejemplo en el que la parametrización adecuada es
fundamental.
NOTE
Los contadores de eventos de EF Core notifican la tasa de aciertos de caché de consultas. En una aplicación normal, este
contador alcanza el 100% poco después del inicio del programa, una vez que la mayoría de las consultas se han ejecutado
al menos una vez. Si este contador permanece estable por debajo del 100%, es una indicación de que la aplicación puede
estar haciendo algo que derrota a la caché de consultas. es una buena idea investigarlo.

NOTE
La forma en que la base de datos administra los planes de consulta depende de la base de datos. Por ejemplo, SQL Server
mantiene implícitamente una memoria caché del plan de consulta LRU, mientras que PostgreSQL no (pero las
instrucciones preparadas pueden producir un efecto final muy similar). Consulte la documentación de la base de datos
para obtener más detalles.

Consultas construidas dinámicamente


En algunas situaciones, es necesario crear dinámicamente las consultas LINQ en lugar de especificarlas de forma
inadecuada en el código fuente. Esto puede ocurrir, por ejemplo, en un sitio web que recibe detalles de consulta
arbitrarios de un cliente, con operadores de consulta abiertos (ordenación, filtrado, paginación...). En principio, si
se realiza correctamente, las consultas construidas dinámicamente pueden ser tan eficaces como las normales
(aunque no es posible usar la optimización de consultas compiladas con consultas dinámicas). Sin embargo, en
la práctica, suelen ser el origen de los problemas de rendimiento, ya que es fácil producir accidentalmente
árboles de expresión con formas que difieren cada vez.
En el ejemplo siguiente se usan dos técnicas para crear una consulta de forma dinámica: agregamos un Where
operador a la consulta solo si el parámetro especificado no es NULL. Tenga en cuenta que no se trata de un buen
caso de uso para la creación dinámica de una consulta, pero se usa para simplificar:

Con constante
With (parámetro)
[Benchmark]
public int WithConstant()
{
return GetBlogCount("blog" + [Link](ref _blogNumber));

static int GetBlogCount(string url)


{
using var context = new BloggingContext();

IQueryable<Blog> blogs = [Link];

if (url is not null)


{
var blogParam = [Link](typeof(Blog), "b");
var whereLambda = [Link]<Func<Blog, bool>>(
[Link](
[Link](
blogParam,
typeof(Blog).GetMember(nameof([Link])).Single()
),
[Link](url)),
blogParam);

blogs = [Link](whereLambda);
}

return [Link]();
}
}

Las pruebas comparativas de estas dos técnicas proporcionan los siguientes resultados:

A L LO C AT E
M ÉTO DO M EDIA ERRO R ST DDEV GEN . 0 GEN . 1 GEN . 2 D

WithConsta 1.096,7 EE. 12,54 EE. 11,12 EE. 13,6719 1,9531 - 83,91 KB
nt UU. UU. UU.

WithParam 570,8 EE. 42,43 EE. 124,43 EE. 5,8594 - - 37,16 KB


eter UU. UU. UU.

Aunque la diferencia de submilisegundos parezca pequeña, tenga en cuenta que la versión constante contamina
continuamente la memoria caché y hace que se vuelvan a compilar otras consultas, lo que ralentiza también su
funcionamiento.

NOTE
Evite construir consultas con la API de árbol de expresión a menos que realmente necesite. Aparte de la complejidad de la
API, es muy fácil producir involuntariamente problemas de rendimiento significativos al utilizarlos.
Implementaciones de .NET compatibles con EF
Core
12/03/2021 • 4 minutes to read

Queremos que EF Core esté disponible para los desarrolladores en todas las implementaciones de .NET
modernas y seguimos trabajando para alcanzar ese objetivo. Aunque la compatibilidad de EF Core en .NET Core
está cubierta por pruebas automatizadas y se sabe que muchas aplicaciones van a usarlo correctamente, Mono,
Xamarin y UWP presentan algunos problemas.

Información general
En la siguiente tabla se ofrecen instrucciones para cada implementación de .NET:

EF C O RE 2. 1 Y 3. 1 5. 0

.NET Standard 2.0 2.1

.NET Core 2.0 3.0

.NET Framework(1) 4.7.2 (no se admite)

Mono 5.4 6.4

[Link](2) 10.14 12.16

[Link](2) 3.8 5.16

[Link](2) 8.0 10.0

UWP(3) 10.0.16299 TBD

Unity(4) 2018.1 TBD

(1) Consulte la sección .NET Framework a continuación.


(2)Existen problemas y limitaciones conocidas con Xamarin que pueden impedir que algunas aplicaciones
desarrolladas con EF Core funcionen correctamente. Compruebe la lista de problemas activos para ver
soluciones alternativas.
(3)
Se recomienda EF Core 2.0.1 y versiones más recientes. Instale el paquete .NET Core UWP 6.x. Consulte la
sección Plataforma universal de Windows de este artículo.
(4) Hay problemas y limitaciones conocidas con Unity. Revise la lista de problemas activos.

.NET Framework
Es posible que las aplicaciones que tengan como destino .NET Framework deban modificarse para poder
trabajar con bibliotecas de .NET Standard:
Edite el archivo de proyecto y asegúrese de que la siguiente entrada aparece en el grupo de propiedades inicial:
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>

En los proyectos de prueba, asegúrese también de que la entrada siguiente está presente:

<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>

Si quiere usar una versión anterior de Visual Studio, asegúrese de que actualiza el cliente de NuGet a la versión
3.6.0 para poder trabajar con bibliotecas de .NET Standard 2.0.
Si es posible, también se recomienda migrar de [Link] de NuGet a PackageReference. Agregue la
propiedad siguiente al archivo del proyecto:

<RestoreProjectStyle>PackageReference</RestoreProjectStyle>

Plataforma universal de Windows


Las versiones anteriores de EF Core y .NET UWP tuvieron numerosos problemas de compatibilidad,
especialmente con aplicaciones compiladas con la cadena de herramientas de .NET Native. La nueva versión de
.NET UWP agrega compatibilidad con .NET Standard 2.0 y contiene .NET Native 2.0, que soluciona la mayoría de
los problemas de compatibilidad que se notificaban anteriormente. EF Core 2.0.1 se ha probado más
exhaustivamente con UWP pero la prueba no está automatizada.
Al usar EF Core en UWP:
Para optimizar el rendimiento de las consultas, evite tipos anónimos en las consultas LINQ. Para
implementar una aplicación de UWP en la tienda de aplicaciones, la aplicación debe estar compilada con
.NET Native. Las consultas con tipos anónimos tienen un menor rendimiento en .NET Native.
Para optimizar el rendimiento de SaveChanges() , use
[Link] e implemente INotifyPropertyChanged,
INotifyPropertyChanging y INotifyCollectionChanged en los tipos de entidad.

Problemas de informes
En el caso de cualquier combinación que no funcione según lo esperado, se recomienda crear nuevos
problemas en el seguimiento de problemas de EF Core. En el caso de problemas relacionados con Xamarin, use
el seguimiento de problemas de [Link] o de [Link].
Programación asincrónica
12/03/2021 • 4 minutes to read

Las operaciones asincrónicas evitan el bloqueo de un subproceso mientras la consulta se ejecuta en la base de
datos. Las operaciones asincrónicas son importantes para mantener una interfaz de usuario receptiva en las
aplicaciones cliente enriquecidas y también pueden aumentar el rendimiento en las aplicaciones web donde
liberan el subproceso para atender otras solicitudes en aplicaciones Web.
A continuación de .NET Standard, EF Core proporciona homólogos asincrónicos a todos los métodos sincrónicos
que realizan operaciones de e/s. Tienen los mismos efectos que los métodos de sincronización y se pueden usar
con las async await palabras clave y C#. Por ejemplo, en lugar de usar DbContext. SaveChanges, que
bloqueará un subproceso mientras se realiza la e/s de la base de datos, se puede usar DbContext.
SaveChangesAsync:

var blog = new Blog { Url = "[Link] };


[Link](blog);
await [Link]();

Para obtener más información, vea los documentos generales de programación asincrónica de 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.

WARNING
La implementación asincrónica de Microsoft. Data. SqlClient lamentablemente tiene algunos problemas conocidos (por
ejemplo, #593, #601y otros).

NOTE
EF Core pasa los tokens de cancelación al proveedor de base de datos subyacente en uso (por ejemplo, Microsoft. Data.
SqlClient). Estos tokens pueden o no ser respetados; consulte la documentación del proveedor de la base de datos.

Operadores LINQ asincrónicos


Para permitir la ejecución de consultas LINQ de forma asincrónica, EF Core proporciona un conjunto de
métodos de extensión Async que ejecutan la consulta y devuelven resultados. Estos homólogos a los operadores
de LINQ estándar y sincrónicos son ToListAsync, SingleAsync, AsAsyncEnumerable, etc.:

var blogs = await [Link](b => [Link] > 3).ToListAsync();

Tenga en cuenta que no hay versiones asincrónicas de algunos operadores LINQ, como Where o OrderBy,
porque solo compilan el árbol de expresión LINQ y no hacen que la consulta se ejecute en la base de datos. Solo
los operadores que causan la ejecución de la consulta tienen homólogos asincrónicos.
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.

Operadores LINQ asincrónicos del lado cliente


Los operadores LINQ Async descritos anteriormente solo se pueden usar en consultas EF: no se pueden usar
con consultas LINQ to Objects del lado cliente. Para realizar operaciones LINQ asincrónicas en el lado cliente
fuera de EF, use el paquete System. Linq. Async; Este paquete puede ser especialmente útil para realizar
operaciones en el cliente que no se pueden traducir para su evaluación en el servidor.
Desafortunadamente, la referencia a System. Interactive. Async produce errores de compilación de invocación
ambigua en los operadores de LINQ aplicados al DbSets de EF; Esto hace que sea difícil usar tanto EF como
System. Interactive. Async en el mismo proyecto. Para solucionar este problema, agregue la función de consulta
a su DbSet:

var groupedHighlyRatedBlogs = await [Link]


.AsQueryable()
.Where(b => [Link] > 3) // server-evaluated
.AsAsyncEnumerable()
.GroupBy(b => [Link]) // client-evaluated
.ToListAsync();
Trabajar con tipos de referencia que aceptan valores
NULL
12/03/2021 • 10 minutes to read

C# 8 presentó una nueva característica denominada tipos de referencia que aceptan valores NULL (NRT), 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 documentos de C#.
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.

Propiedades y inicialización que no aceptan valores NULL


Cuando se habilitan los tipos de referencia que aceptan valores NULL, el compilador de C# emite advertencias
para cualquier propiedad no inicializada que no acepte valores NULL, ya que estos contendrían el valor null.
Como resultado, no se puede usar la siguiente manera común de escribir tipos de entidad:

public class CustomerWithWarning


{
public int Id { get; set; }

// Generates CS8618, uninitialized non-nullable property:


public string Name { get; set; }
}

El enlace de constructor es una técnica útil para asegurarse de que se inicializan las propiedades que no aceptan
valores NULL:
public class CustomerWithConstructorBinding
{
public int Id { get; set; }
public string Name { get; set; }

public CustomerWithConstructorBinding(string name)


{
Name = name;
}
}

Desafortunadamente, en algunos escenarios, el enlace del constructor no es una opción; por ejemplo, las
propiedades de navegación 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.

DbContext y DbSet
La práctica común de tener propiedades DbSet sin inicializar en tipos de contexto también es problemática, ya
que el compilador ahora emitirá advertencias para ellos. Esto puede corregirse de la siguiente manera:

public class NullableReferenceTypesContext : DbContext


{
public DbSet<Customer> Customers => Set<Customer>();
public DbSet<Order> Orders => Set<Order>();

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


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

Otra estrategia consiste en usar propiedades automáticas que no aceptan valores NULL, pero para inicializarlas
en null, mediante el operador null-permisivo (!) para silenciar la advertencia del compilador. El constructor base
DbContext garantiza que se inicialicen todas las propiedades DbSet y que nunca se observará null en ellas.

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 actualmente los tipos de referencia que aceptan valores NULL de C# 8
(NRTs): EF Core siempre genera código de c# que supone que la característica está desactivada. Por ejemplo,
las columnas de texto que aceptan valores NULL se scaffolding como una propiedad con string el tipo, 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 nulabilidad de C#.
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 6,0.
Intercalaciones y distinción entre mayúsculas y
minúsculas
12/03/2021 • 12 minutes to read

NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

El procesamiento de texto en las bases de datos puede ser un proceso complejo y requiere más atención al
usuario de que se sospeche. Por un lado, las bases de datos varían considerablemente en el modo en que
controlan el texto; por ejemplo, mientras que algunas bases de datos distinguen mayúsculas de minúsculas de
forma predeterminada (por ejemplo, SQLite, PostgreSQL), otras no distinguen mayúsculas de minúsculas (SQL
Server, MySQL). Además, debido al uso de índices, la distinción de mayúsculas y minúsculas y los aspectos
similares pueden tener un impacto de gran alcance en el rendimiento de las consultas: aunque puede ser
tentador utilizar [Link] para forzar una comparación sin distinción entre mayúsculas y minúsculas en
una base de datos que distingue mayúsculas de minúsculas, si lo hace, puede evitar que la aplicación use
índices. En esta página se detalla cómo configurar la distinción de mayúsculas y minúsculas, o más en general,
las intercalaciones y cómo hacerlo de manera eficaz sin poner en peligro el rendimiento de las consultas.

Introducción a las intercalaciones


Un concepto fundamental en el procesamiento de texto es la Intercalación, que es un conjunto de reglas que
determinan cómo se ordenan y comparan la igualdad de los valores de texto. Por ejemplo, mientras que una
intercalación que no distingue entre mayúsculas y minúsculas no tiene en cuenta las diferencias entre las letras
mayúsculas y minúsculas para los fines de la comparación de igualdad, una intercalación que distingue entre
mayúsculas y minúsculas no lo hace. Sin embargo, dado que la distinción de mayúsculas y minúsculas es
dependiente de la referencia cultural (por ejemplo, i y I representa una letra diferente en Turco), existen
varias intercalaciones que distinguen entre mayúsculas y minúsculas, cada una con su propio conjunto de
reglas. El ámbito de las intercalaciones también se extiende más allá de la distinción entre mayúsculas y
minúsculas y otros aspectos de los datos de caracteres. en alemán, por ejemplo, a veces es conveniente tratar
(pero no siempre) ä ae como idéntico. Por último, las intercalaciones también definen cómo se ordenan los
valores de texto: mientras que el alemán coloca ä después a de, sueco lo coloca al final del alfabeto.
Todas las operaciones de texto en una base de datos utilizan una intercalación, ya sea explícita o implícitamente,
para determinar cómo la operación compara y ordena las cadenas. La lista real de intercalaciones disponibles y
sus esquemas de nomenclatura son específicos de la base de datos; consulte la sección siguiente para obtener
vínculos a las páginas de documentación relevantes de varias bases de datos. Afortunadamente, la base de
datos normalmente permite definir una intercalación predeterminada en el nivel de base de datos o de columna,
y especificar explícitamente qué intercalación debe usarse para las operaciones específicas en una consulta.

Intercalación de base de datos


En la mayoría de los sistemas de base de datos, se define una intercalación predeterminada en el nivel de base
de datos; a menos que se invalide, esa intercalación se aplica implícitamente a todas las operaciones de texto
que se producen dentro de esa base de datos. La intercalación de base de datos se establece normalmente en el
momento de creación de la base de datos (mediante la CREATE DATABASE instrucción DDL) y, si no se especifica,
el valor predeterminado es un valor de nivel de servidor determinado en el momento de la instalación. Por
ejemplo, la intercalación de nivel de servidor predeterminada en SQL Server es SQL_Latin1_General_CP1_CI_AS ,
que es una intercalación que distingue entre mayúsculas y minúsculas y con distinción de acentos. Aunque los
sistemas de base de datos normalmente permiten modificar la intercalación de una base de datos existente,
hacerlo puede provocar complicaciones; se recomienda elegir una intercalación antes de la creación de la base
de datos.
Al usar migraciones de EF Core para administrar el esquema de la base de datos, lo siguiente en el método del
modelo OnModelCreating configura una base de datos SQL Server para que use una intercalación que distinga
entre mayúsculas y minúsculas:

[Link]("SQL_Latin1_General_CP1_CS_AS");

Intercalación de columnas
Las intercalaciones también se pueden definir en columnas de texto, invalidando el valor predeterminado de la
base de datos. Esto puede ser útil si algunas columnas deben distinguir entre mayúsculas y minúsculas,
mientras que el resto de la base de datos debe distinguir entre mayúsculas y minúsculas.
Al usar migraciones de EF Core para administrar el esquema de la base de datos, lo siguiente configura la
columna para Name que la propiedad no distinga entre mayúsculas y minúsculas en una base de datos que, de
otro modo, está configurada para distinguir entre mayúsculas y minúsculas:

[Link]<Customer>().Property(c => [Link])


.UseCollation("SQL_Latin1_General_CP1_CI_AS");

Intercalación explícita en una consulta


En algunos casos, la misma columna debe consultarse utilizando distintas intercalaciones en consultas
diferentes. Por ejemplo, una consulta puede necesitar realizar una comparación con distinción entre mayúsculas
y minúsculas en una columna, mientras que otra puede necesitar realizar una comparación sin distinción entre
mayúsculas y minúsculas en la misma columna. Esto puede realizarse especificando explícitamente una
intercalación dentro de la propia consulta:

var customers = [Link]


.Where(c => [Link]([Link], "SQL_Latin1_General_CP1_CS_AS") == "John")
.ToList();

Esto genera una COLLATE cláusula en la consulta SQL, que aplica una intercalación que distingue entre
mayúsculas y minúsculas, independientemente de la intercalación definida en el nivel de columna o de base de
datos:

SELECT [c].[Id], [c].[Name]


FROM [Customers] AS [c]
WHERE [c].[Name] COLLATE SQL_Latin1_General_CP1_CS_AS = N'John'

Intercalaciones e índices explícitos


Los índices son uno de los factores más importantes en el rendimiento de la base de datos: una consulta que se
ejecuta eficazmente con un índice puede triturarse en una detención sin ese índice. Los índices heredan
implícitamente la intercalación de su columna; Esto significa que todas las consultas de la columna son válidas
automáticamente para usar índices definidos en esa columna, siempre que la consulta no especifique una
intercalación diferente. Especificar una intercalación explícita en una consulta normalmente impedirá que la
consulta use un índice definido en esa columna, ya que las intercalaciones dejarán de coincidir; por lo tanto, se
recomienda tener cuidado al usar esta característica. Siempre es preferible definir la intercalación en el nivel de
columna (o base de datos), lo que permite que todas las consultas utilicen implícitamente esa intercalación y se
beneficien de cualquier índice.
Tenga en cuenta que algunas bases de datos permiten definir la intercalación al crear un índice (por ejemplo,
PostgreSQL, SQLite). Esto permite definir varios índices en la misma columna, con lo que se aceleran las
operaciones con diferentes intercalaciones (por ejemplo, comparaciones que distinguen mayúsculas de
minúsculas y no distinguen mayúsculas de minúsculas). Consulte la documentación del proveedor de bases de
datos para obtener más detalles.

WARNING
Inspeccione siempre los planes de consulta de las consultas y asegúrese de que se usan los índices adecuados en las
consultas críticas para el rendimiento que se ejecutan en grandes cantidades de datos. Reemplazar la distinción de
mayúsculas y minúsculas en una consulta mediante [Link] (o llamando a [Link] ) puede
tener un impacto muy significativo en el rendimiento de la aplicación.

Traducción de operaciones de cadena integradas de .NET


En .NET, la igualdad de cadena distingue entre mayúsculas y minúsculas de forma predeterminada: s1 == s2
realiza una comparación ordinal que requiere que las cadenas sean idénticas. Dado que la intercalación
predeterminada de las bases de datos varía y porque es deseable para la igualdad simple usar índices, EF Core
no intenta traducir la igualdad simple a una operación que distingue entre mayúsculas y minúsculas: la igualdad
de C# se traduce directamente a la igualdad de SQL, que puede o no distinguir mayúsculas de minúsculas,
según la base de datos específica en uso y su configuración de
Además, .NET proporciona sobrecargas [Link] para aceptar una StringComparison enumeración, que
permite especificar la distinción de mayúsculas y minúsculas y la referencia cultural de la comparación. Por
diseño, EF Core se abstendrán de traducir estas sobrecargas a SQL y se producirá una excepción al intentar
usarlas. En un caso, EF Core no sabe qué intercalación que distingue entre mayúsculas y minúsculas o que no
distingue mayúsculas de minúsculas. Lo que es más importante, al aplicar una intercalación en la mayoría de los
casos, se evita el uso del índice, lo que afecta significativamente al rendimiento de una construcción .NET muy
básica y de uso frecuente. Para forzar que una consulta use la comparación con distinción de mayúsculas y
minúsculas o sin distinción de mayúsculas y minúsculas, especifique una intercalación explícitamente mediante
[Link] como se detailó anteriormente.

Recursos adicionales
Información específica de la base de datos
SQL Server documentación sobre las intercalaciones.
Documentación de Microsoft. Data. SQLite sobre las intercalaciones.
Documentación de PostgreSQL sobre las intercalaciones.
Documentación de MySQL sobre intercalaciones.
Otros recursos
EF Core sesión reuniónde la comunidad, introducción a las intercalaciones y exploración de los aspectos de
rendimiento e indexación.
Resistencia de conexión
12/03/2021 • 10 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. EF Core proveedores
pueden proporcionar estrategias de ejecución adaptadas a condiciones de error de base de datos específicas y
directivas de reintentos óptimas.
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 OnConfiguring método del contexto derivado:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


{
optionsBuilder
.UseSqlServer(
@"Server=
(localdb)\mssqllocaldb;Database=[Link];Trusted_Connection=True;ConnectRetryCoun
t=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]()));
}

NOTE
La habilitación del reintento en caso de error hace que EF almacene internamente el conjunto de resultados, lo que puede
aumentar significativamente los requisitos de memoria para las consultas que devuelven conjuntos de resultados
Consulte almacenamiento en búfer y transmisión por secuencias para obtener más detalles.

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() la definición de su propio grupo
de operaciones que deben tratarse como una unidad, y es necesario reproducir todo el contenido dentro de la
transacción, se producirá 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 vuelve a invocar al delegado.

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 actual DbContext .
2. Cree un nuevo 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 tarea
[Link] .

Este método inicia y confirma una transacción y también acepta una función en el verifySucceeded parámetro
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 Blog entidad a si se Unchanged SaveChanges 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.

Recursos adicionales
Solución de problemas de errores de conexión transitorios en Azure SQL Database y SQL Instancia
administrada
Cadenas de conexión
12/03/2021 • 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.

[Link] Core
En [Link] Core el sistema de configuración es muy flexible y la cadena de conexión podría 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.
Por ejemplo, puede usar la herramienta Administrador de secretos para almacenar la contraseña de la base de
datos y, a continuación, en la técnica scaffolding, usar una cadena de conexión que solo conste de
Name=<database-alias> .

dotnet user-secrets set ConnectionStrings:YourDatabaseAlias "Data Source=(localdb)\MSSQLLocalDB;Initial


Catalog=YourDatabase"
dotnet ef dbcontext scaffold Name=ConnectionStrings:YourDatabaseAlias
[Link]

O en el ejemplo siguiente se muestra la cadena de conexión almacenada en [Link] .

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

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

public void ConfigureServices(IServiceCollection services)


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

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 de [Link] de la aplicación ([Link] 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 configuración protegida.
<?xml version="1.0" encoding="utf-8"?>
<configuration>

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

TIP
La providerName configuración no es necesaria en EF Core cadenas de conexión almacenadas en [Link] porque el
proveedor de base de datos se configura mediante código.

Después, puede leer la cadena de conexión con la ConfigurationManager API en el método del contexto
OnConfiguring . 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);
}
}

Plataforma universal de Windows (UWP)


Las cadenas de conexión en una aplicación UWP suelen ser una conexión 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. Como tal, estas cadenas de conexión suelen quedar 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]");
}
}
Proveedores de bases de datos
07/04/2021 • 6 minutes to read

Entity Framework Core puede tener acceso a muchas bases de datos diferentes a través de bibliotecas de
complementos denominadas proveedores de bases de datos.

Proveedores actuales
IMPORTANT
Los proveedores de EF Core se componen de una serie de orígenes. No todos los proveedores se mantienen como parte
del proyecto Entity Framework Core. Al considerar un proveedor, evalúe la calidad, las licencias, el soporte técnico, etc. a
fin de asegurarse de que satisface los requisitos. Además, asegúrese de revisar la documentación de cada proveedor para
obtener información detallada de compatibilidad de versiones.

IMPORTANT
Por lo general, los proveedores de EF Core funcionan en versiones secundarias, pero no en todas las versiones principales.
Por ejemplo, un proveedor publicado para EF Core 2.1 debería funcionar con EF Core 2.2, pero no con EF Core 3.0.

M OTO RES DE
B A SE DE DATO S M A N T EN EDO R O N OTA S O C REA DO PA RA VÍN C ULO S
PA Q UET E N UGET C O M PAT IB L ES P RO VEEDO R REQ UISITO S L A VERSIÓ N ÚT IL ES

[Link] De Proyecto EF Core 5.0 Documentación


rameworkCore.S SQL Server 2012 (Microsoft)
qlServer en adelante

[Link] De SQLite 3.7 en Proyecto EF Core 5.0 Documentación


rameworkCore.S adelante (Microsoft)
qlite

[Link] Base de datos en Proyecto EF Core Limitaciones 5.0 Documentación


[Link] memoria de EF (Microsoft)
Memory Core

[Link] API de SQL de Proyecto EF Core 5.0 Documentación


rameworkCore.C Azure Cosmos (Microsoft)
osmos DB

[Link] PostgreSQL Equipo de 5.0 Documentación


[Link] desarrollo de
tgreSQL Npgsql

[Link] MySQL, MariaDB Proyecto Pomelo 3.1 Archivo Léame


[Link] Foundation
Sql

[Link] MySQL Proyecto MySQL 5.0 Documentación


meworkCore (Oracle)
M OTO RES DE
B A SE DE DATO S M A N T EN EDO R O N OTA S O C REA DO PA RA VÍN C ULO S
PA Q UET E N UGET C O M PAT IB L ES P RO VEEDO R REQ UISITO S L A VERSIÓ N ÚT IL ES

[Link] Oracle DB 11.2 y Oracle 5.0 Sitio web


meworkCore versiones
posteriores

[Link] De MySQL 5 en DevArt Pagado 5.0 Documentación


[Link] adelante

[Link] Oracle DB DevArt Pagado 5.0 Documentación


[Link] [Link] y
versiones
posteriores

[Link] De PostgreSQL DevArt Pagado 5.0 Documentación


[Link] 8.0 en adelante

[Link] De SQLite 3 en DevArt Pagado 5.0 Documentación


[Link] adelante

[Link] A partir de Jiří Č inčura 5.0 Documentación


FrameworkCore. Firebird 3.0
Firebird

[Link] Db2, Informix IBM De pago, 3.1 sitio web del


workCore Windows cliente

[Link] Db2, Informix IBM De pago, Linux 3.1 sitio web del
workCore-lnx cliente

[Link] Db2, Informix IBM De pago, macOS 3.1 sitio web del
workCore-osx cliente

[Link] Teradata Teradata 3.1 Sitio web


ameworkCore Database 16.10
en adelante

FileContextCore Almacena datos Morris Janatzek Con fines de 3.0 Archivo Léame
en archivos. desarrollo.

EntityFramework Archivos de Bubi .NET Framework 2.2 Archivo Léame


[Link] Microsoft Access

EntityFramework SQL Server Erik Ejlskov .NET Framework 2.2 Wiki


[Link] Compact 3,5 Jensen
mpact35

EntityFramework SQL Server Erik Ejlskov .NET Framework 2.2 Wiki


[Link] Compact 4.0 Jensen
mpact40

EntityFramework Progress Alex Wiese 2.1 Archivo Léame


[Link] OpenEdge
Agregar un proveedor de bases de datos a la aplicación
La mayoría de los proveedores de bases de datos para EF Core se distribuyen como paquetes NuGet y se
pueden instalar siguiendo estos pasos:

CLI de .NET Core


Visual Studio

dotnet add package provider_package_name

Una vez instalado, se configurará el proveedor en su DbContext , ya sea en el método OnConfiguring o el


método AddDbContext si se usa un contenedor de inserción de dependencias. Por ejemplo, la línea siguiente
configura el proveedor de SQL Server con la cadena de conexión pasada:

[Link](
"Server=(localdb)\mssqllocaldb;Database=MyDatabase;Trusted_Connection=True;");

Los proveedores de bases de datos pueden ampliar EF Core para habilitar una funcionalidad única para bases
de datos específicas. Algunos conceptos son comunes a la mayoría de las bases de datos y se incluyen en los
componentes principales de EF Core. Estos conceptos incluyen la expresión de consultas en LINQ, las
transacciones y el seguimiento de cambios en objetos una vez cargados desde la base de datos. Algunos
conceptos son específicos de un proveedor determinado. Por ejemplo, el proveedor de SQL Server permite
configurar tablas optimizadas para memoria (una característica específica de SQL Server). Otros conceptos son
específicos de una clase de proveedores. Por ejemplo, los proveedores de EF Core para bases de datos
relacionales se basan en la biblioteca común [Link] , que proporciona API
para configurar asignaciones de tabla y columna, restricciones de clave externa, etc. Los proveedores
normalmente se distribuyen como paquetes NuGet.

IMPORTANT
Cuando se publica una nueva versión de revisión de EF Core, suele incluir actualizaciones del paquete
[Link] . Cuando se agrega un proveedor de bases de datos relacional, este
paquete se convierte en una dependencia transitiva de la aplicación. Pero muchos proveedores se publican
independientemente de EF Core y es posible que no puedan actualizarse para que se basen en la versión de revisión más
reciente de ese paquete. A fin de asegurarse de que obtendrá todas las correcciones de errores, se recomienda agregar la
versión de revisión de [Link] como dependencia directa de la aplicación.
Proveedor de base de datos de Microsoft SQL
Server para EF Core
12/03/2021 • 2 minutes to read

Este proveedor de base de datos permite usar Entity Framework Core con Microsoft SQL Server (incluido
Azure SQL Database). Este proveedor se mantiene como parte del proyecto Entity Framework Core.

Instalar
Instale el paquete NuGet [Link].
CLI de .NET Core
Visual Studio

dotnet add package [Link]

NOTE
A partir de la versión 3.0.0, el proveedor hace referencia a [Link] (las versiones anteriores dependían de
[Link]). Si su proyecto depende directamente de SqlClient, asegúrese de que haga referencia al paquete
[Link].

Motores de base de datos compatibles


Microsoft SQL Server (de 2012 en adelante)
Generación de SQL Server valor
12/03/2021 • 2 minutes to read

En esta página se detallan los patrones y la configuración de generación de valores específicos del proveedor de
SQL Server. Se recomienda leer primero la página general en la generación de valores.

Columnas IDENTITY
Por Convención, las columnas numéricas que están configuradas para que los valores generados en Add se
configuran como SQL Server columnas de identidad.
Inicialización e incremento
De forma predeterminada, las columnas de identidad se inician en 1 (el valor de inicialización) y se incrementan
en 1 cada vez que se agrega una fila (el incremento). Puede configurar una inicialización y un incremento
diferentes como se indica a continuación:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>()
.Property(b => [Link])
.UseIdentityColumn(seed: 10, increment: 10);
}

NOTE
La capacidad de configurar el incremento e inicialización de identidad se presentó en EF Core 3,0.

Insertar valores explícitos en columnas de identidad


De forma predeterminada, SQL Server no permite insertar valores explícitos en columnas de identidad. Para
ello, debe habilitarlo manualmente IDENTITY_INSERT antes de llamar a SaveChanges() , como se indica a
continuación:

using (var context = new ExplicitIdentityValuesContext())


{
[Link](new Blog { BlogId = 100, Url = "[Link] });
[Link](new Blog { BlogId = 101, Url = "[Link] });

[Link]();
try
{
[Link]("SET IDENTITY_INSERT [Link] ON");
[Link]();
[Link]("SET IDENTITY_INSERT [Link] OFF");
}
finally
{
[Link]();
}
}
NOTE
Tenemos una solicitud de característica en el trabajo pendiente para hacer esto de manera automática dentro del
proveedor de SQL Server.

Secuencias
Como alternativa a las columnas de identidad, puede usar secuencias estándar. Esto puede ser útil en varios
escenarios; por ejemplo, puede que desee que varias columnas dibuje sus valores predeterminados de una sola
secuencia.
SQL Server permite crear secuencias y usarlas tal y como se detalla en la página general en secuencias.
Depende de usted configurar las propiedades para usar secuencias a través de HasDefaultValueSql() .
Asignaciones de función del proveedor de Microsoft
SQL Server
12/03/2021 • 9 minutes to read

En esta página se muestran los miembros de .NET que se traducen en SQL Functions cuando se usa el
proveedor de SQL Server.

Funciones binarias
. N ET SQ L A GREGA DO EN

bytes. Contains (valor) CHARINDEX ( @value , @bytes ) > 0 EF Core 5.0

bytes. Primero () Substring ( @bytes , 1,1) EF Core 6.0

bytes. Longitud DATALENGTH ( @bytes ) EF Core 5.0

bytes. SequenceEqual (segundo) @bytes = @second EF Core 5.0

bytes [i] Substring ( @bytes , @i + 1,1) EF Core 6.0

EF. Functions. DATALENGTH (Arg) DATALENGTH ( @arg ) EF Core 5.0

Funciones de conversión
. N ET SQ L A GREGA DO EN

bytes. ToString () CONVERT (varchar (100), @bytes )

byteValue. ToString () CONVERT (varchar (3), @byteValue )

charValue. ToString () CONVERT (varchar (1), @charValue )

Convert. ToBoolean (valor) CONVERT (bit, @value ) EF Core 5.0

Convert. ToByte (valor) CONVERT (tinyint, @value )

Convert. ToDecimal (valor) CONVERT (decimal (18, 2), @value )

Convert. ToDouble (valor) CONVERT (float, @value )

Convert. ToInt16 (valor) CONVERT (smallint, @value )

Convert. ToInt32 (valor) CONVERT (int, @value )

Convert. ToInt64 (valor) CONVERT (BIGINT, @value )


. N ET SQ L A GREGA DO EN

Convert. ToString (valor) CONVERT (nvarchar (Max), @value )

dateTime. ToString () CONVERT (varchar (100), @dateTime )

dateTimeOffset. ToString () CONVERT (varchar (100),


@dateTimeOffset )

decimalValue. ToString () CONVERT (varchar (100),


@decimalValue )

doubleValue. ToString () CONVERT (varchar (100),


@doubleValue )

floatValue. ToString () CONVERT (varchar (100), @floatValue )

volumen. ToString () CONVERT (varchar (36), @guid )

intValue. ToString () CONVERT (varchar (11), @intValue )

longValue. ToString () CONVERT (varchar (20), @longValue )

sbyteValue. ToString () CONVERT (varchar (4), @sbyteValue )

shortValue. ToString () CONVERT (varchar (6), @shortValue )

timeSpan. ToString () CONVERT (varchar (100), @timeSpan )

uintValue. ToString () CONVERT (varchar (10), @uintValue )

ulongValue. ToString () CONVERT (varchar (19), @ulongValue )

ushortValue. ToString () CONVERT (varchar (5), @ushortValue )

Funciones de fecha y hora


. N ET SQ L A GREGA DO EN

[Link] GETDATE ()

Fecha y hora. hoy CONVERT (fecha, GETDATE ())

[Link] GETUTCDATE ()

dateTime. AddDays (valor) DATEADD (día, @value , @dateTime )

dateTime. AddHours (valor) DATEADD (hora, @value , @dateTime )

dateTime. AddMilliseconds (valor) DATEADD (milisegundo, @value ,


@dateTime )
. N ET SQ L A GREGA DO EN

dateTime. AddMinutes (valor) DATEADD (minuto, @value ,


@dateTime )

dateTime. AddMonths (meses) DATEADD (mes, @months ,


@dateTime )

dateTime. AddSeconds (valor) DATEADD (segundo, @value ,


@dateTime )

dateTime. AddYears (valor) DATEADD (año, @value , @dateTime )

Fecha y hora CONVERT (fecha, @dateTime )

dateTime. Day DATEPART (día, @dateTime )

dateTime. DayOfYear DATEPART (DayOfYear, @dateTime )

Fecha y hora DATEPART (hora, @dateTime )

dateTime. Milliseconds DATEPART (milisegundo, @dateTime )

dateTime. minute DATEPART (minuto, @dateTime )

dateTime. month DATEPART (mes, @dateTime )

dateTime. Second DATEPART (segundo, @dateTime )

dateTime. TimeOfDay CONVERTIR (tiempo, @dateTime ) EF Core 2.2

dateTime. Year DATEPART (año, @dateTime )

[Link] SYSDATETIMEOFFSET ()

DateTimeOffset. UtcNow SYSUTCDATETIME ()

dateTimeOffset. AddDays (días) DATEADD (día, @days ,


@dateTimeOffset )

dateTimeOffset. AddHours (horas) DATEADD (hora, @hours ,


@dateTimeOffset )

dateTimeOffset. AddMilliseconds DATEADD (milisegundo, @milliseconds


(milisegundos) , @dateTimeOffset )

dateTimeOffset. AddMinutes (minutos) DATEADD (minuto, @minutes ,


@dateTimeOffset )

dateTimeOffset. AddMonths (meses) DATEADD (mes, @months ,


@dateTimeOffset )

dateTimeOffset. AddSeconds DATEADD (segundo, @seconds ,


(segundos) @dateTimeOffset )
. N ET SQ L A GREGA DO EN

dateTimeOffset. AddYears (años) DATEADD (año, @years ,


@dateTimeOffset )

dateTimeOffset. Date CONVERT (fecha, @dateTimeOffset )

dateTimeOffset. Day DATEPART (día, @dateTimeOffset )

dateTimeOffset. DayOfYear DATEPART (DayOfYear,


@dateTimeOffset )

dateTimeOffset. hour DATEPART (hora, @dateTimeOffset )

dateTimeOffset. milisegundos DATEPART (milisegundo,


@dateTimeOffset )

dateTimeOffset. minute DATEPART (minuto, @dateTimeOffset )

dateTimeOffset. month DATEPART (mes, @dateTimeOffset )

dateTimeOffset. Second DATEPART (segundo, @dateTimeOffset


)

dateTimeOffset. TimeOfDay CONVERTIR (tiempo, @dateTimeOffset EF Core 2.2


)

dateTimeOffset. Year DATEPART (año, @dateTimeOffset )

EF. Functions. DateDiffDay (Inicio, DATEDIFF (día, @start , @end )


finalización)

EF. Functions. DateDiffHour (Inicio, DATEDIFF (hora, @start , @end )


finalización)

EF. Functions. DateDiffMicrosecond DATEDIFF (microsegundo, @start ,


(Inicio, finalización) @end )

EF. Functions. DateDiffMillisecond DATEDIFF (milisegundo, @start , @end


(Inicio, finalización) )

EF. Functions. DateDiffMinute (Inicio, DATEDIFF (minuto, @start , @d2 )


finalización)

EF. Functions. DateDiffMonth (Inicio, DATEDIFF (mes, @start , @end )


finalización)

EF. Functions. DateDiffNanosecond DATEDIFF (nanosegundo, @start ,


(Inicio, finalización) @end )

EF. Functions. DateDiffSecond (Inicio, DATEDIFF (segundo, @start , @end )


finalización)

EF. Functions. DateDiffWeek (Inicio, DATEDIFF (semana, @start , @end ) EF Core 5.0
finalización)
. N ET SQ L A GREGA DO EN

EF. Functions. DateDiffYear (Inicio, DATEDIFF (año, @start , @end )


finalización)

EF. Functions. DateFromParts (año, DATEFROMPARTS ( @year , @month , EF Core 5.0


mes, día) @day )

EF. Functions. DateTime2FromParts DATETIME2FROMPARTS ( @year , EF Core 5.0


(año, mes, día,...) @month , @day ,...)

EF. Functions. DateTimeFromParts DATETIMEFROMPARTS ( @year , EF Core 5.0


(año, mes, día,...) @month , @day ,...)

EF. Functions. DATETIMEOFFSETFROMPARTS ( @year EF Core 5.0


DateTimeOffsetFromParts (año, mes, , @month , @day ,...)
día,...)

EF. Functions. IsDate (expresión) ISDATE ( @expression ) EF Core 3.0

EF. Functions. SMALLDATETIMEFROMPARTS ( @year EF Core 5.0


SmallDateTimeFromParts (año, mes, , @month , @day ,...)
día,...)

EF. Functions. TimeFromParts (hora, TIMEFROMPARTS ( @hour , @minute , EF Core 5.0


minuto, segundo,...) @second ,...)

timeSpan. hours DATEPART (hora, @timeSpan ) EF Core 5.0

timeSpan. Milliseconds DATEPART (milisegundo, @timeSpan ) EF Core 5.0

timeSpan. minutes DATEPART (minuto, @timeSpan ) EF Core 5.0

timeSpan. seconds DATEPART (segundo, @timeSpan ) EF Core 5.0

Funciones numéricas
. N ET SQ L A GREGA DO EN

EF. Functions. RANDOM () RAND () EF Core 6.0

Math. ABS (valor) ABS ( @value )

Math. ACOS (d) ACOS ( @d )

Math. Asin (d) ASIN ( @d )

Math. atan (d) ATAN ( @d )

Math. Atan2 (y, x) ATN2 ( @y , @x )

Math. Ceiling (d) CEILING ( @d )


. N ET SQ L A GREGA DO EN

Math. cos (d) COS ( @d )

Math. exp (d) EXP ( @d )

Math. Floor (d) FLOOR ( @d )

Math. log (d) REGISTRO ( @d )

Math. log (a, newBase) REGISTRO ( @a , @newBase )

Math. Log10 (d) LOG10 ( @d )

Math. Pow (x, y) ALIMENTACIÓN ( @x , @y )

Math. round (d) ROUND ( @d ,0)

Math. round (d, decimales) ROUND ( @d , @decimals )

Math. Sign (valor) FIRMAR ( @value )

Math. sin (a) SIN ( @a )

Math. sqrt (d) SQRT ( @d )

Math. tan (a) TAN ( @a )

Math. TRUNCATE (d) ROUND ( @d , 0, 1)

Funciones de cadena
. N ET SQ L A GREGA DO EN

EF. Functions. COLLATE (operando, @operand COLLATE @collation EF Core 5.0


intercalación)

EF. Functions. Contains Contains ( @propertyReference , EF Core 2.2


(propertyReference, searchCondition) @searchCondition )

EF. Functions. Contains Contains ( @propertyReference , EF Core 2.2


(propertyReference, searchCondition, @searchCondition , Language
languageTerm) @languageTerm )

EF. Functions. FreeText FREETEXT ( @propertyReference ,


(propertyReference, freeText) @freeText )

EF. Functions. FreeText FREETEXT ( @propertyReference ,


(propertyReference, freeText, @freeText , idioma @languageTerm )
languageTerm)

EF. Functions. IsNumeric (expresión) ISNUMERIC ( @expression ) EF Core 6.0


. N ET SQ L A GREGA DO EN

EF. Functions. like (matchExpression, @matchExpression FORMA @pattern


Pattern)

EF. Functions. like (matchExpression, @matchExpression COMO @pattern


Pattern, escapeCharacter) escape @escapeCharacter

String. Compare (strA, strB) CASE WHEN @strA = @strB 0...


EXTREMO

String@. Concat (str0, Str1) @str0 + @str1

String@. IsNullOrEmpty (valor) @value ES NULL o es @value como N '


'

String@. IsNullOrWhiteSpace (valor) @value ES NULL o LTRIM (RTRIM (


@value )) = N ' '

Valorstring. CompareTo (strB) CASE WHEN @stringValue = @strB 0...


EXTREMO

Valorstring. Contains (valor) @stringValue COMO N '% ' + @value


+ n '% '

Valorstring. EndsWith (valor) @stringValue COMO N '% ' + @value

Valorstring. FirstOrDefault () Substring ( @stringValue , 1,1) EF Core 5.0

Valorstring. IndexOf (valor) CHARINDEX ( @value , @stringValue )-


1

Valorstring. LastOrDefault () Substring ( @stringValue , Len ( EF Core 5.0


@stringValue ), 1)

Valorstring. length LEN ( @stringValue )

Valorstring. Replace ( @oldValue , REEMPLAZAR ( @stringValue ,


@newValue ) @oldValue , @newValue )

Valorstring. StartsWith (valor) @stringValue LIKE @value + N '% '

Valorstring. substring (startIndex, Substring ( @stringValue , @startIndex


longitud) + 1, @length )

Valorstring. ToLower () LOWER ( @stringValue )

Valorstring. ToUpper () UPPER ( @stringValue )

Valorstring. Trim () LTRIM (RTRIM ( @stringValue ))

Valorstring. TrimEnd () RTRIM ( @stringValue )

Valorstring. TrimStart () LTRIM ( @stringValue )


Funciones misceláneas
. N ET SQ L A GREGA DO EN

Collection. Contains (Item) @item DE @collection EF Core 3.0

enumValue. HasFlag (marca) @enumValue & @flag = @flag

[Link] () NEWID()

acepta valores NULL. Coalesce ( @nullable , 0)


GetValueOrDefault ()

acepta valores NULL. Coalesce ( @nullable , @defaultValue )


GetValueOrDefault (defaultValue)

NOTE
Algunos SQL se han simplificado con fines ilustrativos. El SQL real es más complejo para controlar un rango más amplio
de valores.

Consulte también
Asignaciones de funciones espaciales
Características de índice específicas del proveedor
de SQL Server de Entity Framework Core
12/03/2021 • 3 minutes to read

En esta página se detallan las opciones de configuración del índice que son específicas del proveedor de SQL
Server.

Agrupación en clústeres
Los índices clúster ordenan y almacenan las filas de los datos de la tabla o vista de acuerdo con los valores de la
clave del índice. La creación del índice clúster adecuado para la tabla puede mejorar significativamente la
velocidad de las consultas, ya que los datos ya están dispuestos en el orden óptimo. Solo puede haber un índice
clúster por cada tabla, porque las filas de datos solo pueden estar almacenadas de una forma. Para obtener más
información, consulte la documentación de SQL Server sobre los índices agrupados y no agrupados.
De forma predeterminada, la columna de clave principal de una tabla está respaldada implícitamente por un
índice clúster y el resto de los índices no están agrupados.
Puede configurar un índice o una clave para agruparlos como se indica a continuación:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>().HasIndex(b => [Link]).IsClustered();
}

NOTE
SQL Server solo admite un índice clúster por tabla y la clave principal está agrupada de forma predeterminada. Si desea
tener un índice clúster en una columna que no sea de clave, debe hacer explícitamente que la clave no esté en clúster.

Factor de relleno
NOTE
Esta característica se incluyó por primera vez en EF Core 5.0.

La opción de factor de relleno de índice se proporciona para ajustar el rendimiento y el almacenamiento de


datos de índice. Para obtener más información, consulte la documentación de SQL Server sobre el factor de
relleno.
Puede configurar el factor de relleno de un índice como se indica a continuación:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>().HasIndex(b => [Link]).HasFillFactor(10);
}

Creación en línea
La opción ONLINE permite el acceso simultáneo de los usuarios a los datos de la tabla subyacente o del índice
clúster, así como a los índices no clúster asociados durante la creación del índice, de modo que los usuarios
puedan seguir actualizando y consultando los datos subyacentes. Al realizar operaciones DDL (lenguaje de
definición de datos) sin conexión, como generar o volver a generar un índice clúster, estas operaciones
mantienen bloqueos exclusivos de los datos subyacentes y los índices asociados. Para obtener más información,
consulte la documentación de SQL Server en la opción de índice en línea.
Puede configurar un índice con la opción ONLINE de la siguiente manera:

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>().HasIndex(b => [Link]).IsCreatedOnline();
}
Compatibilidad de tablas Memory-Optimized en
SQL Server proveedor de bases de datos EF Core
12/03/2021 • 2 minutes to read

Las tablas optimizadas para memoria son una característica de SQL Server en la que toda la tabla reside en
memoria. Una segunda copia de los datos de la tabla se conserva en el disco pero solo por la durabilidad. Los
datos de las tablas optimizadas para memoria solo se leen del disco durante la recuperación de la base de datos.
Por ejemplo, después de reiniciar el servidor.

Configurar una tabla optimizada para memoria


Puede especificar que la tabla a la que está asignada una entidad está optimizada para memoria. Al usar EF Core
para crear y mantener una base de datos basada en el modelo (ya sea con migraciones o EnsureCreated), se
creará una tabla optimizada para memoria para estas entidades.

protected override void OnModelCreating(ModelBuilder modelBuilder)


{
[Link]<Blog>().IsMemoryOptimized();
}
Datos espaciales en el proveedor de EF Core de
SQL Server
12/03/2021 • 5 minutes to read

Esta página incluye información adicional sobre el uso de datos espaciales con el proveedor de base de datos de
Microsoft SQL Server. Para obtener información general sobre el uso de datos espaciales en EF Core, consulte la
documentación principal de los datos espaciales .

Geografía o geometría
De forma predeterminada, las propiedades espaciales se asignan a geography las columnas de SQL Server. Para
usar geometry , Configure el tipo de columna en el modelo.

Anillos de polígono de geografía


Al usar el geography tipo de columna, 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. NetTopologySuite (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
geography tipo de columna. 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.

Curvas
Como se mencionó en la documentación principal de los datos espaciales , NTS actualmente no puede
representar curvas. Esto significa que deberá transformar los valores CircularString, CompoundCurve y
CurePolygon con el método STCurveToLine antes de usarlos en EF Core.

WARNING
La CircularString, CompoundCurve y CurePolygon no son compatibles con NTS.

Asignaciones de funciones espaciales


En esta tabla se muestran los miembros de NTS que se traducen en las funciones de SQL. Tenga en cuenta que
las traducciones varían en función de si la columna es de tipo Geography o Geometry.
. N ET SQ L ( GEO GRA F ÍA ) SQ L ( GEO M ET RÍA )

Geometry. Áreas @[Link]() @[Link]()

Geometry. AsBinary () @[Link]() @[Link]()

Geometry. Astext () @[Link]() @[Link]()

Geometry. Exceso @[Link]()

Geometry. Búfer (distancia) @[Link](@distance) @[Link](@distance)

Geometry. Centroide @[Link]()

Geometry. Contains (g) @[Link](@g) @[Link](@g)

Geometry. ConvexHull() @[Link]() @[Link]()

Geometry. Cruza (g) @[Link](@g)

Geometry. Diferencia (otros) @[Link](@other) @[Link](@other)

Geometry. Galería @[Link]() @[Link]()

Geometry. Disunion (g) @[Link](@g) @[Link](@g)

Geometry. Distancia (g) @[Link](@g) @[Link](@g)

Geometry. Tales @[Link]()

Geometry. EqualsTopologically (g) @[Link](@g) @[Link](@g)

Geometry. GeometryType @[Link]() @[Link]()

Geometry. GetGeometryN (n) @[Link]( @n + 1) @[Link]( @n + 1)

Geometry. InteriorPoint @[Link]()

Geometry. Intersección (otro) @[Link](@other) @[Link](@other)

Geometry. Interseccións (g) @[Link](@g) @[Link](@g)

Geometry. Vacío @[Link]() @[Link]()

Geometry. IsSimple @[Link]()

Geometry. IsValid @[Link]() @[Link]()

Geometry. IsWithinDistance (Geom, @[Link]( @geom ) <= @[Link]( @geom ) <=


Distance) @distance @distance

Geometry. Longitud @[Link]() @[Link]()


. N ET SQ L ( GEO GRA F ÍA ) SQ L ( GEO M ET RÍA )

Geometry. NumGeometries @[Link]() @[Link]()

Geometry. NumPoints @[Link]() @[Link]()

Geometry. OgcGeometryType CASE @[Link] () CASE @[Link] ()


cuando N'Point ' then 1... EXTREMO cuando N'Point ' then 1... EXTREMO

Geometry. Superposiciones (g) @[Link](@g) @[Link](@g)

Geometry. PointOnSurface @[Link]()

Geometry. Relacionar (g, @[Link](@g,


intersectionPattern) @intersectionPattern)

Geometry. SRID @[Link] @[Link]

Geometry. SymmetricDifference (otro) @[Link](@other) @[Link](@other)

Geometry. ToBinary() @[Link]() @[Link]()

Geometry. ToText() @[Link]() @[Link]()

Geometry. Toques (g) @[Link](@g)

Geometry. Unión (otro) @[Link](@other) @[Link](@other)

Geometry. Dentro de (g) @[Link](@g) @[Link](@g)

geometryCollection [i] @[Link]( @[Link](


@i + 1) @i + 1)

geometryCollection. Count @[Link] @[Link]


es() es()

lineString. Count @[Link]() @[Link]()

lineString. EndPoint @[Link]() @[Link]()

lineString. GetPointN (n) @[Link]( @n + 1) @[Link]( @n + 1)

lineString. IsClosed @[Link]() @[Link]()

lineString. IsRing @[Link]()

lineString. StartPoint @[Link]() @[Link]()

multiLineString. IsClosed @[Link]() @[Link]()

Elija. F @point.M @point.M

Elija. X1 @[Link] @[Link]


. N ET SQ L ( GEO GRA F ÍA ) SQ L ( GEO M ET RÍA )

Elija. Sí @[Link] @[Link]

Elija. Z @point.Z @point.Z

Polígono. ExteriorRing @[Link] @[Link]()

Polígono. GetInteriorRingN (n) @[Link]( @n + 2) @[Link]( @n + 1)

Polígono. NumInteriorRings @[Link]()-1 @[Link]()

Recursos adicionales
Datos espaciales de SQL Server
Documentos de NetTopologySuite
Especificación de opciones de Azure SQL Database
12/03/2021 • 2 minutes to read

NOTE
Esta API se presentó en EF Core 3,1.

Azure SQL Database proporciona una variedad de opciones de precios que normalmente se configuran a través
de Azure portal. Sin embargo, si administra el esquema mediante migraciones de EF Core , puede especificar las
opciones deseadas en el propio modelo.
Puede especificar el nivel de servicio de la base de datos (edición) mediante HasServiceTier:

[Link]("BusinessCritical");

Puede especificar el tamaño máximo de la base de datos mediante HasDatabaseMaxSize:

[Link]("2 GB");

Puede especificar el nivel de rendimiento de la base de datos (SERVICE_OBJECTIVE) mediante


HasPerformanceLevel:

[Link]("BC_Gen4_1");

Use HasPerformanceLevelSql para configurar el grupo elástico, ya que el valor no es un literal de cadena:

[Link]("ELASTIC_POOL ( name = myelasticpool )");

TIP
Puede encontrar todos los valores admitidos en la documentación de ALTER DATABASE.
Proveedor de base de datos SQLite para EF Core
12/03/2021 • 2 minutes to read

Este proveedor de base de datos permite usar Entity Framework Core con SQLite. Este proveedor se mantiene
como parte del proyecto Entity Framework Core.

Instalar
Instale el paquete NuGet [Link].
CLI de .NET Core
Visual Studio

dotnet add package [Link]

Motores de base de datos compatibles


SQLite (3.7 y superiores)

Limitaciones
Vea Limitaciones de SQLite para conocer algunas limitaciones importantes del proveedor de SQLite.
Limitaciones del proveedor de base de datos
SQLite para EF Core
12/03/2021 • 5 minutes to read

El proveedor de SQLite tiene varias limitaciones de migración. La mayoría de estas limitaciones son
consecuencia de las limitaciones del motor de base de datos SQLite subyacente y no son específicas de EF.

Limitaciones del modelado


La biblioteca relacional común (compartida por Entity Framework proveedores de bases de datos relacionales)
define las API para los conceptos de modelado que son comunes a la mayoría de los motores de bases de datos
relacionales. Un par de estos conceptos no es compatible con el proveedor de SQLite.
Esquemas
Secuencias

Limitaciones de las consultas


SQLite no admite de forma nativa los siguientes tipos de datos. EF Core puede leer y escribir valores de estos
tipos, y también se admite la consulta de igualdad ( where [Link] == value ). Sin embargo, otras
operaciones, como la comparación y la ordenación, requerirán la evaluación en el cliente.
DateTimeOffset
Decimal
TimeSpan
UInt64
En lugar de DateTimeOffset , se recomienda usar valores DateTime. Al controlar varias zonas horarias,
recomendamos convertir los valores a UTC antes de guardar y, a continuación, volver a convertirlos a la zona
horaria adecuada.
El Decimal tipo proporciona un alto nivel de precisión. Sin embargo, si no necesita ese nivel de precisión, se
recomienda usar Double en su lugar. Puede usar un convertidor de valores para seguir usando decimal en las
clases.

[Link]<MyEntity>()
.Property(e => [Link])
.HasConversion<double>();

Limitaciones de las migraciones


El motor de base de datos de SQLite no es compatible con una serie de operaciones de esquema que son
compatibles con la mayoría de las demás bases de datos relacionales. Si intenta aplicar una de las operaciones
no admitidas a una base de datos de SQLite, se NotSupportedException iniciará una.
Se intentará volver a generar para realizar determinadas operaciones. Las recompilaciones solo son posibles
para los artefactos de base de datos que forman parte del modelo de EF Core. Si un artefacto de base de datos
no forma parte del modelo (por ejemplo, si se creó manualmente dentro de una migración),
NotSupportedException se sigue produciendo una.
O P ERA C IÓ N ¿C O M PAT IB L E? REQ UIERE VERSIÓ N

AddCheckConstraint ✔ (recompilar) 5.0

AddColumn ✔

AddForeignKey ✔ (recompilar) 5.0

AddPrimaryKey ✔ (recompilar) 5.0

AddUniqueConstraint ✔ (recompilar) 5.0

AlterColumn ✔ (recompilar) 5.0

CreateIndex ✔

CreateTable ✔

DropCheckConstraint ✔ (recompilar) 5.0

DropColumn ✔ (recompilar) 5.0

DropForeignKey ✔ (recompilar) 5.0

DropIndex ✔

DropPrimaryKey ✔ (recompilar) 5.0

DropTable ✔

DropUniqueConstraint ✔ (recompilar) 5.0

RenameColumn ✔ 2.2

RenameIndex ✔ (recompilar)

RenameTable ✔

EnsureSchema ✔ (no OP)

DropSchema ✔ (no OP)

Insertar ✔

Actualizar ✔

Eliminar ✔

Solución de limitaciones de migraciones


Puede solucionar algunas de estas limitaciones escribiendo manualmente el código en las migraciones para
realizar una recompilación. Las recompilaciones de tablas implican la creación de una nueva tabla, la copia de
datos en la nueva tabla, la eliminación de la tabla anterior y el cambio de nombre de la nueva tabla. Tendrá que
usar el Sql(string) método para realizar algunos de estos pasos.
Vea realizar otros tipos de cambios de esquema de tabla en la documentación de SQLite para obtener más
detalles.
Limitaciones del script idempotente
A diferencia de otras bases de datos, SQLite no incluye un lenguaje de procedimientos. Por esta razón, no hay
ninguna manera de generar la lógica if-then requerida por los scripts de migración idempotente.
Si conoce la última migración que se aplica a una base de datos, puede generar un script a partir de esa
migración a la última migración.

dotnet ef migrations script CurrentMigration

De lo contrario, se recomienda usar dotnet ef database update para aplicar las migraciones. A partir de EF Core
5,0, puede especificar el archivo de base de datos al ejecutar el comando.

dotnet ef database update --connection "Data Source=[Link]"

Consulta también
Limitaciones de Microsoft. Data. SQLite Async
Limitaciones de Microsoft. Data. SQLite [Link]
Asignaciones de función del proveedor de EF Core
de SQLite
12/03/2021 • 8 minutes to read

En esta página se muestran los miembros de .NET que se traducen en SQL Functions cuando se usa el
proveedor de SQLite.

Funciones binarias
. N ET SQ L A GREGA DO EN

bytes. Contains (valor) instr ( @bytes , Char ( @value )) > 0 EF Core 5.0

bytes. Longitud longitud ( @bytes ) EF Core 5.0

bytes. SequenceEqual (segundo) @bytes = @second EF Core 5.0

EF. Functions. hex (bytes) Hex ( @bytes ) EF Core 6.0

EF. Functions. substr (bytes, startIndex) substr ( @bytes , @startIndex ) EF Core 6.0

EF. Functions. substr (bytes, startIndex, substr ( @bytes , @startIndex , EF Core 6.0
longitud) @length )

Funciones de conversión
. N ET SQ L A GREGA DO EN

boolValue. ToString () CAST ( @boolValue como texto) EF Core 6.0

byteValue. ToString () CAST ( @byteValue como texto) EF Core 6.0

bytes. ToString () CAST ( @bytes como texto) EF Core 6.0

charValue. ToString () CAST ( @charValue como texto) EF Core 6.0

dateTime. ToString () CAST ( @dateTime como texto) EF Core 6.0

dateTimeOffset. ToString () CAST ( @dateTimeOffset como texto) EF Core 6.0

decimalValue. ToString () CAST ( @decimalValue como texto) EF Core 6.0

doubleValue. ToString () CAST ( @doubleValue como texto) EF Core 6.0

floatValue. ToString () CAST ( @floatValue como texto) EF Core 6.0

volumen. ToString () CAST ( @guid como texto) EF Core 6.0


. N ET SQ L A GREGA DO EN

intValue. ToString () CAST ( @intValue como texto) EF Core 6.0

longValue. ToString () CAST ( @longValue como texto) EF Core 6.0

sbyteValue. ToString () CAST ( @sbyteValue como texto) EF Core 6.0

shortValue. ToString () CAST ( @shortValue como texto) EF Core 6.0

timeSpan. ToString () CAST ( @timeSpan como texto) EF Core 6.0

uintValue. ToString () CAST ( @uintValue como texto) EF Core 6.0

ushortValue. ToString () CAST ( @ushortValue como texto) EF Core 6.0

Funciones de fecha y hora


. N ET SQ L A GREGA DO EN

[Link] DateTime (' Now ', ' localtime ')

Fecha y hora. hoy DateTime (' Now ', ' localtime ', ' Start
of Day ')

[Link] DateTime (' Now ')

dateTime. AddDays (valor) DateTime ( @dateTime , @value | | '


Days ')

dateTime. AddHours (valor) DateTime ( @dateTime , @d | | ' Hours


')

dateTime. AddMilliseconds (valor) DateTime ( @dateTime , ( @value EF Core 2.2


/1000,0) | | ' seconds ')

dateTime. AddMinutes (valor) DateTime ( @dateTime , @value | | '


minutes ')

dateTime. AddMonths (meses) DateTime ( @dateTime , @months | | '


months ')

dateTime. AddSeconds (valor) DateTime ( @dateTime , @value | | '


seconds ')

dateTime. AddTicks (valor) DateTime ( @dateTime , ( @value EF Core 2.2


/10000000,0) | | ' seconds ')

dateTime. AddYears (valor) DateTime ( @dateTime , @value | | '


years ')

Fecha y hora DateTime ( @dateTime , ' Start of Day


')
. N ET SQ L A GREGA DO EN

dateTime. Day strftime ('% d ', @dateTime )

dateTime. DayOfWeek strftime ('% w ', @dateTime ) EF Core 2.2

dateTime. DayOfYear strftime ('% j ', @dateTime )

Fecha y hora strftime ('% H ', @dateTime )

dateTime. Milliseconds (strftime ('% f ', @dateTime ) * 1000)


%1000

dateTime. minute strftime ('% M ', @dateTime )

dateTime. month strftime ('% m ', @dateTime )

dateTime. Second strftime ('% S ', @dateTime )

dateTime. Ticks (julianday ( @dateTime )-julianday (' EF Core 2.2


0001-01-01 00:00:00 ')) * 864 mil
millones

dateTime. TimeOfDay hora ( @dateTime ) EF Core 3.0

dateTime. Year strftime ('% Y ', @dateTime )

NOTE
Algunos SQL se han simplificado con fines ilustrativos. El SQL real es más complejo para controlar un rango más amplio
de valores.

Funciones numéricas
. N ET SQ L A GREGA DO EN

-decimalValue ef_negate ( @decimalValue ) EF Core 5.0

decimalValue-d ef_add ( @decimalValue , ef_negate ( EF Core 5.0


@d ))

decimalValue * d ef_multiply ( @decimalValue , @d ) EF Core 5.0

decimalValue/d ef_divide ( @decimalValue , @d ) EF Core 5.0

decimalValue% d ef_mod ( @decimalValue , @d ) EF Core 5.0

decimalValue + d ef_add ( @decimalValue , @d ) EF Core 5.0

decimalValue < d ef_compare ( @decimalValue , @d ) < EF Core 5.0


0
. N ET SQ L A GREGA DO EN

<decimalValue = d ef_compare ( @decimalValue , @d ) <= EF Core 5.0


0

decimalValue > d ef_compare ( @decimalValue , @d ) > EF Core 5.0


0

>decimalValue = d ef_compare ( @decimalValue , @d ) >= EF Core 5.0


0

doubleValue% d ef_mod ( @doubleValue , @d ) EF Core 5.0

EF. Functions. RANDOM () ABS (Random EF Core 6.0


()/9223372036854780000.0)

floatValue% d ef_mod ( @floatValue , @d ) EF Core 5.0

Math. ABS (valor) ABS ( @value )

Math. Max (val1, val2) Max ( @val1 , @val2 )

Math. min (val1, val2) min ( @val1 , @val2 )

Math. round (d) Round ( @d )

Math. round (d, digits) Round ( @d , @digits )

TIP
Las funciones SQL con el prefijo EF se crean mediante EF Core.

Funciones de cadena
. N ET SQ L A GREGA DO EN

Juegos. ToLower (c) Lower ( @c ) EF Core 6.0

Juegos. ToUpper (c) Upper ( @c ) EF Core 6.0

EF. Functions. COLLATE (operando, @operand COLLATE @collation EF Core 5.0


intercalación)

EF. Functions. glob (matchExpression, Glob ( @pattern , @matchExpression ) EF Core 6.0


Pattern)

EF. Functions. like (matchExpression, @matchExpression FORMA @pattern


Pattern)

EF. Functions. like (matchExpression, @matchExpression COMO @pattern


Pattern, escapeCharacter) escape @escapeCharacter
. N ET SQ L A GREGA DO EN

Regex. IsMatch (entrada, patrón) RegExp ( @pattern , @input ) EF Core 6.0

String. Compare (strA, strB) CASE WHEN @strA = @strB 0...


EXTREMO

String@. Concat (str0, Str1) @str0 || @str1

String@. IsNullOrEmpty (valor) @value ES NULL o @value = ' '

String@. IsNullOrWhiteSpace (valor) @value ES NULL o Trim ( @value ) = ' '

Valorstring. CompareTo (strB) CASE WHEN @stringValue = @strB 0...


EXTREMO

Valorstring. Contains (valor) instr ( @stringValue , @value ) > 0

Valorstring. EndsWith (valor) @stringValueLIKE '% ' | |@value

Valorstring. FirstOrDefault () substr ( @stringValue , 1,1) EF Core 5.0

Valorstring. IndexOf (valor) instr ( @stringValue , @value )-1

Valorstring. LastOrDefault () substr ( @stringValue , longitud ( EF Core 5.0


@stringValue ), 1)

Valorstring. length longitud ( @stringValue )

Valorstring. Replace (oldValue, reemplazar ( @stringValue , @oldValue


newValue) , @newValue )

Valorstring. StartsWith (valor) @stringValueLIKE @value | | '% '

Valorstring. substring (startIndex, substr ( @stringValue , @startIndex +


longitud) 1, @length )

Valorstring. ToLower () Lower ( @stringValue )

Valorstring. ToUpper () Upper ( @stringValue )

Valorstring. Trim () Trim ( @stringValue )

Valorstring. Trim (trimChar) Trim ( @stringValue , @trimChar )

Valorstring. TrimEnd () RTRIM ( @stringValue )

Valorstring. TrimEnd (trimChar) RTRIM ( @stringValue , @trimChar )

Valorstring. TrimStart () ltrim ( @stringValue )

Valorstring. TrimStart (trimChar) ltrim ( @stringValue , @trimChar )


NOTE
Algunos SQL se han simplificado con fines ilustrativos. El SQL real es más complejo para controlar un rango más amplio
de valores.

Funciones misceláneas
. N ET SQ L A GREGA DO EN

Collection. Contains (Item) @item DE @collection EF Core 3.0

enumValue. HasFlag (marca) @enumValue & @flag = @flag

acepta valores NULL. Coalesce ( @nullable , 0)


GetValueOrDefault ()

acepta valores NULL. Coalesce ( @nullable , @defaultValue )


GetValueOrDefault (defaultValue)

Consulte también
Asignaciones de funciones espaciales
Datos espaciales en el proveedor de EF Core de
SQLite
07/04/2021 • 5 minutes to read

Esta página incluye información adicional sobre el uso de datos espaciales con el proveedor de base de datos de
SQLite. Para obtener información general sobre el uso de datos espaciales en EF Core, consulte la
documentación principal de los datos espaciales .

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 Debian y Ubuntu; y homebrew en MacOS.

# Debian/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. Puede solucionar este fin mediante el uso de la biblioteca
de SQLite del sistema en su lugar.

<ItemGroup>
<!-- Use bundle_sqlite3 instead with SpatiaLite on macOS and Linux -->
<!--<PackageReference Include="[Link]" Version="3.1.0" />-->
<PackageReference Include="[Link]" Version="3.1.0" />
<PackageReference Include="SQLitePCLRaw.bundle_sqlite3" Version="2.0.4" />

<PackageReference Include="[Link]" Version="3.1.0" />


</ItemGroup>

En MacOS , también necesitará establecer una variable de entorno antes de ejecutar la aplicación para que use
la versión de homebrew de SQLite. En Visual Studio para Mac, puede establecer esta opción en project >
Project options > Run > configurations > default

DYLD_LIBRARY_PATH=/usr/local/opt/sqlite/lib

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 HasSrid.

[Link]<City>().Property(c => [Link])


.HasSrid(4326);
NOTE
4326 hace referencia a WGS 84, un estándar que se usa en GPS y en otros sistemas geográficos.

Dimensión
La dimensión predeterminada (o ordenadas) de una columna es X e y. Para habilitar otras coordenadas como Z
o M, configure el tipo de columna.

[Link]<City>().Property(c => [Link])


.HasColumnType("POINTZ");

Asignaciones de funciones espaciales


En esta tabla se muestran los miembros de NetTopologySuite (NTS) que se traducen en las funciones de SQL.

. N ET SQ L

Geometry. Áreas Área ( @geometry )

Geometry. AsBinary () AsBinary ( @geometry )

Geometry. Astext () Astext ( @geometry )

Geometry. Exceso Límite ( @geometry )

Geometry. Búfer (distancia) Búfer ( @geometry , @distance )

Geometry. Búfer (distancia, quadrantSegments) Búfer ( @geometry , @distance , @quadrantSegments )

Geometry. Centroide Centroide ( @geometry )

Geometry. Contains (g) Contains ( @geometry , @g )

Geometry. ConvexHull() ConvexHull ( @geometry )

Geometry. CoveredBy (g) CoveredBy ( @geometry , @g )

Geometry. Portadas (g) Portadas ( @geometry , @g )

Geometry. Cruza (g) Cruza ( @geometry , @g )

Geometry. Diferencia (otros) Diferencia ( @geometry , @other )

Geometry. Galería Dimension ( @geometry )

Geometry. Disunion (g) Disjuntos ( @geometry , @g )

Geometry. Distancia (g) Distancia ( @geometry , @g )

Geometry. Tales Sobre ( @geometry )


. N ET SQ L

Geometry. EqualsTopologically (g) Es igual a ( @geometry , @g )

Geometry. GeometryType GeometryType ( @geometry )

Geometry. GetGeometryN (n) Geometryn ( @geometry , @n + 1)

Geometry. InteriorPoint PointOnSurface ( @geometry )

Geometry. Intersección (otro) Intersección ( @geometry , @other )

Geometry. Interseccións (g) Interseccións ( @geometry , @g )

Geometry. Vacío IsEmpty ( @geometry )

Geometry. IsSimple IsSimple ( @geometry )

Geometry. IsValid IsValid ( @geometry )

Geometry. IsWithinDistance (Geom, Distance) Distance ( @geometry , @geom ) <= @distance

Geometry. Longitud GLength ( @geometry )

Geometry. NumGeometries NumGeometries ( @geometry )

Geometry. NumPoints NumPoints ( @geometry )

Geometry. OgcGeometryType CASE GeometryType ( @geometry ) cuando ' Point ' then 1...
EXTREMO

Geometry. Superposiciones (g) Solapamientos ( @geometry , @g )

Geometry. PointOnSurface PointOnSurface ( @geometry )

Geometry. Relacionar (g, intersectionPattern) Relacionar ( @geometry , @g , @intersectionPattern )

Geometry. REVERSE () ST_Reverse ( @geometry )

Geometry. SRID SRID ( @geometry )

Geometry. SymmetricDifference (otro) SymDifference ( @geometry , @other )

Geometry. ToBinary() AsBinary ( @geometry )

Geometry. ToText() Astext ( @geometry )

Geometry. Toques (g) Toques ( @geometry , @g )

Geometry. Unión () UnaryUnion ( @geometry )

Geometry. Unión (otro) GUnion ( @geometry , @other )


. N ET SQ L

Geometry. Dentro de (g) Dentro de ( @geometry , @g )

geometryCollection [i] Geometryn ( @geometryCollection , @i + 1)

geometryCollection. Count NumGeometries ( @geometryCollection )

lineString. Count NumPoints ( @lineString )

lineString. EndPoint Punto de conexión ( @lineString )

lineString. GetPointN (n) PointN ( @lineString , @n + 1)

lineString. IsClosed IsClosed ( @lineString )

lineString. IsRing IsRing ( @lineString )

lineString. StartPoint StartPoint ( @lineString )

multiLineString. IsClosed IsClosed ( @multiLineString )

Elija. F M ( @point )

Elija. X1 X ( @point )

Elija. Sí Y ( @point )

Elija. Z Z ( @point )

Polígono. ExteriorRing ExteriorRing ( @polygon )

Polígono. GetInteriorRingN (n) InteriorRingN ( @polygon , @n + 1)

Polígono. NumInteriorRings NumInteriorRing ( @polygon )

Recursos adicionales
Página principal de SpatiaLite
Documentos de NetTopologySuite
Proveedor de Azure Cosmos DB para EF Core
12/03/2021 • 10 minutes to read

NOTE
Este proveedor se incluyó por primera vez en EF Core 3.0.

Este proveedor de base de datos permite usar Entity Framework Core con Azure Cosmos DB. Este proveedor se
mantiene como parte del proyecto Entity Framework Core.
Se recomienda encarecidamente que se familiarice con la documentación sobre Azure Cosmos DB antes de leer
esta sección.

NOTE
Este proveedor solo funciona con SQL API de Azure Cosmos DB.

Instalar
Instale el paquete NuGet [Link].
CLI de .NET Core
Visual Studio

dotnet add package [Link]

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

Al igual que para otros proveedores, el primer paso es llamar a UseCosmos:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)


=> [Link](
"[Link]
"C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
databaseName: "OrdersDB");

WARNING
El punto de conexión y la clave se codifican aquí de forma rígida por motivos de simplicidad pero, en una aplicación de
producción, se deben almacenar de manera segura.

En este ejemplo, Order es una entidad sencilla con una referencia al tipo owned StreetAddress .
public class Order
{
public int Id { get; set; }
public int? TrackingNumber { get; set; }
public string PartitionKey { get; set; }
public StreetAddress ShippingAddress { get; set; }
}

public class StreetAddress


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

La acción de guardar y consultar datos sigue el patrón de EF normal:

using (var context = new OrderContext())


{
await [Link]();
await [Link]();

[Link](
new Order
{
Id = 1, ShippingAddress = new StreetAddress { City = "London", Street = "221 B Baker St" },
PartitionKey = "1"
});

await [Link]();
}

using (var context = new OrderContext())


{
var order = await [Link]();
[Link]($"First order will ship to: {[Link]},
{[Link]}");
[Link]();
}

IMPORTANT
Llamar a EnsureCreatedAsync es necesario para crear los contenedores necesarios e insertar los datos de inicialización si
están presentes en el modelo. Sin embargo, se debe llamar a EnsureCreatedAsync solo durante la implementación y no
durante la operación normal, porque podría provocar problemas de rendimiento.

Opciones de Cosmos
El proveedor de Cosmos DB también se puede configurar con una sola cadena de conexión y especificar otras
opciones para personalizar la conexión:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> [Link](

"AccountEndpoint=[Link]
b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
databaseName: "OptionsDB",
options =>
{
[Link]([Link]);
[Link](new WebProxy());
[Link]();
[Link]([Link]);
[Link](32);
[Link](8);
[Link](16);
[Link]([Link](1));
[Link]([Link](1));
[Link]([Link](1));
});

NOTE
La mayoría de estas opciones se incluyeron por primera vez en EF Core 5.0.

TIP
Consulte la documentación de las opciones de Azure Cosmos DB para ver una descripción detallada del efecto que tiene
cada opción arriba mencionada.

Personalización del modelo específico de Cosmos


De manera predeterminada, todos los tipos de entidad están asignados al mismo contenedor, con un nombre
que depende del contexto derivado ( "OrderContext" en este caso). Para cambiar el nombre de contenedor
predeterminado, use HasDefaultContainer:

[Link]("Store");

Para asignar un tipo de entidad a un contenedor distinto, use ToContainer:

[Link]<Order>()
.ToContainer("Orders");

Para identificar el tipo de entidad que un elemento determinado representa, EF Core agrega un valor de
discriminador incluso si no hay tipos de entidad derivados. El nombre y el valor del discriminador se pueden
modificar.
Si ningún otro tipo de entidad se va a almacenar en el mismo contenedor, se puede quitar el discriminador
mediante la llamada a HasNoDiscriminator:

[Link]<Order>()
.HasNoDiscriminator();

Claves de partición
De forma predeterminada, EF Core creará contenedores con la clave de partición establecida en
"__partitionKey" sin proporcionarle ningún valor al insertar elementos. Sin embargo, para sacar el máximo
partido a las funcionalidades de rendimiento de Azure Cosmos, se debe usar una clave de partición
seleccionada cuidadosamente. Se puede configurar mediante la llamada a HasPartitionKey:

[Link]<Order>()
.HasPartitionKey(o => [Link]);

NOTE
La propiedad de clave de partición puede ser de cualquier tipo, siempre y cuando se convierta en una cadena.

Una vez configurada, la propiedad de clave de partición siempre debe tener un valor distinto de NULL. Una
consulta se puede convertir en partición única agregando una llamada WithPartitionKey.

using (var context = new OrderContext())


{
[Link](
new Order
{
Id = 2, ShippingAddress = new StreetAddress { City = "New York", Street = "11 Wall Street" },
PartitionKey = "2"
});

await [Link]();
}

using (var context = new OrderContext())


{
var order = await [Link]("2").LastAsync();
[Link]("Last order will ship to: ");
[Link]($"{[Link]}, {[Link]}");
[Link]();
}

NOTE
WithPartitionKey se incluyó por primera vez en EF Core 5.0.

Con carácter general, se recomienda agregar la clave de partición a la clave principal, ya que es lo que mejor
refleja la semántica de servidor y permite algunas optimizaciones, por ejemplo, en FindAsync .

Entidades insertadas
En Cosmos, las entidades en propiedad se insertan en el mismo elemento que el propietario. Para cambiar el
nombre de una propiedad, use ToJsonProperty:

[Link]<Order>().OwnsOne(
o => [Link],
sa =>
{
[Link]("Address");
[Link](p => [Link]).ToJsonProperty("ShipsToStreet");
[Link](p => [Link]).ToJsonProperty("ShipsToCity");
});

Con esta configuración, el pedido del ejemplo anterior se almacena de esta manera:
{
"Id": 1,
"PartitionKey": "1",
"TrackingNumber": null,
"id": "1",
"Address": {
"ShipsToCity": "London",
"ShipsToStreet": "221 B Baker St"
},
"_rid": "6QEKAM+BOOABAAAAAAAAAA==",
"_self": "dbs/6QEKAA==/colls/6QEKAM+BOOA=/docs/6QEKAM+BOOABAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-683c-692e763901d5\"",
"_attachments": "attachments/",
"_ts": 1568163674
}

Las colecciones de entidades en propiedad también se insertan. En el ejemplo siguiente, usaremos la clase
Distributor con una colección de StreetAddress :

public class Distributor


{
public int Id { get; set; }
public string ETag { get; set; }
public ICollection<StreetAddress> ShippingCenters { get; set; }
}

No es necesario que las entidades en propiedad proporcionen valores de clave explícitos que se deban
almacenar:

var distributor = new Distributor


{
Id = 1,
ShippingCenters = new HashSet<StreetAddress>
{
new StreetAddress { City = "Phoenix", Street = "500 S 48th Street" },
new StreetAddress { City = "Anaheim", Street = "5650 Dolly Ave" }
}
};

using (var context = new OrderContext())


{
[Link](distributor);

await [Link]();
}

Se conservarán de esta manera:


{
"Id": 1,
"Discriminator": "Distributor",
"id": "Distributor|1",
"ShippingCenters": [
{
"City": "Phoenix",
"Street": "500 S 48th Street"
},
{
"City": "Anaheim",
"Street": "5650 Dolly Ave"
}
],
"_rid": "6QEKANzISj0BAAAAAAAAAA==",
"_self": "dbs/6QEKAA==/colls/6QEKANzISj0=/docs/6QEKANzISj0BAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-683c-7b2b439701d5\"",
"_attachments": "attachments/",
"_ts": 1568163705
}

De manera interna, EF Core siempre debe tener valores de clave únicos para todas las entidades sometidas a
seguimiento. La clave principal creada de manera predeterminada para las colecciones de tipos en propiedad
consta de las propiedades de clave externa que apuntan al propietario y una propiedad int correspondiente al
índice de la matriz JSON. Para recuperar estos valores, se podría usar la API de entrada:

using (var context = new OrderContext())


{
var firstDistributor = await [Link]();
[Link]($"Number of shipping centers: {[Link]}");

var addressEntry = [Link]([Link]());


var addressPKProperties = [Link]().Properties;

[Link](
$"First shipping center PK: ({[Link](addressPKProperties[0].Name).CurrentValue},
{[Link](addressPKProperties[1].Name).CurrentValue})");
[Link]();
}

TIP
Cuando sea necesario, se puede cambiar la clave principal predeterminada para los tipos de entidad en propiedad, pero
los valores de clave se deben proporcionar de manera explícita.

Trabajo con entidades desconectadas


Cada elemento debe tener un valor id único para la clave de partición específica. De manera predeterminada,
EF Core genera el valor mediante la concatenación de los valores de discriminador y de clave principal, con "|"
como delimitador. Los valores de clave solo se generan cuando una entidad entra en el estado Added . Esto
podría suponer un problema al adjuntar entidades si no tienen una propiedad id en el tipo .NET para
almacenar el valor.
Para evitar esta limitación, se podría crear y establecer el valor id de manera manual o marcar la entidad como
agregada y, luego, cambiarla al estado deseado:
using (var context = new OrderContext())
{
var distributorEntry = [Link](distributor);
[Link] = [Link];

[Link]([Link]());

await [Link]();
}

using (var context = new OrderContext())


{
var firstDistributor = await [Link]();
[Link]($"Number of shipping centers is now: {[Link]}");

var distributorEntry = [Link](firstDistributor);


var idProperty = [Link]<string>("__id");
[Link]($"The distributor 'id' is: {[Link]}");
}

Este es el JSON resultante:

{
"Id": 1,
"Discriminator": "Distributor",
"id": "Distributor|1",
"ShippingCenters": [
{
"City": "Phoenix",
"Street": "500 S 48th Street"
}
],
"_rid": "JBwtAN8oNYEBAAAAAAAAAA==",
"_self": "dbs/JBwtAA==/colls/JBwtAN8oNYE=/docs/JBwtAN8oNYEBAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-9377-d7a1ae7c01d5\"",
"_attachments": "attachments/",
"_ts": 1572917100
}

Simultaneidad optimista con mecanismos ETag


NOTE
La compatibilidad con la simultaneidad de ETag se incluyó por primera vez en EF Core 5.0.

Si necesita configurar un tipo de entidad para que use la simultaneidad optimista, llame a UseETagConcurrency.
Esta llamada creará una propiedad _etag en estado de propiedad reemplazada y la establecerá como el token
de simultaneidad.

[Link]<Order>()
.UseETagConcurrency();

Para facilitar la resolución de errores de simultaneidad, puede asignar el ETag a una propiedad de CLR mediante
IsETagConcurrency.
[Link]<Distributor>()
.Property(d => [Link])
.IsETagConcurrency();
Trabajar con datos no estructurados en EF Core
proveedor de Azure Cosmos DB
07/04/2021 • 3 minutes to read

EF Core se diseñó para facilitar el trabajo con datos que siguen un esquema definido en el modelo. Sin embargo,
uno de los puntos fuertes de Azure Cosmos DB es la flexibilidad en la forma de los datos almacenados.

Acceso a JSON sin formato


Es posible obtener acceso a las propiedades de las que no realiza un seguimiento EF Core a través de una
propiedad especial en el Estado de sombra denominado "__jObject" que contiene un JObject que representa
los datos recibidos del almacén y los datos que se van a almacenar:

using (var context = new OrderContext())


{
await [Link]();
await [Link]();

var order = new Order


{
Id = 1, ShippingAddress = new StreetAddress { City = "London", Street = "221 B Baker St" },
PartitionKey = "1"
};

[Link](order);

await [Link]();
}

using (var context = new OrderContext())


{
var order = await [Link]();
var orderEntry = [Link](order);

var jsonProperty = [Link]<JObject>("__jObject");


[Link]["BillingAddress"] = "Clarence House";

[Link] = [Link];

await [Link]();
}

using (var context = new OrderContext())


{
var order = await [Link]();
var orderEntry = [Link](order);
var jsonProperty = [Link]<JObject>("__jObject");

[Link]($"First order will be billed to: {[Link]["BillingAddress"]}");


}
{
"Id": 1,
"PartitionKey": "1",
"TrackingNumber": null,
"id": "1",
"Address": {
"ShipsToCity": "London",
"ShipsToStreet": "221 B Baker St"
},
"_rid": "eLMaAK8TzkIBAAAAAAAAAA==",
"_self": "dbs/eLMaAA==/colls/eLMaAK8TzkI=/docs/eLMaAK8TzkIBAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-683e-0a12bf8d01d5\"",
"_attachments": "attachments/",
"BillingAddress": "Clarence House",
"_ts": 1568164374
}

WARNING
La "__jObject" propiedad forma parte de la infraestructura de EF Core y solo se debe utilizar como último recurso, ya
que es probable que tenga un comportamiento diferente en futuras versiones.

NOTE
Los cambios en la entidad invalidarán los valores almacenados en "__jObject" durante SaveChanges .

Usar CosmosClient
Para desacoplar completamente de EF Core obtenga el objeto CosmosClient que forma parte del SDK de Azure
Cosmos dB de DbContext :

using (var context = new OrderContext())


{
var cosmosClient = [Link]();
var database = [Link]("OrdersDB");
var container = [Link]("Orders");

var resultSet = [Link]<JObject>(new QueryDefinition("select * from o"));


var order = (await [Link]()).First();

[Link]($"First order JSON: {order}");

[Link]("TrackingNumber");

await [Link](order, order["id"].ToString());


}

Faltan valores de propiedad


En el ejemplo anterior, se ha quitado la "TrackingNumber" propiedad del pedido. Debido a cómo funciona la
indexación en Cosmos DB, las consultas que hacen referencia a la propiedad que falta en otro lugar y en la
proyección podrían devolver resultados inesperados. Por ejemplo:
using (var context = new OrderContext())
{
var orders = await [Link]();
var sortedOrders = await [Link](o => [Link]).ToListAsync();

[Link]($"Number of orders: {[Link]}");


[Link]($"Number of sorted orders: {[Link]}");
}

La consulta ordenada realmente no devuelve ningún resultado. Esto significa que debe tener cuidado de rellenar
siempre las propiedades asignadas por EF Core al trabajar directamente con el almacén.

NOTE
Este comportamiento puede cambiar en versiones futuras de Cosmos. Por ejemplo, actualmente, si la Directiva de
indexación define el índice compuesto {ID/? ASC, TrackingNumber/? ASC)}, una consulta que tiene ' ORDER BY [Link] ASC, c.
Discriminator ASC ' devolvería elementos a los que les falta la "TrackingNumber" propiedad.
Limitaciones del proveedor de Azure Cosmos DB
de EF Core
12/03/2021 • 2 minutes to read

El proveedor de Cosmos tiene una serie de limitaciones. Muchas de estas limitaciones son consecuencia de las
limitaciones del motor de base de datos de Cosmos subyacente y no son específicas de EF. Pero la mayoría no se
ha implementado todavía.
Estas son algunas de las características solicitadas comúnmente:
Include ser
Join ser
Colecciones de tipos primitivos compatibles

Limitaciones del SDK de Azure Cosmos DB


Solo se proporcionan métodos asincrónicos

WARNING
Dado que no hay ninguna versión de sincronización de los métodos de nivel bajo EF Core se basa en, la funcionalidad
correspondiente se implementa actualmente mediante una llamada a .Wait() en el devuelto Task . Esto significa que
el uso de métodos como SaveChanges o ToList en lugar de sus homólogos asincrónicos podría provocar un
interbloqueo en la aplicación

Limitaciones de Azure Cosmos DB


Puede ver la información general completa de Azure Cosmos dB características admitidas, estas son las
diferencias más importantes en comparación con una base de datos relacional:
No se admiten las transacciones iniciadas por el cliente
Algunas consultas entre particiones son más lentas en función de los operadores implicados (por ejemplo,
Skip/Take o OFFSET LIMIT )
Asignaciones de función del proveedor de EF Core
de Azure Cosmos DB
12/03/2021 • 2 minutes to read

En esta página se muestran los miembros de .NET que se traducen en SQL Functions cuando se usa el
proveedor de Azure Cosmos DB.

. N ET SQ L A GREGA DO EN

Collection. Contains (Item) @item DE @collection

EF. Functions. RANDOM () RAND () EF Core 6.0

Valorstring. Contains (valor) Contains ( @stringValue , @value ) EF Core 5.0

Valorstring. EndsWith (valor) ENDSWITH ( @stringValue , @value ) EF Core 5.0

Valorstring. FirstOrDefault () IZQUIERDA ( @stringValue , 1) EF Core 5.0

Valorstring. LastOrDefault () RIGHT ( @stringValue , 1) EF Core 5.0

Valorstring. StartsWith (valor) STARTSWITH ( @stringValue , @value ) EF Core 5.0


Proveedor de base de datos InMemory para EF
Core
12/03/2021 • 2 minutes to read

Este proveedor de base de datos permite usar Entity Framework Core con una base de datos en memoria. La
base de datos en memoria puede resultar útil para realizar pruebas, aunque es posible que el proveedor SQLite
del modo en memoria sea un reemplazo de pruebas más adecuado para bases de datos relacionales. La base de
datos en memoria está diseñada únicamente para realizar pruebas. Este proveedor se mantiene como parte del
proyecto Entity Framework Core.

Instalar
Instale el paquete NuGet [Link].
CLI de .NET Core
Visual Studio

dotnet add package [Link]

Introducción
Los siguientes recursos le ayudarán a empezar a trabajar con este proveedor.
Pruebas con InMemory
Pruebas de la aplicación de ejemplo UnicornStore

Motores de base de datos compatibles


Base de datos de memoria en proceso, diseñada únicamente para realizar pruebas.
Escritura de un proveedor de base de datos
12/03/2021 • 3 minutes to read

Para obtener información sobre cómo escribir un proveedor de bases de datos de Entity Framework Core,
consulte para que pueda escribir un proveedor de EF Core por Arthur Vickers.

NOTE
Estas publicaciones no se han actualizado desde EF Core 1,1 y ha habido cambios significativos desde ese momento. El
problema 681 está realizando el seguimiento de las actualizaciones de esta documentación.

La EF Core código base es de código abierto y contiene varios proveedores de bases de datos que se pueden
utilizar como referencia. Puede encontrar el código fuente en [Link] . También puede
ser útil examinar el código para proveedores de terceros de uso frecuente, como Npgsql, Pomelo MySQLy SQL
Server Compact. En concreto, estos proyectos se configuran para extender desde y ejecutar pruebas funcionales
que publicamos en NuGet. Se recomienda encarecidamente este tipo de instalación.

Mantenerse al día con los cambios del proveedor


A partir del trabajo después de la versión 2,1, hemos creado un registro de cambios que pueden necesitar
cambios correspondientes en el código del proveedor. Esto está pensado para ayudarle a actualizar un
proveedor existente para que funcione con una nueva versión de EF Core.
Antes de 2,1, usamos las providers-beware etiquetas y providers-fyi en los problemas de Github y las
solicitudes de incorporación de cambios para un propósito similar. Vamos a usar estas etiquetas en los
problemas para proporcionar una indicación de qué elementos de trabajo de una versión determinada también
pueden requerir que el trabajo se realice en los proveedores. Una providers-beware etiqueta normalmente
significa que la implementación de un elemento de trabajo puede interrumpir los proveedores, mientras
providers-fyi que una etiqueta normalmente significa que los proveedores no se interrumpirán, pero puede
que sea necesario cambiar el código, por ejemplo, para habilitar la nueva funcionalidad.

Nombres sugeridos de proveedores de terceros


Se recomienda usar la siguiente nomenclatura para los paquetes NuGet. Esto es coherente con los nombres de
los paquetes proporcionados por el equipo de EF Core.
<Optional project/company name>.EntityFrameworkCore.<Database engine name>

Por ejemplo:
[Link]
[Link]
EntityFrameworkCore.SqlServerCompact40
Cambios que afectan al proveedor
12/03/2021 • 10 minutes to read

Esta página contiene vínculos a las solicitudes de incorporación de cambios realizadas en el repositorio de EF
Core que puede requerir que reaccionen los autores de otros proveedores de bases de datos. La intención es
proporcionar un punto de partida para los autores de proveedores de bases de datos de terceros existentes al
actualizar su proveedor a una nueva versión.
Estamos iniciando este registro con los cambios de 2,1 a 2,2. Antes de 2,1 usamos las providers-beware
etiquetas y providers-fyi en nuestros problemas y solicitudes de incorporación de cambios.

2,2---> 3. x
Tenga en cuenta que muchos de los cambios importantes en el nivel de la aplicación también afectarán a los
proveedores.
[Link]
Se han quitado las API obsoletas y las sobrecargas de parámetros opcionales contraídas
Se quitó DatabaseColumn. GetUnderlyingStoreType ()
[Link]
Se han quitado las API obsoletas
[Link]
Es posible que las subclases de CharTypeMapping se hayan interrumpido debido a cambios de
comportamiento necesarios para corregir un par de errores en la implementación base.
[Link]
Se ha agregado una clase base para IDatabaseModelFactory y se ha actualizado para usar un objeto
de parámetro para mitigar los saltos futuros.
[Link]
Usó objetos de parámetro en MigrationsSqlGenerator para mitigar los saltos futuros.
[Link]
La configuración explícita de los niveles de registro requiere algunos cambios en las API que los
proveedores pueden estar usando. En concreto, si los proveedores usan la infraestructura de registro
directamente, este cambio puede interrumpir ese uso. Además, los proveedores que usan la
infraestructura (que será pública) al avanzar deberán derivar de LoggingDefinitions o
RelationalLoggingDefinitions . Para obtener ejemplos, vea los SQL Server y los proveedores en
memoria.
[Link]
Las cadenas de recursos Core, relacional y abstracciones son ahora públicas.
CoreLoggerExtensions y RelationalLoggerExtensions son ahora públicos. Los proveedores deben usar
estas API al registrar eventos definidos en el nivel básico o relacional. No tener acceso a los recursos
de registro directamente; todavía son internas.
IRawSqlCommandBuilder ha cambiado de un servicio singleton a un servicio con ámbito
IMigrationsSqlGenerator ha cambiado de un servicio singleton a un servicio con ámbito
[Link]
La infraestructura para la creación de comandos relacionales se ha convertido en pública, de modo
que los proveedores pueden utilizarla de forma segura y refactorizar ligeramente.
[Link]
ILazyLoader ha cambiado de un servicio con ámbito a un servicio transitorio
[Link]
IUpdateSqlGenerator ha cambiado de un servicio con ámbito a un servicio singleton
Además, se ISingletonUpdateSqlGenerator ha quitado
[Link]
Una gran cantidad de código interno que usaban los proveedores ahora se ha hecho público
Ya no debe necssary a la referencia porque se ha IndentedStringBuilder factorizado fuera de los
lugares que lo exponían
Los usos de NonCapturingLazyInitializer deben reemplazarse por LazyInitializer de la BCL
[Link]
Este cambio está totalmente incluido en el documento sobre cambios importantes de la aplicación. En
el caso de los proveedores, esto puede ser más impactante, ya que la prueba de EF Core a menudo
puede dar lugar a este problema, por lo que la infraestructura de prueba ha cambiado para que sea
menos probable.
[Link]
EntityMaterializerSource se ha simplificado
[Link]
La traducción StartsWith ha cambiado de manera que los proveedores pueden querer o deben
reaccionar
[Link]
Los servicios del conjunto de convenciones han cambiado. Los proveedores ahora deben heredar de
"ProviderConventionSet" o "RelationalConventionSet".
Las personalizaciones se pueden agregar a través de los IConventionSetCustomizer servicios, pero está
pensada para que las usen otras extensiones, no los proveedores.
Las convenciones usadas en tiempo de ejecución se deben resolver desde IConventionSetBuilder .
[Link]
La propagación de datos se ha refactorizado en una API pública para evitar la necesidad de usar tipos
internos. Esto solo debe afectar a los proveedores no relacionales, puesto que la propagación se
controla mediante la clase relacional base para todos los proveedores relacionales.

2,1---> 2,2
Cambios de solo prueba
[Link] -Permitir delimitadores SQL personalizable en las pruebas
Comprobar los cambios que permiten comparaciones de punto flotante no estrictas en
BuiltInDataTypesTestBase
Probar los cambios que permiten que las pruebas de consulta se vuelvan a usar con diferentes
delimitadores de SQL
[Link] -Agregar pruebas de DbFunction a las pruebas de
especificación relacional
De este modo, estas pruebas se pueden ejecutar en todos los proveedores de bases de datos
[Link] -Limpieza de prueba asincrónica
Quitar Wait llamadas, Async innecesario y cambiar el nombre de algunos métodos de prueba
[Link] -Unificar la infraestructura de prueba de registro
Se ha agregado CreateListLoggerFactory y quitado una infraestructura de registro anterior, que
requerirá que los proveedores que usan estas pruebas reaccionen
[Link] -Ejecutar más pruebas de consulta de forma sincrónica y
asincrónica
Los nombres de prueba y la factorización han cambiado, lo que requerirá que los proveedores usen
estas pruebas para reaccionar
[Link] -Cambiar el nombre de las navegaciones en el modelo
ComplexNavigations
Es posible que los proveedores que usan estas pruebas tengan que reaccionar
[Link] -Devolver el contexto al grupo en lugar de desecharlo en las
pruebas funcionales
Este cambio incluye alguna refactorización de prueba que puede requerir que los proveedores
reaccionen
Cambios de código de prueba y de producto
[Link] -Consolide los métodos RelationalTypeMapping. Clone
Se permiten cambios en 2,1 en RelationalTypeMapping para una simplificación en las clases derivadas.
No creemos que esto se interrumpiera en los proveedores, pero los proveedores pueden aprovechar
este cambio en sus clases derivadas de asignación de tipos.
[Link] -Consultas con nombre o etiquetadas
Agrega la infraestructura para etiquetar las consultas LINQ y hacer que esas etiquetas se muestren
como comentarios en SQL. Esto puede requerir que los proveedores reaccionen en la generación de
SQL.
[Link] -Admitir datos espaciales a través de NTS
Permite que las asignaciones de tipos y los traductores de miembros se registren fuera del proveedor.
Los proveedores deben llamar a base. FindMapping () en su implementación de
ITypeMappingSource para que funcione
Siga este patrón para agregar compatibilidad espacial a su proveedor que sea coherente entre los
proveedores.
[Link] -Agregar depuración mejorada para la creación del proveedor
de servicios
Permite a DbContextOptionsExtensions implementar una nueva interfaz que puede ayudar a los
usuarios a entender por qué se vuelve a crear el proveedor de servicios internos.
[Link] -Agrega CanConnect API para su uso por las comprobaciones
de estado
Este PR agrega el concepto de CanConnect que va a usar [Link] Core comprobaciones de estado para
determinar si la base de datos está disponible. De forma predeterminada, la implementación
relacional solo llama a Exist , pero los proveedores pueden implementar algo diferente si es
necesario. Los proveedores no relacionales deberán implementar la nueva API para poder usar la
comprobación de estado.
[Link] -Actualizar RelationalTypeMapping base para no establecer el
tamaño de DbParameter
Deje de establecer el tamaño de forma predeterminada, ya que puede provocar el truncamiento. Los
proveedores pueden necesitar agregar su propia lógica si es necesario establecer el tamaño.
[Link] -RevEng: especificar siempre el tipo de columna para las
columnas decimales
Configure siempre el tipo de columna para las columnas decimales en código con scaffolding en lugar
de configurar por Convención.
Los proveedores no deben requerir cambios en su extremo.
[Link] -Agrega CaseExpression para generar expresiones CASE de
SQL
[Link] -Agrega la capacidad de especificar asignaciones de tipos en
SqlFunctionExpression para mejorar la inferencia de tipos de almacén de argumentos y resultados.
Herramientas y extensiones de EF Core
07/04/2021 • 16 minutes to read • Edit Online

Estas herramientas y extensiones proporcionan más funcionalidades para Entity Framework Core 2.1 y
versiones posteriores.

IMPORTANT
Las extensiones están compiladas a partir de una gran variedad de orígenes, por lo que su mantenimiento no está
incluido en el proyecto Entity Framework Core. En lo que respecta a las extensiones de terceros y para asegurarse de que
cumplan sus requisitos, no se olvide de evaluar la calidad, las licencias, la compatibilidad, el soporte técnico, etc. En
concreto, es posible que sea necesario actualizar una extensión compilada para una versión anterior de EF Core para que
funcione con las versiones más recientes.

Herramientas
LLBLGen Pro
LLBLGen Pro es una solución de modelado de entidad compatible con Entity Framework y Entity Framework
Core. Permite definir fácilmente el modelo de entidad y asignarlo a la base de datos mediante Database First o
Model First, de modo que pueda empezar a escribir consultas de inmediato. Para EF Core: 2, 3.
Sitio web
Devart Entity Developer
Entity Developer es un potente diseñador O/RM para [Link] Entity Framework, NHibernate, LinqConnect,
Telerik Data Access y LINQ to SQL. Permite diseñar modelos EF Core de forma visual mediante los enfoques
Database First y Model First, así como generar código C# y de Visual Basic. Para EF Core: 2, 3, 5.
Sitio web
nHydrate ORM para Entity Framework
O/RM que crea clases extensibles y fuertemente tipadas para Entity Framework. El código generado es
Entity Framework Core. No hay ninguna diferencia. Esto no es un reemplazo de EF ni un O/RM personalizado. Es
una capa de modelado visual que permite a un equipo administrar esquemas de base de datos complejos.
Funciona bien con software SCM como GIT, lo que permite el acceso de varios usuarios al modelo con conflictos
mínimos. El instalador realiza un seguimiento de los cambios del modelo y crea scripts de actualización. Para EF
Core: 3.
Repositorio de GitHub
EF Core Power Tools
EF Core Power Tools es una extensión de Visual Studio que expone varias tareas de tiempo de diseño de EF Core
en una interfaz de usuario sencilla. Incluye técnicas de ingeniería inversa de DbContext y clases de entidades a
partir de bases de datos existentes y DACPAC de SQL Server, así como la administración de la migración de
bases de datos y la visualización de modelos. Para EF Core: 3.5.
Wiki de GitHub
Editor visual de Entity Framework
El Editor de objetos visuales de Entity Framework es una extensión de Visual Studio que agrega un diseñador
O/RM que permite generar objetos visuales de las clases EF 6 y EF Core. El código se genera mediante plantillas
T4, por lo que se puede personalizar. Asimismo, admite enumeraciones y asociaciones de herencia,
unidireccionales y bidireccionales, y permite asignar colores a las clases y agregar bloques de texto para explicar
ciertas partes del diseño que puedan resultar difíciles de comprender. Para EF Core: 2, 3, 5.
Marketplace
CatFactory
CatFactory es un motor de scaffolding para .NET Core que permite automatizar la generación de clases
DbContext, entidades, opciones de configuración de la asignación y clases de repositorios de una base de datos
SQL Server. Para EF Core: 2.
Repositorio de GitHub
LoreSoft's Entity Framework Core Generator
Entity Framework Core Generator (efg) es una herramienta de la CLI de .NET Core que permite generar modelos
EF Core a partir de una base de datos existente. Es similar a dotnet ef dbcontext scaffold , pero también admite
la regeneración de código seguro mediante el reemplazo de la región o el análisis de los archivos de asignación.
Asimismo, la herramienta permite generar modelos de vista, efectuar la validación y crear el código de los
asignadores de objetos. Para EF Core: 2.
Tutorial Documentación

Extensiones
[Link]
Biblioteca de extensiones que permite registrar automáticamente los cambios en los datos realizados por EF
Core e incluirlos en una tabla a modo de historial. Para EF Core: 2, 3.
Repositorio de GitHub
EFCoreSecondLevelCacheInterceptor
El almacenamiento en caché de segundo nivel es una caché de consulta. Los resultados de los comandos de EF
se almacenarán en la memoria caché, para que los mismos comandos de EF recuperen los datos de la memoria
caché en lugar de ejecutarlos de nuevo en la base de datos. Para EF Core: 3.5.
Repositorio de GitHub
Geco
Geco (Generator Console) es un generador de código muy sencillo que se basa en un proyecto de consola. Para
generar el código, se ejecuta en .NET Core y utiliza cadenas C# interpoladas. Geco incluye un generador de
modelos de ingeniería inversa para EF Core que admite la pluralización, la singularización y las plantillas
editables. También proporciona un generador de scripts de datos semilla, un ejecutor de scripts y una
herramienta para limpiar las bases de datos. Para EF Core: 2.
Repositorio de GitHub
[Link]
Permite la personalización de clases con ingeniería inversa a partir de una base de datos existente mediante la
cadena de herramientas de Entity Framework Core con las plantillas de Handlebars. Para EF Core: 2, 3, 5.
Repositorio de GitHub
[Link]
NeinLinq amplía las características de proveedores LINQ como Entity Framework para permitir la reutilización
de las funciones, la reescritura de las consultas y la creación de consultas dinámicas mediante selectores y
predicados traducibles. Para EF Core: 2, 3, 5.
Repositorio de GitHub
[Link]
Extensión de [Link] para admitir los repositorios, los patrones de unidades de trabajo
y varias bases de datos compatibles con las transacciones distribuidas. Para EF Core: 2, 3.
Repositorio de GitHub
[Link]
Extensiones de EF Core para operaciones masivas (Insert, Update y Delete). Para EF Core: 2, 3.
Repositorio de GitHub
[Link]
Agrega pluralización en tiempo de diseño. Para EF Core: 2, 3.
Repositorio de GitHub
[Link]
Recuperación del atributo [Index] (con extensión para la creación de modelos). Para EF Core: 2, 3, 5.
Repositorio de GitHub
[Link]
Extiende la comprobación para permitir las pruebas de instantáneas con EntityFramework. Para EF Core: 3.5.
Repositorio de GitHub
LocalDb
Proporciona un contenedor alrededor de SQL Server Express LocalDB para simplificar las pruebas en ejecución
en Entity Framework. Para EF Core: 3.5.
Repositorio de GitHub
EfFluentValidation
Agrega la compatibilidad de FluentValidation a Entity Framework. Para EF Core: 3.5.
Repositorio de GitHub
[Link]
Implementación de compatibilidad temporal. Para EF Core: 2.
Repositorio de GitHub
EfCoreTemporalTable
Realice consultas temporales fácilmente en su base de datos favorita con los métodos de extensión
incorporados: AsTemporalAll() , AsTemporalAsOf(date) , AsTemporalFrom(startDate, endDate) ,
AsTemporalBetween(startDate, endDate) , AsTemporalContained(startDate, endDate) . Para EF Core: 3.

Repositorio de GitHub
[Link]
Biblioteca de extensiones para Entity Framework Core que permite a los desarrolladores que usan SQL Server
utilizar fácilmente tablas temporales. Para EF Core: 2, 3.
Repositorio de GitHub
[Link]
Caché de consulta de segundo nivel y alto rendimiento. Para EF Core: 2.
Repositorio de GitHub
[Link]
NCache de Entity Framework Core es un proveedor de caché de segundo nivel distribuido para almacenar en
caché resultados de consultas. La arquitectura distribuida de NCache hace que sea más escalable y de alta
disponibilidad. Para EF Core 2, 3.
Sitio web
[Link]
Desencadenadores para EF Core. Responda a los cambios en DbContext antes y después de que se confirmen en
la base de datos. Los desencadenadores son totalmente asincrónicos y admiten la inserción de dependencias, la
herencia, las funciones en cascada y mucho más. Para EF Core: 3.5.
Repositorio de GitHub
Entity Framework Plus
Amplía su DbContext con características como: Incluir filtro, Auditoría, Cache, Consulta de futuro, Batch Delete,
Actualización por lotes y más. Para EF Core: 2, 3, 5.
Sitio web Repositorio de GitHub
Extensiones de Entity Framework
Extiende su DbContext con operaciones masivas de alto rendimiento: BulkSaveChanges, BulkInsert, BulkUpdate,
BulkDelete, BulkMerge y más. Para EF Core: 2, 3, 5.
Sitio web
Expressionify
Agregue compatibilidad para llamar a métodos de extensión en expresiones lambda LINQ. Para EF Core: 3.
Repositorio de GitHub
ELinq
Tecnología Language Integrated Query (LINQ) para bases de datos relacionales. Permite usar C# para escribir
consultas fuertemente tipadas. Para EF Core: 3.
Compatibilidad total de C# con la creación de consultas: varias instrucciones dentro de lambda, variables,
funciones, etc.
Sin vacío semántico con SQL. ELinq declara instrucciones SQL (como SELECT , FROM , WHERE ) como métodos
de C# de primera clase, y combina la sintaxis conocida con IntelliSense, seguridad de tipos y refactorización.
Como resultado, SQL se convierte en "otra" biblioteca de clases que expone su API localmente, literalmente
"Language Integrated SQL" .
Sitio web
Ramses
Enlaces de ciclo de vida (para SaveChanges). Para EF Core: 2, 3.
Repositorio de GitHub
[Link]
Todos los nombres de tabla y columna tendrán automáticamente un formato con palabras combinadas unidas
por barras bajas (snake_case), todo en MAYÚSCULAS o bien todo en minúsculas. Para EF Core: 3.
Repositorio de GitHub
[Link]
Agrega compatibilidad nativa a EntityFrameworkCore para SQL Server para los tipos NodaTime. Para EF Core:
3.5.
Repositorio de GitHub
[Link]
Extensiones LINQ en Entity Framework Core 3.1 para admitir la realización de consultas a tablas temporales de
Microsoft SQL Server. Para EF Core: 3.
Repositorio de GitHub
[Link]
Agrega compatibilidad con hierarchyid al proveedor de EF Core de SQL Server. Para EF Core: 3.
Repositorio de GitHub
[Link]
Traductor alternativo de consultas LINQ a expresiones SQL. Para EF Core: 3.5.
Incluye compatibilidad con características SQL avanzadas como CTE, copia masiva, sugerencias de tabla,
funciones de división de particiones, tablas temporales y operaciones de creación, actualización y eliminación en
la base de datos.
Repositorio de GitHub
[Link]
Implementación de entidades de eliminación temporal. Para EF Core: 3.
NuGet
[Link]
Extiende EF Core para resolver cadenas de conexión de [Link]. Para EF Core: 3.
Repositorio de GitHub
Asignador desasociado
Un asignador de entidades DTO con control de composición/agregación (similar a GraphDiff). Para EF Core: 3.5.
NuGet
[Link]
Agrega compatibilidad con los tipos NodaTime cuando se usa SQLite. Para EF Core: 5.
Repositorio de GitHub
[Link]
Habilita la utilización de técnicas de ingeniería inversa en un modelo de EF Core a partir de un paquete de
aplicación de capa de datos de SQL Server (.dacpac). Para EF Core: 3.5.
Wiki de GitHub
[Link]
Genera contenido de DGML (Graph) que visualiza su DbContext. Agrega el método de extensión AsDgml() a la
clase DbContext. Para EF Core: 3.5.
Wiki de GitHub
[Link]
Cuando se usa Entity Framework Core, todas las excepciones de base de datos se ajustan en
DbUpdateException. [Link] controla todos los detalles específicos de la base de datos para
averiguar qué restricción se ha infringido y permite usar excepciones con tipo como UniqueConstraintException ,
CannotInsertNullException , MaxLengthExceededException , NumericOverflowException ,
ReferenceConstraintException cuando la consulta infringe las restricciones de la base de datos.

Admite SQL Server, Postgres, MySql, SQLite y Oracle.


Para EF Core: 3.5.
Repositorio de GitHub
EFCoreAuditing
Una biblioteca para Entity Framework Core que admita la grabación automática del historial de cambios de
datos (registro de auditoría), la eliminación temporal y la funcionalidad de la convención de nomenclatura
snake_case. Para EF Core: 3.
Repositorio de GitHub
[Link]
Agrega compatibilidad en tiempo de diseño de F# a EF Core. Para EF Core: 5.
Repositorio de GitHub
Referencia sobre las herramientas de Entity
Framework Core
12/03/2021 • 2 minutes to read • Edit Online

Las herramientas de Entity Framework Core ayudan con las tareas de desarrollo en tiempo de diseño. Se usan
principalmente para administrar migraciones y para aplicar scaffolding a DbContext y a tipos de entidad
mediante utilización de técnicas de ingeniería inversa en el esquema de una base de datos.
Puede instalar cualquiera de las herramientas siguientes, ya que las dos exponen la misma funcionalidad:
Las herramientas de la Consola del Administrador de paquetes de EF Core se ejecutan en la Consola del
Administrador de paquetes de Visual Studio. Si desarrolla en Visual Studio, se recomienda usar estas
herramientas, ya que proporcionan una experiencia más integrada.
Las herramientas de la interfaz de la línea de comandos (CLI) de EF Core .NET son una extensión de las
herramientas de la CLI de .NET Core multiplataforma. Estas herramientas necesitan un proyecto de SDK
de .NET Core (uno con Sdk="[Link]" o similar en el archivo de proyecto).

Pasos siguientes
Referencia sobre las herramientas de la Consola del Administrador de paquetes de EF Core
Referencia sobre las herramientas de la CLI de EF Core .NET
Referencia de herramientas de Entity Framework
Core: consola del administrador de paquetes en
Visual Studio
12/03/2021 • 18 minutes to read • Edit Online

Las herramientas de la consola del administrador de paquetes (PMC) para Entity Framework Core realizar tareas
de desarrollo en tiempo de diseño. Por ejemplo, se crean migraciones, se aplican migraciones y se genera
código para un modelo basado en una base de datos existente. Los comandos se ejecutan dentro de Visual
Studio mediante la consola del administrador de paquetes. Estas herramientas funcionan con proyectos de .NET
Framework y .NET Core.
Si no usa Visual Studio, se recomienda usar en su lugar las herramientas de línea de comandos de EF Core . Las
herramientas de CLI de .NET Core son multiplataforma y se ejecutan en un símbolo del sistema.

Instalación de las herramientas


Instale las herramientas de la consola del administrador de paquetes ejecutando el siguiente comando en la
consola del administrador de paquetes :

Install-Package [Link]

Actualice las herramientas ejecutando el siguiente comando en la consola del administrador de paquetes .

Update-Package [Link]

Comprobación de la instalación
Ejecute este comando para comprobar que las herramientas están instaladas:

Get-Help about_EntityFrameworkCore

La salida tiene el siguiente aspecto (no indica qué versión de las herramientas está usando):

_/\__
---==/ \\
___ ___ |. \|\
| __|| __| | ) \\\
| _| | _| \_/ | //|\\
|___||_| / \\\/\\

TOPIC
about_EntityFrameworkCore

SHORT DESCRIPTION
Provides information about the Entity Framework Core Package Manager Console Tools.

<A list of available commands follows, omitted here.>


Uso de las herramientas
Antes de usar las herramientas:
Comprenda la diferencia entre el proyecto de destino y el de inicio.
Aprenda a usar las herramientas con .NET Standard bibliotecas de clases.
En el caso de los proyectos de [Link] Core, establezca el entorno.
Proyecto de destino e inicio
Los comandos hacen referencia a un proyecto y un proyecto de inicio.
El proyecto también se conoce como proyecto de destino porque es donde los comandos agregan o
quitan archivos. De forma predeterminada, el proyecto predeterminado seleccionado en la consola
del administrador de paquetes es el proyecto de destino. Puede especificar otro proyecto como
proyecto de destino mediante la --project opción.
El proyecto de inicio es el que las herramientas compilan y ejecutan. Las herramientas tienen que ejecutar
código de aplicación en tiempo de diseño para obtener información sobre el proyecto, como la cadena de
conexión a la base de datos y la configuración del modelo. De forma predeterminada, el proyecto de
inicio en Explorador de soluciones es el proyecto de inicio. Puede especificar otro proyecto como
proyecto de inicio mediante la --startup-project opción.

El proyecto de inicio y el proyecto de destino suelen ser el mismo proyecto. Un escenario típico en el que se
trata de proyectos independientes es cuando:
El contexto de EF Core y las clases de entidad se encuentran en una biblioteca de clases de .NET Core.
Una aplicación de consola de .NET Core o una aplicación web hace referencia a la biblioteca de clases.
También es posible colocar el código de las migraciones en una biblioteca de clases independiente del contexto
de EF Core.
Otras plataformas de destino
Las herramientas de la consola del administrador de paquetes funcionan con proyectos de .NET Core o .NET
Framework. Es posible que las aplicaciones que tienen el modelo de EF Core en una biblioteca de clases .NET
Standard no tengan un proyecto de .NET Core o .NET Framework. Por ejemplo, esto es cierto para las
aplicaciones Xamarin y Plataforma universal de Windows. En tales casos, puede crear un proyecto de aplicación
de consola de .NET Core o .NET Framework cuyo único propósito es actuar como proyecto de inicio para las
herramientas. El proyecto puede ser un proyecto ficticio sin código real — , solo es necesario para proporcionar
un destino para las herramientas.
¿Por qué es necesario un proyecto ficticio? Como se mencionó anteriormente, las herramientas tienen que
ejecutar código de aplicación en tiempo de diseño. Para ello, deben usar .NET Core o .NET Framework Runtime.
Cuando el modelo de EF Core está en un proyecto que tiene como destino .NET Core o .NET Framework, las
herramientas de EF Core toman prestado el tiempo de ejecución del proyecto. No pueden hacerlo si el modelo
de EF Core está en una biblioteca de clases .NET Standard. El .NET Standard no es una implementación real de
.NET; es una especificación de un conjunto de API que las implementaciones de .NET deben admitir. Por lo tanto
.NET Standard no es suficiente para que las herramientas de EF Core ejecuten código de aplicación. El proyecto
ficticio que cree para usarlo como proyecto de inicio proporciona una plataforma de destino concreta en la que
las herramientas pueden cargar la biblioteca de clases de .NET Standard.
Entorno de [Link] Core
Para especificar el entorno de [Link] Core proyectos, establezca env: ASPNETCORE_ENVIRONMENT antes
de ejecutar los comandos.
A partir de EF Core 5,0, también se pueden pasar argumentos adicionales a Program. CreateHostBuilder, lo que
le permite especificar el entorno en la línea de comandos:
Update-Database -Args '--environment Production'

Parámetros comunes
En la tabla siguiente se muestran los parámetros que son comunes a todos los comandos EF Core:

PA RÁ M ET RO DESC RIP C IÓ N

-Contexto <String> La clase DbContext que se va a usar. Nombre de clase solo


o completo con espacios de nombres. Si se omite este
parámetro, EF Core encuentra la clase de contexto. Si hay
varias clases de contexto, este parámetro es obligatorio.

-Proyecto <String> Proyecto de destino. Si se omite este parámetro, el


proyecto predeterminado de la consola del
administrador de paquetes se utiliza como proyecto de
destino.

-Proyecto<String> Proyecto de inicio. Si se omite este parámetro, el proyecto


de inicio de las propiedades de la solución se usa como
proyecto de destino.

-Args <String> Argumentos pasados a la aplicación. Agregado en EF Core


5,0.

-Verbose Mostrar resultado detallado.

Para mostrar información de ayuda sobre un comando, use el Get-Help comando de PowerShell.

TIP
Los parámetros context, Project y proyecto admiten la expansión de pestañas.

Add-Migration
Agrega una nueva migración.
Parámetros:

PA RÁ M ET RO DESC RIP C IÓ N

-Nombre <String> El nombre de la migración. Este es un parámetro posicional y


es obligatorio.

-OutputDir <String> El directorio que se usa para generar los archivos. Las rutas
de acceso son relativas al directorio del proyecto de destino.
El valor predeterminado es "migraciones".

Espacio de nombres <String> Espacio de nombres que se va a usar para las clases
generadas. De forma predeterminada, se genera desde el
directorio de salida. Agregado en EF Core 5,0.

Los parámetros comunes se enumeran a continuación.


Drop-Database
Quita la base de datos.
Parámetros:

PA RÁ M ET RO DESC RIP C IÓ N

-WhatIf Mostrar la base de datos que se va a quitar, pero no quitarla.

Los parámetros comunes se enumeran a continuación.

Get-DbContext
Muestra y obtiene información acerca de los DbContext tipos disponibles.
Los parámetros comunes se enumeran a continuación.

Get-Migration
Muestra las migraciones disponibles. Agregado en EF Core 5,0.
Parámetros:

PA RÁ M ET RO DESC RIP C IÓ N

-Conexión <String> La cadena de conexión a la base de datos. Tiene como valor


predeterminado el especificado en AddDbContext o en
alconfigure.

-Noconnect No se conecte a la base de datos.

Los parámetros comunes se enumeran a continuación.

Remove-Migration
Quita la última migración (revierte los cambios de código que se realizaron para la migración).
Parámetros:

PA RÁ M ET RO DESC RIP C IÓ N

-Force Revertir la migración (revertir los cambios que se aplicaron a


la base de datos).

Los parámetros comunes se enumeran a continuación.

Scaffold-DbContext
Genera código para los DbContext tipos de entidad y para una base de datos. Para Scaffold-DbContext que
genere un tipo de entidad, la tabla de base de datos debe tener una clave principal.
Parámetros:
PA RÁ M ET RO DESC RIP C IÓ N

-Conexión <String> La cadena de conexión a la base de datos. En el caso de los


proyectos de [Link] Core 2. x, el valor puede ser name =
<name of connection string>. En ese caso, el nombre
procede de los orígenes de configuración que se configuran
para el proyecto. Este es un parámetro posicional y es
obligatorio.

-Proveedor <String> Proveedor que se va a usar. Normalmente, es el nombre del


paquete NuGet, por ejemplo:
[Link] . Este es un
parámetro posicional y es obligatorio.

-OutputDir <String> Directorio en el que se colocarán los archivos. Las rutas de


acceso son relativas al directorio del proyecto.

-ContextDir <String> Directorio en el que se va a colocar el DbContext archivo.


Las rutas de acceso son relativas al directorio del proyecto.

Espacio de nombres <String> Espacio de nombres que se va a usar para todas las clases
generadas. De forma predeterminada, se genera a partir del
espacio de nombres raíz y el directorio de salida. Agregado
en EF Core 5,0.

-ContextNamespace <String> Espacio de nombres que se va a utilizar para la clase


generada DbContext . Nota: invalida -Namespace .
Agregado en EF Core 5,0.

-Contexto <String> Nombre de la DbContext clase que se va a generar.

-Esquemas <String[]> Esquemas de las tablas para las que se van a generar tipos
de entidad. Si se omite este parámetro, se incluyen todos los
esquemas.

-Tablas <String[]> Tablas para las que se van a generar tipos de entidad. Si se
omite este parámetro, se incluyen todas las tablas.

-DataAnnotations Use los atributos para configurar el modelo (siempre que sea
posible). Si se omite este parámetro, solo se usa la API fluida.

-UseDatabaseNames Utilice nombres de tabla y columna exactamente como


aparecen en la base de datos. Si se omite este parámetro, los
nombres de base de datos se cambian para ajustarse mejor
a las convenciones de estilo de nombre de C#.

-Force Sobrescribe los archivos existentes.

-NoOnConfiguring No generar [Link] . Agregado en EF


Core 5,0.

-Nopluralización No use pluralizador. Agregado en EF Core 5,0.

Los parámetros comunes se enumeran a continuación.


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

Ejemplo que scaffolding solo selecciona tablas y crea el contexto en una carpeta independiente con un nombre y
un espacio de nombres especificados:

Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;"
[Link] -OutputDir Models -Tables "Blog","Post" -ContextDir Context -Context
BlogContext -ContextNamespace [Link]

En el ejemplo siguiente se lee la cadena de conexión de la configuración del proyecto, posiblemente establecida
mediante la herramienta Administrador de secretos.

Scaffold-DbContext "Name=ConnectionStrings:Blogging" [Link]

Script-DbContext
Genera un script SQL desde DbContext. Omite las migraciones. Agregado en EF Core 3,0.
Parámetros:

PA RÁ M ET RO DESC RIP C IÓ N

-Salida <String> Archivo en el que se va a escribir el resultado.

Los parámetros comunes se enumeran a continuación.

Script-Migration
Genera un script SQL que aplica todos los cambios de una migración seleccionada a otra migración
seleccionada.
Parámetros:

PA RÁ M ET RO DESC RIP C IÓ N

-Desde<String> La migración inicial. Las migraciones pueden identificarse por


nombre o por identificador. El número 0 es un caso especial
que significa antes de la primera migración. El valor
predeterminado es 0.

-Hasta<String> La migración final. Tiene como valor predeterminado la


última migración.

-Idempotente Generar un script que se puede usar en una base de datos


en cualquier migración.

-Transtransacciones No genere instrucciones de transacciones de SQL. Agregado


en EF Core 5,0.
PA RÁ M ET RO DESC RIP C IÓ N

-Salida <String> Archivo en el que se va a escribir el resultado. Si se omite


este parámetro, el archivo se crea con un nombre generado
en la misma carpeta en que se crean los archivos en tiempo
de ejecución de la aplicación, por ejemplo:
/obj/Debug/netcoreapp2.1/[Link]/.

Los parámetros comunes se enumeran a continuación.

TIP
Los parámetros para, de y de salida admiten la expansión de pestañas.

En el ejemplo siguiente se crea un script para la migración de InitialCreate (desde una base de datos sin ninguna
migración), mediante el nombre de la migración.

Script-Migration 0 InitialCreate

En el ejemplo siguiente se crea un script para todas las migraciones después de la migración de InitialCreate con
el identificador de migración.

Script-Migration 20180904195021_InitialCreate

Update-Database
Actualiza la base de datos a la última migración o a una migración especificada.

PA RÁ M ET RO DESC RIP C IÓ N

-Migración<String> La migración de destino. Las migraciones pueden


identificarse por nombre o por identificador. El número 0 es
un caso especial que significa antes de la primera migración
y hace que se reviertan todas las migraciones. Si no se
especifica ninguna migración, el comando toma como valor
predeterminado la última migración.

-Conexión <String> La cadena de conexión a la base de datos. Tiene como valor


predeterminado el especificado en AddDbContext o
OnConfiguring . Agregado en EF Core 5,0.

Los parámetros comunes se enumeran a continuación.

TIP
El parámetro Migration admite la expansión de pestañas.

En el ejemplo siguiente se revierten todas las migraciones.

Update-Database 0

En los siguientes ejemplos se actualiza la base de datos a una migración especificada. El primero usa el nombre
de la migración y el segundo usa el identificador de migración y una conexión especificada:

Update-Database InitialCreate
Update-Database 20180904195021_InitialCreate -Connection your_connection_string

Recursos adicionales
Migraciones
Ingeniería inversa
Referencia de herramientas de Entity Framework
Core-CLI de .NET Core
07/04/2021 • 21 minutes to read • Edit Online

Las herramientas de la interfaz de la línea de comandos (CLI) para Entity Framework Core realizar tareas de
desarrollo en tiempo de diseño. Por ejemplo, se crean migraciones, se aplican migraciones y se genera código
para un modelo basado en una base de datos existente. Los comandos son una extensión del comando dotnet
multiplataforma, que forma parte de la SDK de .net Core. Estas herramientas funcionan con proyectos de .NET
Core.
Al usar Visual Studio, considere la posibilidad de usar las herramientas de la consola del administrador de
paquetes en lugar de las herramientas de la CLI. Herramientas de la consola del administrador de paquetes
automáticamente:
Funciona con el proyecto actual seleccionado en la consola del administrador de paquetes sin
necesidad de cambiar manualmente los directorios.
Abre los archivos generados por un comando una vez completado el comando.
Proporciona la finalización con tabulación de los comandos, los parámetros, los nombres de proyecto, los
tipos de contexto y los nombres de migración.

Instalación de las herramientas


dotnet ef se puede instalar como una herramienta global o local. La mayoría de los desarrolladores prefieren
instalar dotnet ef como herramienta global con el siguiente comando:

dotnet tool install --global dotnet-ef

Para usarlo como herramienta local, restaure las dependencias de un proyecto que lo declare como dependencia
de herramientas mediante un archivo de manifiesto de herramientas.
Actualice la herramienta con el siguiente comando:

dotnet tool update --global dotnet-ef

Antes de poder usar las herramientas en un proyecto específico, deberá agregar el


[Link] paquete a ella.

dotnet add package [Link]

Comprobar la instalación
Ejecute los siguientes comandos para comprobar que las herramientas de la CLI de EF Core están instaladas
correctamente:

dotnet ef

La salida del comando identifica la versión de las herramientas en uso:


_/\__
---==/ \\
___ ___ |. \|\
| __|| __| | ) \\\
| _| | _| \_/ | //|\\
|___||_| / \\\/\\

Entity Framework Core .NET Command-line Tools 2.1.3-rtm-32065

<Usage documentation follows, not shown.>

Actualización de las herramientas


Use dotnet tool update --global dotnet-ef para actualizar las herramientas globales a la última versión
disponible. Si tiene instaladas las herramientas localmente en el proyecto, use dotnet tool update dotnet-ef .
Instale una versión específica anexando --version <VERSION> al comando. Consulte la sección actualización de la
documentación de la herramienta dotnet para obtener más detalles.

Uso de las herramientas


Antes de usar las herramientas, puede que tenga que crear un proyecto de inicio o establecer el entorno.
Proyecto de destino y proyecto de inicio
Los comandos hacen referencia a un proyecto y un proyecto de inicio.
El proyecto también se conoce como proyecto de destino porque es donde los comandos agregan o
quitan archivos. De forma predeterminada, el proyecto en el directorio actual es el proyecto de destino.
Puede especificar otro proyecto como proyecto de destino mediante la --project opción.
El proyecto de inicio es el que las herramientas compilan y ejecutan. Las herramientas tienen que ejecutar
código de aplicación en tiempo de diseño para obtener información sobre el proyecto, como la cadena de
conexión a la base de datos y la configuración del modelo. De forma predeterminada, el proyecto en el
directorio actual es el proyecto de inicio. Puede especificar otro proyecto como proyecto de inicio
mediante la --startup-project opción.
El proyecto de inicio y el proyecto de destino suelen ser el mismo proyecto. Un escenario típico en el que se
trata de proyectos independientes es cuando:
El contexto de EF Core y las clases de entidad se encuentran en una biblioteca de clases de .NET Core.
Una aplicación de consola de .NET Core o una aplicación web hace referencia a la biblioteca de clases.
También es posible colocar el código de las migraciones en una biblioteca de clases independiente del contexto
de EF Core.
Otras plataformas de destino
Las herramientas de la CLI funcionan con proyectos de .NET Core y proyectos de .NET Framework. Es posible
que las aplicaciones que tienen el modelo de EF Core en una biblioteca de clases .NET Standard no tengan un
proyecto de .NET Core o .NET Framework. Por ejemplo, esto es cierto para las aplicaciones Xamarin y Plataforma
universal de Windows. En tales casos, puede crear un proyecto de aplicación de consola de .NET Core cuyo único
propósito es actuar como proyecto de inicio para las herramientas. El proyecto puede ser un proyecto ficticio sin
código real — , solo es necesario para proporcionar un destino para las herramientas.
¿Por qué es necesario un proyecto ficticio? Como se mencionó anteriormente, las herramientas tienen que
ejecutar código de aplicación en tiempo de diseño. Para ello, deben usar el tiempo de ejecución de .NET Core.
Cuando el modelo de EF Core está en un proyecto que tiene como destino .NET Core o .NET Framework, las
herramientas de EF Core toman prestado el tiempo de ejecución del proyecto. No pueden hacerlo si el modelo
de EF Core está en una biblioteca de clases .NET Standard. El .NET Standard no es una implementación real de
.NET; es una especificación de un conjunto de API que las implementaciones de .NET deben admitir. Por lo tanto
.NET Standard no es suficiente para que las herramientas de EF Core ejecuten código de aplicación. El proyecto
ficticio que cree para usarlo como proyecto de inicio proporciona una plataforma de destino concreta en la que
las herramientas pueden cargar la biblioteca de clases de .NET Standard.
Entorno de [Link] Core
Para especificar el entorno de [Link] Core proyectos, establezca la variable de entorno
ASPNETCORE_ENVIRONMENT antes de ejecutar los comandos.
A partir de EF Core 5,0, también se pueden pasar argumentos adicionales a Program. CreateHostBuilder, lo que
le permite especificar el entorno en la línea de comandos:

dotnet ef database update -- --environment Production

TIP
El -- token dirige dotnet ef para tratar todo lo que sigue como argumento y no intentar analizarlos como opciones.
Los argumentos adicionales que no use dotnet ef se reenvían a la aplicación.

Opciones comunes
O P C IÓ N SH O RT DESC RIP C IÓ N

--json Muestra la salida JSON.

--context <DBCONTEXT> -c La clase DbContext que se va a usar.


Nombre de clase solo o completo con
espacios de nombres. Si se omite esta
opción, EF Core buscará la clase de
contexto. Si hay varias clases de
contexto, se requiere esta opción.

--project <PROJECT> -p Ruta de acceso relativa a la carpeta de


proyecto del proyecto de destino. El
valor predeterminado es la carpeta
actual.

--startup-project <PROJECT> -s Ruta de acceso relativa a la carpeta de


proyecto del proyecto de inicio. El valor
predeterminado es la carpeta actual.

--framework <FRAMEWORK> Moniker de la plataforma de destino


para la plataforma de destino. Use
cuando el archivo del proyecto
especifique varias plataformas de
destino y desee seleccionar una de
ellas.

--configuration <CONFIGURATION> La configuración de compilación, por


ejemplo: Debug o Release .
O P C IÓ N SH O RT DESC RIP C IÓ N

--runtime <IDENTIFIER> Identificador del Runtime de destino


para el que se van a restaurar los
paquetes. Para obtener una lista de
identificadores de tiempo de ejecución
(RID), consulte el catálogo de RID.

--no-build No compile el proyecto. Diseñado para


usarse cuando la compilación está
actualizada.

--help -h Muestra información de ayuda.

--verbose -v Mostrar resultado detallado.

--no-color No colorear la salida.

--prefix-output Prefijo de salida con nivel.

A partir de EF Core 5,0, se pasan los argumentos adicionales a la aplicación.

dotnet ef database drop


Elimina la base de datos.
Opciones:

O P C IÓ N SH O RT DESC RIP C IÓ N

--force -f No confirme.

--dry-run Mostrar la base de datos que se va a


quitar, pero no quitarla.

A continuación se enumeran las opciones comunes .

dotnet ef database update


Actualiza la base de datos a la última migración o a una migración especificada.
Argumentos:

A RGUM EN TO DESC RIP C IÓ N

<MIGRATION> La migración de destino. Las migraciones pueden


identificarse por nombre o por identificador. El número 0 es
un caso especial que significa antes de la primera migración
y hace que se reviertan todas las migraciones. Si no se
especifica ninguna migración, el comando toma como valor
predeterminado la última migración.

Opciones:
O P C IÓ N DESC RIP C IÓ N

--connection <CONNECTION> La cadena de conexión a la base de datos. Tiene como valor


predeterminado el especificado en AddDbContext o
OnConfiguring . Agregado en EF Core 5,0.

A continuación se enumeran las opciones comunes .


En los siguientes ejemplos se actualiza la base de datos a una migración especificada. El primero usa el nombre
de la migración y el segundo usa el identificador de migración y una conexión especificada:

dotnet ef database update InitialCreate


dotnet ef database update 20180904195021_InitialCreate --connection your_connection_string

dotnet ef dbcontext info


Obtiene información sobre un DbContext tipo.
A continuación se enumeran las opciones comunes .

dotnet ef dbcontext list


Enumera los DbContext tipos disponibles.
A continuación se enumeran las opciones comunes .

dotnet ef dbcontext scaffold


Genera código para los DbContext tipos de entidad y para una base de datos. Para que este comando genere un
tipo de entidad, la tabla de base de datos debe tener una clave principal.
Argumentos:

A RGUM EN TO DESC RIP C IÓ N

<CONNECTION> La cadena de conexión a la base de datos. En el caso de los


proyectos de [Link] Core 2. x, el valor puede ser name =
<name of connection string>. En ese caso, el nombre
procede de los orígenes de configuración que se configuran
para el proyecto.

<PROVIDER> Proveedor que se va a usar. Normalmente, es el nombre del


paquete NuGet, por ejemplo:
[Link] .

Opciones:

O P C IÓ N SH O RT DESC RIP C IÓ N

--data-annotations -d Use los atributos para configurar el


modelo (siempre que sea posible). Si se
omite esta opción, solo se usa la API
fluida.
O P C IÓ N SH O RT DESC RIP C IÓ N

--context <NAME> -c Nombre de la DbContext clase que se


va a generar.

--context-dir <PATH> Directorio en el que se va a colocar el


DbContext archivo de clase. Las rutas
de acceso son relativas al directorio del
proyecto. Los espacios de nombres se
derivan de los nombres de carpeta.

--context-namespace <NAMESPACE> Espacio de nombres que se va a utilizar


para la clase generada DbContext .
Nota: invalida --namespace .
Agregado en EF Core 5,0.

--force -f Sobrescribe los archivos existentes.

--output-dir <PATH> -o Directorio en el que se colocarán los


archivos de clase de entidad. Las rutas
de acceso son relativas al directorio del
proyecto.

--namespace <NAMESPACE> -n Espacio de nombres que se va a usar


para todas las clases generadas. De
forma predeterminada, se genera a
partir del espacio de nombres raíz y el
directorio de salida. Agregado en EF
Core 5,0.

--schema <SCHEMA_NAME>... Esquemas de las tablas para las que se


van a generar tipos de entidad. Para
especificar varios esquemas, repita
--schema cada uno de ellos. Si se
omite esta opción, se incluyen todos
los esquemas.

--table <TABLE_NAME> ... -t Tablas para las que se van a generar


tipos de entidad. Para especificar varias
tablas, repita -t o --table para
cada una de ellas. Si se omite esta
opción, se incluyen todas las tablas.

--use-database-names Utilice nombres de tabla y columna


exactamente como aparecen en la base
de datos. Si se omite esta opción, se
cambian los nombres de base de datos
para que se ajusten mejor a las
convenciones de estilo de nombre de
C#.

--no-onconfiguring Suprime la generación del


OnConfiguring método en la clase
generada DbContext . Agregado en
EF Core 5,0.

--no-pluralize No use pluralizador. Agregado en EF


Core 5,0
A continuación se enumeran las opciones comunes .
En el ejemplo siguiente se scaffoldingan todos los esquemas y las tablas y se colocan los nuevos archivos en la
carpeta Models .

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


[Link] -o Models

En el ejemplo siguiente se scaffolding solo las tablas seleccionadas y se crea el contexto en una carpeta
independiente con un nombre y un espacio de nombres especificados:

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


[Link] -o Models -t Blog -t Post --context-dir Context -c BlogContext --
context-namespace [Link]

En el ejemplo siguiente se lee la cadena de conexión del conjunto de configuración del proyecto mediante la
herramienta Administrador de secretos.

dotnet user-secrets set ConnectionStrings:Blogging "Data Source=(localdb)\MSSQLLocalDB;Initial


Catalog=Blogging"
dotnet ef dbcontext scaffold Name=ConnectionStrings:Blogging [Link]

En el ejemplo siguiente se omite un método de scaffolding OnConfiguring . Esto puede ser útil si desea
configurar DbContext fuera de la clase. Por ejemplo, [Link] Core aplicaciones normalmente la configuran en
[Link]. Agregado en EF Core 5,0.

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


Id=myUsername;Password=myPassword;" [Link] --no-onconfiguring

dotnet ef dbcontext script


Genera un script SQL desde DbContext. Omite las migraciones. Agregado en EF Core 3,0.
Opciones:

O P C IÓ N SH O RT DESC RIP C IÓ N

--output <FILE> -o Archivo en el que se va a escribir el


resultado.

A continuación se enumeran las opciones comunes .

dotnet ef migrations add


Agrega una nueva migración.
Argumentos:

A RGUM EN TO DESC RIP C IÓ N

<NAME> El nombre de la migración.

Opciones:
O P C IÓ N SH O RT DESC RIP C IÓ N

--output-dir <PATH> -o El directorio que se usa para generar


los archivos. Las rutas de acceso son
relativas al directorio del proyecto de
destino. El valor predeterminado es
"migraciones".

--namespace <NAMESPACE> -n Espacio de nombres que se va a usar


para las clases generadas. De forma
predeterminada, se genera desde el
directorio de salida. Agregado en EF
Core 5,0.

A continuación se enumeran las opciones comunes .

dotnet ef migrations list


Muestra las migraciones disponibles.
Opciones:

O P C IÓ N DESC RIP C IÓ N

--connection <CONNECTION> La cadena de conexión a la base de datos. Tiene como valor


predeterminado el especificado en AddDbContext o en
alconfigure. Agregado en EF Core 5,0.

--no-connect No se conecte a la base de datos. Agregado en EF Core 5,0.

A continuación se enumeran las opciones comunes .

dotnet ef migrations remove


Quita la última migración y revierte los cambios de código que se realizaron para la última migración.
Opciones:

O P C IÓ N SH O RT DESC RIP C IÓ N

--force -f Revertir la migración más reciente y


revertir los cambios en el código y la
base de datos que se realizaron para la
última migración. Continúa revirtiendo
solo los cambios de código si se
produce un error durante la conexión a
la base de datos.

A continuación se enumeran las opciones comunes .

dotnet ef migrations script


Genera un script SQL a partir de las migraciones.
Argumentos:
A RGUM EN TO DESC RIP C IÓ N

<FROM> La migración inicial. Las migraciones pueden identificarse por


nombre o por identificador. El número 0 es un caso especial
que significa antes de la primera migración. El valor
predeterminado es 0.

<TO> La migración final. Tiene como valor predeterminado la


última migración.

Opciones:

O P C IÓ N SH O RT DESC RIP C IÓ N

--output <FILE> -o Archivo en el que se va a escribir el


script.

--idempotent -i Generar un script que se puede usar


en una base de datos en cualquier
migración.

--no-transactions No genere instrucciones de


transacciones de SQL. Agregado en EF
Core 5,0.

A continuación se enumeran las opciones comunes .


En el ejemplo siguiente se crea un script para la migración de InitialCreate:

dotnet ef migrations script 0 InitialCreate

En el ejemplo siguiente se crea un script para todas las migraciones después de la migración de InitialCreate.

dotnet ef migrations script 20180904195021_InitialCreate

Recursos adicionales
Migraciones
Ingeniería inversa
Creación de DbContext en tiempo de diseño
07/04/2021 • 5 minutes to read • Edit Online

Algunos de los comandos de herramientas de EF Core (por ejemplo, los comandos Migrations ) requieren
DbContext que se cree una instancia derivada en tiempo de diseño para recopilar detalles sobre los tipos de
entidad de la aplicación y cómo se asignan a un esquema de base de datos. En la mayoría de los casos, es
conveniente que el DbContext creado por tanto se configure de forma similar a como se configuraría en tiempo
de ejecución.
Hay varias maneras en las que las herramientas intentan crear DbContext :

De servicios de aplicación
Si el proyecto de inicio usa el host de Web [Link] Core o el host genérico de .net Core, las herramientas
intentan obtener el objeto DbContext del proveedor de servicios de la aplicación.
En primer lugar, las herramientas intentan obtener el proveedor de servicios invocando
[Link]() , llamando a Build() y, a continuación, accediendo a la Services propiedad.

public class Program


{
public static void Main(string[] args)
=> CreateHostBuilder(args).Build().Run();

// EF Core uses this method at design time to access the DbContext


public static IHostBuilder CreateHostBuilder(string[] args)
=> [Link](args)
.ConfigureWebHostDefaults(
webBuilder => [Link]<Startup>());
}

public class Startup


{
public void ConfigureServices(IServiceCollection services)
=> [Link]<ApplicationDbContext>();

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)


{
}
}

public class ApplicationDbContext : DbContext


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

NOTE
Cuando se crea una nueva aplicación de [Link] Core, este enlace se incluye de forma predeterminada.

El DbContext propio y las dependencias de su constructor deben registrarse como servicios en el proveedor de
servicios de la aplicación. Esto se puede lograr fácilmente si se tiene un constructor en DbContext que toma una
instancia de DbContextOptions<TContext> como argumento y usa el AddDbContext<TContext> método.

Usar un constructor sin parámetros


Si DbContext no se puede obtener del proveedor de servicios de aplicación, las herramientas buscan el
DbContext tipo derivado dentro del proyecto. A continuación, intentan crear una instancia mediante un
constructor sin parámetros. Este puede ser el constructor predeterminado si DbContext se configura mediante
el OnConfiguring método.

Desde un generador en tiempo de diseño


También puede indicar a las herramientas cómo crear su DbContext implementando la
[Link]<TContext> interfaz: Si una clase que
implementa esta interfaz se encuentra en el mismo proyecto que el derivado DbContext o en el proyecto de
inicio de la aplicación, las herramientas omiten las otras formas de crear el dbcontext y usar en su lugar el
generador en tiempo de diseño.

public class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>


{
public BloggingContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
[Link]("Data Source=[Link]");

return new BloggingContext([Link]);


}
}

NOTE
Antes de EFCore 5,0 args , el parámetro no se usaba (vea este problema). Esto se corrigió en EFCore 5,0 y cualquier
argumento adicional en tiempo de diseño se pasa a la aplicación a través de ese parámetro.

Un generador en tiempo de diseño puede ser especialmente útil si necesita configurar de DbContext forma
diferente el tiempo de diseño que en tiempo de ejecución, si el DbContext constructor toma parámetros
adicionales que no están registrados en di, si no usa di en absoluto, o si por alguna razón prefiere no tener un
CreateHostBuilder método en la clase de la aplicación [Link] Core Main .

Args
IDesignTimeDbContextFactory<TContext>.CreateDbContextY [Link] aceptan argumentos
de la línea de comandos.
A partir de EF Core 5,0, puede especificar estos argumentos en las herramientas:
CLI de .NET Core
Visual Studio

dotnet ef database update -- --environment Production

El -- token dirige dotnet ef para tratar todo lo que sigue como argumento y no intentar analizarlos como
opciones. Los argumentos adicionales que no use dotnet ef se reenvían a la aplicación.
Servicios en tiempo de diseño
12/03/2021 • 2 minutes to read • Edit Online

Algunos servicios usados por las herramientas solo se usan en tiempo de diseño. Estos servicios se administran
de forma independiente de los servicios en tiempo de ejecución de EF Core para evitar que se implementen con
la aplicación. Para invalidar uno de estos servicios (por ejemplo, el servicio para generar archivos de migración),
agregue una implementación de IDesignTimeServices al proyecto de inicio.

internal class MyDesignTimeServices : IDesignTimeServices


{
public void ConfigureDesignTimeServices(IServiceCollection services)
=> [Link]<IMigrationsCodeGenerator, MyMigrationsCodeGenerator>();
}

Referencia a Microsoft. EntityFrameworkCore. Design


Microsoft. EntityFrameworkCore. Design es un paquete de DevelopmentDependency. Esto significa que la
dependencia no fluye de manera transitiva en otros proyectos y que, de forma predeterminada, no puede hacer
referencia a sus tipos.
Para hacer referencia a sus tipos e invalidar los servicios en tiempo de diseño, actualice los metadatos del
elemento PackageReference en el archivo del proyecto.

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


<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 forma transitiva a través de Microsoft. EntityFrameworkCore. Tools, tendrá
que agregar un PackageReference explícito al paquete y cambiar sus metadatos.

Lista de servicios
A continuación se muestra una lista de los servicios en tiempo de diseño.

SERVIC IO DESC RIP C IÓ N

IAnnotationCodeGenerator Genera el código para las anotaciones del modelo


correspondientes.

ICSharpHelper Ayuda a generar código de C#.

IPluralizer Palabras singular y plural nombres.

IMigrationsCodeGenerator Genera código para una migración.

IMigrationsScaffolder La clase principal para administrar los archivos de migración.


SERVIC IO DESC RIP C IÓ N

IDatabaseModelFactory Crea un modelo de base de datos a partir de una base de


datos.

IModelCodeGenerator Genera código para un modelo.

IProviderConfigurationCodeGenerator Genera código de configuración.

IReverseEngineerScaffolder La clase principal para la scaffolding de modelos con


ingeniería inversa.

Usar servicios
Estos servicios también pueden ser útiles para crear sus propias herramientas. Por ejemplo, si desea
automatizar parte del flujo de trabajo de su tiempo de diseño.
Puede crear un proveedor de servicios que contenga estos servicios mediante los métodos de extensión
AddEntityFrameworkDesignTimeServices y AddDbContextDesignTimeServices.

var db = new MyDbContext();

// Create design-time services


var serviceCollection = new ServiceCollection();
[Link]();
[Link](db);
var serviceProvider = [Link]();

// Add a migration
var migrationsScaffolder = [Link]<IMigrationsScaffolder>();
var migration = [Link](migrationName, rootNamespace);
[Link](projectDir, migration, outputDir);
Entity Framework 6
12/03/2021 • 4 minutes to read • Edit Online

Entity Framework 6 (EF6) es un asignador relacional de objetos (O/RM) probado para .NET con muchos años de
desarrollo de características y estabilización.
Como O/RM, EF6 reduce la discordancia de impedancia entre los mundos relacionales y orientados a objetos, lo
que permite a los desarrolladores escribir aplicaciones que interactúan con datos almacenados en bases de
datos relacionales con objetos .NET fuertemente tipados que representan el dominio de la aplicación, y eliminar
la necesidad de una gran parte del código de "mecánica" de acceso de datos que normalmente deben escribir.
EF6 implementa muchas características de O/RM populares:
Asignación de clases de entidad POCO que no dependen de ningún tipo de EF
Seguimiento de cambios automático
Resolución de identidad y unidad de trabajo
Carga diligente, diferida y explícita
Traducción de consultas fuertemente tipadas con LINQ (Language Integrated Query)
Capacidades de asignación enriquecidas que incluyen compatibilidad con:
Relaciones de uno a uno, de uno a varios y entre varios
Herencia (tabla por jerarquía, tabla por tipo y tabla por clase concreta)
Tipos complejos
Procedimientos almacenados
Un diseñador visual para crear modelos de entidad.
Una experiencia "Code First" para crear modelos de entidad mediante la escritura de código.
Los modelos pueden generarse a partir de bases de datos existentes y luego editarse manualmente, o bien
se pueden crear desde cero y luego usarse para generar nuevas bases de datos.
Integración con modelos de aplicación de .NET Framework, incluido [Link], y mediante enlace de datos, con
WPF y WinForms.
Conectividad de base de datos basada en [Link] y varios proveedores disponibles para conectarse a
SQL Server, Oracle, MySQL, SQLite, PostgreSQL, DB2, etc.

¿Debo usar EF6 o EF Core?


EF Core es una versión más moderna, ligera y extensible de Entity Framework que tiene capacidades y ventajas
muy similares a EF6. EF Core es una reescritura completa y contiene muchas características nuevas que no están
disponibles en EF6, aunque todavía carece de algunas de las funcionalidades más avanzadas de asignación de
EF6. Considere el uso de EF Core en las aplicaciones nuevas si el conjunto de características se ajusta a los
requisitos. En Comparar EF Core y EF6 se examina el proceso de elección más detalladamente.

Primeros pasos
Agregue el paquete NuGet de EntityFramework al proyecto o instale Entity Framework Tools para Visual Studio.
Luego, vea vídeos, lea tutoriales y consulte documentación avanzada, que le ayudarán a sacar el máximo partido
de EF6.

Versiones anteriores de Entity Framework


Esta es la documentación de la versión más reciente de Entity Framework 6, aunque la mayor parte también se
aplica a las versiones anteriores. Vea Novedades y Versiones anteriores para obtener una lista completa de las
versiones de EF y las características incluidas.
Novedades de EF6
12/03/2021 • 5 minutes to read

Se recomienda encarecidamente usar la versión más reciente de Entity Framework para asegurarse de obtener
las últimas características y la mayor estabilidad. Pero es posible que deba usar una versión anterior o que
quiera experimentar con las nuevas mejoras de la última versión preliminar. Para instalar versiones concretas de
EF, vea Get Entity Framework (Obtener Entity Framework).

EF 6.4.0
El runtime de EF 6.4.0 para NuGet se publicó en diciembre de 2019. El objetivo principal de EF 6.4 es
perfeccionar las características y los escenarios que se ofrecieron en EF 6.3. Consulte la lista de correcciones
importantes en Github.

EF 6.3.0
El runtime de EF 6.3.0 para NuGet se publicó en septiembre de 2019. El objetivo principal de esta versión era
facilitar la migración de las aplicaciones existentes que usan EF 6 a .NET Core 3.0. La comunidad también ha
contribuido con varias correcciones de errores y mejoras. Consulte los problemas cerrados en cada hito de la
versión 6.3.0 para información detallada. Estos son algunos de los más importantes:
Compatibilidad con .NET Core 3.0.
El paquete EntityFramework ahora tiene como destino .NET Standard 2.1 además de
.NET Framework 4.x.
Esto significa que EF 6.3 es multiplataforma y se admite en otros sistemas operativos además de
Windows, como Linux y macOS.
Se han vuelto a escribir los comandos de migración para ejecutarlos fuera de proceso y que trabajen
con proyectos de estilo de SDK.
Compatibilidad con HierarchyId de SQL Server.
Compatibilidad mejorada con PackageReference de Roslyn y NuGet.
Se agregó la utilidad [Link] para habilitar, agregar, generar scripts y aplicar migraciones desde los
ensamblados. Reemplaza a [Link] .
Compatibilidad con el diseñador de EF
Actualmente no se admite el uso del diseñador de EF directamente en proyectos de .NET Core o .NET Standard o
en un proyecto de .NET Framework de estilo SDK.
Puede solucionar esta limitación agregando el archivo EDMX y las clases generadas para las entidades y
DbContext como archivos vinculados a un proyecto de .NET Core 3.0 o .NET Standard 2.1 en la misma solución.
Los archivos vinculados tendrían el aspecto siguiente:

<ItemGroup>
<EntityDeploy Include="..\EdmxDesignHost\[Link]" Link="Model\[Link]" />
<Compile Include="..\EdmxDesignHost\[Link]" Link="Model\[Link]" />
<Compile Include="..\EdmxDesignHost\[Link]" Link="Model\[Link]" />
<Compile Include="..\EdmxDesignHost\[Link]" Link="Model\[Link]" />
</ItemGroup>

Tenga en cuenta que el archivo EDMX está vinculado con la acción de compilación EntityDeploy. Se trata de una
tarea especial de MSBuild (ahora incluida en el paquete EF 6.3) que se encarga de agregar el modelo EF en el
ensamblado de destino como recursos incrustados (o de copiarlo como archivos en la carpeta de salida, en
función de la configuración de procesamiento de artefactos de metadatos en EDMX). Para más información
sobre cómo configurar esta configuración, consulte nuestro ejemplo de .NET Core de EDMX.
Advertencia: Asegúrese de que el estilo antiguo (es decir, no SDK) del proyecto de .NET Framework que define el
archivo. edmx "real" se incluye antes del proyecto que define el vínculo dentro del archivo. sln. De lo contrario, al
abrir el archivo. edmx en el diseñador, verá el mensaje de error "The Entity Framework is not available in the
target framework currently specified for the project. You can change the target framework of the project or edit
the model in the XmlEditor" ("Entity Framework no está disponible en la plataforma de destino especificada
actualmente para el proyecto. Puede cambiar la plataforma de destino del proyecto o editar el modelo en
XmlEditor").

Versiones anteriores
La página Versiones anteriores contiene un archivo de todas las versiones anteriores de EF y de las
características principales incluidas en cada versión.
Versiones anteriores de Entity Framework
12/03/2021 • 30 minutes to read

La primera versión de Entity Framework se lanzó en 2008, como parte de .NET Framework 3,5 SP1 y Visual
Studio 2008 SP1.
A partir de la versión EF 4.1, se ha distribuido como el paquete NuGet EntityFramework , actualmente uno de los
paquetes más populares en [Link].
Entre las versiones 4,1 y 5,0, el paquete NuGet EntityFramework amplió las bibliotecas de EF que se incluían
como parte de .NET Framework.
A partir de la versión 6, EF se convirtió en un proyecto de código abierto y también se movía completamente
fuera de banda desde el .NET Framework. Esto significa que cuando se agrega el paquete NuGet de la versión 6
de EntityFramework a una aplicación, se obtiene una copia completa de la biblioteca EF que no depende de los
bits EF que se incluyen como parte de .NET Framework. Esto ayudó en cierto modo a acelerar el ritmo del
desarrollo y la entrega de nuevas características.
En junio de 2016, se publicó EF Core 1,0. EF Core se basa en un nuevo código base y está diseñado como una
versión más ligera y extensible de EF. Actualmente EF Core es el principal enfoque del desarrollo para el equipo
de Entity Framework en Microsoft. Esto significa que no hay nuevas características principales planeadas para
EF6. Sin embargo, EF6 se sigue manteniendo como un proyecto de código abierto y un producto de Microsoft
compatible.
Esta es la lista de versiones anteriores, en orden cronológico inverso, con información sobre las nuevas
características que se introdujeron en cada versión.

Actualización de EF Tools para Visual Studio 2017 15.7


En mayo de 2018 se publicó una versión actualizada de EF6 Tools como parte de Visual Studio 2017 15.7.
Incluye mejoras para algunas de las áreas problemáticas más habituales:
Correcciones de varios errores de accesibilidad de la interfaz de usuario
Solución alternativa para la regresión del rendimiento de SQL Server al generar modelos a partir de bases
de datos existentes #4
Compatibilidad para la actualización de modelos más grandes en SQL Server #185
Otra mejora de esta versión nueva de EF Tools es que ahora instala el runtime de EF 6.2 al crear un modelo en
un proyecto nuevo. Con versiones anteriores de Visual Studio, es posible usar el runtime de EF 6.2 (así como
cualquier versión anterior de EF) mediante la instalación de la versión correspondiente del paquete NuGet.

EF 6.2.0
El runtime de EF 6.2 para NuGet se publicó en octubre de 2017. Gracias en gran medida a los esfuerzos de la
comunidad de colaboradores de código abierto, EF 6.2 incluye bastantes correcciones de errores y mejoras de
producto.
Esta es una breve lista de los cambios más importantes que afectan al runtime de EF 6.2:
Reducción del tiempo de inicio al cargar los primeros modelos de código finalizados desde una caché
persistente #275
API fluida para definir índices #274
[Link]() para habilitar la escritura de consultas LINQ que se traducen en LIKE en SQL #241
[Link] ahora admite la opción -script #240
EF6 ahora puede trabajar con valores de clave generados por una secuencia en SQL Server #165
Lista de actualización de errores transitorios de la estrategia de ejecución de SQL Azure #83
Error: al volver a intentar consultas o comandos SQL, se produce un error "SqlParameter ya está incluido en
otro elemento SqlParameterCollection" #81
Error: la evaluación de [Link]() a menudo agota el tiempo de espera en el depurador #73

EF 6.1.3
El tiempo de ejecución de EF 6.1.3 se lanzó a NuGet en octubre de 2015. Esta versión solo contiene correcciones
para los defectos de alta prioridad y las regresiones detectadas en la versión 6.1.2. Las correcciones incluyen:
Consulta: regresión en EF 6.1.2: aplicación externa introducida y consultas más complejas para 1:1 relaciones
y la cláusula "Let"
Problema de TPT al ocultar la propiedad de clase base en la clase heredada
Error de DbMigration. SQL cuando la palabra ' Go ' está contenida en el texto
Crear marca de compatibilidad para la compatibilidad con el acoplamiento de UnionAll y Intersect
La consulta con varios includes no funciona en 6.1.2 (trabajando en 6.1.1)
"Hay un error en la sintaxis de SQL" excepción después de actualizar de EF 6.1.1 a 6.1.2

EF 6.1.2
El tiempo de ejecución de EF 6.1.2 se lanzó a NuGet en diciembre de 2014. Esta versión está principalmente
relacionada con las correcciones de errores. También hemos aceptado un par de cambios notables de los
miembros de la comunidad:
Los parámetros de la caché de consulta se pueden configurar desde el archivo
app/[Link]

<entityFramework>
<queryCache size='1000' cleaningIntervalInSeconds='-1'/>
</entityFramework>

Los métodos SqlFile y SqlResource de DbMigration permiten ejecutar un script SQL almacenado
como un archivo o un recurso incrustado.

EF 6.1.1
El tiempo de ejecución de EF 6.1.1 se lanzó a NuGet en junio de 2014. Esta versión contiene correcciones para
los problemas que ha encontrado un número de personas. Entre otras:
Diseñador: error al abrir edmx EF5 con precisión decimal en el diseñador de EF6
La lógica de detección de instancia predeterminada para LocalDB no funciona con SQL Server 2014

EF 6.1.0
El tiempo de ejecución de EF 6.1.0 se lanzó a NuGet en marzo de 2014. Esta actualización secundaria incluye un
número significativo de características nuevas:
La consolidación de herramientas proporciona una manera coherente de crear un nuevo modelo EF. Esta
característica amplía el Asistente para Entity Data Model de [Link] para admitir la creación de modelos
Code First, incluida la ingeniería inversa de una base de datos existente. Estas características estaban
previamente disponibles en calidad beta en las herramientas avanzadas de EF.
El control de los errores de confirmación de la transacción proporciona la CommitFailureHandler, que
hace uso de la nueva capacidad introducida para interceptar las operaciones de transacción.
CommitFailureHandler permite la recuperación automática de errores de conexión mientras se confirma una
transacción.
IndexAttribute permite especificar los índices colocando un [Index] atributo en una propiedad (o
propiedades) en el modelo de Code First. A continuación, Code First creará un índice correspondiente en la
base de datos.
La API de asignación pública proporciona acceso a la información EF tiene sobre cómo se asignan las
propiedades y los tipos a las columnas y tablas de la base de datos. En las versiones anteriores, esta API era
interna.
La capacidad de configurar los interceptores mediante el archivo de aplicación/[Link]
permite agregar interceptores sin volver a compilar la aplicación.
System. Data. Entity. Infrastructure. intercepción. DatabaseLogger es un nuevo interceptor que
facilita el registro de todas las operaciones de base de datos en un archivo. En combinación con la
característica anterior, esto le permite cambiar fácilmente el registro de las operaciones de base de datos
para una aplicación implementada, sin necesidad de volver a compilar.
La detección de cambios del modelo de migración se ha mejorado para que las migraciones con
scaffolding sean más precisas. también se ha mejorado el rendimiento del proceso de detección de cambios.
Mejoras en el rendimiento, incluidas las operaciones de base de datos reducidas durante la inicialización,
optimizaciones para la comparación de igualdad nula en consultas LINQ, generación más rápida de vistas
(creación de modelos) en más escenarios y materialización más eficaz de las entidades sometidas a
seguimiento con varias asociaciones.

EF 6.0.2
El tiempo de ejecución de EF 6.0.2 se lanzó a NuGet en diciembre de 2013. Esta versión de revisión se limita a
solucionar los problemas que se introdujeron en la versión EF6 (regresiones en rendimiento/comportamiento
desde EF5).

EF 6.0.1
El tiempo de ejecución de EF 6.0.1 se lanzó a NuGet en octubre de 2013 simultáneamente con EF 6.0.0, ya que
este último se incrustó en una versión de Visual Studio que se había bloqueado unos meses antes. Esta versión
de revisión se limita a solucionar los problemas que se introdujeron en la versión EF6 (regresiones en
rendimiento/comportamiento desde EF5). Los cambios más importantes fueron resolver algunos problemas de
rendimiento durante el calentamiento de los modelos EF. Esto era importante porque el rendimiento de la
preparación era un área de enfoque en EF6 y estos problemas eran la negación de algunas de las otras mejoras
de rendimiento realizadas en EF6.

EF 6,0
El tiempo de ejecución de EF 6.0.0 se lanzó a NuGet en octubre de 2013. Esta es la primera versión en la que se
incluye un tiempo de ejecución de EF completo en el paquete NuGet EntityFramework que no depende de los
bits EF que forman parte de la .NET Framework. Mover las partes restantes del tiempo de ejecución al paquete
de NuGet requirió un número de cambios importantes en el código existente. Consulte la sección sobre la
actualización a Entity Framework 6 para obtener más detalles sobre los pasos manuales necesarios para la
actualización.
Esta versión incluye numerosas características nuevas. Las siguientes características funcionan para los modelos
creados con Code First o el diseñador de EF:
Consulta asincrónica y guardar agrega compatibilidad con los patrones asincrónicos basados en tareas
que se introdujeron en .net 4,5.
La resistencia de la conexión permite la recuperación automática de errores de conexión transitorios.
La configuración basada en código ofrece la opción de realizar la configuración, que tradicionalmente se
llevó a cabo en un archivo de configuración, en el código.
La resolución de dependencias presenta compatibilidad con el patrón de localizador de servicio y hemos
factorizado algunas partes de la funcionalidad que se pueden reemplazar con implementaciones
personalizadas.
El registro de intercepción/SQL proporciona bloques de creación de bajo nivel para la interceptación de
operaciones EF con un registro de SQL simple basado en la parte superior.
Las mejoras en la capacidad de prueba facilitan la creación de dobles de pruebas para DbContext y DbSet
cuando se usa un marco ficticio o se escriben sus propios dobles de pruebas.
DbContext ahora se puede crear con un DbConnection que ya está abier to , lo que permite
escenarios en los que resultaría útil si la conexión pudiera estar abierta al crear el contexto (por ejemplo,
compartiendo una conexión entre los componentes en los que no se puede garantizar el estado de la
conexión).
La compatibilidad con transacciones mejorada proporciona compatibilidad con una transacción
externa al marco de trabajo, así como formas mejoradas de crear una transacción en el marco de trabajo.
Enumeraciones, un rendimiento espacial y mejor en .net 4,0 : moviendo los componentes principales
que solía haber en el .NET Framework al paquete de NUGET de EF, ahora podemos ofrecer compatibilidad
con enumeraciones, tipos de datos espaciales y mejoras de rendimiento de EF5 en .net 4,0.
Rendimiento mejorado de Enumerable. contiene en consultas LINQ .
Tiempo de preparación mejorado (generación de vistas) , especialmente para los modelos de gran
tamaño.
Pluralización & conectable Ser vicio singular .
Ahora se admiten las implementaciones personalizadas de Equals o GetHashCode en las clases de
entidad.
DbSet. AddRange/RemoveRange proporciona una manera optimizada de agregar o quitar varias
entidades de un conjunto.
DbChangeTracker. HasChanges proporciona una manera sencilla y eficaz de ver si hay cambios
pendientes que guardar en la base de datos.
SqlCeFunctions proporciona un equivalente de SQL Compact a SqlFunctions.
Las siguientes características solo se aplican a Code First:
Las convenciones de Code First personalizadas permiten escribir sus propias convenciones para evitar
una configuración repetida. Proporcionamos una API simple para convenciones ligeras, así como algunos
bloques de creación más complejos para que pueda crear convenciones más complicadas.
Ahora se admite la asignación de Code First a los procedimientos almacenados de inserción,
actualización y eliminación .
Los scripts de migración idempotente permiten generar un script SQL que puede actualizar una base de
datos en cualquier versión hasta la versión más reciente.
La tabla de historial de migraciones configurable le permite personalizar la definición de la tabla de
historial de migraciones. Esto es especialmente útil para los proveedores de bases de datos que requieren los
tipos de datos adecuados, etc., para que la tabla de historial de migraciones funcione correctamente.
Varios contextos por base de datos quitan la limitación anterior de un modelo de Code First por base de
datos cuando se usan migraciones o cuando Code First crea automáticamente la base de datos.
DbModelBuilder. HasDefaultSchema es una nueva API de Code First que permite configurar el esquema
de la base de datos predeterminado para un modelo de Code First en un solo lugar. Anteriormente, el
esquema predeterminado de Code First estaba codificado de forma rígida en " DBO " y la única manera de
configurar el esquema al que pertenecía una tabla era a través de la API de ToTable.
[Link]. El método AddFromAssembly permite agregar fácilmente todas las
clases de configuración definidas en un ensamblado cuando se usan clases de configuración con la API fluida
de Code First.
Las operaciones de migración personalizadas le permitían agregar operaciones adicionales para
usarlas en las migraciones basadas en código.
El nivel de aislamiento de transacción predeterminado se cambia a
READ_COMMITTED_SNAPSHOT para las bases de datos creadas con Code First, lo que permite una
mayor escalabilidad y menos interbloqueos.
Los tipos de entidad y complejos ahora pueden ser clases nestedinside .

EF 5,0
El tiempo de ejecución de EF 5.0.0 se lanzó a NuGet en agosto de 2012. En esta versión se presentan algunas
características nuevas, como la compatibilidad con enumeraciones, las funciones con valores de tabla, los tipos
de datos espaciales y diversas mejoras de rendimiento.
En el Entity Framework Designer de Visual Studio 2012 también se incluye la compatibilidad con varios
diagramas por modelo, el color de las formas en la superficie de diseño y la importación por lotes de
procedimientos almacenados.
Esta es una lista de contenido que colocamos en concreto para la versión EF 5:
Publicación de la versión de EF 5
Nuevas características de EF5
Compatibilidad de enumeración en Code First
Compatibilidad de enumeración en EF Designer
Tipos de datos espaciales en Code First
Tipos de datos espaciales en EF Designer
Compatibilidad del proveedor con tipos espaciales
Funciones con valores de tabla
Varios diagramas por modelo
Configuración del modelo
Creación de un modelo
Conexiones y modelos
Consideraciones de rendimiento
Trabajar con Microsoft SQL Azure
Configuración del archivo de configuración
Glosario
Code First
Code First a una nueva base de datos (tutorial y vídeo)
Code First a una base de datos existente (tutorial y vídeo)
Convenciones
Anotaciones de datos
API fluida: configuración/asignación de propiedades & tipos
API fluida: configuración de relaciones
API fluida con [Link]
Migraciones de Code First
Migraciones de Code First automática
[Link]
Definir DbSets
EF Designer
Model First (tutorial y vídeo)
Database First (tutorial y vídeo)
Tipos complejos
Asociaciones/relaciones
Patrón de herencia de TPT
Patrón de herencia TPH
Consulta con procedimientos almacenados
Procedimientos almacenados con varios conjuntos de resultados
INSERT, Update & Delete con procedimientos almacenados
Asignación de una entidad a varias tablas (División de entidades)
Asignar varias entidades a una tabla (División de tablas)
Definir consultas
Plantillas de generación de código
Revertir a ObjectContext
Uso del modelo
Trabajar con DbContext
Consulta/búsqueda de entidades
Trabajar con relaciones
Carga de entidades relacionadas
Trabajar con datos locales
Aplicaciones de N niveles
Consultas SQL sin formato
Patrones de simultaneidad optimista
Trabajar con servidores proxy
Detección automática de cambios
Consultas sin seguimiento
El método de carga
Agregar, adjuntar y Estados de entidad
Trabajar con valores de propiedad
Enlace de datos con WPF (Windows Presentation Foundation)
Enlace de datos con WinForms (Windows Forms)

EF 4.3.1
El tiempo de ejecución de EF 4.3.1 se lanzó a NuGet en febrero de 2012 poco después de EF 4.3.0. Esta versión
de revisión incluyó algunas correcciones de errores en la versión EF 4,3 y presentó una mejor compatibilidad
con LocalDB para clientes que usan EF 4,3 con Visual Studio 2012.
Esta es una lista de contenido que colocamos en concreto para la versión EF 4.3.1. la mayor parte del contenido
proporcionado para EF 4,1 también se aplica también a EF 4,3:
Publicación de blog de la versión de EF 4.3.1

EF 4,3
El tiempo de ejecución de EF 4.3.0 se lanzó a NuGet en febrero de 2012. Esta versión incluye la nueva
característica Migraciones de Code First que permite cambiar incrementalmente una base de datos creada por
Code First a medida que el modelo de Code First evolucione.
Esta es una lista de contenido que colocamos en concreto para la versión EF 4,3, la mayor parte del contenido
proporcionado para EF 4,1 sigue siendo aplicable a EF 4,3 también:
Publicación de la versión de EF 4,3
Tutorial para migraciones de EF 4,3 Code-Based
Tutorial de migraciones automáticas de EF 4,3

EF 4,2
El tiempo de ejecución de EF 4.2.0 se lanzó a NuGet en noviembre de 2011. En esta versión se incluyen
correcciones de errores de la versión EF 4.1.1. Dado que esta versión solo incluye correcciones de errores,
podría haber sido la versión de revisión de EF 4.1.2 pero hemos optado por pasar a 4,2 para dejar de usar los
números de versión de revisión de fecha que usamos en las versiones 4.1. x y adoptar el estándar de control de
versiones semántico para el control de versiones semántico.
Esta es una lista de contenido que colocamos en concreto para la versión EF 4,2, el contenido proporcionado
para EF 4,1 todavía se aplica también a EF 4,2:
Publicación de la versión de EF 4,2
Code First tutorial
Tutorial de Database First de & de modelo

EF 4.1.1
El tiempo de ejecución de EF 4.1.10715 se lanzó a NuGet en julio de 2011. Además de las correcciones de
errores, esta versión de revisión incorporó algunos componentes para facilitar el trabajo de las herramientas en
tiempo de diseño con un modelo de Code First. Estos componentes se usan en Migraciones de Code First
(incluido en EF 4,3) y las herramientas avanzadas de EF.
Observará que el número de versión extraño 4.1.10715 del paquete. Usamos para usar versiones de revisión
basadas en fechas antes de decidir adoptar las versiones semánticas. Piense en esta versión como EF 4,1 patch 1
(o EF 4.1.1).
Esta es una lista de contenido que colocamos para la versión 4.1.1:
Publicación de la versión de EF 4.1.1

EF 4,1
El tiempo de ejecución de EF 4.1.10331 fue el primero en publicarse en NuGet, en abril de 2011. Esta versión
incluye la API de DbContext simplificada y el flujo de trabajo Code First.
Observará el número de versión extraño, 4.1.10331, que realmente debería ser 4,1. Además, hay una versión de
4.1.10311 que debe ser 4.1.0-RC (' RC ' significa ' Release Candidate '). Usamos para usar versiones de revisión
basadas en fechas antes de decidir adoptar las versiones semánticas.
Esta es una lista de contenido que colocamos juntos para la versión 4,1. Gran parte de este todavía se aplica a
las versiones posteriores de Entity Framework:
Publicación de la versión de EF 4,1
Code First tutorial
Tutorial de Database First de & de modelo
SQL Azure federaciones y el Entity Framework

EF 4,0
Esta versión se incluyó en .NET Framework 4 y Visual Studio 2010, en abril de 2010. Las nuevas características
importantes de esta versión incluyen compatibilidad POCO, asignación de claves externas, carga diferida,
mejoras en la capacidad de prueba, generación de código personalizable y el flujo de trabajo Model First.
Aunque era la segunda versión de Entity Framework, se llamaba EF 4 para que se alinee con la versión de .NET
Framework con la que se distribuyó. Después de esta versión, comenzamos a poner Entity Framework
disponible en NuGet y adoptamos las versiones semánticas, puesto que ya no se asociaron a la versión .NET
Framework.
Tenga en cuenta que algunas versiones posteriores de .NET Framework se han incluido con actualizaciones
significativas de los bits EF incluidos. De hecho, muchas de las nuevas características de EF 5,0 se
implementaron como mejoras en estos bits. Sin embargo, para racionalizar el caso de control de versiones para
EF, seguimos haciendo referencia a los bits EF que forman parte de la .NET Framework como el tiempo de
ejecución de EF 4,0, mientras que todas las versiones más recientes están compuestas por el paquete NuGet
EntityFramework.

EF 3,5
La versión inicial de Entity Framework se incluyó en .NET 3,5 Service Pack 1 y Visual Studio 2008 SP1, publicada
en agosto de 2008. Esta versión proporcionó compatibilidad básica con O/RM mediante el flujo de trabajo de
Database First.
Actualización a Entity Framework 6
12/03/2021 • 8 minutes to read

En versiones anteriores de EF, el código se dividió entre las bibliotecas principales (principalmente
[Link]) distribuidas como parte de las bibliotecas de .NET Framework y fuera de banda (OOB)
(principalmente [Link]) incluidas en un paquete NuGet. EF6 toma el código de las bibliotecas
principales y lo incorpora a las bibliotecas de OOB. Esto era necesario para permitir que EF se convirtió en
código abierto y para que pueda evolucionar a un ritmo diferente del .NET Framework. La consecuencia de esto
es que las aplicaciones deben volver a generarse con los tipos que se han descargado.
Esto debe ser sencillo para las aplicaciones que hacen uso de DbContext, tal y como se distribuyen en EF 4,1 y
versiones posteriores. Se requiere un poco más de trabajo para las aplicaciones que hacen uso de ObjectContext
pero que todavía no es difícil de hacer.
Esta es una lista de comprobación de las cosas que debe hacer para actualizar una aplicación existente a EF6.

1. Instale el paquete NuGet de EF6


Debe actualizar al nuevo tiempo de ejecución de Entity Framework 6.
1. Haga clic con el botón derecho en el proyecto y seleccione administrar paquetes NuGet...
2. En la pestaña en línea , seleccione EntityFramework y haga clic en instalar .

NOTE
Si se instaló una versión anterior del paquete NuGet EntityFramework, se actualizará a EF6.

Como alternativa, puede ejecutar el siguiente comando desde la consola del administrador de paquetes:

Install-Package EntityFramework

2. Asegúrese de que se quitan las referencias de ensamblado a


[Link]
La instalación del paquete NuGet de EF6 debe quitar automáticamente del proyecto todas las referencias a
System. Data. Entity.

3. intercambiar modelos EF Designer (EDMX) para usar la generación


de código EF 6. x
Si tiene modelos creados con el diseñador de EF, deberá actualizar las plantillas de generación de código para
generar código compatible con EF6.

NOTE
Actualmente solo hay plantillas de generador de DbContext de EF 6. x disponibles para Visual Studio 2012 y 2013.

1. Elimine las plantillas de generación de código existentes. Normalmente, estos archivos se denominarán
** <edmx_file_name> . TT** y ** <edmx_file_name> . [Link]** y estar anidados en el archivo edmx en
explorador de soluciones. Puede seleccionar las plantillas en Explorador de soluciones y presionar la tecla
Supr para eliminarlas.

NOTE
En los proyectos de sitio web, las plantillas no se anidarán en el archivo edmx, sino que se enumeran en
Explorador de soluciones.

NOTE
En los proyectos de [Link], deberá habilitar "Mostrar todos los archivos" para poder ver los archivos de plantilla
anidados.

2. Agregue la plantilla de generación de código EF 6. x adecuada. Abra el modelo en EF Designer, haga clic
con el botón derecho en la superficie de diseño y seleccione Agregar elemento de generación de
código.. .
Si usa la API DbContext (recomendado), el generador de dbcontext de EF 6. x estará
disponible en la pestaña datos .

NOTE
Si usa Visual Studio 2012, tendrá que instalar las herramientas de EF 6 para tener esta plantilla. Consulte
obtener Entity Framework para obtener más información.

Si usa la API de ObjectContext, tendrá que seleccionar la pestaña en línea y buscar el generador
de EntityObject EF 6. x .
3. Si ha aplicado cualquier personalización a las plantillas de generación de código, deberá volver a
aplicarlas a las plantillas actualizadas.

4. actualizar los espacios de nombres de los tipos de EF principales


utilizados
Los espacios de nombres de los tipos DbContext y Code First no han cambiado. Esto significa que para muchas
aplicaciones que usan EF 4,1 o posterior, no es necesario cambiar nada.
Los tipos como ObjectContext que anteriormente estaban en [Link] se han pasado a nuevos
espacios de nombres. Esto significa que es posible que tenga que actualizar las directivas using o Import para
compilar en EF6.
La regla general para los cambios en el espacio de nombres es que cualquier tipo de System. Data. * se mueve a
System. Data. Entity. Core. *. En otras palabras, simplemente inserte Entity. Core. después de System. Data. Por
ejemplo:
System. Data. EntityException => System. Data. Entity. Core . EntityException
System. Data. Objects. ObjectContext => System. Data. Entity. Core . Objects. ObjectContext
System. Data. Objects. Classes. RelationshipManager => System. Data. Entity. Core . Objects. Classes.
RelationshipManager
Estos tipos se encuentran en los espacios de nombres básicos porque no se usan directamente para la mayoría
de las aplicaciones basadas en DbContext. Algunos tipos que formaban parte de [Link] se siguen
usando normalmente y directamente para aplicaciones basadas en DbContext, por lo que no se han pasado a
los espacios de nombres principales . Dichos componentes son:
System. Data. EntityState => System. Data. Entidad . EntityState
System. Data. Objects. Classes. EdmFunctionAttribute => System. Data. Entity. DbFunctionAttribute

NOTE
Se ha cambiado el nombre de esta clase; todavía existe una clase con el nombre anterior y funciona, pero ahora
está marcada como obsoleta.

System. Data. Objects. EntityFunctions => System. Data. Entity. DbFunctions

NOTE
Se ha cambiado el nombre de esta clase; todavía existe una clase con el nombre antiguo y funciona, pero ahora
está marcada como obsoleta).

Las clases espaciales (por ejemplo, DbGeography, DbGeometry) se han pasado de System. Data. Spatial =>
System. Data. Entidad . PDF

NOTE
Algunos tipos del espacio de nombres System. Data están en [Link] que no es un ensamblado EF. Estos tipos no
se han cambiado y, por tanto, sus espacios de nombres permanecen inalterados.
Versiones de Visual Studio
12/03/2021 • 8 minutes to read

Se recomienda usar siempre la versión más reciente de Visual Studio, ya que contiene las herramientas más
recientes para .NET, NuGet y Entity Framework. De hecho, los distintos ejemplos y tutoriales de la
documentación de Entity Framework suponen que está usando una versión reciente de Visual Studio.
Sin embargo, es posible usar versiones anteriores de Visual Studio con versiones diferentes de Entity
Framework siempre que tenga en cuenta algunas diferencias:

Visual Studio 2017 15,7 y versiones más recientes


Esta versión de Visual Studio incluye la versión más reciente de las herramientas de Entity Framework y el
tiempo de ejecución de EF 6,2 y no requiere pasos de configuración adicionales. Vea las novedades para
obtener más información sobre estas versiones.
Al agregar Entity Framework a los nuevos proyectos con las herramientas de EF, se agregará
automáticamente el paquete NuGet EF 6,2. Puede instalar o actualizar manualmente cualquier paquete de
NuGet de EF disponible en línea.
De forma predeterminada, la instancia de SQL Server disponible con esta versión de Visual Studio es una
instancia de LocalDB denominada MSSQLLocalDB. La sección del servidor de la cadena de conexión que se
debe usar es "(LocalDB) \ MSSQLLocalDB". Recuerde usar una cadena textual con prefijo @ o doble barra
diagonal inversa " \ \ " al especificar una cadena de conexión en el código de C#.

Visual Studio 2015 a Visual Studio 2017 15,6


Estas versiones de Visual Studio incluyen herramientas de Entity Framework y 6.1.3 en tiempo de ejecución.
Vea versiones anteriores para obtener más información sobre estas versiones.
Al agregar Entity Framework a los nuevos proyectos con las herramientas de EF, se agregará
automáticamente el paquete NuGet EF 6.1.3. Puede instalar o actualizar manualmente cualquier paquete de
NuGet de EF disponible en línea.
De forma predeterminada, la instancia de SQL Server disponible con esta versión de Visual Studio es una
instancia de LocalDB denominada MSSQLLocalDB. La sección del servidor de la cadena de conexión que se
debe usar es "(LocalDB) \ MSSQLLocalDB". Recuerde usar una cadena textual con prefijo @ o doble barra
diagonal inversa " \ \ " al especificar una cadena de conexión en el código de C#.

Visual Studio 2013


Esta versión de Visual Studio incluye y la versión anterior de las herramientas de Entity Framework y el
tiempo de ejecución. Se recomienda que actualice a Entity Framework Tools 6.1.3, mediante el instalador
disponible en el centro de descarga de Microsoft. Vea versiones anteriores para obtener más información
sobre estas versiones.
Al agregar Entity Framework a los nuevos proyectos mediante las herramientas de EF actualizadas, se
agregará automáticamente el paquete NuGet EF 6.1.3. Puede instalar o actualizar manualmente cualquier
paquete de NuGet de EF disponible en línea.
De forma predeterminada, la instancia de SQL Server disponible con esta versión de Visual Studio es una
instancia de LocalDB denominada MSSQLLocalDB. La sección del servidor de la cadena de conexión que se
debe usar es "(LocalDB) \ MSSQLLocalDB". Recuerde usar una cadena textual con prefijo @ o doble barra
diagonal inversa " \ \ " al especificar una cadena de conexión en el código de C#.
Visual Studio 2012
Esta versión de Visual Studio incluye y la versión anterior de las herramientas de Entity Framework y el
tiempo de ejecución. Se recomienda que actualice a Entity Framework Tools 6.1.3, mediante el instalador
disponible en el centro de descarga de Microsoft. Vea versiones anteriores para obtener más información
sobre estas versiones.
Al agregar Entity Framework a los nuevos proyectos mediante las herramientas de EF actualizadas, se
agregará automáticamente el paquete NuGet EF 6.1.3. Puede instalar o actualizar manualmente cualquier
paquete de NuGet de EF disponible en línea.
De forma predeterminada, la instancia de SQL Server disponible con esta versión de Visual Studio es una
instancia de LocalDB denominada v 11.0. La sección del servidor de la cadena de conexión que se debe usar
es "(LocalDB) \ v 11.0". Recuerde usar una cadena textual con prefijo @ o doble barra diagonal inversa " \ \ "
al especificar una cadena de conexión en el código de C#.

Visual Studio 2010


La versión de Entity Framework Tools disponible con esta versión de Visual Studio no es compatible con el
tiempo de ejecución de Entity Framework 6 y no se puede actualizar.
De forma predeterminada, las herramientas de Entity Framework agregarán Entity Framework 4,0 a los
proyectos. Para crear aplicaciones con las versiones más recientes de EF, primero deberá instalar la extensión
del administrador de paquetes NuGet.
De forma predeterminada, toda la generación de código en la versión de las herramientas de EF se basa en
EntityObject y Entity Framework 4. Se recomienda cambiar la generación de código para que se base en
DbContext y Entity Framework 5, mediante la instalación de plantillas de generación de código DbContext
para C# o Visual Basic.
Una vez que haya instalado las extensiones del administrador de paquetes NuGet, puede instalar o actualizar
manualmente cualquier paquete de NuGet de EF disponible en línea y usar EF6 con Code First, que no
requiere un diseñador.
De forma predeterminada, la instancia de SQL Server disponible con esta versión de Visual Studio se SQL
Server Express denominada SQLEXPRESS. La sección del servidor de la cadena de conexión que se debe usar
es ". \ SQLEXPRESS ". Recuerde usar una cadena textual con prefijo @ o doble barra diagonal inversa " \ \ " al
especificar una cadena de conexión en el código de C#.
Introducción a Entity Framework 6
12/03/2021 • 3 minutes to read • Edit Online

En esta guía se recopilan vínculos a artículos de documentación, tutoriales y vídeos seleccionados que le pueden
ayudar a empezar rápidamente.

Aspectos básicos
Obtener Entity Framework
Aquí obtendrá información sobre cómo agregar Entity Framework a sus aplicaciones y, si quiere usar EF
Designer, asegúrese de que lo instala en Visual Studio.
Creación de un modelo: Code First, EF Designer y flujos de trabajo EF
¿Prefiere especificar un código de escritura de modelos de EF o líneas y cuadros de dibujo? ¿Va a usar EF
para asignar objetos a una base de datos existente o quiere que cree una base de datos específica para
los objetos? Aquí podrá obtener información sobre dos enfoques diferentes para usar EF6: EF Designer y
Code First. Asegúrese de seguir el debate y vea el vídeo sobre las diferencias.
Trabajar con DbContext
DbContext es el primer tipo de EF y el más importante. Necesita conocerlo para saber cómo usarlo. Actúa
como plataforma para consultas de bases de datos y mantiene un seguimiento de los cambios que hace a
objetos, de modo que puedan persistir en la base de datos.
Formular una pregunta
Descubra cómo obtener ayuda de expertos y contribuya con sus respuestas a la comunidad.
Colaboracion
Entity Framework 6 usa un modelo de desarrollo abierto. Descubra cómo puede ayudar a asegurarse de
que EF sea aún mejor en nuestro repositorio de GitHub.

Recursos de Code First


Code First en un flujo de trabajo de base de datos existente
Code First en un nuevo flujo de trabajo de base de datos
Asignación de enumeraciones con Code First
Asignación de tipos espaciales con Code First
Creación de convenciones personalizadas e Code First
Usar la configuración de Fluent de Code First con Visual Basic
Migraciones de Code First
Code First Migrations in Team Environments (Migraciones de Code First en entornos de equipo)
Automatic Code First Migrations (Migraciones automáticas de Code First, ya no se recomienda)

Recursos de EF Designer
Flujo de trabajo de Database First
Flujo de trabajo de Model First
Asignación de enumeraciones
Asignación de tipos espaciales
Asignación de herencia de tabla por jerarquía
Asignación de herencia de tabla por tipo
Asignación de procedimientos almacenados para actualizaciones
Asignación de procedimientos almacenados para consultas
División de entidades
División de tablas
Defining Query (Definición de consultas, avanzado)
Table-Valued Functions (Funciones con valores de tabla, avanzado)

Otros recursos
Async Query and Save (Guardado y consultas asincrónicas)
Databinding with WinForms (Enlace de datos con WinForms)
Databinding with WPF (Enlace de datos con WPF)
Escenarios desconectados con entidades de autoseguimiento (ya no se recomienda)
Obtener Entity Framework
12/03/2021 • 4 minutes to read

Entity Framework se compone de las herramientas de EF para Visual Studio y el tiempo de ejecución de EF.

Herramientas de EF para Visual Studio


El Entity Framework Tools para Visual Studio incluye el diseñador de EF y el Asistente para modelo de EF y son
necesarios para los flujos de trabajo primero y modelo primero. Las herramientas de EF se incluyen en todas las
versiones recientes de Visual Studio. Si realiza una instalación personalizada de Visual Studio, debe asegurarse
de que el elemento "Entity Framework 6 herramientas" esté seleccionado eligiendo una carga de trabajo que lo
incluya o seleccionándola como componente individual.
En algunas versiones anteriores de Visual Studio, las herramientas de EF actualizadas están disponibles como
descarga. Consulte versiones de Visual Studio para obtener instrucciones sobre cómo obtener la versión más
reciente de las herramientas de EF disponibles para su versión de Visual Studio.

Tiempo de ejecución de EF
La versión más reciente de Entity Framework está disponible como el paquete NuGet EntityFramework. Si no
está familiarizado con el administrador de paquetes NuGet, le recomendamos que lea la información general de
Nuget.
Instalación del paquete de NuGet de EF
Para instalar el paquete EntityFramework, haga clic con el botón derecho en la carpeta referencias del proyecto
y seleccione administrar paquetes NuGet.. .

Instalar desde la consola del administrador de paquetes


Como alternativa, puede instalar EntityFramework ejecutando el siguiente comando en la consola del
Administrador de paquetes.

Install-Package EntityFramework

Instalación de una versión específica de EF


Desde EF 4,1 en adelante, se han publicado nuevas versiones del tiempo de ejecución de EF como el paquete
NuGet EntityFramework. Cualquiera de esas versiones se puede Agregar a un proyecto basado en .NET
Framework ejecutando el siguiente comando en la consola del administrador de paquetesde Visual Studio:

Install-Package EntityFramework -Version <number>

Tenga en cuenta que <number> representa la versión específica de EF que se va a instalar. Por ejemplo, 6.2.0 es la
versión del número para EF 6,2.
Los tiempos de ejecución de EF anteriores a 4,1 formaban parte de .NET Framework y no se pueden instalar por
separado.
Instalación de la versión preliminar más reciente
Los métodos anteriores le proporcionarán la última versión compatible de Entity Framework. A menudo hay
versiones preliminares de Entity Framework disponibles que le encantaría probar y enviarnos sus comentarios.
Para instalar la versión preliminar más reciente de EntityFramework, puede seleccionar incluir versión
preliminar en la ventana administrar paquetes NuGet. Si no hay disponibles versiones preliminares, obtendrá
automáticamente la versión más reciente compatible de Entity Framework.

Como alternativa, puede ejecutar el siguiente comando en la consola del administrador de paquetes.

Install-Package EntityFramework -Pre


Trabajar con DbContext
12/03/2021 • 7 minutes to read

Para usar Entity Framework para consultar, insertar, actualizar y eliminar datos mediante objetos .NET, primero
debe crear un modelo que asigne las entidades y relaciones que se definen en el modelo a las tablas de una
base de datos.
Una vez que tiene un modelo, la clase principal con la que interactúa la aplicación es
[Link] (a menudo se conoce como clase de contexto). Puede usar un DbContext
asociado a un modelo para:
Escribir y ejecutar consultas
Materializar los resultados de la consulta como objetos entidad
Realizar un seguimiento de los cambios que se realizan en esos objetos
Volver a guardar los cambios de objetos en la base de datos
Enlazar objetos en memoria a controles de interfaz de usuario
En esta página se proporcionan instrucciones sobre cómo administrar la clase de contexto.

Definir una clase derivada de DbContext


La manera recomendada de trabajar con el contexto es definir una clase que derive de DbContext y exponga
propiedades DbSet que representen colecciones de las entidades especificadas en el contexto. Si está trabajando
con el diseñador de EF, el contexto se generará automáticamente. Si está trabajando con Code First,
normalmente escribirá el contexto.

public class ProductContext : DbContext


{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}

Una vez que tenga un contexto, debe consultar, agregar (mediante Add métodos o Attach ) o quitar (mediante
Remove ) entidades en el contexto mediante estas propiedades. El acceso a una DbSet propiedad en un objeto
de contexto representa una consulta de inicio que devuelve todas las entidades del tipo especificado. Tenga en
cuenta que, al tener acceso a una propiedad, no se ejecutará la consulta. Una consulta se ejecuta cuando:
Se enumera mediante una instrucción foreach (C#) o For Each (Visual Basic).
Se enumera mediante una operación de colección como ToArray , ToDictionary o ToList .
Los operadores de LINQ como First o Any se especifican en la parte más externa de la consulta.
Se llama a uno de los métodos siguientes: el Load método de extensión, [Link] ,
[Link] y DbSet<T>.Find , si una entidad con la clave especificada no se encuentra ya
cargada en el contexto.

Período de duración
La duración del contexto comienza cuando se crea la instancia y finaliza cuando la instancia se desecha o se
recolecta como elemento no utilizado. Use el uso de si desea que todos los recursos que controla el contexto se
eliminen al final del bloque. Cuando se usa con , el compilador crea automáticamente un bloque try/finally y
llama a Dispose en el bloque Finally .
public void UseProducts()
{
using (var context = new ProductContext())
{
// Perform data access using the context
}
}

Estas son algunas directrices generales a la hora de decidir la duración del contexto:
Al trabajar con aplicaciones Web, use una instancia de contexto por solicitud.
Al trabajar con Windows Presentation Foundation (WPF) o Windows Forms, utilice una instancia de contexto
por formulario. Esto le permite usar la funcionalidad de seguimiento de cambios que proporciona el
contexto.
Si la instancia de contexto la crea un contenedor de inserción de dependencias, suele ser responsabilidad del
contenedor desechar el contexto.
Si el contexto se crea en el código de la aplicación, no olvide eliminar el contexto cuando ya no sea necesario.
Al trabajar con un contexto de ejecución prolongada, tenga en cuenta lo siguiente:
A medida que se cargan más objetos y sus referencias en la memoria, el consumo de memoria del
contexto puede aumentar rápidamente. lo que puede ocasionar problemas de rendimiento.
El contexto no es seguro para subprocesos, por lo que no debe compartirse entre varios subprocesos
que realizan trabajo en él simultáneamente.
Si una excepción hace que el contexto esté en un estado irrecuperable, toda la aplicación puede
finalizar.
Las posibilidades de que haya problemas relacionados con la simultaneidad se incrementan a medida
que aumenta la distancia entre el momento en que se consultan los datos y el momento en que se
actualizan.

Conexiones
De forma predeterminada, el contexto administra las conexiones a la base de datos. El contexto abre y cierra las
conexiones según sea necesario. Por ejemplo, el contexto abre una conexión para ejecutar una consulta y, a
continuación, cierra la conexión cuando se han procesado todos los conjuntos de resultados.
Hay casos en los que se desea más control sobre el momento en que se abre y cierra la conexión. Por ejemplo, al
trabajar con SQL Server Compact, a menudo se recomienda mantener una conexión abierta independiente con
la base de datos mientras dure la aplicación para mejorar el rendimiento. Puede administrar este proceso
manualmente mediante la propiedad Connection .
Relaciones, propiedades de navegación y claves
externas
12/03/2021 • 17 minutes to read

En este artículo se proporciona información general sobre cómo Entity Framework administra las relaciones
entre entidades. También proporciona instrucciones sobre cómo asignar y manipular relaciones.

Relaciones en EF
En las bases de datos relacionales, las relaciones (también denominadas asociaciones) entre las tablas se definen
mediante claves externas. Una clave externa (FK) es una columna o combinación de columnas que se utiliza para
establecer y exigir un vínculo entre los datos de dos tablas. Por lo general, hay tres tipos de relaciones: uno a
uno, uno a varios y varios a varios. En una relación de uno a varios, la clave externa se define en la tabla que
representa el extremo de la relación. La relación de varios a varios implica definir una tercera tabla (denominada
tabla de unión o de combinación), cuya clave principal se compone de las claves externas de ambas tablas
relacionadas. En una relación uno a uno, la clave principal actúa además como clave externa y no hay ninguna
columna de clave externa independiente para cualquiera de las tablas.
En la imagen siguiente se muestran dos tablas que participan en una relación de uno a varios. La tabla Course
es la tabla dependiente porque contiene la columna depar tmentId que la vincula a la tabla Depar tment .

En Entity Framework, una entidad se puede relacionar con otras entidades a través de una asociación o relación.
Cada relación contiene dos extremos que describen el tipo de entidad y la multiplicidad del tipo (uno, cero o uno
o varios) para las dos entidades de esa relación. La relación puede venir gobernada por una restricción
referencial, la cual describe qué extremo de la relación constituye un rol principal y cuál es un rol dependiente.
Las propiedades de navegación proporcionan una manera de navegar por una asociación entre dos tipos de
entidad. Cada objeto puede tener una propiedad de navegación para cada relación en la que participa. Las
propiedades de navegación permiten navegar y administrar las relaciones en ambas direcciones, devolviendo
un objeto de referencia (si la multiplicidad es uno o cero o uno) o una colección (si la multiplicidad es muchas).
También puede optar por tener una navegación unidireccional, en cuyo caso definirá la propiedad de
navegación solo en uno de los tipos que participan en la relación y no en ambos.
Se recomienda incluir las propiedades en el modelo que se asignan a las claves externas en la base de datos.
Con las propiedades de clave externa incluidas, puede crear o cambiar una relación modificando el valor de
clave externa sobre un objeto dependiente. Este tipo de asociación se denomina asociación de clave externa. El
uso de claves externas es incluso más importante cuando se trabaja con entidades desconectadas. Tenga en
cuenta que al trabajar con de 1 a 1 o de 1 a 0. 1 relaciones, no hay ninguna columna de clave externa
independiente, la propiedad de clave principal actúa como clave externa y siempre se incluye en el modelo.
Cuando las columnas de clave externa no se incluyen en el modelo, la información de asociación se administra
como un objeto independiente. Se realiza un seguimiento de las relaciones a través de referencias de objeto en
lugar de propiedades de clave externa. Este tipo de asociación se denomina asociación independiente. La
manera más común de modificar una asociación independiente es modificar las propiedades de navegación que
se generan para cada entidad que participa en la asociación.
Puede decidir entre utilizar uno o ambos tipos de asociaciones en su modelo. Sin embargo, si tiene una relación
de varios a varios pura que está conectada mediante una tabla de combinación que solo contiene claves
externas, el EF usará una asociación independiente para administrar dicha relación de varios a varios.
La siguiente imagen muestra un modelo conceptual que se creó con el Entity Framework Designer. El modelo
contiene dos entidades que participan en una relación de uno a varios. Ambas entidades tienen propiedades de
navegación. Course es la entidad dependiente y tiene definida la propiedad de clave externa depar tmentId .

En el fragmento de código siguiente se muestra el mismo modelo que se creó con Code First.

public class Course


{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public int DepartmentID { get; set; }
public virtual Department Department { get; set; }
}

public class Department


{
public Department()
{
[Link] = new HashSet<Course>();
}
public int DepartmentID { get; set; }
public string Name { get; set; }
public decimal Budget { get; set; }
public DateTime StartDate { get; set; }
public int? Administrator {get ; set; }
public virtual ICollection<Course> Courses { get; set; }
}

Configurar o asignar relaciones


En el resto de esta página se explica cómo obtener acceso a los datos y cómo manipularlos mediante relaciones.
Para obtener información sobre cómo configurar las relaciones en el modelo, vea las páginas siguientes.
Para configurar relaciones en Code First, vea anotaciones de datos y API fluida: relaciones.
Para configurar relaciones mediante el Entity Framework Designer, consulte relaciones con el diseñador de
EF.

Crear y modificar relaciones


En una Asociación de clave externa, al cambiar la relación, el estado de un objeto dependiente con un
[Link] estado cambia a [Link] . En una relación independiente, al cambiar la
relación no se actualiza el estado del objeto dependiente.
En los siguientes ejemplos se muestra cómo usar las propiedades de clave externa y las propiedades de
navegación para asociar los objetos relacionados. Con las asociaciones de clave externa, puede usar cualquiera
de los métodos para cambiar, crear o modificar las relaciones. Con asociaciones independientes, no puede
utilizar la propiedad de clave externa.
Mediante la asignación de un nuevo valor a una propiedad de clave externa, como en el ejemplo
siguiente.

[Link] = [Link];

En el código siguiente se quita una relación estableciendo la clave externa en null . Tenga en cuenta que la
propiedad de clave externa debe admitir valores NULL.

[Link] = null;

NOTE
Si la referencia se encuentra en el estado Added (en este ejemplo, el objeto Course), la propiedad de navegación
Reference no se sincronizará con los valores de clave de un nuevo objeto hasta que se llame a SaveChanges. La
sincronización no se produce porque el contexto del objeto no contiene claves permanentes para objetos
agregados hasta que se guardan. Si debe tener nuevos objetos sincronizados completamente en cuanto
establezca la relación, use uno de los métodos siguientes. *

Asignando un nuevo objeto a una propiedad de navegación. En el código siguiente se crea una relación
entre un curso y un department . Si los objetos están asociados al contexto, course también se agrega a
la [Link] colección y la propiedad de clave externa correspondiente en el course objeto se
establece en el valor de la propiedad clave del Departamento.

[Link] = department;

Para eliminar la relación, establezca la propiedad de navegación en null . Si está trabajando con Entity
Framework basado en .NET 4,0, el extremo relacionado debe cargarse antes de establecerlo en NULL. Por
ejemplo:

[Link](course).Reference(c => [Link]).Load();


[Link] = null;

A partir de Entity Framework 5,0, que se basa en .NET 4,5, puede establecer la relación en NULL sin
cargar el extremo relacionado. También puede establecer el valor actual en NULL mediante el método
siguiente.

[Link](course).Reference(c => [Link]).CurrentValue = null;

Eliminando o agregando un objeto en una colección de entidades. Por ejemplo, puede Agregar un objeto
de tipo Course a la [Link] colección. Esta operación crea una relación entre un curso
determinado y un determinado department . Si los objetos están asociados al contexto, la referencia de
departamento y la propiedad de clave externa en el objeto Course se establecerán en el adecuado
department .
[Link](newCourse);

Mediante el ChangeRelationshipState método para cambiar el estado de la relación especificada entre


dos objetos entidad. Este método se utiliza normalmente cuando se trabaja con aplicaciones de N niveles
y una asociación independiente (no se puede usar con una asociación de clave externa). Además, para
usar este método, debe desplegar en ObjectContext , como se muestra en el ejemplo siguiente.
En el ejemplo siguiente, hay una relación de varios a varios entre instructores y cursos. Llamar al
ChangeRelationshipState método y pasar el [Link] parámetro permite SchoolContext saber
que se ha agregado una relación entre los dos objetos:

((IObjectContextAdapter)context).ObjectContext.
ObjectStateManager.
ChangeRelationshipState(course, instructor, c => [Link], [Link]);

Tenga en cuenta que si está actualizando (no solo agregando) una relación, debe eliminar la relación
anterior después de agregar la nueva:

((IObjectContextAdapter)context).ObjectContext.
ObjectStateManager.
ChangeRelationshipState(course, oldInstructor, c => [Link], [Link]);

Sincronizar los cambios entre las claves externas y las propiedades de


navegación
Al cambiar la relación de los objetos adjuntos al contexto mediante uno de los métodos descritos anteriormente,
Entity Framework necesita mantener sincronizadas las claves externas, las referencias y las colecciones. Entity
Framework administra automáticamente esta sincronización (también conocida como corrección de relación)
para las entidades POCO con servidores proxy. Para obtener más información, consulte trabajar con servidores
proxy.
Si usa entidades POCO sin proxy, debe asegurarse de que se llama al método DetectChanges para sincronizar
los objetos relacionados en el contexto. Tenga en cuenta que las siguientes API desencadenan automáticamente
una llamada a DetectChanges .
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
[Link]
Ejecutar una consulta LINQ en un DbSet

Cargar objetos relacionados


En Entity Framework normalmente se usan las propiedades de navegación para cargar entidades relacionadas
con la entidad devuelta por la Asociación definida. Para obtener más información, vea cargar objetos
relacionados.

NOTE
En una asociación de clave externa, al cargar un extremo relacionado de un objeto dependiente, el objeto relacionado se
cargará dependiendo del valor de clave externa del objeto dependiente actualmente en memoria:

// Get the course where currently DepartmentID = 2.


Course course = [Link](c => [Link] == 2);

// Use DepartmentID foreign key property


// to change the association.
[Link] = 3;

// Load the related Department where DepartmentID = 3


[Link](course).Reference(c => [Link]).Load();

En una asociación independiente, se consulta el extremo relacionado de un objeto dependiente de acuerdo con
el valor de clave externa actualmente en la base de datos. Sin embargo, si se modificó la relación y la propiedad
Reference del objeto dependiente apunta a un objeto principal diferente que se carga en el contexto del objeto,
Entity Framework intentará crear una relación tal y como se define en el cliente.

Administrar la simultaneidad
En las asociaciones de clave externa e independiente, las comprobaciones de simultaneidad se basan en las
claves de entidad y otras propiedades de entidad que se definen en el modelo. Al usar el diseñador de EF para
crear un modelo, establezca el ConcurrencyMode atributo en fixed para especificar que se debe comprobar la
simultaneidad de la propiedad. Al usar Code First para definir un modelo, use la ConcurrencyCheck anotación en
las propiedades cuya simultaneidad desea comprobar. Al trabajar con Code First también puede usar la
TimeStamp anotación para especificar que se debe comprobar la simultaneidad de la propiedad. Solo puede
tener una propiedad timestamp en una clase determinada. Code First asigna esta propiedad a un campo que no
acepta valores NULL en la base de datos.
Se recomienda usar siempre la Asociación de clave externa al trabajar con entidades que participan en la
comprobación de simultaneidad y la resolución.
Para obtener más información, vea controlar los conflictos de simultaneidad.

Trabajar con claves superpuestas


Las claves superpuestas son claves compuestas en las que algunas propiedades de la clave también forman
parte de otra clave de la entidad. No es posible tener una clave superpuesta en una asociación independiente.
Para cambiar una asociación de clave externa que incluya claves superpuestas, recomendamos modificar los
valores de clave externa en lugar de utilizar las referencias a objetos.
Consulta asincrónica y guardar
12/03/2021 • 11 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

EF6 presentó la compatibilidad con la consulta asincrónica y la guarda con las palabras clave Async y Await que
se introdujeron en .net 4,5. Aunque no todas las aplicaciones pueden beneficiarse de asincronía, se puede usar
para mejorar la capacidad de respuesta del cliente y la escalabilidad del servidor al administrar tareas de
ejecución prolongada, de red o enlazadas a e/s.

Cuándo usar realmente Async


El propósito de este tutorial es presentar los conceptos de Async de una manera que facilite la observación de la
diferencia entre la ejecución de programas asincrónica y sincrónica. Este tutorial no pretende ilustrar ninguno de
los escenarios clave donde la programación asincrónica proporciona ventajas.
La programación asincrónica se centra principalmente en liberar el subproceso administrado actual (subproceso
que ejecuta código .NET) para realizar otro trabajo mientras espera una operación que no requiere tiempo de
proceso de un subproceso administrado. Por ejemplo, mientras el motor de base de datos está procesando una
consulta, no hay nada que hacer por código .NET.
En las aplicaciones cliente (WinForms, WPF, etc.), el subproceso actual se puede usar para mantener la capacidad
de respuesta de la interfaz de usuario mientras se realiza la operación asincrónica. En las aplicaciones de
servidor ([Link] etc.), el subproceso se puede usar para procesar otras solicitudes entrantes; esto puede
reducir el uso de memoria o aumentar el rendimiento del servidor.
En la mayoría de las aplicaciones que usan Async no tendrá ventajas apreciables e incluso podría ser perjudicial.
Use las pruebas, la generación de perfiles y el sentido común para medir el impacto de Async en su escenario
concreto antes de confirmarlo.
Estos son algunos recursos adicionales para obtener información sobre Async:
Información general de Brandon Bray de Async/Await en .NET 4,5
Páginas de programación asincrónicas en MSDN Library
Cómo crear aplicaciones Web de [Link] mediante Async (incluye una demostración del aumento del
rendimiento del servidor)

Creación del modelo


Usaremos el flujo de trabajo Code First para crear nuestro modelo y generar la base de datos; sin embargo, la
funcionalidad asincrónica funcionará con todos los modelos EF, incluidos los creados con el diseñador EF.
Crear una aplicación de consola y llamarla AsyncDemo
Adición del paquete NuGet de EntityFramework
En Explorador de soluciones, haga clic con el botón derecho en el proyecto AsyncDemo
Seleccione administrar paquetes NuGet.. .
En el cuadro de diálogo administrar paquetes NuGet, seleccione la pestaña en línea y elija el paquete
EntityFramework
Haz clic en Instalar
Agregue una clase [Link] con la siguiente implementación

using [Link];
using [Link];

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

public class Blog


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

public virtual 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 virtual Blog Blog { get; set; }
}
}

Crear un programa sincrónico


Ahora que tenemos un modelo EF, vamos a escribir código que lo usa para realizar algún acceso a los datos.
Reemplace el contenido de [Link] por el código siguiente.
using System;
using [Link];

namespace AsyncDemo
{
class Program
{
static void Main(string[] args)
{
PerformDatabaseOperations();

[Link]("Quote of the day");


[Link](" Don't worry about the world coming to an end today... ");
[Link](" It's already tomorrow in Australia.");

[Link]();
[Link]("Press any key to exit...");
[Link]();
}

public static void PerformDatabaseOperations()


{
using (var db = new BloggingContext())
{
// Create a new blog and save it
[Link](new Blog
{
Name = "Test Blog #" + ([Link]() + 1)
});
[Link]("Calling SaveChanges.");
[Link]();
[Link]("SaveChanges completed.");

// Query for all blogs ordered by name


[Link]("Executing query.");
var blogs = (from b in [Link]
orderby [Link]
select b).ToList();

// Write all blogs out to Console


[Link]("Query completed with following results:");
foreach (var blog in blogs)
{
[Link](" " + [Link]);
}
}
}
}
}

Este código llama al PerformDatabaseOperations método, que guarda un nuevo blog en la base de datos y, a
continuación, recupera todos los blogs de la base de datos y los imprime en la consola . Después, el programa
escribe una comilla del día en la consola .
Dado que el código es sincrónico, podemos observar el siguiente flujo de ejecución al ejecutar el programa:
1. SaveChanges comienza a introducir el nuevo blog en la base de datos
2. SaveChanges se completa
3. La consulta de todos los blogs se envía a la base de datos
4. Las devoluciones de consultas y los resultados se escriben en la consola
5. La oferta del día se escribe en la consola
Convertirlo en asincrónico
Ahora que tenemos nuestro programa en funcionamiento, podemos empezar a usar las nuevas palabras clave
Async y Await. Hemos realizado los siguientes cambios en [Link]
1. Línea 2: la instrucción using para el [Link] espacio de nombres nos permite acceder a los
métodos de extensión de EF Async.
2. Línea 4: la instrucción using para el [Link] espacio de nombres nos permite usar el Task
tipo.
3. Línea 12 & 18: se está realizando la captura como tarea que supervisa el progreso de
PerformSomeDatabaseOperations (línea 12) y, después, bloqueando la ejecución del programa para que esta
tarea se complete una vez completado todo el trabajo para el programa (línea 18).
4. Línea 25: PerformSomeDatabaseOperations hay una actualización que se debe marcar como async y devolver
un Task .
5. Línea 35: ahora se llama a la versión asincrónica de SaveChanges y se espera que se complete.
6. Línea 42: ahora se llama a la versión asincrónica de ToList y se espera en el resultado.

Para obtener una lista completa de los métodos de extensión disponibles en el [Link] espacio de
nombres, consulte la QueryableExtensions clase. También deberá agregar using [Link] las
instrucciones using.
using System;
using [Link];
using [Link];
using [Link];

namespace AsyncDemo
{
class Program
{
static void Main(string[] args)
{
var task = PerformDatabaseOperations();

[Link]("Quote of the day");


[Link](" Don't worry about the world coming to an end today... ");
[Link](" It's already tomorrow in Australia.");

[Link]();

[Link]();
[Link]("Press any key to exit...");
[Link]();
}

public static async Task PerformDatabaseOperations()


{
using (var db = new BloggingContext())
{
// Create a new blog and save it
[Link](new Blog
{
Name = "Test Blog #" + ([Link]() + 1)
});
[Link]("Calling SaveChanges.");
await [Link]();
[Link]("SaveChanges completed.");

// Query for all blogs ordered by name


[Link]("Executing query.");
var blogs = await (from b in [Link]
orderby [Link]
select b).ToListAsync();

// Write all blogs out to Console


[Link]("Query completed with following results:");
foreach (var blog in blogs)
{
[Link](" - " + [Link]);
}
}
}
}
}

Ahora que el código es asincrónico, podemos observar otro flujo de ejecución cuando se ejecuta el programa:
1. SaveChanges comienza a introducir el nuevo blog en la base de datos
Una vez que el comando se envía a la base de datos, no se necesita más tiempo de proceso en el subproceso
administrado actual. El PerformDatabaseOperations método devuelve (aunque no ha terminado de ejecutarse)
y continúa el flujo del programa en el método Main.
2. La ofer ta del día se escribe en la consola
Dado que no hay más trabajo que hacer en el método Main, el subproceso administrado se bloquea en la
Wait llamada hasta que se completa la operación de base de datos. Una vez que se completa, se ejecutará el
resto de PerformDatabaseOperations .
3. SaveChanges se completa
4. La consulta de todos los blogs se envía a la base de datos
De nuevo, el subproceso administrado es gratuito para realizar otro trabajo mientras la consulta se procesa
en la base de datos. Dado que el resto de la ejecución se ha completado, el subproceso se detendrá
simplemente en la llamada de espera.
5. Las devoluciones de consultas y los resultados se escriben en la consola

La ventaja
Ahora hemos visto lo fácil que es hacer uso de los métodos asincrónicos de EF. Aunque es posible que las
ventajas de Async no sean muy evidentes con una aplicación de consola simple, se pueden aplicar estas mismas
estrategias en situaciones en las que las actividades de ejecución prolongada o enlazadas a la red podrían
bloquear la aplicación, o hacer que un gran número de subprocesos aumente la superficie de memoria.
Configuración basada en código
12/03/2021 • 8 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

La configuración de una aplicación Entity Framework se puede especificar en un archivo de configuración


([Link]/[Link]) o mediante código. Este último se conoce como configuración basada en código.
La configuración de un archivo de configuración se describe en un artículo independiente. El archivo de
configuración tiene prioridad sobre la configuración basada en código. En otras palabras, si se establece una
opción de configuración en el código y en el archivo de configuración, se usa la configuración del archivo de
configuración.

Uso de DbConfiguration
La configuración basada en código en EF6 y versiones posteriores se consigue mediante la creación de una
subclase de [Link] . Al crear subclases se deben seguir estas directrices
DbConfiguration :

Cree solo una DbConfiguration clase para la aplicación. Esta clase especifica la configuración de todo el
dominio de aplicación.
Coloque la DbConfiguration clase en el mismo ensamblado que la DbContext clase. (Vea la sección
DbConfiguration móvil si desea cambiar esto).
Asigne DbConfiguration a la clase un constructor público sin parámetros.
Establezca las opciones de configuración llamando a DbConfiguration métodos protegidos desde dentro de
este constructor.
Si sigue estas instrucciones, EF podrá detectar y usar la configuración automáticamente con las herramientas
que necesiten tener acceso a su modelo y cuando se ejecute la aplicación.

Ejemplo
Una clase derivada de DbConfiguration podría tener el siguiente aspecto:
using [Link];
using [Link];
using [Link];

namespace MyNamespace
{
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy("[Link]", () => new SqlAzureExecutionStrategy());
SetDefaultConnectionFactory(new LocalDbConnectionFactory("mssqllocaldb"));
}
}
}

Esta clase establece EF para usar la estrategia de ejecución de SQL Azure: para reintentar automáticamente las
operaciones de base de datos con errores y para usar la base de datos local para las bases de datos creadas por
Convención desde Code First.

Moverlo DbConfiguration
Hay casos en los que no es posible colocar la DbConfiguration clase en el mismo ensamblado que la DbContext
clase. Por ejemplo, puede tener dos DbContext clases en ensamblados distintos. Hay dos opciones para
controlar esto.
La primera opción es usar el archivo de configuración para especificar la DbConfiguration instancia que se va a
usar. Para ello, establezca el atributo codeConfigurationType de la sección entityFramework. Por ejemplo:

<entityFramework codeConfigurationType="[Link], MyAssembly">


...Your EF config...
</entityFramework>

El valor de codeConfigurationType debe ser el ensamblado y el nombre completo del espacio de nombres de la
DbConfiguration clase.

La segunda opción es colocar DbConfigurationTypeAttribute en la clase de contexto. Por ejemplo:

[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContextContext : DbContext
{
}

El valor que se pasa al atributo puede ser el DbConfiguration tipo, como se mostró anteriormente, o el
ensamblado y el nombre de espacio de nombres de tipo completo String. Por ejemplo:

[DbConfigurationType("[Link], MyAssembly")]
public class MyContextContext : DbContext
{
}

Establecer DbConfiguration explícitamente


Hay algunas situaciones en las que puede ser necesaria la configuración antes de que se haya DbContext usado
cualquier tipo. Estos son algunos ejemplos:
Usar DbModelBuilder para compilar un modelo sin un contexto
Usar algún otro código de la utilidad o del marco de trabajo que utiliza un DbContext donde se usa ese
contexto antes de que se use el contexto de la aplicación
En estas situaciones, EF no puede detectar la configuración automáticamente y, en su lugar, debe realizar una de
las siguientes acciones:
Establezca el DbConfiguration tipo en el archivo de configuración, como se describe en DbConfiguration la
sección anterior.
Llame al método estático DbConfiguration . Método SetConfiguration durante el inicio de la aplicación

Invalidar DbConfiguration
Hay algunas situaciones en las que es necesario invalidar el conjunto de configuración en el DbConfiguration .
Normalmente, los desarrolladores de aplicaciones no lo hacen, sino que los proveedores y complementos de
terceros que no pueden usar una DbConfiguration clase derivada.
Para ello, EntityFramework permite registrar un controlador de eventos que puede modificar la configuración
existente justo antes de que se bloquee. También proporciona un método de azúcar específico para reemplazar
cualquier servicio devuelto por el localizador del servicio EF. Así es como se pretende usar:
En el inicio de la aplicación (antes de que se use EF), el complemento o el proveedor deben registrar el
método de control de eventos para este evento. (Tenga en cuenta que esto debe ocurrir antes de que la
aplicación use EF).
El controlador de eventos realiza una llamada a ReplaceService para cada servicio que debe reemplazarse.
Por ejemplo, para reemplazar IDbConnectionFactory y DbProviderService registraría un controlador similar al
siguiente:

[Link] += (_, a) =>


{
[Link]<DbProviderServices>((s, k) => new MyProviderServices(s));
[Link]<IDbConnectionFactory>((s, k) => new MyConnectionFactory(s));
};

En el código anterior, MyProviderServices y MyConnectionFactory representan las implementaciones del servicio.


También puede agregar controladores de dependencia adicionales para obtener el mismo efecto.
Tenga en cuenta que también puede ajustar DbProviderFactory de esta manera, pero esto solo afectará a EF y no
a los usos de DbProviderFactory fuera de EF. Por esta razón, es probable que desee seguir ajustando
DbProviderFactory como tiene antes.

También debe tener en cuenta los servicios que se ejecutan externamente en la aplicación; por ejemplo, cuando
se ejecutan migraciones desde la consola del administrador de paquetes. Al ejecutar la migración desde la
consola, intentará encontrar su DbConfiguration . Sin embargo, tanto si obtendrá el servicio ajustado como si
no depende de dónde se registró el controlador de eventos. Si se registra como parte de la construcción de
DbConfiguration , el código debe ejecutarse y el servicio debe ajustarse. Normalmente, este no es el caso y esto
significa que las herramientas no obtendrán el servicio ajustado.
Configuración del archivo de configuración
12/03/2021 • 13 minutes to read

Entity Framework permite especificar una serie de valores desde el archivo de configuración. En general, EF
sigue un comportamiento de "Convención sobre configuración": todos los valores de configuración descritos en
esta publicación tienen un comportamiento predeterminado, solo tiene que preocuparse por cambiar el valor
cuando el valor predeterminado ya no satisface sus requisitos.

Code-Based alternativa
Todos estos valores también se pueden aplicar mediante código. A partir de EF6, se presentó la configuración
basada en código, que proporciona una manera centralizada de aplicar la configuración desde el código. Antes
de EF6, todavía se puede aplicar la configuración desde el código, pero es necesario usar varias API para
configurar distintas áreas. La opción del archivo de configuración permite cambiar fácilmente esta configuración
durante la implementación sin necesidad de actualizar el código.

La sección de configuración Entity Framework


A partir de EF 4.1, podría establecer el inicializador de base de datos para un contexto mediante la sección
appSettings del archivo de configuración. En EF 4,3, se presentó la sección entityFramework personalizada
para controlar la nueva configuración. Entity Framework seguirá reconociendo los inicializadores de base de
datos establecidos con el formato antiguo, pero se recomienda pasar al nuevo formato siempre que sea posible.
La sección entityFramework se agregó automáticamente al archivo de configuración del proyecto al instalar el
paquete NuGet entityFramework.

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


<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit [Link]
LinkID=237468 -->
<section name="entityFramework"
type="[Link], EntityFramework,
Version=[Link], Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</configSections>
</configuration>

Cadenas de conexión
En esta página se proporcionan más detalles sobre cómo Entity Framework determina la base de datos que se
va a usar, incluidas las cadenas de conexión en el archivo de configuración.
Las cadenas de conexión van en el elemento connectionStrings estándar y no requieren la sección
entityFramework .
Los modelos basados en Code First usan cadenas de conexión [Link] normales. Por ejemplo:

<connectionStrings>
<add name="BlogContext"
providerName="[Link]"
connectionString="Server=.\SQLEXPRESS;Database=Blogging;Integrated Security=True;"/>
</connectionStrings>
Los modelos basados en el diseñador EF usan cadenas de conexión EF especiales. Por ejemplo:

<connectionStrings>
<add name="BlogContext"
connectionString=
"metadata=
res://*/[Link]|
res://*/[Link]|
res://*/[Link];
provider=[Link];
provider connection string=
&quot;data source=(localdb)\mssqllocaldb;
initial catalog=Blogging;
integrated security=True;
multipleactiveresultsets=True;&quot;"
providerName="[Link]" />
</connectionStrings>

Code-Based tipo de configuración (EF6 en adelante)


A partir de EF6, puede especificar el DbConfiguration para EF que se va a usar para la configuración basada en
código en la aplicación. En la mayoría de los casos, no es necesario especificar esta configuración, ya que EF
detectará automáticamente el DbConfiguration. Para obtener detalles sobre Cuándo es posible que tenga que
especificar DbConfiguration en el archivo de configuración, consulte la sección mover DbConfiguration de
configuración basada en código.
Para establecer un tipo DbConfiguration, especifique el nombre del tipo calificado con el ensamblado en el
elemento codeConfigurationType .

NOTE
Un nombre completo de ensamblado es el nombre completo del espacio de nombres, seguido de una coma, el
ensamblado en el que reside el tipo. También puede especificar la versión del ensamblado, la referencia cultural y el token
de clave pública.

<entityFramework codeConfigurationType="[Link], MyAssembly">


</entityFramework>

Proveedores de bases de datos de EF (EF6 en adelante)


Antes de EF6, los elementos específicos de Entity Framework de un proveedor de base de datos debían incluirse
como parte del proveedor [Link] básico. A partir de EF6, las partes específicas de EF ahora se administran y
registran por separado.
Normalmente no necesitará registrar los proveedores. Esto lo realiza normalmente el proveedor cuando se
instala.
Los proveedores se registran mediante la inclusión de un elemento de proveedor en la sección de
proveedores secundarios de la sección entityFramework . Hay dos atributos necesarios para una entrada de
proveedor:
invariantName identifica el proveedor [Link] principal al que se destina este proveedor EF
Type es el nombre de tipo calificado con el ensamblado de la implementación del proveedor EF
NOTE
Un nombre completo de ensamblado es el nombre completo del espacio de nombres, seguido de una coma, el
ensamblado en el que reside el tipo. También puede especificar la versión del ensamblado, la referencia cultural y el token
de clave pública.

Como ejemplo, aquí se muestra la entrada que se crea para registrar el proveedor de SQL Server
predeterminado al instalar Entity Framework.

<providers>
<provider invariantName="[Link]" type="[Link],
[Link]" />
</providers>

Interceptores (EF 6.1 y versiones posteriores)


A partir de EF 6.1, puede registrar los interceptores en el archivo de configuración. Los interceptores permiten
ejecutar lógica adicional cuando EF realiza ciertas operaciones, como la ejecución de consultas de base de datos,
la apertura de conexiones, etc.
Los interceptores se registran mediante la inclusión de un elemento interceptor en la sección de
interceptores secundaria de la sección entityFramework . Por ejemplo, la configuración siguiente registra el
interceptor de DatabaseLogger integrado que registrará todas las operaciones de base de datos en la consola.

<interceptors>
<interceptor type="[Link], EntityFramework"/>
</interceptors>

Registro de operaciones de base de datos en un archivo (EF 6.1 en adelante )


El registro de los interceptores mediante el archivo de configuración es especialmente útil si desea agregar el
registro a una aplicación existente para ayudar a depurar un problema. DatabaseLogger admite el registro en
un archivo proporcionando el nombre de archivo como un parámetro de constructor.

<interceptors>
<interceptor type="[Link], EntityFramework">
<parameters>
<parameter value="C:\Temp\[Link]"/>
</parameters>
</interceptor>
</interceptors>

De forma predeterminada, esto hará que el archivo de registro se sobrescriba con un nuevo archivo cada vez
que se inicie la aplicación. En su lugar, anexe al archivo de registro si ya existe, use algo parecido a:

<interceptors>
<interceptor type="[Link], EntityFramework">
<parameters>
<parameter value="C:\Temp\[Link]"/>
<parameter value="true" type="[Link]"/>
</parameters>
</interceptor>
</interceptors>

Para obtener información adicional sobre DatabaseLogger y el registro de interceptores, consulte la entrada
de blog EF 6,1: activar el registro sin volver a compilar.

Code First factoría de conexión predeterminada


La sección configuración permite especificar un generador de conexiones predeterminado que Code First debe
usar para buscar una base de datos que se utilizará para un contexto. El generador de conexiones
predeterminado solo se usa cuando no se ha agregado ninguna cadena de conexión al archivo de configuración
para un contexto.
Al instalar el paquete de NuGet de EF, se registró una factoría de conexión predeterminada que señala a SQL
Express o a LocalDB, en función de la que haya instalado.
Para establecer un generador de conexiones, especifique el nombre del tipo calificado con el ensamblado en el
elemento defaultConnectionFactor y .

NOTE
Un nombre completo de ensamblado es el nombre completo del espacio de nombres, seguido de una coma, el
ensamblado en el que reside el tipo. También puede especificar la versión del ensamblado, la referencia cultural y el token
de clave pública.

A continuación se muestra un ejemplo de configuración de su propio generador de conexiones predeterminado:

<entityFramework>
<defaultConnectionFactory type="[Link], MyAssembly"/>
</entityFramework>

En el ejemplo anterior se requiere que el generador personalizado tenga un constructor sin parámetros. Si es
necesario, puede especificar parámetros de constructor mediante el elemento Parameters .
Por ejemplo, SqlCeConnectionFactory, que se incluye en Entity Framework, requiere que proporcione un
nombre invariable de proveedor al constructor. El nombre invariable del proveedor identifica la versión de SQL
Compact que quiere usar. La configuración siguiente hará que los contextos usen SQL Compact versión 4,0 de
forma predeterminada.

<entityFramework>
<defaultConnectionFactory type="[Link],
EntityFramework">
<parameters>
<parameter value="[Link].4.0" />
</parameters>
</defaultConnectionFactory>
</entityFramework>

Si no establece un generador de conexiones predeterminado, Code First usa SqlConnectionFactory, que apunta
a .\SQLEXPRESS . SqlConnectionFactory también tiene un constructor que permite invalidar partes de la cadena
de conexión. Si desea utilizar una instancia de SQL Server distinta de .\SQLEXPRESS , puede usar este
constructor para establecer el servidor.
La configuración siguiente hará que Code First use MyDatabaseSer ver para los contextos que no tienen un
conjunto de cadenas de conexión explícitas.
<entityFramework>
<defaultConnectionFactory type="[Link], EntityFramework">
<parameters>
<parameter value="Data Source=MyDatabaseServer; Integrated Security=True;
MultipleActiveResultSets=True" />
</parameters>
</defaultConnectionFactory>
</entityFramework>

De forma predeterminada, se supone que los argumentos del constructor son del tipo cadena. Puede usar el
atributo type para cambiar esto.

<parameter value="2" type="System.Int32" />

Inicializadores de base de datos


Los inicializadores de base de datos se configuran en función del contexto. Se pueden establecer en el archivo
de configuración mediante el elemento de contexto . Este elemento usa el nombre completo del ensamblado
para identificar el contexto que se está configurando.
De forma predeterminada, Code First contextos se configuran para usar el inicializador
CreateDatabaseIfNotExists. Hay un atributo disableDatabaseInitialization en el elemento de contexto que se
puede usar para deshabilitar la inicialización de la base de datos.
Por ejemplo, la siguiente configuración deshabilita la inicialización de la base de datos para el contexto blog.
BlogContext definido en [Link].

<contexts>
<context type=" [Link], MyAssembly" disableDatabaseInitialization="true" />
</contexts>

Puede usar el elemento databaseInitializer para establecer un inicializador personalizado.

<contexts>
<context type=" [Link], MyAssembly">
<databaseInitializer type="[Link], MyAssembly" />
</context>
</contexts>

Los parámetros de constructor utilizan la misma sintaxis que los generadores de conexiones predeterminados.

<contexts>
<context type=" [Link], MyAssembly">
<databaseInitializer type="[Link], MyAssembly">
<parameters>
<parameter value="MyConstructorParameter" />
</parameters>
</databaseInitializer>
</context>
</contexts>

Puede configurar uno de los inicializadores de base de datos genéricos que se incluyen en Entity Framework. El
atributo Type utiliza el formato de .NET Framework para los tipos genéricos.
Por ejemplo, si usa Migraciones de Code First, puede configurar la base de datos que se va a migrar
automáticamente mediante el MigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration> inicializador.

<contexts>
<context type="[Link], MyAssembly">
<databaseInitializer type="[Link]`2[[[Link],
MyAssembly], [[Link], MyAssembly]], EntityFramework" />
</context>
</contexts>
Cadenas de conexión y modelos
12/03/2021 • 10 minutes to read

En este tema se explica cómo Entity Framework detecta qué conexión de base de datos se va a usar y cómo se
puede cambiar. Los modelos creados con Code First y EF Designer se describen en este tema.
Normalmente, una aplicación Entity Framework usa una clase derivada de DbContext. Esta clase derivada
llamará a uno de los constructores de la clase base DbContext para controlar:
Cómo se conectará el contexto a una base de datos, es decir, cómo se encuentra o se usa una cadena de
conexión
Si el contexto usará calcular un modelo con Code First o cargar un modelo creado con el diseñador de EF
Opciones avanzadas adicionales
Los siguientes fragmentos muestran algunas de las formas en que se pueden usar los constructores DbContext.

Usar Code First con conexión por Convención


Si no ha realizado ninguna otra configuración en la aplicación, la llamada al constructor sin parámetros en
DbContext hará que DbContext se ejecute en modo de Code First con una conexión de base de datos creada por
Convención. Por ejemplo:

namespace [Link]
{
public class BloggingContext : DbContext
{
public BloggingContext()
// C# will call base class parameterless constructor by default
{
}
}
}

En este ejemplo DbContext usa el nombre completo del espacio de nombres de la clase de contexto derivada
(demo. EF. BloggingContext) como nombre de la base de datos y crea una cadena de conexión para esta base de
datos mediante SQL Express o LocalDB. Si ambos están instalados, se usará SQL Express.
Visual Studio 2010 incluye SQL Express de forma predeterminada y Visual Studio 2012 y versiones posteriores
incluyen LocalDB. Durante la instalación, el paquete NuGet de EntityFramework comprueba qué servidor de
base de datos está disponible. Después, el paquete NuGet actualizará el archivo de configuración estableciendo
el servidor de base de datos predeterminado que Code First usa al crear una conexión por Convención. Si SQL
Express se está ejecutando, se usará. Si SQL Express no está disponible, LocalDB se registrará como
predeterminado en su lugar. No se realiza ningún cambio en el archivo de configuración si ya contiene un valor
para el generador de conexiones predeterminado.

Usar Code First con la conexión por Convención y el nombre de base


de datos especificado
Si no ha realizado ninguna otra configuración en la aplicación, la llamada al constructor de cadena en DbContext
con el nombre de la base de datos que desea usar hará que DbContext se ejecute en modo de Code First con
una conexión de base de datos creada por Convención en la base de datos de ese nombre. Por ejemplo:
public class BloggingContext : DbContext
{
public BloggingContext()
: base("BloggingDatabase")
{
}
}

En este ejemplo DbContext usa "BloggingDatabase" como nombre de la base de datos y crea una cadena de
conexión para esta base de datos mediante SQL Express (instalado con Visual Studio 2010) o LocalDB (instalado
con Visual Studio 2012). Si ambos están instalados, se usará SQL Express.

Usar Code First con la cadena de conexión en el archivo de


[Link]/[Link]
Puede optar por colocar una cadena de conexión en el archivo de [Link] o [Link]. Por ejemplo:

<configuration>
<connectionStrings>
<add name="BloggingCompactDatabase"
providerName="[Link].4.0"
connectionString="Data Source=[Link]"/>
</connectionStrings>
</configuration>

Esta es una manera fácil de indicar a DbContext que use un servidor de base de datos que no sea SQL Express o
LocalDB; el ejemplo anterior especifica una base de datos de SQL Server Compact Edition.
Si el nombre de la cadena de conexión coincide con el nombre del contexto (ya sea con o sin calificación de
espacio de nombres), DbContext lo buscará cuando se use el constructor sin parámetros. Si el nombre de la
cadena de conexión es diferente del nombre del contexto, puede indicar a DbContext que use esta conexión en
el modo Code First pasando el nombre de la cadena de conexión al constructor DbContext. Por ejemplo:

public class BloggingContext : DbContext


{
public BloggingContext()
: base("BloggingCompactDatabase")
{
}
}

Como alternativa, puede usar el formato "name = <connection string name> " para la cadena que se pasa al
constructor DbContext. Por ejemplo:

public class BloggingContext : DbContext


{
public BloggingContext()
: base("name=BloggingCompactDatabase")
{
}
}

Este formulario hace que sea explícito esperar que la cadena de conexión se encuentre en el archivo de
configuración. Se producirá una excepción si no se encuentra una cadena de conexión con el nombre
especificado.
Base de datos/Model First con una cadena de conexión en el archivo
de [Link]/[Link]
Los modelos creados con EF Designer son diferentes de Code First en que el modelo ya existe y no se genera a
partir del código cuando se ejecuta la aplicación. El modelo normalmente existe como archivo EDMX en el
proyecto.
El diseñador agregará una cadena de conexión EF al archivo [Link] o [Link]. Esta cadena de conexión es
especial, ya que contiene información sobre cómo encontrar la información en el archivo EDMX. Por ejemplo:

<configuration>
<connectionStrings>
<add name="Northwind_Entities"
connectionString="metadata=res://*/[Link]|
res://*/[Link]|
res://*/[Link];
provider=[Link];
provider connection string=
&quot;Data Source=.\sqlexpress;
Initial Catalog=Northwind;
Integrated Security=True;
MultipleActiveResultSets=True&quot;"
providerName="[Link]"/>
</connectionStrings>
</configuration>

El diseñador de EF también generará código que indica a DbContext que use esta conexión pasando el nombre
de la cadena de conexión al constructor DbContext. Por ejemplo:

public class NorthwindContext : DbContext


{
public NorthwindContext()
: base("name=Northwind_Entities")
{
}
}

DbContext sabe cargar el modelo existente (en lugar de usar Code First para calcularlo a partir del código)
porque la cadena de conexión es una cadena de conexión EF que contiene los detalles del modelo que se va a
usar.

Otras opciones del constructor DbContext


La clase DbContext contiene otros constructores y patrones de uso que permiten algunos escenarios más
avanzados. Algunas de éstas son:
Puede usar la clase DbModelBuilder para crear un modelo de Code First sin crear instancias de una instancia
de DbContext. El resultado de este es un objeto DbModel. Después, puede pasar este objeto DbModel a uno
de los constructores DbContext cuando esté listo para crear la instancia de DbContext.
Puede pasar una cadena de conexión completa a DbContext en lugar de solo la base de datos o el nombre de
la cadena de conexión. De forma predeterminada, esta cadena de conexión se usa con el proveedor System.
Data. SqlClient; Esto se puede cambiar estableciendo una implementación diferente de IConnectionFactory
en el contexto. Database. DefaultConnectionFactory.
Puede usar un objeto DbConnection existente pasándolo a un constructor DbContext. Si el objeto de
conexión es una instancia de EntityConnection, se usará el modelo especificado en la conexión en lugar de
calcular un modelo mediante Code First. Si el objeto es una instancia de algún otro tipo (por ejemplo,
SqlConnection), el contexto lo utilizará para el modo de Code First.
Puede pasar un ObjectContext existente a un constructor DbContext para crear un DbContext que ajuste el
contexto existente. Se puede usar para las aplicaciones existentes que usan ObjectContext pero que desean
aprovechar DbContext en algunas partes de la aplicación.
Resolución de dependencias
12/03/2021 • 16 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

A partir de EF6, Entity Framework contiene un mecanismo de uso general para obtener implementaciones de
servicios que requiere. Es decir, cuando EF usa una instancia de algunas interfaces o clases base, solicitará una
implementación concreta de la interfaz o la clase base que se va a usar. Esto se logra mediante el uso de la
interfaz IDbDependencyResolver:

public interface IDbDependencyResolver


{
object GetService(Type type, object key);
}

EF normalmente llama al método GetService y se controla mediante una implementación de


IDbDependencyResolver proporcionada por EF o por la aplicación. Cuando se llama, el argumento de tipo es la
interfaz o el tipo de clase base del servicio que se solicita, y el objeto de clave es null o un objeto que
proporciona información contextual sobre el servicio solicitado.
A menos que se indique lo contrario, cualquier objeto devuelto debe ser seguro para subprocesos, ya que se
puede utilizar como singleton. En muchos casos, el objeto devuelto es un generador, en cuyo caso el propio
generador debe ser seguro para subprocesos, pero no es necesario que el objeto devuelto por el generador sea
seguro para subprocesos, ya que se solicita una nueva instancia desde el generador para cada uso.
En este artículo no se incluyen detalles completos sobre cómo implementar IDbDependencyResolver, sino que
actúa como referencia para los tipos de servicio (es decir, la interfaz y los tipos de clase base) para los que EF
llama a GetService y la semántica del objeto de clave para cada una de estas llamadas.

System. Data. Entity. IDatabaseInitializer<TContext>


Versión introducida : EF 6.0.0
Objeto devuelto : inicializador de base de datos para el tipo de contexto especificado
Clave : no se utiliza; será null

FUNC<System. Data. Entity. Migrations. SQL. MigrationSqlGenerator>


Versión introducida : EF 6.0.0
Objeto devuelto : generador para crear un generador de SQL que se puede usar para las migraciones y otras
acciones que hacen que se cree una base de datos, como la creación de bases de datos con inicializadores de
base de datos.
Key : una cadena que contiene el nombre invariable del proveedor [Link] que especifica el tipo de base de
datos para el que se generará SQL. Por ejemplo, se devuelve el generador de SQL Server SQL para la clave
"System. Data. SqlClient".
NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .

System. Data. Entity. Core. Common. DbProviderServices


Versión introducida : EF 6.0.0
Objeto devuelto : el proveedor EF que se va a usar para un nombre invariable de proveedor determinado
Key : una cadena que contiene el nombre invariable del proveedor [Link] que especifica el tipo de base de
datos para el que se necesita un proveedor. Por ejemplo, se devuelve el proveedor de SQL Server para la clave
"System. Data. SqlClient".

NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .

System. Data. Entity. Infrastructure. IDbConnectionFactory


Versión introducida : EF 6.0.0
Objeto devuelto : el generador de conexión que se usará cuando EF cree una conexión de base de datos por
Convención. Es decir, cuando no se proporciona ninguna conexión o cadena de conexión a EF, y no se encuentra
ninguna cadena de conexión en el [Link] o [Link], este servicio se usa para crear una conexión por
Convención. Cambiar el generador de conexiones puede permitir que EF use otro tipo de base de datos (por
ejemplo, SQL Server Compact Edition) de forma predeterminada.
Clave : no se utiliza; será null

NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .

System. Data. Entity. Infrastructure. IManifestTokenService


Versión introducida : EF 6.0.0
Objeto devuelto : un servicio que puede generar un token del manifiesto del proveedor a partir de una
conexión. Este servicio se usa normalmente de dos maneras. En primer lugar, se puede usar para evitar Code
First conectarse a la base de datos al compilar un modelo. En segundo lugar, se puede usar para forzar Code
First para generar un modelo para una versión de base de datos específica, por ejemplo, para forzar un modelo
para SQL Server 2005 incluso si se usa a veces SQL Server 2008.
Duración del objeto : singleton: el mismo objeto se puede usar varias veces y simultáneamente en diferentes
subprocesos
Clave : no se utiliza; será null

System. Data. Entity. Infrastructure. IDbProviderFactoryService


Versión introducida : EF 6.0.0
Objeto devuelto : un servicio que puede obtener un generador de proveedores de una conexión determinada.
En .NET 4,5, el proveedor es accesible públicamente desde la conexión. En .NET 4, la implementación
predeterminada de este servicio usa algunas heurísticas para encontrar el proveedor coincidente. Si se produce
un error, se puede registrar una nueva implementación de este servicio para proporcionar una solución
adecuada.
Clave : no se utiliza; será null

FUNC<DbContext, System. Data. Entity. Infrastructure.


IDbModelCacheKey>
Versión introducida : EF 6.0.0
Objeto devuelto : un generador que generará una clave de caché del modelo para un contexto determinado.
De forma predeterminada, EF almacena en caché un modelo por tipo DbContext por proveedor. Se puede usar
una implementación diferente de este servicio para agregar otra información, como el nombre de esquema, a la
clave de caché.
Clave : no se utiliza; será null

System. Data. Entity. Spatial. DbSpatialServices


Versión introducida : EF 6.0.0
Objeto devuelto : un proveedor espacial de EF que agrega compatibilidad con el proveedor de EF básico para
los tipos espaciales Geography y Geometry.
Clave : DbSptialServices se solicita de dos maneras. En primer lugar, se solicitan servicios espaciales específicos
del proveedor mediante un objeto DbProviderInfo (que contiene el nombre invariable y el token del manifiesto)
como clave. En segundo lugar, se puede solicitar a DbSpatialServices sin clave. Se usa para resolver el
"proveedor espacial global", que se usa al crear tipos independientes de DbGeography o DbGeometry.

NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .

FUNC<System. Data. Entity. Infrastructure. IDbExecutionStrategy>


Versión introducida : EF 6.0.0
Objeto devuelto : generador para crear un servicio que permite a un proveedor implementar reintentos u otro
comportamiento cuando las consultas y los comandos se ejecutan en la base de datos. Si no se proporciona
ninguna implementación, EF simplemente ejecutará los comandos y propagará las excepciones que se
produzcan. Por SQL Server este servicio se utiliza para proporcionar una directiva de reintentos que es
especialmente útil cuando se ejecuta en servidores de bases de datos basados en la nube, como SQL Azure.
Key : un objeto ExecutionStrategyKey que contiene el nombre invariable del proveedor y, opcionalmente, un
nombre de servidor para el que se usará la estrategia de ejecución.
NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .

FUNC<DbConnection, String, System. Data. Entity. Migrations. History.


HistoryContext>
Versión introducida : EF 6.0.0
Objeto devuelto : un generador que permite a un proveedor configurar la asignación de HistoryContext a la
__MigrationHistory tabla usada por las migraciones de EF. HistoryContext es un DbContext de Code First y se
puede configurar mediante la API fluida normal para cambiar aspectos como el nombre de la tabla y las
especificaciones de asignación de columnas.
Clave : no se utiliza; será null

NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .

System. Data. Common. DbProviderFactory


Versión introducida : EF 6.0.0
Objeto devuelto : el proveedor [Link] que se va a usar para un nombre invariable de proveedor
determinado.
Key : una cadena que contiene el nombre invariable del proveedor [Link]

NOTE
Normalmente, este servicio no se cambia directamente, ya que la implementación predeterminada usa el registro del
proveedor [Link] normal. Para obtener más información sobre los servicios relacionados con el proveedor en EF6,
consulte la sección modelo de proveedor EF6 .

System. Data. Entity. Infrastructure. IProviderInvariantName


Versión introducida : EF 6.0.0
Objeto devuelto : un servicio que se utiliza para determinar un nombre invariable de proveedor para un tipo
determinado de DbProviderFactory. La implementación predeterminada de este servicio utiliza el registro del
proveedor [Link]. Esto significa que si el proveedor [Link] no está registrado de la manera normal porque
EF está resolviendo DbProviderFactory, también será necesario para resolver este servicio.
Key : la instancia de DbProviderFactory para la que se requiere un nombre invariable.

NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .
System. Data. Entity. Core. Mapping. ViewGeneration.
IViewAssemblyCache
Versión introducida : EF 6.0.0
Objeto devuelto : memoria caché de los ensamblados que contienen las vistas generadas previamente. Un
reemplazo se usa normalmente para permitir que EF sepa qué ensamblados contienen vistas generadas
previamente sin realizar ninguna detección.
Clave : no se utiliza; será null

System. Data. Entity. Infrastructure. pluralización. IPluralizationService


Versión introducida : EF 6.0.0
Objeto devuelto : un servicio utilizado por EF para pluralar y singularar los nombres. De forma
predeterminada, se usa un servicio de pluralización en inglés.
Clave : no se utiliza; será null

System. Data. Entity. Infrastructure. intercepción. IDbInterceptor


Versión introducida : EF 6.0.0
Objetos devueltos : todos los interceptores que se deben registrar cuando se inicia la aplicación. Tenga en
cuenta que estos objetos se solicitan mediante la llamada a GetServices y todos los interceptores devueltos por
cualquier solucionador de dependencias se registrarán.
Clave : no se utiliza; será null.

FUNC<System. Data. Entity. DbContext, Action<String > , System.


Data. Entity. Infrastructure. intercepción. DatabaseLogFormatter>
Versión introducida : EF 6.0.0
Objeto devuelto : un generador que se usará para crear el formateador del registro de base de datos que se
utilizará cuando el contexto. La propiedad Database. log está establecida en el contexto especificado.
Clave : no se utiliza; será null.

FUNC<System. Data. Entity. DbContext>


Versión introducida : EF 6.1.0
Objeto devuelto : un generador que se usará para crear instancias de contexto para las migraciones cuando el
contexto no tenga un constructor sin parámetros accesible.
Key : el objeto de tipo para el tipo de DbContext derivado para el que se necesita un generador.

FUNC<System. Data. Entity. Core. Metadata. Edm.


IMetadataAnnotationSerializer>
Versión introducida : EF 6.1.0
Objeto devuelto : un generador que se utilizará para crear serializadores para la serialización de anotaciones
personalizadas fuertemente tipadas de modo que se puedan serializar y desesterilizar en XML para su uso en
migraciones de Code First.
Key : nombre de la anotación que se va a serializar o deserializar.

FUNC<System. Data. Entity. Infrastructure. TransactionHandler>


Versión introducida : EF 6.1.0
Objeto devuelto : un generador que se usará para crear controladores para las transacciones, de modo que se
pueda aplicar un control especial para situaciones como el control de errores de confirmación.
Key : un objeto ExecutionStrategyKey que contiene el nombre invariable del proveedor y, opcionalmente, un
nombre de servidor para el que se usará el controlador de transacciones.
Administración de conexiones
12/03/2021 • 8 minutes to read

En esta página se describe el comportamiento de Entity Framework con respecto a cómo pasar conexiones al
contexto y la funcionalidad de la API Database. Connection. Open () .

Pasar conexiones al contexto


Comportamiento de EF5 y versiones anteriores
Hay dos constructores que aceptan conexiones:

public DbContext(DbConnection existingConnection, bool contextOwnsConnection)


public DbContext(DbConnection existingConnection, DbCompiledModel model, bool contextOwnsConnection)

Es posible utilizarlos, pero tiene que solucionar un par de limitaciones:


1. Si pasa una conexión abierta a cualquiera de estas, la primera vez que el marco intenta utilizarla, se produce
una excepción InvalidOperationException que indica que no puede volver a abrir una conexión que ya está
abierta.
2. La marca contextOwnsConnection se interpreta para indicar si la conexión del almacén subyacente debe
desecharse cuando se desecha el contexto. Sin embargo, independientemente de esa configuración, la
conexión del almacén siempre se cierra cuando se desecha el contexto. Por lo tanto, si tiene más de un
DbContext con la misma conexión, el contexto que se elimine primero cerrará la conexión (de forma similar
si ha mezclado una conexión [Link] existente con un DbContext, DbContext siempre cerrará la conexión
cuando se elimine).
Es posible solucionar la primera limitación anterior si se pasa una conexión cerrada y solo se ejecuta código que
la abriría una vez que se han creado todos los contextos:
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace ConnectionManagementExamples
{
class ConnectionManagementExampleEF5
{
public static void TwoDbContextsOneConnection()
{
using (var context1 = new BloggingContext())
{
var conn =
((EntityConnection)
((IObjectContextAdapter)context1).[Link])
.StoreConnection;

using (var context2 = new BloggingContext(conn, contextOwnsConnection: false))


{
[Link](
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'");

var query = [Link](p => [Link] > 5);


foreach (var post in query)
{
[Link] += "[Cool Blog]";
}
[Link]();
}
}
}
}
}

La segunda limitación solo significa que debe abstenerse de desechar cualquier objeto DbContext hasta que esté
listo para que se cierre la conexión.
Comportamiento en EF6 y versiones futuras
En EF6 y versiones futuras, DbContext tiene los mismos dos constructores, pero ya no requiere que la conexión
pasada al constructor se cierre cuando se reciba. Por lo tanto, ahora es posible:
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace ConnectionManagementExamples
{
class ConnectionManagementExample
{
public static void PassingAnOpenConnection()
{
using (var conn = new SqlConnection("{connectionString}"))
{
[Link]();

var sqlCommand = new SqlCommand();


[Link] = conn;
[Link] =
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
[Link]();

using (var context = new BloggingContext(conn, contextOwnsConnection: false))


{
var query = [Link](p => [Link] > 5);
foreach (var post in query)
{
[Link] += "[Cool Blog]";
}
[Link]();
}

var sqlCommand2 = new SqlCommand();


[Link] = conn;
[Link] =
@"UPDATE Blogs SET Rating = 7" +
" WHERE Name LIKE '%Entity Framework Rocks%'";
[Link]();
}
}
}
}

Además, la marca contextOwnsConnection controla ahora si la conexión se cierra y se desecha cuando se


desecha el DbContext. Por lo tanto, en el ejemplo anterior, la conexión no se cierra cuando el contexto se desecha
(línea 32) tal y como se encontraba en versiones anteriores de EF, sino cuando se desecha la propia conexión
(línea 40).
Por supuesto, sigue siendo posible que DbContext tome el control de la conexión (solo tiene que establecer
contextOwnsConnection en true o usar uno de los otros constructores) si así lo desea.

NOTE
Existen algunas consideraciones adicionales al usar transacciones con este nuevo modelo. Para obtener más información,
consulte trabajar con transacciones.

Database. Connection. Open ()


Comportamiento de EF5 y versiones anteriores
En EF5 y versiones anteriores hay un error que indica que ObjectContext. Connection. State no se actualizó
para reflejar el verdadero estado de la conexión del almacén subyacente. Por ejemplo, si ejecutó el código
siguiente, se puede devolver el estado cerrado aunque de hecho la conexión del almacén subyacente esté
abier ta .

((IObjectContextAdapter)context).[Link]

Por separado, si abre la conexión de base de datos mediante una llamada a Database. Connection. Open (), se
abrirá hasta la próxima vez que ejecute una consulta o llame a cualquier elemento que requiera una conexión de
base de datos (por ejemplo, SaveChanges ()), pero después de que se cierre la conexión del almacén subyacente.
El contexto volverá a abrir y a cerrar la conexión cada vez que se requiera otra operación de base de datos:

using System;
using [Link];
using [Link];
using [Link];
using [Link];

namespace ConnectionManagementExamples
{
public class DatabaseOpenConnectionBehaviorEF5
{
public static void DatabaseOpenConnectionBehavior()
{
using (var context = new BloggingContext())
{
// At this point the underlying store connection is closed

[Link]();

// Now the underlying store connection is open


// (though [Link] will report closed)

var blog = new Blog { /* Blog’s properties */ };


[Link](blog);

// The underlying store connection is still open

[Link]();

// After SaveChanges() the underlying store connection is closed


// Each SaveChanges() / query etc now opens and immediately closes
// the underlying store connection

blog = new Blog { /* Blog’s properties */ };


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

Comportamiento en EF6 y versiones futuras


En el caso de EF6 y versiones futuras, hemos tomado el enfoque que es si el código de llamada elige abrir la
conexión llamando al contexto. Database. Connection. Open () tiene una buena razón para hacerlo y el marco
asumirá que desea tener el control sobre la apertura y el cierre de la conexión y dejará de cerrar la conexión
automáticamente.
NOTE
Esto puede dar lugar a conexiones que están abiertas durante mucho tiempo, por lo que debe usarse con cuidado.

También se actualizó el código para que ObjectContext. Connection. State ahora realiza un seguimiento del
estado de la conexión subyacente correctamente.

using System;
using [Link];
using [Link];
using [Link];
using [Link];

namespace ConnectionManagementExamples
{
internal class DatabaseOpenConnectionBehaviorEF6
{
public static void DatabaseOpenConnectionBehavior()
{
using (var context = new BloggingContext())
{
// At this point the underlying store connection is closed

[Link]();

// Now the underlying store connection is open and the


// [Link] correctly reports open too

var blog = new Blog { /* Blog’s properties */ };


[Link](blog);
[Link]();

// The underlying store connection remains open for the next operation

blog = new Blog { /* Blog’s properties */ };


[Link](blog);
[Link]();

// The underlying store connection is still open

} // The context is disposed – so now the underlying store connection is closed


}
}
}
Resistencia de conexión y lógica de reintento
12/03/2021 • 11 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

Las aplicaciones que se conectan a un servidor de base de datos siempre han sido vulnerables a las
interrupciones de conexión debido a errores de back-end y a la inestabilidad de red. Sin embargo, en un entorno
basado en LAN que trabaja con servidores de bases de datos dedicados, estos errores son lo suficientemente
raros como para que no se requiera la lógica adicional para controlar esos errores. Con el aumento de los
servidores de bases de datos basados en la nube, como Windows Azure SQL Database y las conexiones a través
de redes menos confiables, ahora es más común que se produzcan interrupciones de conexión. Esto puede
deberse a técnicas defensivas que usan las bases de datos en la nube para garantizar la equidad del servicio,
como la limitación de la conexión o la inestabilidad en la red, lo que produce tiempos de espera intermitentes y
otros errores transitorios.
La resistencia de la conexión hace referencia a la capacidad de EF de reintentar automáticamente cualquier
comando que produzca un error debido a estas interrupciones de conexión.

Estrategias de ejecución
El reintento de conexión se realiza a través de una implementación de la interfaz IDbExecutionStrategy. Las
implementaciones de IDbExecutionStrategy serán responsables de aceptar una operación y, si se produce una
excepción, determinar si es adecuado y volver a intentar si es así. Hay cuatro estrategias de ejecución que se
incluyen con EF:
1. DefaultExecutionStrategy : esta estrategia de ejecución no vuelve a intentar ninguna operación, es el valor
predeterminado para las bases de datos que no sean SQL Server.
2. DefaultSqlExecutionStrategy : esta es una estrategia de ejecución interna que se utiliza de forma
predeterminada. Sin embargo, esta estrategia no vuelve a intentarlo, sino que encapsulará las excepciones
que podrían ser transitorias para informar a los usuarios de que pueden querer habilitar la resistencia de la
conexión.
3. DbExecutionStrategy : esta clase es adecuada como una clase base para otras estrategias de ejecución,
incluidas sus propias personalizaciones. Implementa una directiva de reintentos exponencial, donde el
reintento inicial se produce con un retraso de cero y el retraso aumenta exponencialmente hasta que se
alcanza el número máximo de reintentos. Esta clase tiene un método ShouldRetryOn abstracto que se puede
implementar en estrategias de ejecución derivadas para controlar las excepciones que se deben Reintentar.
4. SqlAzureExecutionStrategy : esta estrategia de ejecución hereda de DbExecutionStrategy y volverá a
intentarlo en las excepciones que se sabe que son posiblemente transitorias al trabajar con Azure SQL
Database.

NOTE
Las estrategias de ejecución 2 y 4 se incluyen en el proveedor de SQL Server que se incluye con EF, que está en el
ensamblado EntityFramework. SqlServer y están diseñados para funcionar con SQL Server.
Habilitar una estrategia de ejecución
La manera más sencilla de indicar a EF que use una estrategia de ejecución es con el método
SetExecutionStrategy de la clase DbConfiguration :

public class MyConfiguration : DbConfiguration


{
public MyConfiguration()
{
SetExecutionStrategy("[Link]", () => new SqlAzureExecutionStrategy());
}
}

Este código indica a EF que use SqlAzureExecutionStrategy al conectarse a SQL Server.

Configuración de la estrategia de ejecución


El constructor de SqlAzureExecutionStrategy puede aceptar dos parámetros, MaxRetryCount y MaxDelay.
MaxRetry Count es el número máximo de veces que se reintentará la estrategia. MaxDelay es un intervalo de
tiempo que representa el retraso máximo entre los reintentos que usará la estrategia de ejecución.
Para establecer el número máximo de reintentos en 1 y el retraso máximo en 30 segundos, ejecutaría lo
siguiente:

public class MyConfiguration : DbConfiguration


{
public MyConfiguration()
{
SetExecutionStrategy(
"[Link]",
() => new SqlAzureExecutionStrategy(1, [Link](30)));
}
}

El SqlAzureExecutionStrategy se volverá a intentar de inmediato la primera vez que se produzca un error


transitorio, pero se retrasará más tiempo entre cada reintento hasta que se supere el límite máximo de
reintentos o hasta que el tiempo total alcanza el retraso máximo.
Las estrategias de ejecución solo volverán a intentar un número limitado de excepciones que normalmente son
transitorias; todavía tendrá que controlar otros errores, así como detectar la excepción RetryLimitExceeded en
caso de que un error no sea transitorio o tarde demasiado en resolverse.
Hay algunas limitaciones conocidas al usar una estrategia de ejecución de reintento:

No se admiten las consultas de streaming


De forma predeterminada, EF6 y versiones posteriores almacenarán en búfer los resultados de la consulta en
lugar de transmitirlos por secuencias. Si desea que los resultados se transmitan, puede usar el método
asstreaming para cambiar una consulta de LINQ to Entities a streaming.

using (var db = new BloggingContext())


{
var query = (from b in [Link]
orderby [Link]
select b).AsStreaming();
}
}
No se admite la transmisión por secuencias cuando se registra una estrategia de ejecución de reintento. Esta
limitación existe porque la conexión podría quitar parte de los resultados que se devuelven. Cuando esto ocurre,
EF debe volver a ejecutar toda la consulta pero no tiene una forma confiable de saber qué resultados se han
devuelto (los datos pueden haber cambiado desde que se envió la consulta inicial, por lo que los resultados
pueden volver a un orden diferente, los resultados pueden no tener un identificador único, etc.).

No se admiten las transacciones iniciadas por el usuario


Cuando haya configurado una estrategia de ejecución que tenga como resultado reintentos, hay algunas
limitaciones sobre el uso de transacciones.
De forma predeterminada, EF realizará cualquier actualización de la base de datos dentro de una transacción. No
es necesario hacer nada para habilitarlo; EF siempre lo hace automáticamente.
Por ejemplo, en el código siguiente, se realiza automáticamente SaveChanges en una transacción. Si se
produjera un error de SaveChanges después de insertar uno de los nuevos sitios, la transacción se revertiría y
no se aplicaría ningún cambio en la base de datos. El contexto también se deja en un estado que permite llamar
a SaveChanges de nuevo para volver a aplicar los cambios.

using (var db = new BloggingContext())


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

Cuando no se usa una estrategia de ejecución de reintento, se pueden ajustar varias operaciones en una sola
transacción. Por ejemplo, el código siguiente ajusta dos llamadas de SaveChanges en una sola transacción. Si se
produce un error en cualquier parte de cualquiera de las operaciones, no se aplica ninguno de los cambios.

using (var db = new BloggingContext())


{
using (var trn = [Link]())
{
[Link](new Site { Url = "[Link] });
[Link](new Site { Url = "[Link] });
[Link]();

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


[Link]();

[Link]();
}
}

Esto no se admite cuando se usa una estrategia de ejecución de reintento porque EF no es consciente de las
operaciones anteriores y cómo volver a intentarlo. Por ejemplo, si se produce un error en el segundo
SaveChanges, EF ya no tiene la información necesaria para volver a intentar la primera llamada a SaveChanges.
Solución: estrategia de ejecución de llamadas manualmente
La solución consiste en usar manualmente la estrategia de ejecución y darle todo el conjunto de lógica que se va
a ejecutar, de modo que pueda volver a intentar todo lo posible si se produce un error en una de las
operaciones. Cuando una estrategia de ejecución derivada de DbExecutionStrategy se está ejecutando,
suspenderá la estrategia de ejecución implícita usada en SaveChanges.
Tenga en cuenta que los contextos deben construirse dentro del bloque de código para que se vuelva a intentar.
Esto garantiza que se está iniciando con un estado limpio para cada reintento.
var executionStrategy = new SqlAzureExecutionStrategy();

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

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


[Link]();

[Link]();
}
}
});
Control de errores de confirmación de transacción
12/03/2021 • 6 minutes to read

NOTE
EF 6.1 en adelante solo : las características, las API, etc. que se describen en esta página se introdujeron en Entity
Framework 6,1. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

Como parte de 6,1 estamos introduciendo una nueva característica de resistencia de conexión para EF: la
capacidad de detectar y recuperar automáticamente cuando los errores de conexión transitorios afecten a la
confirmación de confirmaciones de transacción. Los detalles completos del escenario se describen mejor en la
entrada de blog SQL Database la conectividad y el problema de Idempotencia. En Resumen, el escenario es que,
cuando se produce una excepción durante la confirmación de una transacción, hay dos causas posibles:
1. Error en la confirmación de la transacción en el servidor
2. La transacción se confirmó correctamente en el servidor pero un problema de conectividad impidió que la
notificación de éxito llegara al cliente
Cuando se produce la primera situación de la aplicación o el usuario puede reintentar la operación, pero cuando
se produce la segunda situación, se deben evitar los reintentos y la aplicación podría recuperarse
automáticamente. El reto es que sin la capacidad de detectar cuál fue el motivo real en el que se comunicó una
excepción durante la confirmación, la aplicación no puede elegir el curso de acción correcto. La nueva
característica de EF 6,1 permite a EF volver a comprobar con la base de datos si la transacción se ha realizado
correctamente y realizar el curso adecuado de acción de forma transparente.

Uso de la característica
Para habilitar la característica que necesita, incluya una llamada a SetTransactionHandler en el constructor de su
DbConfiguration . Si no está familiarizado con DbConfiguration , consulte configuración basada en código.
Esta característica se puede usar en combinación con los reintentos automáticos que se introdujeron en EF6, que
ayudan en la situación en la que la transacción realmente no pudo confirmarse en el servidor debido a un error
transitorio:

using [Link];
using [Link];
using [Link];

public class MyConfiguration : DbConfiguration


{
public MyConfiguration()
{
SetTransactionHandler([Link], () => new CommitFailureHandler());
SetExecutionStrategy([Link], () => new SqlAzureExecutionStrategy());
}
}

Cómo se realiza el seguimiento de las transacciones


Cuando la característica está habilitada, EF agregará automáticamente una nueva tabla a la base de datos
denominada __Transactions . En esta tabla se inserta una nueva fila cada vez que EF crea una transacción y se
comprueba la existencia de esa fila si se produce un error de transacción durante la confirmación.
Aunque EF realizará el mejor esfuerzo para eliminar filas de la tabla cuando ya no se necesiten, la tabla puede
crecer si la aplicación se cierra prematuramente y, por ese motivo, es posible que necesite purgar la tabla
manualmente en algunos casos.

Cómo controlar los errores de confirmación con versiones anteriores


Antes de EF 6,1 no había ningún mecanismo para controlar los errores de confirmación en el producto EF. Hay
varias maneras de solucionar esta situación que se pueden aplicar a versiones anteriores de EF6:
Opción 1: no hacer 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.
Opción 2: usar la base de datos para restablecer el estado
1. Descartar el DbContext actual
2. Crear un nuevo DbContext y restaurar 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: realizar un seguimiento manual de la transacción
1. Agregue una tabla sin seguimiento 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.
Si la fila está presente, continúe normalmente, ya que la transacción se confirmó
correctamente.
Si la fila no existe, use una estrategia de ejecución para volver a intentar la operación actual.
4. Si la confirmación se realiza correctamente, elimine la fila correspondiente para evitar el crecimiento
de la tabla.
Esta entrada de blog contiene código de ejemplo para realizar esta instalación en SQL Azure.
Databinding with WinForms (Enlace de datos con
WinForms)
12/03/2021 • 25 minutes to read

En este tutorial paso a paso se muestra cómo enlazar tipos POCO a controles de formularios Windows Forms
(WinForms) en un formulario "principal-detalle". La aplicación utiliza Entity Framework para rellenar los objetos
con datos de la base de datos, realizar un seguimiento de los cambios y conservar los datos en la base de datos.
El modelo define dos tipos que participan en una relación de uno a varios: categoría ( \ maestro principal) y
producto ( \ detalle dependiente). A continuación, se usan las herramientas de Visual Studio para enlazar los
tipos definidos en el modelo con los controles de WinForms. El marco de enlace de datos de WinForms permite
la navegación entre objetos relacionados: la selección de filas en la vista maestra hace que la vista de detalles se
actualice con los datos secundarios correspondientes.
Las capturas de pantalla y las listas de código de este tutorial se han tomado de Visual Studio 2013 pero puede
completar este tutorial con Visual Studio 2012 o Visual Studio 2010.

Requisitos previos
Debe tener Visual Studio 2013, Visual Studio 2012 o Visual Studio 2010 instalado para completar este tutorial.
Si usa Visual Studio 2010, también tiene que instalar NuGet. Para obtener más información, consulte instalación
de NuGet.

Crear la aplicación
Apertura de Visual Studio
Archivo- > nuevo- > proyecto....
Seleccione Windows en el panel izquierdo y Windows FormsApplication en el panel derecho.
Escriba WinFormswithEFSample como nombre
Seleccione Aceptar .

Instalar el paquete NuGet de Entity Framework


En Explorador de soluciones, haga clic con el botón derecho en el proyecto WinFormswithEFSample
Seleccione administrar paquetes NuGet.. .
En el cuadro de diálogo administrar paquetes NuGet, seleccione la pestaña en línea y elija el paquete
EntityFramework
Haz clic en Instalar

NOTE
Además del ensamblado EntityFramework, también se agrega una referencia a System. ComponentModel.
DataAnnotations. Si el proyecto tiene una referencia a System. Data. Entity, se quitará cuando se instale el paquete
EntityFramework. El ensamblado System. Data. Entity ya no se utiliza para las aplicaciones de Entity Framework 6.

Implementación de IListSource para colecciones


Las propiedades de colección deben implementar la interfaz IListSource para habilitar el enlace de datos
bidireccional con la ordenación al utilizar Windows Forms. Para ello, vamos a ampliar ObservableCollection para
agregar la funcionalidad IListSource.
Agregue una clase Obser vableListSource al proyecto:
Haga clic con el botón derecho en el nombre del proyecto
Seleccionar agregar- > nuevo elemento
Seleccione clase y escriba Obser vableListSource para el nombre de clase.
Reemplace el código generado de forma predeterminada con el código siguiente:
Esta clase permite el enlace de datos bidireccional y la ordenación. La clase se deriva de ObservableCollection <
T > y agrega una implementación explícita de IListSource. El método GetList () de IListSource se implementa
para devolver una implementación de IBindingList que permanece sincronizada con ObservableCollection. La
implementación de IBindingList generada por ToBindingList admite la ordenación. El método de extensión
ToBindingList se define en el ensamblado EntityFramework.

using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace WinFormswithEFSample
{
public class ObservableListSource<T> : ObservableCollection<T>, IListSource
where T : class
{
private IBindingList _bindingList;

bool [Link] { get { return false; } }

IList [Link]()
{
return _bindingList ?? (_bindingList = [Link]());
}
}
}

Definición de un modelo
En este tutorial, puede elegir implementar un modelo mediante Code First o el diseñador de EF. Complete una
de las dos secciones siguientes.
Opción 1: definir un modelo con Code First
En esta sección se muestra cómo crear un modelo y su base de datos asociada mediante Code First. Vaya a la
sección siguiente (opción 2: definir un modelo con Database First) si prefiere usar Database First para
aplicar ingeniería inversa al modelo desde una base de datos mediante el diseñador de EF.
Al usar Code First desarrollo, normalmente comienza por escribir .NET Framework clases que definen el modelo
conceptual (de dominio).
Agregar una nueva clase de producto a Project
Reemplace el código generado de forma predeterminada con el código siguiente:
using System;
using [Link];
using [Link];
using [Link];
using [Link];

namespace WinFormswithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }

public int CategoryId { get; set; }


public virtual Category Category { get; set; }
}
}

Agregue una clase de categoría al proyecto.


Reemplace el código generado de forma predeterminada con el código siguiente:

using System;
using [Link];
using [Link];
using [Link];
using [Link];

namespace WinFormswithEFSample
{
public class Category
{
private readonly ObservableListSource<Product> _products =
new ObservableListSource<Product>();

public int CategoryId { get; set; }


public string Name { get; set; }
public virtual ObservableListSource<Product> Products { get { return _products; } }
}
}

Además de definir entidades, debe definir una clase que derive de DbContext y exponga propiedades de
**DbSet < > ** . Las propiedades DbSet permiten que el contexto sepa qué tipos desea incluir en el modelo. Los
tipos DbContext y DbSet se definen en el ensamblado EntityFramework.
Una instancia del tipo derivado de DbContext administra los objetos de entidad durante el tiempo de ejecución,
lo que incluye rellenar los objetos con datos de una base de datos, el seguimiento de cambios y la persistencia
de datos en la base de datos.
Agregue una nueva clase ProductContext al proyecto.
Reemplace el código generado de forma predeterminada con el código siguiente:
using System;
using [Link];
using [Link];
using [Link];
using [Link];

namespace WinFormswithEFSample
{
public class ProductContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
}

Compile el proyecto.
Opción 2: definir un modelo con Database First
En esta sección se muestra cómo usar Database First para aplicar ingeniería inversa al modelo desde una base
de datos mediante el diseñador de EF. Si ha completado la sección anterior (opción 1: definir un modelo con
Code First) , omita esta sección y vaya directamente a la sección de carga diferida .
Crear una base de datos existente
Normalmente, cuando el destino es una base de datos existente, ya se creará, pero para este tutorial es
necesario crear una base de datos para tener acceso a.
El servidor de base de datos que se instala con Visual Studio es diferente en función de la versión de Visual
Studio que haya instalado:
Si usa Visual Studio 2010, va a crear una base de datos de SQL Express.
Si usa Visual Studio 2012, va a crear una base de datos de LocalDB .
Vamos a generar la base de datos.
Vista > Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos: > Agregar conexión...
Si no se ha conectado a una base de datos desde Explorador de servidores antes de que tenga que
seleccionar Microsoft SQL Server como origen de datos

Conéctese a LocalDB o a SQL Express, en función de la que haya instalado y escriba Products como
nombre de la base de datos.
Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .

La nueva base de datos aparecerá ahora en Explorador de servidores, haga clic con el botón derecho en
ella y seleccione nueva consulta .
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)

CREATE TABLE [dbo].[Products] (


[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)

CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])

ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY


([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE

Modelo de ingeniería inversa


Vamos a hacer uso de Entity Framework Designer, que se incluye como parte de Visual Studio, para crear
nuestro modelo.
Proyecto- > Agregar nuevo elemento...
Seleccione datos en el menú de la izquierda y, a continuación, [Link] Entity Data Model
Escriba ProductModel como nombre y haga clic en Aceptar .
Se iniciará el Asistente para Entity Data Model
Seleccione generar desde la base de datos y haga clic en siguiente .

Seleccione la conexión a la base de datos creada en la primera sección, escriba ProductContext como
nombre de la cadena de conexión y haga clic en siguiente .
Haga clic en la casilla situada junto a "tablas" para importar todas las tablas y haga clic en "finalizar".

Una vez que se completa el proceso de ingeniería inversa, el nuevo modelo se agrega al proyecto y se abre para
que pueda verlo en el Entity Framework Designer. También se ha agregado un archivo [Link] al proyecto
con los detalles de conexión de la base de datos.
Pasos adicionales en Visual Studio 2010
Si está trabajando en Visual Studio 2010, tendrá que actualizar el diseñador de EF para usar la generación de
código EF6.
Haga clic con el botón derecho en una zona vacía del modelo en el diseñador de EF y seleccione Agregar
elemento de generación de código.. .
Seleccione plantillas en línea en el menú de la izquierda y busque DbContext
Seleccione el generador de DbContext de EF 6. x para C # , escriba ProductsModel como nombre y
haga clic en Agregar.
Actualizar la generación de código para el enlace de datos
EF genera código a partir del modelo mediante plantillas T4. Las plantillas que se incluyen con Visual Studio o
que se descargan de la galería de Visual Studio están pensadas para uso general. Esto significa que las entidades
generadas a partir de estas plantillas tienen < propiedades ICollection T simples > . Sin embargo, cuando se
realiza el enlace de datos, es aconsejable tener propiedades de colección que implementen IListSource. Este es el
motivo por el que hemos creado la clase ObservableListSource anterior y ahora vamos a modificar las plantillas
para hacer uso de esta clase.
Abra el Explorador de soluciones y busque el archivo ProductModel. edmx.
Busque el archivo [Link] que se anidará en el archivo ProductModel. edmx.

Haga doble clic en el archivo [Link] para abrirlo en el editor de Visual Studio
Busque y reemplace las dos apariciones de "ICollection " por "Obser vableListSource ". Se encuentran
en aproximadamente las líneas 296 y 484.
Busque y reemplace la primera aparición de "HashSet " por "Obser vableListSource ". Esta repetición se
encuentra en aproximadamente la línea 50. No Reemplace la segunda aparición de HashSet que se
encuentra más adelante en el código.
Guarde el archivo [Link]. Esto debería hacer que se vuelva a generar el código para las
entidades. Si el código no se vuelve a generar automáticamente, haga clic con el botón derecho en
[Link] y elija "ejecutar herramienta personalizada".
Si ahora abre el archivo [Link] (que está anidado en [Link]), debería ver que la colección
Products tiene el tipo **ObservableListSource < Product > **.
Compile el proyecto.

Carga diferida
La propiedad Products de la clase Categor y y la propiedad Categor y de la clase Product son propiedades de
navegación. En Entity Framework, las propiedades de navegación proporcionan una manera de navegar por una
relación entre dos tipos de entidad.
EF ofrece una opción para cargar automáticamente entidades relacionadas desde la base de datos la primera
vez que se tiene acceso a la propiedad de navegación. Con este tipo de carga (denominado carga diferida), tenga
en cuenta que la primera vez que se accede a cada propiedad de navegación se ejecutará una consulta
independiente en la base de datos si el contenido no está ya en el contexto.
Cuando se usan tipos de entidad POCO, EF consigue la carga diferida mediante la creación de instancias de tipos
de proxy derivados durante el tiempo de ejecución y, a continuación, la invalidación de las propiedades virtuales
en las clases para agregar el enlace de carga. Para obtener la carga diferida de los objetos relacionados, debe
declarar captadores de propiedades de navegación como Public y vir tual (reemplazable en Visual Basic) y la
clase no debe estar sellada (NotOverridable en Visual Basic). Cuando se usa Database First propiedades de
navegación se convierten automáticamente en virtual para habilitar la carga diferida. En la sección Code First
hemos optado por que las propiedades de navegación sean virtuales por la misma razón

Enlace de objetos a controles


Agregue las clases que se definen en el modelo como orígenes de datos para esta aplicación de WinForms.
En el menú principal, seleccione proyecto- > Agregar nuevo origen de datos. .. (en Visual Studio
2010, debe seleccionar datos- > Agregar nuevo origen de datos...)
En la ventana elegir un tipo de origen de datos, seleccione objeto y haga clic en siguiente .
En el cuadro de diálogo Seleccionar los objetos de datos, WinFormswithEFSample dos veces y
seleccione categoría no es necesario seleccionar el origen de datos del producto, porque lo haremos a
través de la propiedad del producto en el origen de datos de la categoría.
Haga clic en Finish (Finalizar). Si la ventana orígenes de datos no aparece, seleccione Ver- > otras
ventanas-otros > orígenes de datos .
Presione el icono de anclaje para que la ventana orígenes de datos no se oculte automáticamente. Es
posible que tenga que presionar el botón actualizar si la ventana ya estaba visible.

En Explorador de soluciones, haga doble clic en el archivo [Link] para abrir el formulario principal en
el diseñador.
Seleccione el origen de datos de la categoría y arrástrelo en el formulario. De forma predeterminada, se
agregan al diseñador un nuevo control DataGridView (categor yDataGridView ) y controles de la barra
de herramientas de navegación. Estos controles están enlazados a los componentes BindingSource
(categor yBindingSource ) y navegador de enlace (categor yBindingNavigator ) que también se crean.
Edite las columnas en el categor yDataGridView . Queremos establecer la columna Categor yID en solo
lectura. La base de datos genera el valor de la propiedad Categor yID después de guardar los datos.
Haga clic con el botón secundario en el control DataGridView y seleccione Editar columnas...
Seleccione la columna CategoryId y establezca ReadOnly en true.
Presione Aceptar
Seleccione productos en el origen de datos de la categoría y arrástrelos en el formulario.
ProductDataGridView y productBindingSource se agregan al formulario.
Edite las columnas en el productDataGridView. Queremos ocultar las columnas CategoryId y categoría y
establecer ProductId en solo lectura. La base de datos genera el valor de la propiedad ProductId después
de guardar los datos.
Haga clic con el botón secundario en el control DataGridView y seleccione Editar columnas....
Seleccione la columna ProductID y establezca ReadOnly en true .
Seleccione la columna Categor yID y presione el botón quitar . Haga lo mismo con la columna
categoría .
Haga clic en Aceptar .
Hasta ahora, asociamos nuestros controles DataGridView a componentes BindingSource en el diseñador.
En la siguiente sección, agregaremos código al código subyacente para establecer
categoryBindingSource. DataSource en la colección de entidades cuyo seguimiento realiza actualmente
DbContext. Cuando hemos arrastrado y colocado productos desde la categoría, el WinForms se encarga
de configurar la propiedad productsBindingSource. DataSource en categoryBindingSource y la propiedad
productsBindingSource. DataMember en Products. Debido a este enlace, solo se mostrarán en el
productDataGridView los productos que pertenecen a la categoría seleccionada actualmente.
Habilite el botón Guardar en la barra de herramientas de navegación; para ello, haga clic con el botón
secundario del mouse y seleccione habilitado .
Agregue el controlador de eventos para el botón Guardar haciendo doble clic en el botón. Esto agregará
el controlador de eventos y le llevará al código subyacente para el formulario. El código del controlador
de eventos ** _ click de categoryBindingNavigatorSaveItem** se agregará en la sección siguiente.

Agregar el código que controla la interacción de datos


Ahora agregaremos el código para usar el ProductContext para realizar el acceso a los datos. Actualice el código
para la ventana de formulario principal, como se muestra a continuación.
El código declara una instancia de ejecución prolongada de ProductContext. El objeto ProductContext se usa
para consultar y guardar datos en la base de datos. A continuación, se llama al método Dispose () en la instancia
de ProductContext desde el método de cierre de sesión reemplazado. Los comentarios de código proporcionan
detalles sobre lo que hace el código.

using System;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace WinFormswithEFSample
{
public partial class Form1 : Form
{
ProductContext _context;
public Form1()
{
InitializeComponent();
}

protected override void OnLoad(EventArgs e)


{
[Link](e);
_context = new ProductContext();

// Call the Load method to get the data for the given DbSet
// from the database.
// The data is materialized as entities. The entities are managed by
// the DbContext instance.
_context.[Link]();

// Bind the [Link] to


// all the Unchanged, Modified and Added Category objects that
// are currently tracked by the DbContext.
// Note that we need to call ToBindingList() on the
// ObservableCollection<TEntity> returned by
// the [Link] property to get the BindingList<T>
// in order to facilitate two-way binding in WinForms.
[Link] =
_context.[Link]();
_context.[Link]();
}

private void categoryBindingNavigatorSaveItem_Click(object sender, EventArgs e)


{
[Link]();

// Currently, the Entity Framework doesn’t mark the entities


// that are removed from a navigation property (in our example the Products)
// as deleted in the context.
// The following code uses LINQ to Objects against the Local collection
// to find all products and marks any that do not have
// a Category reference as deleted.
// The ToList call is required because otherwise
// the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can do LINQ to Objects directly
// against the Local property without using ToList first.
foreach (var product in _context.[Link]())
{
if ([Link] == null)
{
_context.[Link](product);
}
}

// Save the changes to the database.


this._context.SaveChanges();

// Refresh the controls to show the values


// that were generated by the database.
[Link]();
[Link]();
}

protected override void OnClosing(CancelEventArgs e)


{
[Link](e);
this._context.Dispose();
}
}
}

Prueba de la aplicación Windows Forms


Compile y ejecute la aplicación y puede probar la funcionalidad.

Después de guardar las claves generadas por el almacén, se muestran en la pantalla.


Si ha usado Code First, también verá que se ha creado una base de datos WinFormswithEFSample.
ProductContext .
Databinding with WPF (Enlace de datos con WPF)
12/03/2021 • 26 minutes to read

IMPORTANT
Este documento solo es válido para WPF en el .NET Framework
En este documento se describe el enlace de DataBindings para WPF en el .NET Framework. En el caso de los nuevos
proyectos de .NET Core, se recomienda usar EF Core en lugar de Entity Framework 6. La documentación de DataBinding
en EF Core está aquí: Introducción con WPF.

En este tutorial paso a paso se muestra cómo enlazar tipos POCO a controles de WPF en un formulario
"maestro-detalles". La aplicación utiliza las API de Entity Framework para rellenar los objetos con datos de la
base de datos, realizar un seguimiento de los cambios y conservar los datos en la base de datos.
El modelo define dos tipos que participan en una relación de uno a varios: categoría ( \ maestro principal) y
producto ( \ detalle dependiente). A continuación, se usan las herramientas de Visual Studio para enlazar los
tipos definidos en el modelo a los controles de WPF. El marco de enlace de datos de WPF permite la navegación
entre objetos relacionados: la selección de filas en la vista maestra hace que la vista de detalles se actualice con
los datos secundarios correspondientes.
Las capturas de pantalla y las listas de código de este tutorial se han tomado de Visual Studio 2013 pero puede
completar este tutorial con Visual Studio 2012 o Visual Studio 2010.

Usar la opción ' Object ' para crear orígenes de datos de WPF
Con la versión anterior de Entity Framework usamos para recomendar el uso de la opción de base de datos al
crear un nuevo origen de datos basado en un modelo creado con el diseñador de EF. Esto se debe a que el
diseñador genera un contexto que se deriva de las clases de ObjectContext y de entidad derivadas de
EntityObject. El uso de la opción de base de datos le ayudará a escribir el mejor código para interactuar con esta
superficie de API.
Los diseñadores de EF para Visual Studio 2012 y Visual Studio 2013 generan un contexto que se deriva de
DbContext junto con clases de entidad POCO simples. Con Visual Studio 2010, se recomienda cambiar a una
plantilla de generación de código que use DbContext, tal y como se describe más adelante en este tutorial.
Al usar la superficie de API de DbContext, debe usar la opción de objeto al crear un nuevo origen de datos,
como se muestra en este tutorial.
Si es necesario, puede revertir a la generación de código basada en ObjectContext para los modelos creados con
el diseñador de EF.

Requisitos previos
Debe tener Visual Studio 2013, Visual Studio 2012 o Visual Studio 2010 instalado para completar este tutorial.
Si usa Visual Studio 2010, también tiene que instalar NuGet. Para obtener más información, consulte instalación
de NuGet.

Crear la aplicación
Apertura de Visual Studio
Archivo- > nuevo- > proyecto....
Seleccione ventanas en el panel izquierdo y WPFApplication en el panel derecho.
Escriba WPFwithEFSample como nombre
Seleccione Aceptar .

Instalar el paquete NuGet de Entity Framework


En Explorador de soluciones, haga clic con el botón derecho en el proyecto WinFormswithEFSample
Seleccione administrar paquetes NuGet.. .
En el cuadro de diálogo administrar paquetes NuGet, seleccione la pestaña en línea y elija el paquete
EntityFramework
Haz clic en Instalar

NOTE
Además del ensamblado EntityFramework, también se agrega una referencia a System. ComponentModel.
DataAnnotations. Si el proyecto tiene una referencia a System. Data. Entity, se quitará cuando se instale el paquete
EntityFramework. El ensamblado System. Data. Entity ya no se utiliza para las aplicaciones de Entity Framework 6.

Definición de un modelo
En este tutorial, puede elegir implementar un modelo mediante Code First o el diseñador de EF. Complete una
de las dos secciones siguientes.
Opción 1: definir un modelo con Code First
En esta sección se muestra cómo crear un modelo y su base de datos asociada mediante Code First. Vaya a la
sección siguiente (opción 2: definir un modelo con Database First) si prefiere usar Database First para
aplicar ingeniería inversa al modelo desde una base de datos mediante el diseñador de EF.
Al usar Code First desarrollo, normalmente comienza por escribir .NET Framework clases que definen el modelo
conceptual (de dominio).
Agregue una nueva clase a WPFwithEFSample:
Haga clic con el botón derecho en el nombre del proyecto
Seleccione Agregar y, a continuación, nuevo elemento .
Seleccione clase y escriba Product como nombre de clase.
Reemplace la definición de clase de producto por el código siguiente:
namespace WPFwithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }

public int CategoryId { get; set; }


public virtual Category Category { get; set; }
}
}

- Add a **Category** class with the following definition:

using [Link];

namespace WPFwithEFSample
{
public class Category
{
public Category()
{
[Link] = new ObservableCollection<Product>();
}

public int CategoryId { get; set; }


public string Name { get; set; }

public virtual ObservableCollection<Product> Products { get; private set; }


}
}

La propiedad Products de la clase Categor y y la propiedad Categor y de la clase Product son propiedades de
navegación. En Entity Framework, las propiedades de navegación proporcionan una manera de navegar por una
relación entre dos tipos de entidad.
Además de definir entidades, debe definir una clase que derive de DbContext y exponga las propiedades
DbSet<TEntity>. Las propiedades DbSet<TEntity> permiten que el contexto sepa qué tipos desea incluir en el
modelo.
Una instancia del tipo derivado de DbContext administra los objetos de entidad durante el tiempo de ejecución,
lo que incluye rellenar los objetos con datos de una base de datos, el seguimiento de cambios y la persistencia
de datos en la base de datos.
Agregue una nueva clase ProductContext al proyecto con la siguiente definición:

using [Link];

namespace WPFwithEFSample
{
public class ProductContext : DbContext
{
public DbSet<Category> Categories { get; set; }
public DbSet<Product> Products { get; set; }
}
}

Compile el proyecto.
Opción 2: definir un modelo con Database First
En esta sección se muestra cómo usar Database First para aplicar ingeniería inversa al modelo desde una base
de datos mediante el diseñador de EF. Si ha completado la sección anterior (opción 1: definir un modelo con
Code First) , omita esta sección y vaya directamente a la sección de carga diferida .
Crear una base de datos existente
Normalmente, cuando el destino es una base de datos existente, ya se creará, pero para este tutorial es
necesario crear una base de datos para tener acceso a.
El servidor de base de datos que se instala con Visual Studio es diferente en función de la versión de Visual
Studio que haya instalado:
Si usa Visual Studio 2010, va a crear una base de datos de SQL Express.
Si usa Visual Studio 2012, va a crear una base de datos de LocalDB .
Vamos a generar la base de datos.
Vista > Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos: > Agregar conexión...
Si no se ha conectado a una base de datos desde Explorador de servidores antes de que tenga que
seleccionar Microsoft SQL Server como origen de datos

Conéctese a LocalDB o a SQL Express, en función de la que haya instalado y escriba Products como
nombre de la base de datos.
Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .

La nueva base de datos aparecerá ahora en Explorador de servidores, haga clic con el botón derecho en
ella y seleccione nueva consulta .
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
CREATE TABLE [dbo].[Categories] (
[CategoryId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId])
)

CREATE TABLE [dbo].[Products] (


[ProductId] [int] NOT NULL IDENTITY,
[Name] [nvarchar](max),
[CategoryId] [int] NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId])
)

CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId])

ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY


([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE

Modelo de ingeniería inversa


Vamos a hacer uso de Entity Framework Designer, que se incluye como parte de Visual Studio, para crear
nuestro modelo.
Proyecto- > Agregar nuevo elemento...
Seleccione datos en el menú de la izquierda y, a continuación, [Link] Entity Data Model
Escriba ProductModel como nombre y haga clic en Aceptar .
Se iniciará el Asistente para Entity Data Model
Seleccione generar desde la base de datos y haga clic en siguiente .

Seleccione la conexión a la base de datos creada en la primera sección, escriba ProductContext como
nombre de la cadena de conexión y haga clic en siguiente .
Haga clic en la casilla situada junto a "tablas" para importar todas las tablas y haga clic en "finalizar".

Una vez que se completa el proceso de ingeniería inversa, el nuevo modelo se agrega al proyecto y se abre para
que pueda verlo en el Entity Framework Designer. También se ha agregado un archivo [Link] al proyecto
con los detalles de conexión de la base de datos.
Pasos adicionales en Visual Studio 2010
Si está trabajando en Visual Studio 2010, tendrá que actualizar el diseñador de EF para usar la generación de
código EF6.
Haga clic con el botón derecho en una zona vacía del modelo en el diseñador de EF y seleccione Agregar
elemento de generación de código.. .
Seleccione plantillas en línea en el menú de la izquierda y busque DbContext
Seleccione el generador de DbContext de EF 6. x para C # , escriba ProductsModel como nombre y
haga clic en Agregar.
Actualizar la generación de código para el enlace de datos
EF genera código a partir del modelo mediante plantillas T4. Las plantillas que se incluyen con Visual Studio o
que se descargan de la galería de Visual Studio están pensadas para uso general. Esto significa que las entidades
generadas a partir de estas plantillas tienen < propiedades ICollection T simples > . Sin embargo, al realizar el
enlace de datos mediante WPF, es conveniente usar Obser vableCollection para las propiedades de colección
para que WPF pueda realizar un seguimiento de los cambios realizados en las colecciones. A este fin,
modificaremos las plantillas para usar ObservableCollection.
Abra el Explorador de soluciones y busque el archivo ProductModel. edmx.
Busque el archivo [Link] que se anidará en el archivo ProductModel. edmx.

Haga doble clic en el archivo [Link] para abrirlo en el editor de Visual Studio
Busque y reemplace las dos apariciones de "ICollection " por "Obser vableCollection ". Se encuentran
aproximadamente en las líneas 296 y 484.
Busque y reemplace la primera aparición de "HashSet " con "Obser vableCollection ". Esta repetición se
encuentra aproximadamente en la línea 50. No Reemplace la segunda aparición de HashSet que se
encuentra más adelante en el código.
Busque y reemplace la única aparición de "System. Collections. Generic " por "System. Collections.
ObjectModel ". Esto se encuentra aproximadamente en la línea 424.
Guarde el archivo [Link]. Esto debería hacer que se vuelva a generar el código para las
entidades. Si el código no se vuelve a generar automáticamente, haga clic con el botón derecho en
[Link] y elija "ejecutar herramienta personalizada".
Si ahora abre el archivo [Link] (que está anidado en [Link]), debería ver que la colección
Products tiene el tipo **ObservableCollection < Product > **.
Compile el proyecto.

Carga diferida
La propiedad Products de la clase Categor y y la propiedad Categor y de la clase Product son propiedades de
navegación. En Entity Framework, las propiedades de navegación proporcionan una manera de navegar por una
relación entre dos tipos de entidad.
EF ofrece una opción para cargar automáticamente entidades relacionadas desde la base de datos la primera
vez que se tiene acceso a la propiedad de navegación. Con este tipo de carga (denominado carga diferida), tenga
en cuenta que la primera vez que se accede a cada propiedad de navegación se ejecutará una consulta
independiente en la base de datos si el contenido no está ya en el contexto.
Cuando se usan tipos de entidad POCO, EF consigue la carga diferida mediante la creación de instancias de tipos
de proxy derivados durante el tiempo de ejecución y, a continuación, la invalidación de las propiedades virtuales
en las clases para agregar el enlace de carga. Para obtener la carga diferida de los objetos relacionados, debe
declarar captadores de propiedades de navegación como Public y vir tual (reemplazable en Visual Basic) y la
clase no debe estar sellada (NotOverridable en Visual Basic). Cuando se usa Database First propiedades de
navegación se convierten automáticamente en virtual para habilitar la carga diferida. En la sección Code First
hemos optado por que las propiedades de navegación sean virtuales por la misma razón

Enlace de objetos a controles


Agregue las clases que se definen en el modelo como orígenes de datos para esta aplicación WPF.
Haga doble clic en [Link] en el Explorador de soluciones para abrir el formulario principal.
En el menú principal, seleccione proyecto- > Agregar nuevo origen de datos. .. (en Visual Studio
2010, debe seleccionar datos- > Agregar nuevo origen de datos...)
En elegir un origen de datos Typewindow, seleccione objeto y haga clic en siguiente .
En el cuadro de diálogo Seleccionar los objetos de datos, WPFwithEFSample dos veces y seleccione
categoría
No es necesario seleccionar el origen de datos del producto , porque lo haremos a través de la
propiedad del producto en el origen de datos de la categoría .
Haga clic en Finish (Finalizar).
La ventana orígenes de datos se abre junto a la ventana MainWindow. XAML *si la ventana orígenes de
datos no aparece, seleccione Ver- > otros orígenes de > datos de Windows * .
Presione el icono de anclaje para que la ventana orígenes de datos no se oculte automáticamente. Es
posible que tenga que presionar el botón actualizar si la ventana ya estaba visible.

Seleccione el origen de datos de la categoría y arrástrelo en el formulario.


Al arrastrar este origen, ocurre lo siguiente:
El recurso categor yViewSource y el control categor yDataGrid se agregaron a XAML
La propiedad DataContext del elemento Grid primario se estableció en "{StaticResource
categor yViewSource }".El recurso categor yViewSource actúa como origen de enlace para el \ elemento
primario de la cuadrícula externa. Los elementos de la cuadrícula interna heredan entonces el valor
DataContext de la cuadrícula primaria (la propiedad ItemsSource de categoryDataGrid se establece en "
{Binding}")
<[Link]>
<CollectionViewSource x:Key="categoryViewSource"
d:DesignSource="{d:DesignInstance {x:Type local:Category},
CreateList=True}"/>
</[Link]>
<Grid DataContext="{StaticResource categoryViewSource}">
<DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="13,13,43,191"
RowDetailsVisibilityMode="VisibleWhenSelected">
<[Link]>
<DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"
Header="Category Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"
Header="Name" Width="SizeToHeader"/>
</[Link]>
</DataGrid>
</Grid>

Adición de una cuadrícula de detalles


Ahora que tenemos una cuadrícula para mostrar las categorías, vamos a agregar una cuadrícula de detalles para
mostrar los productos asociados.
Seleccione la propiedad Products en el origen de datos Categor y y arrástrela en el formulario.
El recurso categor yProductsViewSource y la cuadrícula productDataGrid se agregan a XAML
La ruta de acceso de enlace de este recurso se establece en Products
El marco de enlace de datos de WPF garantiza que solo los productos relacionados con la categoría
seleccionada se muestran en productDataGrid
En el cuadro de herramientas, arrastre el botón al formulario. Establezca la propiedad nombre en
buttonSave y la propiedad contenido en Guardar .
El formulario debe tener un aspecto similar al siguiente:

Adición de código que controla la interacción con los datos


Es el momento de agregar algunos controladores de eventos a la ventana principal.
En la ventana XAML, haga clic en el elemento ** < Window** ; Esto selecciona la ventana principal.
En la ventana propiedades , elija eventos en la parte superior derecha y, a continuación, haga doble clic
en el cuadro de texto a la derecha de la etiqueta cargada .
Agregue también el evento click del botón Guardar haciendo doble clic en el botón Guardar en el
diseñador.
Esto le lleva al código subyacente para el formulario. ahora editaremos el código para usar el ProductContext
para realizar el acceso a los datos. Actualice el código para MainWindow, tal como se muestra a continuación.
El código declara una instancia de ejecución prolongada de ProductContext . El objeto ProductContext se usa
para consultar y guardar datos en la base de datos. A continuación, se llama a Dispose () en la instancia de
ProductContext desde el método de cierre de sesión [Link] comentarios de código proporcionan
detalles sobre lo que hace el código.

using [Link];
using [Link];
using [Link];

namespace WPFwithEFSample
{
public partial class MainWindow : Window
{
private ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)


{
[Link] categoryViewSource =
(([Link])([Link]("categoryViewSource")));

// Load is an extension method on IQueryable,


// defined in the [Link] namespace.
// This method enumerates the results of the query,
// similar to ToList but without creating a list.
// When used with Linq to Entities this method
// creates entity objects and adds them to the context.
_context.[Link]();

// After the data is loaded call the DbSet<T>.Local property


// to use the DbSet<T> as a binding source.
[Link] = _context.[Link];
}

private void buttonSave_Click(object sender, RoutedEventArgs e)


{
// When you delete an object from the related entities collection
// (in this case Products), the Entity Framework doesn’t mark
// these child entities as deleted.
// Instead, it removes the relationship between the parent and the child
// by setting the parent reference to null.
// So we manually have to delete the products
// that have a Category reference set to null.
// The following code uses LINQ to Objects
// against the Local collection of Products.
// The ToList call is required because otherwise the collection will be modified
// by the Remove call while it is being enumerated.
// In most other situations you can use LINQ to Objects directly
// against the Local property without using ToList first.
foreach (var product in _context.[Link]())
{
if ([Link] == null)
{
_context.[Link](product);
}
}

_context.SaveChanges();
// Refresh the grids so the database generated values show up.
[Link]();
[Link]();
}

protected override void OnClosing([Link] e)


{
[Link](e);
this._context.Dispose();
}
}

Prueba de una aplicación WPF


Compile y ejecute la aplicación. Si ha usado Code First, verá que se ha creado una base de datos
WPFwithEFSample. ProductContext .
Escriba un nombre de categoría en la cuadrícula superior y los nombres de producto en la cuadrícula
inferior no escriba nada en las columnas de ID., porque la base de datos genera la clave principal.

Presione el botón Guardar para guardar los datos en la base de datos.


Después de la llamada a SaveChanges () de DbContext, los identificadores se rellenan con los valores
generados por la base de datos. Dado que llamamos a Refresh () después de SaveChanges () , los controles
DataGrid también se actualizan con los nuevos valores.
Recursos adicionales
Para obtener más información sobre el enlace de datos a colecciones mediante WPF, vea este tema en la
documentación de WPF.
Trabajo con entidades desconectadas
12/03/2021 • 4 minutes to read

En una aplicación basada en Entity Framework, una clase de contexto es responsable de detectar los cambios
aplicados a las entidades sometidas a seguimiento. Una llamada al método SaveChanges almacena los cambios
que controla el contexto en la base de datos. Cuando se trabaja con aplicaciones de n niveles, los objetos de
entidad generalmente se modifican mientras están desconectados del contexto y es necesario decidir cómo
realizar el seguimiento de los cambios y notificar esos cambios al contexto. En este tema se habla de las distintas
opciones disponibles cuando se usa Entity Framework con entidades desconectadas.

Marcos de trabajo de servicio web


Las tecnologías de servicios web suelen admitir modelos que pueden usarse para almacenar los cambios en
objetos individuales desconectados. Por ejemplo, [Link] Web API permite codificar acciones de controlador
que pueden incluir llamadas a EF para almacenar los cambios realizados en un objeto en una base de datos. De
hecho, las herramientas de Web API de Visual Studio facilitan la aplicación de scaffolding a un controlador de
Web API desde el modelo de Entity Framework 6. Para obtener más información, vea Usar Web API con Entity
Framework 6.
Históricamente, ha habido otras tecnologías de servicios web que ofrecían integración con Entity Framework,
como WCF Data Services y RIA Services.

API de EF de bajo nivel


Si no quiere usar una solución de n niveles existente, o si quiere personalizar lo que ocurre dentro de una acción
de controlador en los servicios de Web API, Entity Framework proporciona API que permiten aplicar los cambios
realizados en un nivel desconectado. Para obtener más información, vea Add, Attach, and entity state (Agregar,
adjuntar y estado de entidad).

Entidades de seguimiento propio


El seguimiento de los cambios en gráficos arbitrarios de entidades mientras se está desconectado del contexto
de EF es un problema difícil. Uno de los intentos de solucionar el problema fue la plantilla de generación de
código Entidades de autoseguimiento. Esta plantilla genera clases de entidad que contienen lógica para seguir
los cambios realizados en un nivel desconectado como estado en las propias entidades. También se genera un
conjunto de métodos de extensión para aplicar esos cambios a un contexto.
Esta plantilla se puede usar con los modelos creados mediante EF Designer, pero no con modelos de Code First.
Para obtener más información, vea Entidades de autoseguimiento.

IMPORTANT
Ya no se recomienda usar la plantilla Entidades de autoseguimiento. Solo sigue estando disponible para la compatibilidad
con las aplicaciones existentes. Si la aplicación necesita trabajar con gráficos desconectados de entidades, considere otras
alternativas, como Trackable Entities, que es una tecnología similar a Entidades de autoseguimiento pero que la
comunidad desarrolla de forma más activa, o escriba código personalizado mediante la API de seguimiento de cambios de
bajo nivel.
Entidades de autoseguimiento
12/03/2021 • 11 minutes to read

IMPORTANT
Ya no se recomienda usar la plantilla Entidades de autoseguimiento. Solo sigue estando disponible para la compatibilidad
con las aplicaciones existentes. Si la aplicación necesita trabajar con gráficos desconectados de entidades, considere otras
alternativas, como Trackable Entities, que es una tecnología similar a Entidades de autoseguimiento pero que la
comunidad desarrolla de forma más activa, o escriba código personalizado mediante la API de seguimiento de cambios de
bajo nivel.

En una aplicación basada en Entity Framework, el responsable de realizar el seguimiento de los cambios en los
objetos es un contexto. Luego se usa el método SaveChanges para almacenar los cambios en la base de datos.
Cuando se trabaja con aplicaciones de n niveles, los objetos de entidad generalmente están desconectados del
contexto y es necesario decidir cómo realizar el seguimiento de los cambios y notificar esos cambios al contexto.
Las entidades de autoseguimiento (STE) pueden ayudar a realizar el seguimiento de los cambios en cualquier
nivel y luego reproducir estos cambios en un contexto que se vaya a guardar.
Use STE solo si el contexto no está disponible en un nivel en el que se realizan cambios en el gráfico del objeto.
Si el contexto está disponible, no hay necesidad de usar STE, ya que él mismo se encarga del seguimiento de los
cambios.
Este elemento de plantilla genera dos archivos .tt (plantilla de texto):
El archivo <model name>.tt genera los tipos de entidad y una clase del asistente que contiene la lógica de
seguimiento de cambios que usan las entidades de autoseguimiento y los métodos de extensión que
permiten establecer el estado en las entidades de autoseguimiento.
El archivo <model name>.[Link] genera un contexto derivado y una clase de extensión que contiene
métodos ApplyChanges para las clases ObjectContext y ObjectSet . Estos métodos examinan la
información del seguimiento de cambios contenida en el grafo de entidades con seguimiento propio para
deducir el conjunto de operaciones que se deben realizar con el fin de guardar los cambios en la base de
datos.

Introducción
Para comenzar, visite la página Self-Tracking Entities Walkthrough (Tutorial sobre entidades de autoseguimiento).

Consideraciones funcionales al trabajar con entidades de seguimiento


propio
IMPORTANT
Ya no se recomienda usar la plantilla Entidades de autoseguimiento. Solo sigue estando disponible para la compatibilidad
con las aplicaciones existentes. Si la aplicación necesita trabajar con gráficos desconectados de entidades, considere otras
alternativas, como Trackable Entities, que es una tecnología similar a Entidades de autoseguimiento pero que la
comunidad desarrolla de forma más activa, o escriba código personalizado mediante la API de seguimiento de cambios de
bajo nivel.

Tenga en cuenta lo siguiente al trabajar con entidades de seguimiento propio:


Asegúrese de que el proyecto cliente tenga una referencia al ensamblado con los tipos de entidad. Si
agrega solo la referencia de servicio al proyecto cliente, este proyecto usará los tipos de proxy WCF y no
los tipos reales de entidad de seguimiento propio. Es decir, no obtendrá las características de notificación
automatizadas que administran el seguimiento de las entidades en el cliente. Si, a propósito, no desea
incluir los tipos de entidad, deberá establecer manualmente la información de seguimiento de cambios
en el cliente de los cambios que se van a devolver al servicio.
Las llamadas a la operación de servicio no deben tener estado y deben crear una nueva instancia del
contexto del objeto. Asimismo, se recomienda crear el contexto del objeto en un bloque using .
Si envía el gráfico modificado en el cliente al servicio y luego planea seguir trabajando con el mismo
gráfico en el cliente, debe iterar manualmente el gráfico y llamar al método AcceptChanges en cada
objeto para restablecer la herramienta de seguimiento de cambios.

Si los objetos del gráfico contienen propiedades con valores generados por la base de datos (por
ejemplo, valores de identidad o simultaneidad), Entity Framework reemplaza los valores de estas
propiedades por los valores generados por la base de datos después de llamar al método
SaveChanges . Puede implementar la operación del servicio para que devuelva objetos guardados o
una lista de los valores de propiedad generados de los objetos devueltos al cliente. El cliente debe
reemplazar las instancias del objeto o los valores de propiedad del objeto con los objetos o los
valores de propiedad devueltos desde la operación de servicio.

Si se combinan gráficos de varias solicitudes de servicio, se pueden presentar objetos con valores de
clave duplicada en el gráfico resultante. Entity Framework no quita los objetos con claves duplicadas
cuando llama al método ApplyChanges , sino que produce una excepción. Para evitar tener gráficos con
valores de clave duplicados, siga uno de los modelos descritos en el siguiente blog: Self-Tracking Entities:
ApplyChanges and duplicate entities (Entidades de autoseguimiento: ApplyChanges y entidades
duplicadas).
Cuando se cambia la relación entre objetos mediante el establecimiento de la propiedad de clave externa,
la propiedad de navegación de referencia se establece en NULL y no se sincroniza con la entidad de
seguridad adecuada en el cliente. Después de adjuntar el gráfico al contexto del objeto (por ejemplo,
después de llamar al método ApplyChanges ), se sincronizan las propiedades de clave externa y de
navegación.

No disponer de ninguna propiedad de navegación de referencia sincronizada con el objeto principal


adecuado podría representar un problema si ha especificado la eliminación en cascada en la relación
de clave externa. Si elimina la entidad de seguridad, la eliminación no se propagará a los objetos
dependientes. Si ha especificado eliminaciones en cascada, use las propiedades de navegación para
cambiar las relaciones en vez de establecer la propiedad de clave externa.

Las entidades con seguimiento propio no están habilitadas para realizar una carga diferida.
La serialización binaria y la serialización a los objetos de administración de estado de [Link] no se
admiten en las entidades de autoseguimiento. Sin embargo, puede personalizar la plantilla para agregar
la compatibilidad de serialización binaria. Para obtener más información, vea Using Binary Serialization
and ViewState with Self-Tracking Entities (Uso de la serialización binaria y ViewState con entidades de
autoseguimiento).

Consideraciones sobre la seguridad


Al trabajar con entidades de autoseguimiento, deben contemplarse las siguientes consideraciones de seguridad:
Un servicio no debería confiar en solicitudes de recuperación o actualización de datos que proceden de un
cliente que no es de plena confianza o transmitidas a través de un canal que no es de plena confianza. Se
debe autenticar un cliente: es necesario utilizar un canal seguro o la envoltura de mensajes. Se deben validar
las solicitudes de los clientes de actualización o recuperación de datos para asegurarse de que se ajustan a
los cambios legítimos y previstos del escenario dado.
Evite utilizar información confidencial como claves de entidad (por ejemplo, números de seguridad social).
Esto reduce la posibilidad de que pueda serializarse de forma inadvertida información confidencial en los
grafos de entidades con seguimiento propio de un cliente que no es de plena confianza. Con asociaciones
independientes, se podría enviar también al cliente la clave original de una entidad que esté relacionada con
la que se está serializando.
Para evitar propagar mensajes de excepción con datos confidenciales en el nivel de cliente, las llamadas a
ApplyChanges y SaveChanges en el nivel del servidor se deben encapsular en un código de control de
excepciones.
Tutorial de Self-Tracking entidades
12/03/2021 • 21 minutes to read

IMPORTANT
Ya no se recomienda usar la plantilla Entidades de autoseguimiento. Solo sigue estando disponible para la compatibilidad
con las aplicaciones existentes. Si la aplicación necesita trabajar con gráficos desconectados de entidades, considere otras
alternativas, como Trackable Entities, que es una tecnología similar a Entidades de autoseguimiento pero que la
comunidad desarrolla de forma más activa, o escriba código personalizado mediante la API de seguimiento de cambios de
bajo nivel.

En este tutorial se muestra el escenario en el que un servicio de Windows Communication Foundation (WCF)
expone una operación que devuelve un gráfico de entidades. A continuación, una aplicación cliente manipula
dicho gráfico y envía las modificaciones a una operación de servicio que valida y guarda las actualizaciones en
una base de datos mediante Entity Framework.
Antes de completar este tutorial, asegúrese de leer la página entidades de seguimiento propio .
Este tutorial realiza las siguientes acciones:
Crea una base de datos a la que se tiene acceso.
Crea una biblioteca de clases que contiene el modelo.
Intercambia la plantilla Self-Tracking Entity generator.
Mueve las clases de entidad a un proyecto independiente.
Crea un servicio WCF que expone las operaciones para consultar y guardar entidades.
Crea aplicaciones cliente (consola y WPF) que consumen el servicio.
Usaremos Database First en este tutorial, pero las mismas técnicas se aplican igualmente a Model First.

Requisitos previos
Para completar este tutorial, necesitará una versión reciente de Visual Studio.

Crear una base de datos


El servidor de base de datos que se instala con Visual Studio es diferente en función de la versión de Visual
Studio que haya instalado:
Si usa Visual Studio 2012, va a crear una base de datos de LocalDB.
Si usa Visual Studio 2010, va a crear una base de datos de SQL Express.
Vamos a generar la base de datos.
Apertura de Visual Studio
Vista > Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos: > Agregar conexión...
Si no se ha conectado a una base de datos desde Explorador de servidores antes de que tenga que
seleccionar Microsoft SQL Ser ver como origen de datos
Conéctese a LocalDB o a SQL Express, en función de la que haya instalado.
Escriba STESample como nombre de la base de datos
Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .
La nueva base de datos aparecerá ahora en Explorador de servidores
Si usa Visual Studio 2012
Haga clic con el botón derecho en la base de datos en el Explorador de servidores y seleccione Nueva
consulta
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
Si usa Visual Studio 2010
Seleccionar datos: > Editor de Transact SQL: > nueva conexión de consulta...
Escriba . \ SQLEXPRESS como nombre del servidor y haga clic en Aceptar
Seleccione la base de datos STESample en la lista desplegable de la parte superior del editor de
consultas.
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione ejecutar SQL .

CREATE TABLE [dbo].[Blogs] (


[BlogId] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (200) NULL,
[Url] NVARCHAR (200) NULL,
CONSTRAINT [PK_dbo.Blogs] PRIMARY KEY CLUSTERED ([BlogId] ASC)
);

CREATE TABLE [dbo].[Posts] (


[PostId] INT IDENTITY (1, 1) NOT NULL,
[Title] NVARCHAR (200) NULL,
[Content] NTEXT NULL,
[BlogId] INT NOT NULL,
CONSTRAINT [PK_dbo.Posts] PRIMARY KEY CLUSTERED ([PostId] ASC),
CONSTRAINT [FK_dbo.Posts_dbo.Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [dbo].[Blogs]
([BlogId]) ON DELETE CASCADE
);

SET IDENTITY_INSERT [dbo].[Blogs] ON


INSERT INTO [dbo].[Blogs] ([BlogId], [Name], [Url]) VALUES (1, N'[Link] Blog',
N'[Link]/adonet')
SET IDENTITY_INSERT [dbo].[Blogs] OFF
INSERT INTO [dbo].[Posts] ([Title], [Content], [BlogId]) VALUES (N'Intro to EF', N'Interesting
stuff...', 1)
INSERT INTO [dbo].[Posts] ([Title], [Content], [BlogId]) VALUES (N'What is New', N'More interesting
stuff...', 1)

Crear el modelo
En primer lugar, necesitamos un proyecto en el que colocar el modelo.
Archivo- > nuevo- > proyecto...
Seleccione **Visual C # ** en el panel izquierdo y, a continuación, biblioteca de clases .
Escriba STESample como nombre y haga clic en Aceptar .
Ahora vamos a crear un modelo simple en EF Designer para tener acceso a la base de datos:
Proyecto- > Agregar nuevo elemento...
Seleccione datos en el panel izquierdo y, a continuación, [Link] Entity Data Model
Escriba BloggingModel como nombre y haga clic en Aceptar .
Seleccione generar desde la base de datos y haga clic en siguiente .
Escriba la información de conexión de la base de datos que creó en la sección anterior
Escriba BloggingContext como nombre de la cadena de conexión y haga clic en siguiente .
Active la casilla situada junto a tablas y haga clic en Finalizar .

Intercambio a la generación de código de STE


Ahora es necesario deshabilitar la generación de código y el intercambio predeterminados para Self-Tracking
entidades.
Si usa Visual Studio 2012
Expanda BloggingModel. edmx en Explorador de soluciones y elimine [Link] y
[Link] ; de esta forma, se deshabilitará la generación de código predeterminada
Haga clic con el botón secundario en un área vacía de la superficie de EF Designer y seleccione Agregar
elemento de generación de código.. .
Seleccione en línea en el panel izquierdo y busque el generador Ste .
Seleccione el generador Ste para la # plantilla de C , escriba STETemplate como nombre y haga clic en
Agregar .
Los archivos [Link] y [Link] se agregan anidados en el archivo
BloggingModel. edmx.
Si usa Visual Studio 2010
Haga clic con el botón secundario en un área vacía de la superficie de EF Designer y seleccione Agregar
elemento de generación de código.. .
Seleccione código en el panel izquierdo y, a continuación, [Link] Self-Tracking Entity generator
Escriba STETemplate como nombre y haga clic en Agregar .
Los archivos [Link] y [Link] se agregan directamente al proyecto

Traslado de tipos de entidad a un proyecto independiente


Para usar Self-Tracking entidades, nuestra aplicación cliente necesita acceso a las clases de entidad generadas a
partir de nuestro modelo. Dado que no queremos exponer todo el modelo a la aplicación cliente, vamos a
trasladar las clases de entidad a un proyecto independiente.
El primer paso es dejar de generar las clases de entidad en el proyecto existente:
Haga clic con el botón derecho en [Link] en Explorador de soluciones y seleccione
propiedades .
En la ventana propiedades , desactive TextTemplatingFileGenerator de la propiedad CustomTool .
Expandir [Link] en Explorador de soluciones y eliminar todos los archivos anidados en él
A continuación, vamos a agregar un nuevo proyecto y a generar las clases de entidad en él.
Archivo- > agregar- > proyecto...
Seleccione **Visual C # ** en el panel izquierdo y, a continuación, biblioteca de clases .
Escriba STESample. Entities como nombre y haga clic en Aceptar .
Proyecto: > Agregar elemento existente...
Navegue hasta la carpeta del proyecto STESample
Seleccione esta información para ver todos los archivos ( * . * )
Seleccione el archivo [Link]
Haga clic en la flecha desplegable situada junto al botón Agregar y seleccione Agregar como vínculo .
También vamos a asegurarnos de que las clases de entidad se generan en el mismo espacio de nombres que el
contexto. Esto solo reduce el número de instrucciones Using que necesitamos agregar en toda la aplicación.
Haga clic con el botón derecho en el [Link] vinculado en Explorador de soluciones y seleccione
propiedades .
En la ventana propiedades , establezca el espacio de nombres de la herramienta personalizada en
STESample
El código generado por la plantilla STE necesitará una referencia a System. Runtime. Serialization para
compilar. Esta biblioteca es necesaria para los atributos DataContract y DataMember de WCF que se utilizan
en los tipos de entidad serializables.
Haga clic con el botón derecho en el proyecto STESample. Entities en Explorador de soluciones y
seleccione Agregar referencia.. .
En Visual Studio 2012: Active la casilla situada junto a System. Runtime. Serialization y haga clic
en Aceptar .
En Visual Studio 2010: seleccione System. Runtime. Serialization y haga clic en Aceptar .
Por último, el proyecto con nuestro contexto en él necesitará una referencia a los tipos de entidad.
Haga clic con el botón derecho en el proyecto STESample en Explorador de soluciones y seleccione
Agregar referencia.. .
En Visual Studio 2012: seleccione solución en el panel izquierdo, active la casilla situada junto a
STESample. entidades y haga clic en Aceptar .
En Visual Studio 2010: seleccione la pestaña proyectos , seleccione STESample. Entities y haga clic
en Aceptar .

NOTE
Otra opción para mover los tipos de entidad a un proyecto independiente consiste en mover el archivo de plantilla, en
lugar de vincularlo desde su ubicación predeterminada. Si lo hace, tendrá que actualizar la variable ArchivoDeEntrada en
la plantilla para proporcionar la ruta de acceso relativa al archivo edmx (en este ejemplo sería .. \ BloggingModel.
edmx).
Crear un servicio WCF
Ahora es el momento de agregar un servicio WCF para exponer nuestros datos, comenzaremos por crear el
proyecto.
Archivo- > agregar- > proyecto...
Seleccione **Visual C # ** en el panel izquierdo y, a continuación, aplicación de ser vicio WCF .
Escriba STESample. Ser vice como nombre y haga clic en Aceptar .
Agregar una referencia al ensamblado System. Data. Entity
Agregar una referencia a los proyectos STESample y STESample. Entities
Necesitamos copiar la cadena de conexión de EF en este proyecto para que se encuentre en tiempo de ejecución.
Abra el archivo [Link] para el proyecto **STESample **y copie el elemento connectionStrings .
Pegue el elemento connectionStrings como un elemento secundario del elemento de configuración del
archivo de [Link] en el proyecto STESample. Ser vice.
Ahora es el momento de implementar el servicio real.
Abra ISer [Link] y reemplace el contenido por el código siguiente.

using [Link];
using [Link];

namespace [Link]
{
[ServiceContract]
public interface IService1
{
[OperationContract]
List<Blog> GetBlogs();

[OperationContract]
void UpdateBlog(Blog blog);
}
}

Abra Ser vice1. SVC y reemplace el contenido por el código siguiente.


using System;
using [Link];
using [Link];
using [Link];

namespace [Link]
{
public class Service1 : IService1
{
/// <summary>
/// Gets all the Blogs and related Posts.
/// </summary>
public List<Blog> GetBlogs()
{
using (BloggingContext context = new BloggingContext())
{
return [Link]("Posts").ToList();
}
}

/// <summary>
/// Updates Blog and its related Posts.
/// </summary>
public void UpdateBlog(Blog blog)
{
using (BloggingContext context = new BloggingContext())
{
try
{
// TODO: Perform validation on the updated order before applying the changes.

// The ApplyChanges method examines the change tracking information


// contained in the graph of self-tracking entities to infer the set of operations
// that need to be performed to reflect the changes in the database.
[Link](blog);
[Link]();

}
catch (UpdateException)
{
// To avoid propagating exception messages that contain sensitive data to the client
tier
// calls to ApplyChanges and SaveChanges should be wrapped in exception handling
code.
throw new InvalidOperationException("Failed to update. Try your request again.");
}
}
}
}
}

Consumo del servicio desde una aplicación de consola


Vamos a crear una aplicación de consola que use nuestro servicio.
Archivo- > nuevo- > proyecto...
Seleccione **Visual C # ** en el panel izquierdo y, a continuación, aplicación de consola
Escriba STESample. ConsoleTest como nombre y haga clic en Aceptar .
Agregar una referencia al proyecto STESample. Entities
Necesitamos una referencia de servicio a nuestro servicio WCF
Haga clic con el botón derecho en el proyecto STESample. ConsoleTest en Explorador de soluciones y
seleccione Agregar referencia de ser vicio...
Haga clic en detectar
Escriba BloggingSer vice como espacio de nombres y haga clic en Aceptar .
Ahora podemos escribir código para consumir el servicio.
Abra [Link] y reemplace el contenido por el código siguiente.

using [Link];
using System;
using [Link];

namespace [Link]
{
class Program
{
static void Main(string[] args)
{
// Print out the data before we change anything
[Link]("Initial Data:");
DisplayBlogsAndPosts();

// Add a new Blog and some Posts


AddBlogAndPost();
[Link]("After Adding:");
DisplayBlogsAndPosts();

// Modify the Blog and one of its Posts


UpdateBlogAndPost();
[Link]("After Update:");
DisplayBlogsAndPosts();

// Delete the Blog and its Posts


DeleteBlogAndPost();
[Link]("After Delete:");
DisplayBlogsAndPosts();

[Link]("Press any key to exit...");


[Link]();
}

static void DisplayBlogsAndPosts()


{
using (var service = new Service1Client())
{
// Get all Blogs (and Posts) from the service
// and print them to the console
var blogs = [Link]();
foreach (var blog in blogs)
{
[Link]([Link]);
foreach (var post in [Link])
{
[Link](" - {0}", [Link]);
}
}
}

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

static void AddBlogAndPost()


{
using (var service = new Service1Client())
{
// Create a new Blog with a couple of Posts
// Create a new Blog with a couple of Posts
var newBlog = new Blog
{
Name = "The New Blog",
Posts =
{
new Post { Title = "Welcome to the new blog"},
new Post { Title = "What's new on the new blog"}
}
};

// Save the changes using the service


[Link](newBlog);
}
}

static void UpdateBlogAndPost()


{
using (var service = new Service1Client())
{
// Get all the Blogs
var blogs = [Link]();

// Use LINQ to Objects to find The New Blog


var blog = [Link](b => [Link] == "The New Blog");

// Update the Blogs name


[Link] = "The Not-So-New Blog";

// Update one of the related posts


[Link]().Content = "Some interesting content...";

// Save the changes using the service


[Link](blog);
}
}

static void DeleteBlogAndPost()


{
using (var service = new Service1Client())
{
// Get all the Blogs
var blogs = [Link]();

// Use LINQ to Objects to find The Not-So-New Blog


var blog = [Link](b => [Link] == "The Not-So-New Blog");

// Mark all related Posts for deletion


// We need to call ToList because each Post will be removed from the
// Posts collection when we call MarkAsDeleted
foreach (var post in [Link]())
{
[Link]();
}

// Mark the Blog for deletion


[Link]();

// Save the changes using the service


[Link](blog);
}
}
}
}

Ahora puede ejecutar la aplicación para verla en acción.


Haga clic con el botón derecho en el proyecto STESample. ConsoleTest en Explorador de soluciones y
seleccione depurar- > Iniciar nueva instancia .
Verá el siguiente resultado cuando se ejecute la aplicación.

Initial Data:
[Link] Blog
- Intro to EF
- What is New

After Adding:
[Link] Blog
- Intro to EF
- What is New
The New Blog
- Welcome to the new blog
- What's new on the new blog

After Update:
[Link] Blog
- Intro to EF
- What is New
The Not-So-New Blog
- Welcome to the new blog
- What's new on the new blog

After Delete:
[Link] Blog
- Intro to EF
- What is New

Press any key to exit...

Consumo del servicio desde una aplicación de WPF


Vamos a crear una aplicación WPF que use nuestro servicio.
Archivo- > nuevo- > proyecto...
Seleccione **Visual C # ** en el panel izquierdo y, a continuación, aplicación WPF .
Escriba STESample. WPFTest como nombre y haga clic en Aceptar .
Agregar una referencia al proyecto STESample. Entities
Necesitamos una referencia de servicio a nuestro servicio WCF
Haga clic con el botón derecho en el proyecto STESample. WPFTest en Explorador de soluciones y
seleccione Agregar referencia de ser vicio...
Haga clic en detectar
Escriba BloggingSer vice como espacio de nombres y haga clic en Aceptar .
Ahora podemos escribir código para consumir el servicio.
Abra MainWindow. Xaml y reemplace el contenido por el código siguiente.
<Window
xmlns="[Link]
xmlns:x="[Link]
xmlns:d="[Link]
xmlns:mc="[Link]
xmlns:STESample="clr-namespace:STESample;assembly=[Link]"
mc:Ignorable="d" x:Class="[Link]"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">

<[Link]>
<CollectionViewSource
x:Key="blogViewSource"
d:DesignSource="{d:DesignInstance {x:Type STESample:Blog}, CreateList=True}"/>
<CollectionViewSource
x:Key="blogPostsViewSource"
Source="{Binding Posts, Source={StaticResource blogViewSource}}"/>
</[Link]>

<Grid DataContext="{StaticResource blogViewSource}">


<DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding}" Margin="10,10,10,179">
<[Link]>
<DataGridTextColumn Binding="{Binding BlogId}" Header="Id" Width="Auto"
IsReadOnly="True" />
<DataGridTextColumn Binding="{Binding Name}" Header="Name" Width="Auto"/>
<DataGridTextColumn Binding="{Binding Url}" Header="Url" Width="Auto"/>
</[Link]>
</DataGrid>
<DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True"
ItemsSource="{Binding Source={StaticResource blogPostsViewSource}}"
Margin="10,145,10,38">
<[Link]>
<DataGridTextColumn Binding="{Binding PostId}" Header="Id" Width="Auto"
IsReadOnly="True"/>
<DataGridTextColumn Binding="{Binding Title}" Header="Title" Width="Auto"/>
<DataGridTextColumn Binding="{Binding Content}" Header="Content" Width="Auto"/>
</[Link]>
</DataGrid>
<Button Width="68" Height="23" HorizontalAlignment="Right" VerticalAlignment="Bottom"
Margin="0,0,10,10" Click="buttonSave_Click">Save</Button>
</Grid>
</Window>

Abra el código subyacente de MainWindow ([Link] ) y reemplace el contenido por el


código siguiente.
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace [Link]
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void Window_Loaded(object sender, RoutedEventArgs e)


{
using (var service = new Service1Client())
{
// Find the view source for Blogs and populate it with all Blogs (and related Posts)
// from the Service. The default editing functionality of WPF will allow the objects
// to be manipulated on the screen.
var blogsViewSource = (CollectionViewSource)[Link]("blogViewSource");
[Link] = [Link]().ToList();
}
}

private void buttonSave_Click(object sender, RoutedEventArgs e)


{
using (var service = new Service1Client())
{
// Get the blogs that are bound to the screen
var blogsViewSource = (CollectionViewSource)[Link]("blogViewSource");
var blogs = (List<Blog>)[Link];

// Save all Blogs and related Posts


foreach (var blog in blogs)
{
[Link](blog);
}

// Re-query for data to get database-generated keys etc.


[Link] = [Link]().ToList();
}
}
}
}

Ahora puede ejecutar la aplicación para verla en acción.


Haga clic con el botón derecho en el proyecto STESample. WPFTest en Explorador de soluciones y
seleccione depurar- > Iniciar nueva instancia .
Puede manipular los datos mediante la pantalla y guardarlos a través del servicio mediante el botón
Guardar
Registrar e interceptar operaciones de base de
datos
12/03/2021 • 25 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

A partir de Entity Framework 6, en cualquier momento Entity Framework envía un comando a la base de datos.
este comando puede ser interceptado por el código de la aplicación. Esto se usa normalmente para registrar
SQL, pero también se puede usar para modificar o anular el comando.
En concreto, EF incluye:
Una propiedad de registro para el contexto similar a DataContext. log en LINQ to SQL
Mecanismo para personalizar el contenido y el formato de la salida enviada al registro.
Bloques de creación de bajo nivel para la interceptación, lo que ofrece un mayor control y flexibilidad

Propiedad de registro de contexto


La propiedad DbContext. Database. log se puede establecer en un delegado para cualquier método que toma
una cadena. Normalmente, se usa con cualquier TextWriter estableciéndolo en el método "write" de ese
TextWriter. Todos los SQL generados por el contexto actual se registrarán en ese escritor. Por ejemplo, el código
siguiente registrará SQL en la consola:

using (var context = new BlogContext())


{
[Link] = [Link];

// Your code here...


}

Tenga en cuenta ese contexto. Database. log se establece en Console. Write. Esto es todo lo que se necesita para
registrar SQL en la consola.
Vamos a agregar algunos códigos simples de consulta/inserción/actualización para que podamos ver algunos
resultados:

using (var context = new BlogContext())


{
[Link] = [Link];

var blog = [Link](b => [Link] == "One Unicorn");

[Link]().Title = "Green Eggs and Ham";

[Link](new Post { Title = "I do not like them!" });

[Link]().Wait();
}
Se generará el siguiente resultado:

SELECT TOP (1)


[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title]
FROM [dbo].[Blogs] AS [Extent1]
WHERE (N'One Unicorn' = [Extent1].[Title]) AND ([Extent1].[Title] IS NOT NULL)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 4 ms with result: SqlDataReader

SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Title] AS [Title],
[Extent1].[BlogId] AS [BlogId]
FROM [dbo].[Posts] AS [Extent1]
WHERE [Extent1].[BlogId] = @EntityKeyValue1
-- EntityKeyValue1: '1' (Type = Int32)
-- Executing at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader

UPDATE [dbo].[Posts]
SET [Title] = @0
WHERE ([Id] = @1)
-- @0: 'Green Eggs and Ham' (Type = String, Size = -1)
-- @1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 12 ms with result: 1

INSERT [dbo].[Posts]([Title], [BlogId])


VALUES (@0, @1)
SELECT [Id]
FROM [dbo].[Posts]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'I do not like them!' (Type = String, Size = -1)
-- @1: '1' (Type = Int32)
-- Executing asynchronously at 10/8/2013 10:55:41 AM -07:00
-- Completed in 2 ms with result: SqlDataReader

(Tenga en cuenta que esta es la salida, suponiendo que ya se ha producido cualquier inicialización de base de
datos. Si aún no se ha producido la inicialización de la base de datos, habría mucha más salida mostrando todas
las migraciones de trabajo en segundo plano para comprobar o crear una nueva base de datos.

¿Qué se registra?
Cuando se establece la propiedad log, se registran todos los elementos siguientes:
SQL para todos los tipos de comandos diferentes. Por ejemplo:
Consultas, incluidas las consultas LINQ normales, las consultas eSQL y las consultas sin formato de
métodos como SqlQuery
Inserciones, actualizaciones y eliminaciones generadas como parte de SaveChanges
Consultas de carga de relaciones como las generadas por la carga diferida
Parámetros
Si el comando se ejecuta de forma asincrónica o no
Marca de tiempo que indica cuándo empezó A ejecutarse el comando.
Si el comando se completó correctamente, se produjo un error al iniciar una excepción o, para Async, se
canceló.
Alguna indicación del valor del resultado
Cantidad aproximada de tiempo que se tardó en ejecutar el comando. Tenga en cuenta que es el momento en
el que se envía el comando para volver a obtener el objeto de resultado. No incluye el tiempo para leer los
resultados.
Al examinar la salida de ejemplo anterior, cada uno de los cuatro comandos registrados son:
La consulta que resulta de la llamada a context. Blogs. First
Tenga en cuenta que el método ToString para obtener el SQL no habría funcionado para esta consulta
porque "First" no proporciona un IQueryable en el que se podría llamar a ToString
La consulta que resulta de la carga diferida del blog. Publique
Observe los detalles del parámetro para el valor de clave para el que se está produciendo la carga
diferida.
Solo se registran las propiedades del parámetro que se establecen en valores no predeterminados.
Por ejemplo, la propiedad Size solo se muestra si es distinto de cero.
Dos comandos resultantes de SaveChangesAsync; uno para la actualización para cambiar el título de una
publicación, el otro para una inserción para agregar una nueva publicación
Tenga en cuenta los detalles de los parámetros de las propiedades de FK y title
Tenga en cuenta que estos comandos se ejecutan de forma asincrónica

Registro en distintos lugares


Como se indicó anteriormente, el registro en la consola es muy sencillo. También es fácil de registrar en la
memoria, el archivo, etc. mediante el uso de diferentes tipos de TextWriter.
Si está familiarizado con LINQ to SQL podría observar que en LINQ to SQL la propiedad log está establecida en
el objeto TextWriter real (por ejemplo, Console. out) mientras que en EF la propiedad log está establecida en un
método que acepta una cadena (por ejemplo, Console. Write o Console. out. Write). El motivo es desacoplar EF
de TextWriter mediante la aceptación de cualquier delegado que pueda actuar como receptor de cadenas. Por
ejemplo, Imagine que ya tiene alguna plataforma de registro y que define un método de registro como el
siguiente:

public class MyLogger


{
public void Log(string component, string message)
{
[Link]("Component: {0} Message: {1} ", component, message);
}
}

Se puede enlazar a la propiedad de registro EF de la siguiente manera:

var logger = new MyLogger();


[Link] = s => [Link]("EFApp", s);

Registro de resultados
El registrador predeterminado registra el texto del comando (SQL), los parámetros y la línea "en ejecución" con
una marca de tiempo antes de que el comando se envíe a la base de datos. Una línea "completada" que contiene
el tiempo transcurrido se registra después de la ejecución del comando.
Tenga en cuenta que para los comandos asincrónicos, la línea "completada" no se registra hasta que la tarea
asincrónica se complete realmente, se produzca un error o se cancele.
La línea "completado" contiene información diferente en función del tipo de comando y de si la ejecución se
realizó correctamente o no.
Ejecución correcta
En el caso de los comandos que se completan correctamente, la salida es "Completed in x MS with result:"
seguida de alguna indicación de cuál fue el resultado. Para los comandos que devuelven un lector de datos, la
indicación de resultado es el tipo de DbDataReader devuelto. En el caso de los comandos que devuelven un
valor entero como el comando UPDATE mostrado sobre el resultado que se muestra es ese entero.
Error de ejecución
En el caso de los comandos que producen un error iniciando una excepción, la salida contiene el mensaje de la
excepción. Por ejemplo, el uso de SqlQuery para realizar consultas en una tabla que existe producirá una salida
de registro similar a la siguiente:

SELECT * from ThisTableIsMissing


-- Executing at 5/13/2013 10:19:05 AM
-- Failed in 1 ms with error: Invalid object name 'ThisTableIsMissing'.

Ejecución cancelada
En el caso de los comandos Async en los que se cancela la tarea, el resultado podría ser un error con una
excepción, ya que esto es lo que suele hacer el proveedor [Link] subyacente cuando se intenta cancelar. Si
esto no ocurre y la tarea se cancela correctamente, la salida tendrá un aspecto similar al siguiente:

update Blogs set Title = 'No' where Id = -1


-- Executing asynchronously at 5/13/2013 10:21:10 AM
-- Canceled in 1 ms

Cambiar el contenido y el formato del registro


En, la propiedad Database. log hace uso de un objeto DatabaseLogFormatter. Este objeto enlaza eficazmente una
implementación de IDbCommandInterceptor (consulte a continuación) a un delegado que acepta cadenas y
DbContext. Esto significa que se llama a los métodos de DatabaseLogFormatter antes y después de la ejecución
de comandos por EF. Estos métodos de DatabaseLogFormatter recopilan y dan formato al resultado del registro
y lo envían al delegado.
Personalización de DatabaseLogFormatter
El cambio de lo que se registra y su formato se puede lograr creando una nueva clase que se deriva de
DatabaseLogFormatter e invalida los métodos según corresponda. Los métodos más comunes para invalidar
son:
LogCommand: invalide esto para cambiar el modo en que se registran los comandos antes de que se
ejecuten. De forma predeterminada, LogCommand llama a LogParameter para cada parámetro; en su lugar,
puede elegir hacer lo mismo en los parámetros de invalidación o de control de forma diferente.
LogResult: invalide esto para cambiar el modo en que se registra el resultado de la ejecución de un comando.
LogParameter: invalide esto para cambiar el formato y el contenido del registro de parámetros.
Por ejemplo, supongamos que deseamos registrar solo una línea antes de que cada comando se envíe a la base
de datos. Esto puede hacerse con dos invalidaciones:
Invalide LogCommand para dar formato y escribir la única línea de SQL
Invalide LogResult para no hacer nada.
El código debería tener un aspecto parecido al siguiente:
public class OneLineFormatter : DatabaseLogFormatter
{
public OneLineFormatter(DbContext context, Action<string> writeAction)
: base(context, writeAction)
{
}

public override void LogCommand<TResult>(


DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
Write([Link](
"Context '{0}' is executing command '{1}'{2}",
[Link]().Name,
[Link]([Link], ""),
[Link]));
}

public override void LogResult<TResult>(


DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
}
}

Para registrar la salida, simplemente llame al método Write, que enviará la salida al delegado de escritura
configurado.
(Tenga en cuenta que este código hace la eliminación simplista de los saltos de línea como ejemplo. Lo más
probable es que no funcione bien para ver SQL complejo).
Establecer DatabaseLogFormatter
Una vez que se ha creado una nueva clase DatabaseLogFormatter, debe registrarse con EF. Esto se hace
mediante la configuración basada en código. En pocas palabras, esto significa que se crea una nueva clase que
se deriva de DbConfiguration en el mismo ensamblado que la clase DbContext y, después, se llama a
SetDatabaseLogFormatter en el constructor de esta nueva clase. Por ejemplo:

public class MyDbConfiguration : DbConfiguration


{
public MyDbConfiguration()
{
SetDatabaseLogFormatter(
(context, writeAction) => new OneLineFormatter(context, writeAction));
}
}

Usar el nuevo DatabaseLogFormatter


Este nuevo DatabaseLogFormatter se usará siempre que se establezca Database. log. Por lo tanto, la ejecución
del código de la parte 1 generará ahora el siguiente resultado:

Context 'BlogContext' is executing command 'SELECT TOP (1) [Extent1].[Id] AS [Id], [Extent1].[Title] AS
[Title]FROM [dbo].[Blogs] AS [Extent1]WHERE (N'One Unicorn' = [Extent1].[Title]) AND ([Extent1].[Title] IS
NOT NULL)'
Context 'BlogContext' is executing command 'SELECT [Extent1].[Id] AS [Id], [Extent1].[Title] AS [Title],
[Extent1].[BlogId] AS [BlogId]FROM [dbo].[Posts] AS [Extent1]WHERE [Extent1].[BlogId] = @EntityKeyValue1'
Context 'BlogContext' is executing command 'update [dbo].[Posts]set [Title] = @0where ([Id] = @1)'
Context 'BlogContext' is executing command 'insert [dbo].[Posts]([Title], [BlogId])values (@0, @1)select
[Id]from [dbo].[Posts]where @@rowcount > 0 and [Id] = scope_identity()'

Bloques de creación de interceptación


Hasta ahora hemos visto cómo usar DbContext. Database. log para registrar el SQL generado por EF. Pero este
código es realmente una fachada relativamente fina sobre algunos bloques de creación de bajo nivel para la
interceptación más general.
Interfaces de interceptación
El código de intercepción se crea en torno al concepto de interfaces de interceptación. Estas interfaces heredan
de IDbInterceptor y definen métodos a los que se llama cuando EF realiza alguna acción. La intención es tener
una interfaz por cada tipo de objeto que se va a interceptar. Por ejemplo, la interfaz IDbCommandInterceptor
define métodos a los que se llama antes de que EF realice una llamada a ExecuteNonQuery, ExecuteScalar,
ExecuteReader y a los métodos relacionados. Del mismo modo, la interfaz define métodos a los que se llama
cuando se completa cada una de estas operaciones. La clase DatabaseLogFormatter que hemos examinado
anteriormente implementa esta interfaz para registrar los comandos.
El contexto de interceptación
Si se examinan los métodos definidos en cualquiera de las interfaces del interceptor, es evidente que cada
llamada recibe un objeto de tipo DbInterceptionContext o algún tipo derivado de este, como
DbCommandInterceptionContext <> . Este objeto contiene información contextual sobre la acción que está
llevando a cabo EF. Por ejemplo, si la acción se realiza en nombre de un DbContext, el DbContext se incluye en el
DbInterceptionContext. De forma similar, para los comandos que se ejecutan de forma asincrónica, la marca
IsAsync se establece en DbCommandInterceptionContext.
Control de resultados
La <> clase DbCommandInterceptionContext contiene una propiedad denominada result, OriginalResult,
Exception y OriginalException. Estas propiedades se establecen en NULL/Zero para las llamadas a los métodos
de interceptación a los que se llama antes de que se ejecute la operación, es decir, para... Ejecutar métodos. Si la
operación se ejecuta y se realiza correctamente, result y OriginalResult se establecen en el resultado de la
operación. Estos valores se pueden observar en los métodos de interceptación a los que se llama después de
que se haya ejecutado la operación, es decir, en... Métodos ejecutados. Del mismo modo, si se produce la
operación, se establecerán las propiedades Exception y OriginalException.
Suprimir la ejecución
Si un interceptor establece la propiedad de resultado antes de que se ejecute el comando (en una de las
propiedades... Ejecutar métodos) entonces EF no intentará realmente ejecutar el comando, sino que solo usará el
conjunto de resultados. En otras palabras, el interceptor puede suprimir la ejecución del comando, pero el EF
continúa como si se hubiera ejecutado el comando.
Un ejemplo de cómo se podría usar esto es el procesamiento por lotes de comandos que se ha realizado
tradicionalmente con un proveedor de ajuste. El interceptor almacenaría el comando para su ejecución posterior
como un lote, pero "fingiría" a EF que el comando se ejecutaba como normal. Tenga en cuenta que esto requiere
más que esto para implementar el procesamiento por lotes, pero este es un ejemplo de cómo se puede usar el
resultado de la interceptación.
La ejecución también se puede suprimir si se establece la propiedad Exception en una de las propiedades...
Ejecutar métodos. Esto hace que EF continúe como si se produjera un error en la ejecución de la operación
produciendo la excepción dada. Por supuesto, esto puede hacer que la aplicación se bloquee, pero también
puede ser una excepción transitoria o cualquier otra excepción controlada por EF. Por ejemplo, se puede usar en
entornos de prueba para probar el comportamiento de una aplicación cuando se produce un error en la
ejecución del comando.
Cambiar el resultado después de la ejecución
Si un interceptor establece la propiedad de resultado después de que se haya ejecutado el comando (en una de
las propiedades... Métodos ejecutados) entonces EF usará el resultado cambiado en lugar del resultado que se
devolvió realmente desde la operación. Del mismo modo, si un interceptor establece la propiedad Exception
después de que se haya ejecutado el comando, EF producirá la excepción set como si la operación hubiera
producido la excepción.
Un interceptor también puede establecer la propiedad Exception en null para indicar que no se debe producir
ninguna excepción. Esto puede ser útil si se produce un error en la ejecución de la operación, pero el interceptor
quiere que EF continúe como si la operación se hubiera realizado correctamente. Normalmente, esto también
implica establecer el resultado para que EF tenga algún valor de resultado con el que trabajar mientras continúa.
OriginalResult y OriginalException
Después de que EF haya ejecutado una operación, establecerá las propiedades result y OriginalResult si la
ejecución no produjo un error, o bien las propiedades Exception y OriginalException si se produce un error de
ejecución con una excepción.
Las propiedades OriginalResult y OriginalException son de solo lectura y solo las establece EF después de
ejecutar realmente una operación. Los interceptores no pueden establecer estas propiedades. Esto significa que
cualquier interceptor puede distinguir entre una excepción o un resultado establecido por otro interceptor, en
lugar de la excepción real o el resultado que se produjo cuando se ejecutó la operación.
Registro de interceptores
Una vez que se ha creado una clase que implementa una o varias interfaces de interceptación, se puede registrar
con EF mediante la clase DbInterception. Por ejemplo:

[Link](new NLogCommandInterceptor());

Los interceptores también se pueden registrar en el nivel de dominio de aplicación mediante el mecanismo de
configuración basado en código DbConfiguration.
Ejemplo: registro en NLog
Vamos a reunir todo esto en un ejemplo que usa IDbCommandInterceptor y NLog para:
Registrar una advertencia para cualquier comando que se ejecute de forma no asincrónica
Registrar un error para cualquier comando que se produzca cuando se ejecute
Esta es la clase que realiza el registro, que se debe registrar como se muestra arriba:
public class NLogCommandInterceptor : IDbCommandInterceptor
{
private static readonly Logger Logger = [Link]();

public void NonQueryExecuting(


DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
LogIfNonAsync(command, interceptionContext);
}

public void NonQueryExecuted(


DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
LogIfError(command, interceptionContext);
}

public void ReaderExecuting(


DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
LogIfNonAsync(command, interceptionContext);
}

public void ReaderExecuted(


DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
LogIfError(command, interceptionContext);
}

public void ScalarExecuting(


DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
LogIfNonAsync(command, interceptionContext);
}

public void ScalarExecuted(


DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
LogIfError(command, interceptionContext);
}

private void LogIfNonAsync<TResult>(


DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
if (![Link])
{
[Link]("Non-async command used: {0}", [Link]);
}
}

private void LogIfError<TResult>(


DbCommand command, DbCommandInterceptionContext<TResult> interceptionContext)
{
if ([Link] != null)
{
[Link]("Command {0} failed with exception {1}",
[Link], [Link]);
}
}
}

Observe cómo este código utiliza el contexto de interceptación para detectar cuándo un comando se ejecuta de
forma no asincrónica y para detectar cuándo se produjo un error al ejecutar un comando.
Consideraciones de rendimiento para EF 4, 5 y 6
12/03/2021 • 139 minutes to read

Por David Obando, Eric Dettinger y otros


Publicado: abril 2012
Última actualización: 2014 de mayo

1. Introducción
Object-Relational marcos de asignación son una forma cómoda de proporcionar una abstracción para el acceso
a datos en una aplicación orientada a objetos. En el caso de las aplicaciones .NET, se recomienda que el O el RM
de Microsoft se Entity Framework. Sin embargo, con cualquier abstracción, el rendimiento puede ser un
problema.
Estas notas del producto se han escrito para mostrar las consideraciones de rendimiento al desarrollar
aplicaciones mediante Entity Framework, para ofrecer a los desarrolladores una idea de los algoritmos internos
de Entity Framework que pueden afectar al rendimiento y para proporcionar sugerencias para la investigación y
la mejora del rendimiento en sus aplicaciones que usan Entity Framework. Hay una serie de buenos temas sobre
el rendimiento que ya están disponibles en la web y también hemos intentado apuntar a estos recursos siempre
que sea posible.
El rendimiento es un tema complicado. Estas notas del producto están pensadas como recursos para ayudarle a
tomar decisiones relacionadas con el rendimiento de las aplicaciones que usan Entity Framework. Hemos
incluido algunas métricas de prueba para demostrar el rendimiento, pero estas métricas no están pensadas
como indicadores absolutos del rendimiento que verá en la aplicación.
A efectos prácticos, en este documento se supone Entity Framework 4 se ejecuta en .NET 4,0 y Entity Framework
5 y 6 se ejecutan en .NET 4,5. Muchas de las mejoras de rendimiento realizadas para Entity Framework 5 residen
en los componentes principales que se incluyen con .NET 4,5.
Entity Framework 6 es una versión fuera de banda y no depende de los componentes Entity Framework que se
incluyen con .NET. Entity Framework 6 funcionan tanto en .NET 4,0 como en .NET 4,5 y pueden ofrecer una gran
ventaja de rendimiento a aquellos que no se han actualizado desde .NET 4,0, pero que quieren los bits de Entity
Framework más recientes en su aplicación. Cuando este documento menciona Entity Framework 6, hace
referencia a la última versión disponible en el momento de redactar este documento: versión 6.1.0.

2. actividad en frío frente a ejecución de consultas en caliente


La primera vez que se realiza una consulta en un modelo determinado, el Entity Framework realiza una gran
cantidad de trabajo en segundo plano para cargar y validar el modelo. A menudo, hacemos referencia a esta
primera consulta como una consulta "fría".Las consultas adicionales en un modelo ya cargado se conocen como
consultas "calientes" y son mucho más rápidas.
Vamos a tomar una vista de alto nivel del tiempo que se dedica a la ejecución de una consulta mediante Entity
Framework y ver dónde están mejorando las cosas en Entity Framework 6.
Primera ejecución de la consulta: consulta en frío
IM PA C TO EN EL IM PA C TO EN EL IM PA C TO EN EL
ESC RIT URA DE REN DIM IEN TO DE REN DIM IEN TO DE REN DIM IEN TO DE
C Ó DIGO DE USUA RIO A C C IÓ N EF 4 EF 5 EF 6

using(var db = Creación de contexto Media Media Bajo


new MyContext())
{

var q1 = Creación de Bajo Bajo Bajo


from c in expresiones de
[Link] consulta
where [Link] == id1
select c;

var c1 = Ejecución de -Carga de -Carga de -Carga de


[Link](); consultas LINQ metadatos: alta pero metadatos: alta pero metadatos: alta pero
almacenada en caché almacenada en caché almacenada en caché
-Vista de la -Vista de la -Ver generación:
generación: generación: medio pero
posiblemente muy posiblemente muy almacenado en caché
alta pero almacenada alta pero almacenada -Evaluación de
en caché en caché parámetros: baja
-Evaluación de -Evaluación de -Traducción de
parámetros: media parámetros: baja consultas: mediana
-Traducción de -Traducción de pero almacenada en
consultas: mediana consultas: mediana caché
-Generación de pero almacenada en -Generación de
materializador: medio caché materializador: medio
pero almacenado en -Generación de pero almacenado en
caché materializador: medio caché
-Ejecución de la pero almacenado en -Ejecución de la
consulta de base de caché consulta de base de
datos: -Ejecución de la datos:
potencialmente alta consulta de base de potencialmente alta
+ Conexión. abrir datos: (mejores consultas en
+ potencialmente alta algunas situaciones)
[Link] (mejores consultas en + Conexión. abrir
ader algunas situaciones) +
+ DataReader. Read + Conexión. abrir [Link]
Materialización de + ader
objetos: medio [Link] + DataReader. Read
-Búsqueda de ader Materialización de
identidad: mediana + DataReader. Read objetos: medio (más
Materialización de rápido que EF5)
objetos: medio -Búsqueda de
-Búsqueda de identidad: mediana
identidad: mediana

} Conexión. Close Bajo Bajo Bajo

Segunda ejecución de consultas: consulta en caliente

IM PA C TO EN EL IM PA C TO EN EL IM PA C TO EN EL
ESC RIT URA DE REN DIM IEN TO DE REN DIM IEN TO DE REN DIM IEN TO DE
C Ó DIGO DE USUA RIO A C C IÓ N EF 4 EF 5 EF 6

using(var db = Creación de contexto Media Media Bajo


new MyContext())
{
IM PA C TO EN EL IM PA C TO EN EL IM PA C TO EN EL
ESC RIT URA DE REN DIM IEN TO DE REN DIM IEN TO DE REN DIM IEN TO DE
C Ó DIGO DE USUA RIO A C C IÓ N EF 4 EF 5 EF 6

var q1 = Creación de Bajo Bajo Bajo


from c in expresiones de
[Link] consulta
where [Link] == id1
select c;

var c1 = Ejecución de -Búsqueda de carga -Búsqueda de carga -Búsqueda de carga


[Link](); consultas LINQ de metadatos: alta de metadatos: alta de metadatos: alta
pero caché baja pero caché baja pero caché baja
-Vista de la -Vista de la -Ver generación de la
generación de vistas: generación de vistas: búsqueda: medio
potencialmente muy potencialmente muy pero en caché bajo
alta pero con alta pero con -Evaluación de
almacenamiento en almacenamiento en parámetros: baja
caché bajo caché bajo -Consulta de
-Evaluación de -Evaluación de traducción de
parámetros: media parámetros: baja consultas: mediana
-Búsqueda de -Consulta de pero en caché baja
traducción de traducción de -Búsqueda de la
consultas: mediana consultas: mediana generación del
-Búsqueda de la pero en caché baja materializador: medio
generación del -Búsqueda de la pero en caché bajo
materializador: medio generación del -Ejecución de la
pero en caché bajo materializador: medio consulta de base de
-Ejecución de la pero en caché bajo datos:
consulta de base de -Ejecución de la potencialmente alta
datos: consulta de base de (mejores consultas en
potencialmente alta datos: algunas situaciones)
+ Conexión. abrir potencialmente alta + Conexión. abrir
+ (mejores consultas en +
[Link] algunas situaciones) [Link]
ader + Conexión. abrir ader
+ DataReader. Read + + DataReader. Read
Materialización de [Link] Materialización de
objetos: medio ader objetos: medio (más
-Búsqueda de + DataReader. Read rápido que EF5)
identidad: mediana Materialización de -Búsqueda de
objetos: medio identidad: mediana
-Búsqueda de
identidad: mediana

} Conexión. Close Bajo Bajo Bajo

Hay varias maneras de reducir el costo de rendimiento de las consultas en frío y en caliente, y echaremos un
vistazo a ellas en la sección siguiente. En concreto, veremos cómo reducir el costo de la carga de modelos en
consultas frías mediante el uso de vistas generadas previamente, que ayudan a mitigar los problemas de
rendimiento experimentados durante la generación de la vista. En el caso de las consultas cálidas, trataremos el
almacenamiento en caché del plan de consulta, ninguna consulta de seguimiento y opciones de ejecución de
consulta diferentes.
2,1 ¿Qué es la generación de vistas?
Para comprender qué es la generación de vistas, primero debemos comprender qué son las "vistas de
asignación". Las vistas de asignación son representaciones ejecutables de las transformaciones especificadas en
la asignación para cada conjunto de entidades y asociación. Internamente, estas vistas de asignación adoptan la
forma de CQTs (árboles de consulta canónicos). Hay dos tipos de vistas de asignación:
Vistas de consulta: representan la transformación necesaria para pasar del esquema de la base de datos al
modelo conceptual.
Vistas de actualización: representan la transformación necesaria para pasar del modelo conceptual al
esquema de la base de datos.
Tenga en cuenta que el modelo conceptual podría diferir del esquema de la base de datos de varias maneras.
Por ejemplo, se puede usar una sola tabla para almacenar los datos de dos tipos de entidad diferentes. La
herencia y las asignaciones no triviales desempeñan un papel en la complejidad de las vistas de asignación.
El proceso de cálculo de estas vistas en función de la especificación de la asignación es lo que llamamos
generación de vistas. La generación de vistas puede tener lugar dinámicamente cuando se carga un modelo, o
en el momento de la compilación, mediante "vistas generadas previamente". estos últimos se serializan en
forma de Entity SQL instrucciones en un archivo de C # o VB.
Cuando se generan vistas, también se validan. Desde el punto de vista del rendimiento, la gran mayoría del
costo de la generación de vistas es realmente la validación de las vistas, lo que garantiza que las conexiones
entre las entidades tienen sentido y tienen la cardinalidad correcta para todas las operaciones admitidas.
Cuando se ejecuta una consulta en un conjunto de entidades, la consulta se combina con la vista de consulta
correspondiente y el resultado de esta composición se ejecuta a través del compilador del plan para crear la
representación de la consulta que la memoria auxiliar puede entender. Por SQL Server, el resultado final de esta
compilación será una instrucción SELECT de T-SQL. La primera vez que se realiza una actualización en un
conjunto de entidades, la vista de actualización se ejecuta a través de un proceso similar para transformarla en
instrucciones DML para la base de datos de destino.
2,2 factores que afectan al rendimiento de la generación de vistas
El paso rendimiento de la generación de vistas no solo depende del tamaño del modelo, sino también de cómo
se conecta el modelo. Si dos entidades están conectadas a través de una cadena de herencia o una asociación, se
dice que están conectadas. Del mismo modo, si dos tablas están conectadas a través de una clave externa, están
conectadas. A medida que aumenta el número de entidades conectadas y tablas en los esquemas, aumenta el
costo de la generación de la vista.
El algoritmo que usamos para generar y validar vistas es exponencial en el peor de los casos, aunque usamos
algunas optimizaciones para mejorar esto. Los factores más importantes que parecen afectar negativamente al
rendimiento son:
Tamaño del modelo, que hace referencia al número de entidades y la cantidad de asociaciones entre estas
entidades.
Complejidad del modelo, específicamente la herencia que implica un gran número de tipos.
Usar asociaciones independientes, en lugar de asociaciones de clave externa.
En el caso de los modelos pequeños y sencillos, el costo puede ser lo suficientemente pequeño como para no
molestarse en usar vistas generadas previamente. A medida que aumentan el tamaño y la complejidad del
modelo, hay varias opciones disponibles para reducir el costo de la generación y validación de las vistas.
2,3 usar vistas generadas previamente para reducir el tiempo de carga del modelo
Para obtener información detallada sobre cómo usar las vistas generadas previamente en Entity Framework 6,
visite vistas de asignación previamente generadas
2.3.1 vistas previamente generadas mediante el Entity Framework Power Tools Community Edition
Puede usar la edición Community Tools de Entity Framework 6 para generar vistas de los modelos EDMX y Code
First haciendo clic con el botón secundario en el archivo de clase de modelo y usando el menú Entity Framework
para seleccionar "generar vistas". Entity Framework Power Tools Community Edition solo funciona en contextos
derivados de DbContext.
2.3.2 Cómo usar las vistas generadas previamente con un modelo creado por EDMGen
EDMGen es una utilidad que se incluye con .NET y funciona con Entity Framework 4 y 5, pero no con Entity
Framework 6. EDMGen permite generar un archivo de modelo, la capa de objetos y las vistas desde la línea de
comandos. Una de las salidas será un archivo de vistas en el lenguaje que prefiera, VB o C # . Se trata de un
archivo de código que contiene fragmentos de Entity SQL para cada conjunto de entidades. Para habilitar las
vistas generadas previamente, solo tiene que incluir el archivo en el proyecto.
Si realiza modificaciones manualmente en los archivos de esquema para el modelo, tendrá que volver a generar
el archivo de vistas. Para ello, ejecute EDMGen con la marca /Mode: ViewGeneration .
2.3.3 Cómo usar vistas generadas previamente con un archivo EDMX
También puede usar EDMGen para generar vistas para un archivo EDMX: el tema de MSDN al que se hace
referencia anteriormente describe cómo agregar un evento anterior a la compilación para hacer esto, pero esto
es complicado y hay algunos casos en los que no es posible. Generalmente, es más fácil usar una plantilla T4
para generar las vistas cuando el modelo se encuentra en un archivo edmx.
El blog del equipo de [Link] tiene una publicación que describe cómo usar una plantilla T4 para la generación
de vistas ( <[Link]
). Esta publicación incluye una plantilla que se puede descargar y agregar al proyecto. La plantilla se escribió
para la primera versión de Entity Framework, por lo que no se garantiza que funcionen con las versiones más
recientes de Entity Framework. Sin embargo, puede descargar un conjunto más actualizado de plantillas de
generación de vistas para Entity Framework 4 y 5from la galería de Visual Studio:
[Link]: <[Link]
C # : <[Link]
Si usa Entity Framework 6, puede obtener las plantillas T4 de la generación de vistas de la galería de Visual
Studio en <[Link] .
2,4 reducir el costo de la generación de vistas
El uso de vistas generadas previamente mueve el costo de la generación de vistas de la carga del modelo
(tiempo de ejecución) al tiempo de diseño. Aunque esto mejora el rendimiento de inicio en tiempo de ejecución,
todavía experimentará el problema de la generación de vistas mientras está desarrollando. Hay varios trucos
adicionales que pueden ayudar a reducir el costo de la generación de vistas, tanto en tiempo de compilación
como en tiempo de ejecución.
2.4.1 usar asociaciones de clave externa para reducir el costo de la generación de vistas
Hemos detectado varios casos en los que el cambio de las asociaciones en el modelo de asociaciones
independientes a asociaciones de claves externas mejoró considerablemente el tiempo empleado en la
generación de la vista.
Para demostrar esta mejora, se generaron dos versiones del modelo de Navision mediante EDMGen. Nota: vea
el Apéndice C para obtener una descripción del modelo de Navision. El modelo de Navision es interesante para
este ejercicio debido a su gran cantidad de entidades y relaciones entre ellos.
Se generó una versión de este modelo muy grande con las asociaciones de claves externas y la otra se generó
con asociaciones independientes. A continuación, se agotó el tiempo que se tardó en generar las vistas para
cada modelo. La prueba de Entity Framework 5 usó el método GenerateViews () de la clase EntityViewGenerator
para generar las vistas, mientras que la prueba de Entity Framework 6 usaba el método GenerateViews () de la
clase StorageMappingItemCollection. Esto se debe a la reestructuración del código que se produjo en el código
base de Entity Framework 6.
Con Entity Framework 5, la generación de vistas del modelo con claves externas tardó 65 minutos en un equipo
de laboratorio. Se desconoce cuánto tiempo habría tardado en generar las vistas para el modelo que usaba
asociaciones independientes. Hemos dejado la prueba en ejecución durante un mes antes de que el equipo se
reinicie en nuestro laboratorio para instalar las actualizaciones mensuales.
Con Entity Framework 6, la generación de vistas del modelo con claves externas tardó 28 segundos en el mismo
equipo de laboratorio. La generación de la vista para el modelo que usa asociaciones independientes tardó 58
segundos. Las mejoras realizadas en Entity Framework 6 en el código de generación de vistas significan que
muchos proyectos no necesitarán vistas generadas previamente para obtener tiempos de inicio más rápidos.
Es importante volver a marcar que las vistas que se generan previamente en Entity Framework 4 y 5 se pueden
realizar con EDMGen o con las herramientas avanzadas de Entity Framework. Para Entity Framework la
generación de una vista de 6 puede realizarse mediante el Entity Framework herramientas avanzadas o
mediante programación, tal y como se describe en vistas de asignación previamente generadas.
2 .4 .1 .1 C ó m o u sa r c l a v e s e x t e r n a s e n l u g a r d e a so c i a c i o n e s i n d e p e n d i e n t e s

Cuando se usa EDMGen o el diseñador de entidades en Visual Studio, se obtiene claves externas de forma
predeterminada y solo se usa una marca de la línea de comandos o una casilla para cambiar entre claves
externas e IAs.
Si tiene un modelo de Code First grande, el uso de asociaciones independientes tendrá el mismo efecto en la
generación de la vista. Puede evitar este impacto si incluye propiedades de clave externa en las clases para los
objetos dependientes, aunque algunos desarrolladores considerarán que esto va a contaminar su modelo de
objetos. Puede encontrar más información sobre este tema en <[Link]
the-deal-with-mapping-foreign-keys-using-the-entity-framework/> .

A L USA R H A GA ESTO

Diseñador de entidades Después de agregar una asociación entre dos entidades,


asegúrese de que tiene una restricción referencial. Las
restricciones referenciales indican a Entity Framework que
utilicen claves externas en lugar de asociaciones
independientes. Para obtener más información, visite
<[Link]
keys-in-the-entity-framework> .

EDMGen Al usar EDMGen para generar los archivos de la base de


datos, las claves externas se respetan y se agregan al modelo
como tal. Para obtener más información sobre las distintas
opciones expuestas por EDMGen, visite
[Link] .

Code First Vea la sección "Convención de relación" del tema code First
convenciones para obtener información sobre cómo incluir
propiedades de clave externa en objetos dependientes
cuando se usa Code First.

2.4.2 mover el modelo a un ensamblado independiente


Cuando el modelo se incluya directamente en el proyecto de la aplicación y se generen vistas a través de un
evento anterior a la compilación o de una plantilla T4, se realizará la generación y validación de la vista cada vez
que se vuelva a generar el proyecto, incluso si el modelo no se ha cambiado. Si mueve el modelo a un
ensamblado independiente y hace referencia a él desde el proyecto de la aplicación, puede realizar otros
cambios en la aplicación sin necesidad de volver a compilar el proyecto que contiene el modelo.
Nota: al mover el modelo a ensamblados independientes, recuerde copiar las cadenas de conexión para el
modelo en el archivo de configuración de la aplicación del proyecto de cliente.
2.4.3 deshabilitar la validación de un modelo basado en edmx
Los modelos EDMX se validan en tiempo de compilación, incluso si el modelo no se ha modificado. Si el modelo
ya se ha validado, puede suprimir la validación en tiempo de compilación estableciendo la propiedad "validar al
compilar" en false en la ventana Propiedades. Al cambiar la asignación o el modelo, puede volver a habilitar
temporalmente la validación para comprobar los cambios.
Tenga en cuenta que se han realizado mejoras en el rendimiento del Entity Framework Designer para Entity
Framework 6 y el costo de "validar al compilar" es mucho menor que en versiones anteriores del diseñador.

3 almacenamiento en caché en el Entity Framework


Entity Framework tiene los siguientes formatos de almacenamiento en caché integrados:
1. Almacenamiento en caché de objetos: el ObjectStateManager integrado en una instancia de ObjectContext
realiza un seguimiento de la memoria de los objetos que se han recuperado mediante esa instancia. Esto
también se conoce como caché de primer nivel.
2. Almacenamiento en caché del plan de consulta: reutilizar el comando de almacenamiento generado cuando
una consulta se ejecuta más de una vez.
3. Almacenamiento en caché de metadatos: compartir los metadatos de un modelo entre diferentes conexiones
con el mismo modelo.
Además de las memorias caché que EF proporciona de forma integrada, también se puede usar un tipo especial
de proveedor de datos [Link] conocido como proveedor de ajuste para extender Entity Framework con una
memoria caché para los resultados recuperados de la base de datos, también denominado almacenamiento en
caché de segundo nivel.
ca3,1 almacenamiento en caché de objetos
De forma predeterminada, cuando una entidad se devuelve en los resultados de una consulta, justo antes de
que EF lo materializa, el ObjectContext comprobará si ya se ha cargado una entidad con la misma clave en el
ObjectStateManager. Si ya existe una entidad con las mismas claves, EF la incluirá en los resultados de la
consulta. Aunque EF seguirá emitiendo la consulta en la base de datos, este comportamiento puede omitir la
mayor parte del costo de materializar la entidad varias veces.
3.1.1 obtener entidades de la memoria caché de objetos mediante la búsqueda de DbContext
A diferencia de una consulta normal, el método Find de DbSet (las API que se incluyen por primera vez en EF
4,1) realizará una búsqueda en la memoria antes de emitir la consulta en la base de datos. Es importante tener
en cuenta que dos instancias de ObjectContext diferentes tendrán dos instancias de ObjectStateManager
diferentes, lo que significa que tienen cachés de objetos independientes.
Find usa el valor de la clave principal para intentar buscar una entidad de la que el contexto realiza un
seguimiento. Si la entidad no está en el contexto, se ejecutará y evaluará una consulta en la base de datos y se
devolverá null si no se encuentra la entidad en el contexto o en la base de datos. Tenga en cuenta que la
búsqueda también devuelve las entidades que se han agregado al contexto pero que todavía no se han
guardado en la base de datos.
Se debe tener en cuenta el rendimiento al usar la búsqueda. De forma predeterminada, las invocaciones a este
método desencadenarán una validación de la memoria caché de objetos para detectar los cambios que aún
están pendientes de confirmación en la base de datos. Este proceso puede resultar muy caro si hay un gran
número de objetos en la memoria caché de objetos o en un gráfico de objetos grande que se va a agregar a la
memoria caché de objetos, pero también se puede deshabilitar. En algunos casos, es posible que perciba un
orden de magnitud de diferencia en la llamada al método Find cuando se deshabilitan los cambios de detección
automática. Todavía se percibe un segundo orden de magnitud cuando el objeto realmente está en la memoria
caché en lugar de cuando el objeto tiene que recuperarse de la base de datos. A continuación se muestra un
gráfico de ejemplo con medidas tomadas con algunos de nuestros microbenchmarks, expresados en
milisegundos, con una carga de 5000 entidades:
Ejemplo de búsqueda con cambios de detección automática deshabilitados:

[Link] = false;
var product = [Link](productId);
[Link] = true;
...

Lo que tiene que tener en cuenta al usar el método Find es:


1. Si el objeto no está en la memoria caché, se niegan las ventajas de la búsqueda, pero la sintaxis es todavía
más sencilla que una consulta por clave.
2. Si la detección automática de cambios está habilitada, el costo del método buscar puede aumentar en un
orden de magnitud, o incluso más en función de la complejidad del modelo y de la cantidad de entidades en
la memoria caché de objetos.
Además, tenga en cuenta que la búsqueda solo devuelve la entidad que está buscando y no carga
automáticamente sus entidades asociadas si aún no están en la memoria caché de objetos. Si necesita recuperar
entidades asociadas, puede usar una consulta por clave con carga diligente. Para obtener más información, vea
carga diferida de 8,1 frente a carga diligente .
3.1.2 problemas de rendimiento cuando la memoria caché de objetos tiene muchas entidades
La memoria caché de objetos ayuda a aumentar la capacidad de respuesta general de Entity Framework. Sin
embargo, cuando la memoria caché de objetos tiene una cantidad muy grande de entidades cargadas, puede
afectar a ciertas operaciones, como agregar, quitar, buscar, entrada, SaveChanges, etc. En concreto, las
operaciones que desencadenan una llamada a DetectChanges se verán afectadas negativamente por las
memorias caché de objetos de gran tamaño. DetectChanges sincroniza el gráfico de objetos con el
administrador de estado de objetos y su rendimiento viene determinado directamente por el tamaño del gráfico
de objetos. Para obtener más información sobre DetectChanges, consulte seguimiento de cambios en entidades
poco.
Al usar Entity Framework 6, los desarrolladores pueden llamar a AddRange y RemoveRange directamente en un
DbSet, en lugar de recorrer en iteración una colección y llamar a agregar una vez por instancia. La ventaja de
usar los métodos de intervalo es que el costo de DetectChanges solo se paga una vez para todo el conjunto de
entidades en lugar de una vez por cada entidad agregada.
ca3,2 almacenamiento en caché del plan de consulta
La primera vez que se ejecuta una consulta, pasa por el compilador del plan interno para traducir la consulta
conceptual en el comando de almacenamiento (por ejemplo, el T-SQL que se ejecuta cuando se ejecuta con SQL
Server).Si está habilitado el almacenamiento en caché del plan de consulta, la próxima vez que se ejecute la
consulta, el comando de almacenamiento se recupera directamente de la memoria caché del plan de consulta
para su ejecución, omitiendo el compilador del plan.
La memoria caché del plan de consulta se comparte entre instancias de ObjectContext dentro del mismo
AppDomain. No es necesario mantener en una instancia de ObjectContext para beneficiarse del
almacenamiento en caché del plan de consulta.
3.2.1 algunas notas sobre el almacenamiento en caché del plan de consulta
La memoria caché del plan de consulta se comparte para todos los tipos de consulta: Entity SQL, LINQ to
Entities y objetos CompiledQuery.
De forma predeterminada, el almacenamiento en caché del plan de consulta está habilitado para las
consultas Entity SQL, tanto si se ejecutan a través de EntityCommand como a través de ObjectQuery.
También está habilitada de forma predeterminada para las consultas LINQ to Entities en Entity Framework en
.NET 4,5 y en Entity Framework 6
El almacenamiento en caché del plan de consulta se puede deshabilitar estableciendo la propiedad
EnablePlanCaching (en EntityCommand o ObjectQuery) en false. Por ejemplo:

var query = from customer in [Link]


where [Link] == id
select new
{
[Link],
[Link]
};
ObjectQuery oQuery = query as ObjectQuery;
[Link] = false;

En el caso de las consultas con parámetros, el cambio del valor del parámetro seguirá alcanzando la consulta
almacenada en caché. Pero cambiar las caras de un parámetro (por ejemplo, tamaño, precisión o escala)
alcanzará una entrada diferente en la memoria caché.
Cuando se usa Entity SQL, la cadena de consulta forma parte de la clave. Si se cambia la consulta en todos, se
producirán diferentes entradas de caché, incluso si las consultas son equivalentes funcionalmente. Esto
incluye los cambios en el uso de mayúsculas o minúsculas.
Al utilizar LINQ, la consulta se procesa para generar una parte de la clave. Al cambiar la expresión LINQ, se
generará una clave diferente.
Pueden aplicarse otras limitaciones técnicas; vea consultas compiladas para obtener más información.
3.2.2 algoritmo de expulsión de caché
Comprender cómo funciona el algoritmo interno le ayudará a averiguar cuándo habilitar o deshabilitar el
almacenamiento en caché del plan de consulta. El algoritmo de limpieza es el siguiente:
1. Una vez que la memoria caché contiene un número establecido de entradas (800), se inicia un temporizador
que, periódicamente (una vez por minuto), barre la memoria caché.
2. Durante los barridos de caché, las entradas se quitan de la memoria caché en una base de LFRU (con menos
frecuencia, usada recientemente). Este algoritmo toma en cuenta tanto el número de llamadas como la
antigüedad al decidir qué entradas se expulsan.
3. Al final de cada barrido de caché, la memoria caché contiene 800 entradas.
Todas las entradas de la memoria caché se tratan igualmente al determinar qué entradas se expulsan. Esto
significa que el comando de almacenamiento de una CompiledQuery tiene la misma oportunidad de expulsión
que el comando de almacenamiento de una consulta de Entity SQL.
Tenga en cuenta que el temporizador de expulsión de caché se inicia cuando hay 800 entidades en la caché, pero
la memoria caché solo se explora 60 segundos después de que se inicie este temporizador. Esto significa que
hasta 60 segundos la memoria caché puede crecer de forma bastante grande.
3.2.3 métricas de pruebas que muestran el rendimiento del almacenamiento en caché del plan de consulta
Para demostrar el efecto del almacenamiento en caché del plan de consulta en el rendimiento de la aplicación,
hemos realizado una prueba en la que se ha ejecutado un número de consultas Entity SQL en el modelo de
Navision. Vea el apéndice para obtener una descripción del modelo de Navision y los tipos de consultas que se
ejecutaron. En esta prueba, primero se recorre en iteración la lista de consultas y se ejecuta cada una de ellas
para agregarlas a la caché (si está habilitado el almacenamiento en caché). Este paso es inesperado. A
continuación, se suspende el subproceso principal durante más de 60 segundos para permitir que se lleve a
cabo el rastreo de la memoria caché. por último, se recorre en iteración la lista una segunda vez para ejecutar las
consultas en caché. Además, la memoria caché del plan de SQL Server se vacía antes de que se ejecute cada
conjunto de consultas, de modo que las horas que se obtengan con precisión reflejen el beneficio dado por la
memoria caché del plan de consulta.
R e su l t a d o s d e p r u e b a s 3 .2 .3 .1

P RUEB A EF 5 SIN C A C H É EF 5 EN C A C H É EF 6 SIN C A C H É EF 6 EN C A C H É

Enumerar todas las 124 125,4 124,3 125,3


consultas de 18723

Evitar el rastreo (solo 41,7 5.5 40,5 5.4


las primeras 800,
independientemente
de la complejidad)

Solo las consultas de 39,5 4.5. 38,1 4.6


AggregatingSubtotal
s (178 en total, lo
que evita el barrido)

Todo el tiempo en segundos.


Moral: cuando se ejecutan muchas consultas distintas (por ejemplo, consultas creadas dinámicamente), el
almacenamiento en caché no ayuda y el vaciado resultante de la memoria caché puede mantener las consultas
que más se beneficiarían del almacenamiento en caché del plan.
Las consultas de AggregatingSubtotals son las consultas más complejas que hemos probado con. Como cabría
esperar, cuanto más compleja sea la consulta, más ventajas verá del almacenamiento en caché del plan de
consulta.
Dado que CompiledQuery es realmente una consulta LINQ con su plan almacenado en memoria caché, la
comparación de un CompiledQuery con respecto a la consulta de Entity SQL equivalente debe tener resultados
similares. De hecho, si una aplicación tiene una gran cantidad de consultas de Entity SQL dinámicas, llenar la
memoria caché con consultas también hará que CompiledQueries se "descompile" cuando se vacíen de la
memoria caché. En este escenario, el rendimiento puede mejorar si se deshabilita el almacenamiento en caché
en las consultas dinámicas para priorizar el CompiledQueries. Aún mejor, por supuesto, sería volver a escribir la
aplicación para usar consultas con parámetros en lugar de consultas dinámicas.
3,3 uso de CompiledQuery para mejorar el rendimiento con consultas LINQ
Nuestras pruebas indican que el uso de CompiledQuery puede aportar una ventaja del 7% sobre las consultas
LINQ compiladas. Esto significa que va a pasar el 7% menos tiempo ejecutando el código de la pila de Entity
Framework; no significa que la aplicación vaya a ser del 7% más rápida. Por lo general, el costo de escribir y
mantener los objetos CompiledQuery en EF 5,0 podría no merecer el problema en comparación con las
ventajas. El kilometraje puede variar, por lo que debe ejecutar esta opción si el proyecto requiere la extracción
adicional. Tenga en cuenta que CompiledQueries solo son compatibles con los modelos derivados de
ObjectContext y no son compatibles con los modelos derivados de DbContext.
Para obtener más información sobre cómo crear e invocar un CompiledQuery, vea consultas compiladas (LINQ
to Entities).
Hay dos consideraciones que debe tomar al usar un CompiledQuery, es decir, el requisito de usar instancias
estáticas y los problemas que tienen con composición. A continuación se muestra una explicación detallada de
estas dos consideraciones.
3.3.1 usar instancias de CompiledQuery estáticas
Dado que la compilación de una consulta LINQ es un proceso que requiere mucho tiempo, no queremos hacerlo
cada vez que necesitamos capturar datos de la base de datos. Las instancias de CompiledQuery permiten
compilar una vez y ejecutarse varias veces, pero tiene que tener cuidado y adquirir para volver a usar la misma
instancia de CompiledQuery cada vez en lugar de compilarla una y otra vez. Se necesita el uso de miembros
estáticos para almacenar las instancias de CompiledQuery. en caso contrario, no verá ninguna ventaja.
Por ejemplo, supongamos que la página tiene el cuerpo del método siguiente para controlar la visualización de
los productos de la categoría seleccionada:

// Warning: this is the wrong way of using CompiledQuery


using (NorthwindEntities context = new NorthwindEntities())
{
string selectedCategory = [Link];

var productsForCategory = [Link]<NorthwindEntities, string, IQueryable<Product>>(


(NorthwindEntities nwnd, string category) =>
[Link](p => [Link] == category)
);

[Link] = [Link](context, selectedCategory).ToList();


[Link]();
}

[Link] = true;

En este caso, creará una nueva instancia de CompiledQuery sobre la marcha cada vez que se llame al método.
En lugar de ver las ventajas de rendimiento mediante la recuperación del comando de almacenamiento desde la
memoria caché del plan de consulta, el CompiledQuery pasará por el compilador del plan cada vez que se cree
una nueva instancia. De hecho, estará contaminando la memoria caché del plan de consulta con una nueva
entrada CompiledQuery cada vez que se llame al método.
En su lugar, se desea crear una instancia estática de la consulta compilada, por lo que se invoca la misma
consulta compilada cada vez que se llama al método. Una manera de hacerlo es agregar la instancia de
CompiledQuery como un miembro del contexto del objeto.A continuación, puede hacer cosas un poco
limpiador accediendo a CompiledQuery a través de un método auxiliar:

public partial class NorthwindEntities : ObjectContext


{
private static readonly Func<NorthwindEntities, string, IEnumerable<Product>> productsForCategoryCQ
= [Link](
(NorthwindEntities context, string categoryName) =>
[Link](p => [Link] == categoryName)
);

public IEnumerable<Product> GetProductsForCategory(string categoryName)


{
return [Link](this, categoryName).ToList();
}

Este método auxiliar se invocaría de la siguiente manera:

[Link] = [Link](selectedCategory);

3.3.2 redacción sobre un CompiledQuery


La capacidad de componer sobre cualquier consulta LINQ es muy útil; para ello, basta con invocar un método
después de IQueryable, como SKIP () o Count (). Sin embargo, si lo hace, se devuelve un nuevo objeto
IQueryable. Aunque no hay nada que se detenga técnicamente de la composición en un CompiledQuery, si lo
hace, se producirá la generación de un nuevo objeto IQueryable que requiera pasar de nuevo el compilador del
plan.
Algunos componentes harán uso de objetos IQueryable compuestos para habilitar la funcionalidad avanzada.
Por ejemplo, ASP. GridView de la red se puede enlazar a datos a un objeto IQueryable a través de la propiedad
SelectMethod. A continuación, GridView se compondrá sobre este objeto IQueryable para permitir la
ordenación y la paginación en el modelo de datos. Como puede ver, el uso de un CompiledQuery para GridView
no alcanzaría la consulta compilada pero generaría una nueva consulta compilada.
Un lugar en el que pueda encontrarse es al agregar filtros progresivos a una consulta. Por ejemplo, supongamos
que tiene una página clientes con varias listas desplegables para filtros opcionales (por ejemplo, Country y
OrdersCount). Puede crear estos filtros a través de los resultados de IQueryable de un CompiledQuery, pero si
lo hace, la nueva consulta pasará a través del compilador del plan cada vez que la ejecute.

using (NorthwindEntities context = new NorthwindEntities())


{
IQueryable<Customer> myCustomers = [Link]();

if ([Link] != defaultFilterText)
{
int orderCount = [Link]([Link]);
myCustomers = [Link](c => [Link] > orderCount);
}

if ([Link] != defaultFilterText)
{
myCustomers = [Link](c => [Link] == [Link]);
}

[Link] = myCustomers;
[Link]();
}

Para evitar esta recompilación, puede volver a escribir CompiledQuery para tener en cuenta los posibles filtros:

private static readonly Func<NorthwindEntities, int, int?, string, IQueryable<Customer>>


customersForEmployeeWithFiltersCQ = [Link](
(NorthwindEntities context, int empId, int? countFilter, string countryFilter) =>
[Link](c => [Link](o => [Link] == empId))
.Where(c => [Link] == false || [Link] > countFilter)
.Where(c => countryFilter == null || [Link] == countryFilter)
);

Que se invocaría en la interfaz de usuario como:

using (NorthwindEntities context = new NorthwindEntities())


{
int? countFilter = ([Link] == 0) ?
(int?)null :
[Link]([Link]);

string countryFilter = ([Link] == 0) ?


null :
[Link];

IQueryable<Customer> myCustomers = [Link](


countFilter, countryFilter);

[Link] = myCustomers;
[Link]();
}
Un inconveniente aquí es que el comando de almacenamiento generado siempre tendrá los filtros con las
comprobaciones de valores NULL, pero deben ser bastante simples para que el servidor de base de datos pueda
optimizar:

...
WHERE ((0 = (CASE WHEN (@p__linq__1 IS NOT NULL) THEN cast(1 as bit) WHEN (@p__linq__1 IS NULL) THEN cast(0
as bit) END)) OR ([Project3].[C2] > @p__linq__2)) AND (@p__linq__3 IS NULL OR [Project3].[Country] =
@p__linq__4)

3,4 almacenamiento en caché de metadatos


La Entity Framework también admite el almacenamiento en caché de metadatos. Esencialmente, se almacena en
caché la información de tipo y la información de asignación de tipo a base de datos en distintas conexiones al
mismo modelo. La caché de metadatos es única por AppDomain.
algoritmo de almacenamiento en caché de metadatos de 3.4.1
1. La información de metadatos de un modelo se almacena en ItemCollection para cada EntityConnection.
Como nota lateral, hay diferentes objetos ItemCollection para diferentes partes del modelo. Por
ejemplo, StoreItemCollections contiene la información acerca del modelo de base de datos;
ObjectItemCollection contiene información sobre el modelo de datos. EdmItemCollection contiene
información sobre el modelo conceptual.
2. Si dos conexiones utilizan la misma cadena de conexión, compartirán la misma instancia de
ItemCollection.
3. Funcionalmente equivalente, pero las cadenas de conexión textualmente diferentes pueden dar lugar a
diferentes cachés de metadatos. Vamos a acortar las cadenas de conexión, por lo que cambiar el orden de
los tokens debe generar metadatos compartidos. Pero dos cadenas de conexión que parecen
funcionalmente iguales no se pueden evaluar como idénticas después de la tokenización.
4. La ItemCollection se comprueba periódicamente para su uso. Si se determina que no se ha tenido acceso
recientemente a un área de trabajo, se marcará para su limpieza en el siguiente barrido de caché.
5. Simplemente la creación de EntityConnection hará que se cree una memoria caché de metadatos
(aunque las colecciones de elementos que contengan no se inicializarán hasta que se abra la conexión).
Esta área de trabajo permanecerá en memoria hasta que el algoritmo de almacenamiento en caché
determine que no está en uso.
El equipo de asesoramiento al cliente ha escrito una entrada de blog que describe la conservación de una
referencia a un ItemCollection para evitar la "degradación" cuando se usan modelos grandes:
<[Link]
wcf-services> .
3.4.2 la relación entre almacenamiento en caché de metadatos y almacenamiento en caché del plan de consulta
La instancia de la memoria caché del plan de consulta reside en el ItemCollection de los tipos de almacén de
MetadataWorkspace. Esto significa que los comandos de almacenamiento almacenados en caché se usarán para
las consultas en cualquier contexto del que se haya creado una instancia con un MetadataWorkspace
determinado. También significa que si tiene dos cadenas de conexión que son ligeramente diferentes y no
coinciden después de la tokenización, tendrá instancias de la memoria caché del plan de consulta diferente.
Caching de resultados de 3,5
Con el almacenamiento en caché de resultados (también conocido como "almacenamiento en caché de segundo
nivel"), se conservan los resultados de las consultas en una caché local. Al emitir una consulta, primero verá si
los resultados están disponibles localmente antes de realizar la consulta en el almacén. Aunque el
almacenamiento en caché de resultados no es compatible directamente con Entity Framework, es posible
agregar una memoria caché de segundo nivel mediante un proveedor de ajuste. Un proveedor de ajuste de
ejemplo con una memoria caché de segundo nivel es la Entity Framework de la memoria caché de segundo
nivel basada en NCache.
Esta implementación del almacenamiento en caché de segundo nivel es una funcionalidad insertada que tiene
lugar después de evaluar la expresión LINQ (y funcletized) y de que el plan de ejecución de consulta se calcula o
recupera de la caché de primer nivel. La memoria caché de segundo nivel solo almacenará los resultados de la
base de datos sin formato, por lo que la canalización de materialización se seguirá ejecutando después.
3.5.1 referencias adicionales para el almacenamiento en caché de resultados con el proveedor de ajuste
Julia Lerman ha escrito un artículo de MSDN "almacenamiento en caché de segundo nivel en Entity
Framework y Windows Azure" que incluye cómo actualizar el proveedor de empaquetado de ejemplo para
usar el almacenamiento en caché de Windows Server AppFabric:
[Link]
Si está trabajando con Entity Framework 5, el blog del equipo tiene una publicación en la que se describe
cómo hacer todo lo que se ejecuta con el proveedor de almacenamiento en caché para Entity Framework 5:
<[Link] . También
incluye una plantilla T4 para ayudar a automatizar la adición del almacenamiento en caché de segundo nivel
al proyecto.

4 consultas compiladas
Cuando se emite una consulta en una base de datos mediante Entity Framework, debe pasar por una serie de
pasos antes de materializar los resultados. uno de estos pasos es la compilación de consultas. Entity SQL se sabe
que las consultas tienen un buen rendimiento, ya que se almacenan en caché automáticamente, por lo que la
segunda o tercera vez que se ejecuta la misma consulta puede omitir el compilador del plan y usar el plan
almacenado en caché en su lugar.
Entity Framework 5 presentó también el almacenamiento en caché automático para las consultas de LINQ to
Entities. En las ediciones anteriores de Entity Framework crear un CompiledQuery para acelerar el rendimiento
era una práctica común, ya que esto haría que su LINQ to Entities consulta se pudiera almacenar en caché. Dado
que el almacenamiento en caché se realiza ahora automáticamente sin el uso de un CompiledQuery, se llama a
esta característica "autocompiled queries". Para obtener más información acerca de la memoria caché del plan
de consulta y su mecánica, vea almacenamiento en caché del plan de consulta.
Entity Framework detecta cuándo es necesario volver a compilar una consulta y lo hace cuando se invoca la
consulta incluso si se había compilado antes. Las condiciones comunes que hacen que la consulta se vuelva a
compilar son:
Cambio de MergeOption asociado a la consulta. No se usará la consulta almacenada en caché, sino que el
compilador del plan se ejecutará de nuevo y el plan recién creado se almacenará en caché.
Cambiar el valor de ContextOptions. UseCSharpNullComparisonBehavior. Tiene el mismo efecto que cambiar
MergeOption.
Otras condiciones pueden impedir que la consulta use la memoria caché. Los ejemplos comunes son:
Usar IEnumerable < T > . Contains < > (valor T).
Usar funciones que generan consultas con constantes.
Usar las propiedades de un objeto no asignado.
Vincular la consulta a otra consulta que requiere que se vuelva a compilar.
4,1 mediante IEnumerable < T > . Contiene < t > (valor t)
Entity Framework no almacena en caché las consultas que invocan a IEnumerable < T > . Contiene < t > (valor t)
en una colección en memoria, ya que los valores de la colección se consideran volátiles. La consulta de ejemplo
siguiente no se almacenará en caché, por lo que el compilador del plan siempre la procesará:
int[] ids = new int[10000];
...
using (var context = new MyContext())
{
var query = [Link]
.Where(entity => [Link]([Link]));

var results = [Link]();


...
}

Tenga en cuenta que el tamaño de IEnumerable en el que se ejecuta se determina la rapidez o la velocidad de
compilación de la consulta. El rendimiento puede verse afectado de forma significativa al usar colecciones
grandes como la que se muestra en el ejemplo anterior.
Entity Framework 6 contiene optimizaciones para la manera IEnumerable < T > . Contiene < t > (valor t)
funciona cuando se ejecutan las consultas. El código SQL que se genera es mucho más rápido para generar y
más legible y, en la mayoría de los casos, también se ejecuta más rápido en el servidor.
4,2 usar funciones que generan consultas con constantes
Los operadores LINQ SKIP (), Take (), Contains () y DefautIfEmpty () no generan consultas SQL con parámetros
sino que, en su lugar, colocan los valores que se les pasan como constantes. Por este motivo, las consultas que,
de otro modo, podrían ser idénticas en la memoria caché del plan de consulta, tanto en la pila de EF como en el
servidor de base de datos, y no se reutilizarán a menos que se usen las mismas constantes en una ejecución de
consulta posterior. Por ejemplo:

var id = 10;
...
using (var context = new MyContext())
{
var query = [Link](entity => [Link]).Contains(id);

var results = [Link]();


...
}

En este ejemplo, cada vez que se ejecuta esta consulta con un valor diferente para el identificador, la consulta se
compilará en un nuevo plan.
En concreto, preste atención al uso de SKIP y Take al realizar la paginación. En EF6, estos métodos tienen una
sobrecarga lambda que hace que el plan de consulta almacenado en caché se vuelva a usar porque EF puede
capturar las variables que se pasan a estos métodos y traducirlas a SQLparameters. Esto también ayuda a
mantener el limpiador de la memoria caché, ya que, de lo contrario, cada consulta con una constante diferente
para Skip y Take obtendría su propia entrada de caché del plan de consulta.
Considere el siguiente código, que es poco óptimo, pero solo está pensado para ejemplificar esta clase de
consultas:

var customers = [Link](c => [Link]);


for (var i = 0; i < count; ++i)
{
var currentCustomer = [Link](i).FirstOrDefault();
ProcessCustomer(currentCustomer);
}

Una versión más rápida de este mismo código implicaría llamar a SKIP con una expresión lambda:
var customers = [Link](c => [Link]);
for (var i = 0; i < count; ++i)
{
var currentCustomer = [Link](() => i).FirstOrDefault();
ProcessCustomer(currentCustomer);
}

El segundo fragmento de código puede ejecutarse hasta un 11% más rápido, ya que se usa el mismo plan de
consulta cada vez que se ejecuta la consulta, lo que ahorra tiempo de CPU y evita contaminar la caché de
consultas. Además, dado que el parámetro que se va a omitir está en un cierre, el código también tendrá el
siguiente aspecto:

var i = 0;
var skippyCustomers = [Link](c => [Link]).Skip(() => i);
for (; i < count; ++i)
{
var currentCustomer = [Link]();
ProcessCustomer(currentCustomer);
}

4,3 uso de las propiedades de un objeto no asignado


Cuando una consulta usa las propiedades de un tipo de objeto no asignado como parámetro, la consulta no se
almacenará en caché. Por ejemplo:

using (var context = new MyContext())


{
var myObject = new NonMappedType();

var query = from entity in [Link]


where [Link]([Link])
select entity;

var results = [Link]();


...
}

En este ejemplo, supongamos que la clase NonMappedType no forma parte del modelo de entidad. Esta
consulta se puede cambiar fácilmente para que no use un tipo no asignado y, en su lugar, use una variable local
como parámetro para la consulta:

using (var context = new MyContext())


{
var myObject = new NonMappedType();
var myValue = [Link];
var query = from entity in [Link]
where [Link](myValue)
select entity;

var results = [Link]();


...
}

En este caso, la consulta se podrá almacenar en caché y se beneficiará de la caché del plan de consulta.
4,4 vincular a consultas que requieren volver a compilar
Siguiendo el mismo ejemplo anterior, si tiene una segunda consulta que se basa en una consulta que se debe
volver a compilar, también se volverá a compilar toda la segunda consulta. Este es un ejemplo para ilustrar este
escenario:

int[] ids = new int[10000];


...
using (var context = new MyContext())
{
var firstQuery = from entity in [Link]
where [Link]([Link])
select entity;

var secondQuery = from entity in [Link]


where [Link](otherEntity => [Link] == [Link])
select entity;

var results = [Link]();


...
}

El ejemplo es genérico, pero muestra cómo la vinculación a firstQuery hace que secondQuery no pueda
almacenarse en caché. Si firstQuery no hubiera sido una consulta que requiera volver a compilar, entonces
secondQuery se habría almacenado en caché.

5 realizar un seguimiento de las consultas


5,1 deshabilitar el seguimiento de cambios para reducir la sobrecarga de administración de estado
Si está en un escenario de solo lectura y desea evitar la sobrecarga que supone cargar los objetos en el
ObjectStateManager, puede emitir consultas "sin seguimiento".El seguimiento de cambios se puede deshabilitar
en el nivel de consulta.
Tenga en cuenta que, si deshabilita el seguimiento de cambios, se desactivará la caché de objetos. Al consultar
una entidad, no se puede omitir la materialización mediante la extracción de los resultados de la consulta
materializados previamente del ObjectStateManager. Si consulta repetidamente las mismas entidades en el
mismo contexto, es posible que realmente vea una ventaja de rendimiento al habilitar el seguimiento de
cambios.
Cuando se realiza una consulta con ObjectContext, las instancias de ObjectQuery y ObjectSet recordarán una
MergeOption una vez que se haya establecido y las consultas que se componen en ellas heredarán la
MergeOption en vigor de la consulta primaria. Al usar DbContext, el seguimiento se puede deshabilitar
llamando al modificador AsNoTracking () en DbSet.
5.1.1 deshabilitar el seguimiento de cambios para una consulta al usar DbContext
Puede cambiar el modo de una consulta a NoTracking mediante el encadenamiento de una llamada al método
AsNoTracking () en la consulta. A diferencia de ObjectQuery, las clases DbSet y DbQuery de la API DbContext no
tienen una propiedad mutable para MergeOption.

var productsForCategory = from p in [Link]()


where [Link] == selectedCategory
select p;

5.1.2 deshabilitar el seguimiento de cambios en el nivel de consulta mediante ObjectContext


var productsForCategory = from p in [Link]
where [Link] == selectedCategory
select p;

((ObjectQuery)productsForCategory).MergeOption = [Link];

5.1.3 deshabilitar el seguimiento de cambios para un conjunto de entidades completo mediante ObjectContext

[Link] = [Link];

var productsForCategory = from p in [Link]


where [Link] == selectedCategory
select p;

5,2 métricas de prueba que muestran las ventajas de rendimiento de las consultas NoTracking
En esta prueba, veremos el costo de rellenar el ObjectStateManager comparando el seguimiento con NoTracking
queries for the Navision Model. Vea el apéndice para obtener una descripción del modelo de Navision y los
tipos de consultas que se ejecutaron. En esta prueba, se recorre en iteración la lista de consultas y se ejecuta
cada una de ellas. Se han ejecutado dos variaciones de la prueba, una vez con NoTracking Queries y una con la
opción de fusión mediante combinación predeterminada de "AppendOnly". Se ejecutó cada variación 3 veces y
se toma el valor medio de las ejecuciones. Entre las pruebas se borra la memoria caché de consultas en el SQL
Server y se reduce la tempdb mediante la ejecución de los siguientes comandos:
1. DBCC DROPCLEANBUFFERS
2. DBCC FREEPROCCACHE
3. DBCC SHRINKDATABASE (tempdb, 0)
Resultados de pruebas, la mediana en 3 ejecuciones:

SIN SEGUIM IEN TO : SO LO A N EXA R:


ESPA C IO DE SIN SEGUIM IEN TO : ESPA C IO DE SO LO A N EXA R:
T RA B A JO T IEM P O T RA B A JO H O RA

Entity Framework 460361728 1163536 MS 596545536 1273042 MS


5

Entity Framework 647127040 190228 MS 832798720 195521 MS


6

Entity Framework 5 tendrá una superficie de memoria menor al final de la ejecución que Entity Framework 6. La
memoria adicional consumida por Entity Framework 6 es el resultado de estructuras de memoria y código
adicionales que permiten nuevas características y un mejor rendimiento.
También hay una diferencia clara en la superficie de memoria cuando se usa ObjectStateManager. Entity
Framework 5 aumentó su superficie en un 30% al realizar un seguimiento de todas las entidades que
materializamos en la base de datos. Entity Framework 6 aumentó su superficie en un 28% al hacerlo.
En términos de tiempo, Entity Framework 6 supera Entity Framework 5 en esta prueba por un margen grande.
Entity Framework 6 completó la prueba en aproximadamente el 16% del tiempo consumido por Entity
Framework 5. Además, Entity Framework 5 tarda un 9% más en completarse cuando se usa
ObjectStateManager. En comparación, Entity Framework 6 utiliza el 3% más de tiempo al usar el
ObjectStateManager.

6 opciones de ejecución de consultas


Entity Framework ofrece varias maneras de consultar. Echaremos un vistazo a las siguientes opciones,
compararemos las ventajas y desventajas de cada una de ellas y examinaremos sus características de
rendimiento:
LINQ to Entities.
Ningún LINQ to Entities de seguimiento.
Entity SQL en una ObjectQuery.
Entity SQL sobre un EntityCommand.
ExecuteStoreQuery.
SqlQuery.
CompiledQuery.
6,1 consultas LINQ to Entities

var q = [Link](p => [Link] == "Beverages");

Ventajas
Adecuado para las operaciones CUD.
Objetos completamente materializados.
Más sencillo de escribir con la sintaxis integrada en el lenguaje de programación.
Buen rendimiento.
Desventajas
Ciertas restricciones técnicas, como:
Los patrones que usan DefaultIfEmpty para consultas de combinación externas dan como resultado
consultas más complejas que las instrucciones de combinación externa simples en Entity SQL.
Todavía no se puede usar como con la coincidencia de patrones general.
6,2 ninguna consulta de LINQ to Entities de seguimiento
Cuando el contexto deriva a ObjectContext:

[Link] = [Link];
var q = [Link](p => [Link] == "Beverages");

Cuando el contexto derive DbContext:

var q = [Link]()
.Where(p => [Link] == "Beverages");

Ventajas
Rendimiento mejorado con respecto a las consultas LINQ normales.
Objetos completamente materializados.
Más sencillo de escribir con la sintaxis integrada en el lenguaje de programación.
Desventajas
No es adecuado para las operaciones de CUD.
Ciertas restricciones técnicas, como:
Los patrones que usan DefaultIfEmpty para consultas de combinación externas dan como resultado
consultas más complejas que las instrucciones de combinación externa simples en Entity SQL.
Todavía no se puede usar como con la coincidencia de patrones general.
Tenga en cuenta que no se realiza el seguimiento de las consultas de las propiedades escalares del proyecto,
aunque no se especifique el NoTracking. Por ejemplo:

var q = [Link](p => [Link] == "Beverages").Select(p => new { [Link]


});

Esta consulta en particular no especifica explícitamente que sea NoTracking, pero como no materializa un tipo
conocido por el administrador de estado de objeto, no se realiza el seguimiento del resultado materializado.
6,3 Entity SQL en una ObjectQuery

ObjectQuery<Product> products = [Link]("[Link] = 'Beverages'");

Ventajas
Adecuado para las operaciones CUD.
Objetos completamente materializados.
Admite el almacenamiento en caché del plan de consulta.
Desventajas
Incluye cadenas de consulta textual que son más propensas a errores del usuario que las construcciones de
consulta integradas en el lenguaje.
6,4 Entity SQL sobre un comando de entidad

EntityCommand cmd = [Link]();


[Link] = "Select p From [Link] As p Where [Link] =
'Beverages'";

using (EntityDataReader reader = [Link]([Link]))


{
while ([Link]())
{
// manually 'materialize' the product
}
}

Ventajas
Admite el almacenamiento en caché del plan de consulta en .NET 4,0 (el almacenamiento en caché del plan
es compatible con todos los demás tipos de consulta en .NET 4,5).
Desventajas
Incluye cadenas de consulta textual que son más propensas a errores del usuario que las construcciones de
consulta integradas en el lenguaje.
No es adecuado para las operaciones de CUD.
Los resultados no se materializan automáticamente y deben leerse desde el lector de datos.
6,5 SqlQuery y ExecuteStoreQuery
SqlQuery en la base de datos:

// use this to obtain entities and not track them


var q1 = [Link]<Product>("select * from products");

SqlQuery en DbSet:
// use this to obtain entities and have them tracked
var q2 = [Link]("select * from products");

ExecyteStoreQuery:

var beverages = [Link]<Product>(


@" SELECT [Link], [Link], [Link], [Link], [Link], [Link],
[Link], [Link], [Link], [Link], [Link]
FROM Products AS P INNER JOIN Categories AS C ON [Link] = [Link]
WHERE ([Link] = 'Beverages')"
);

Ventajas
Rendimiento generalmente más rápido, ya que se omite el compilador del plan.
Objetos completamente materializados.
Adecuado para las operaciones de CUD cuando se usa desde DbSet.
Desventajas
La consulta es de texto y propenso a errores.
La consulta está asociada a un back-end específico mediante la semántica del almacén en lugar de la
semántica conceptual.
Cuando la herencia está presente, la consulta handcrafted debe tener en cuenta las condiciones de
asignación para el tipo solicitado.
6,6 CompiledQuery

private static readonly Func<NorthwindEntities, string, IQueryable<Product>> productsForCategoryCQ =


[Link](
(NorthwindEntities context, string categoryName) =>
[Link](p => [Link] == categoryName)
);

var q = [Link]("Beverages");

Ventajas
Proporciona una mejora del rendimiento del 7% sobre las consultas LINQ normales.
Objetos completamente materializados.
Adecuado para las operaciones CUD.
Desventajas
Mayor complejidad y sobrecarga de programación.
La mejora del rendimiento se pierde cuando se compone de una consulta compilada.
Algunas consultas LINQ no se pueden escribir como CompiledQuery, por ejemplo, proyecciones de tipos
anónimos.
6,7 comparación de rendimiento de diferentes opciones de consulta
Los microbenchmarks simples en los que no se ha agotado el tiempo de creación del contexto se han puesto en
la prueba. Se ha medido la consulta de 5000 veces para un conjunto de entidades sin almacenamiento en caché
en un entorno controlado. Estos números se van a tomar con una advertencia: no reflejan los números reales
generados por una aplicación, sino que son una medición muy precisa de la cantidad de una diferencia de
rendimiento que se produce cuando se comparan diferentes opciones de consulta en las distintas tareas,
excepto el costo de crear un nuevo contexto.
EF P RUEB A T IEM P O ( M S) M EM O RIA

EF5 ObjectContext (ESQL) 2414 38801408

EF5 Consulta de ObjectContext 2692 38277120


para LINQ

EF5 No seguimiento de consulta 2818 41840640


LINQ de DbContext

EF5 Consulta LINQ de 2930 41771008


DbContext

EF5 No seguimiento de 3013 38412288


consultas LINQ de
ObjectContext

EF6 ObjectContext (ESQL) 2059 46039040

EF6 Consulta de ObjectContext 3074 45248512


para LINQ

EF6 No seguimiento de consulta 3125 47575040


LINQ de DbContext

EF6 Consulta LINQ de 3420 47652864


DbContext

EF6 No seguimiento de 3593 45260800


consultas LINQ de
ObjectContext
Los microbenchmarks son muy sensibles a los pequeños cambios en el código. En este caso, la diferencia entre
los costos de Entity Framework 5 y Entity Framework 6 se debe a la adición de mejoras de interceptación y
transaccional. Sin embargo, estos números de microbenchmarks son una visión amplificada en un fragmento
muy pequeño de lo que hace Entity Framework. Los escenarios reales de consultas cálidas no deben ver una
regresión del rendimiento al actualizar desde Entity Framework 5 a Entity Framework 6.
Para comparar el rendimiento real de las distintas opciones de consulta, hemos creado 5 variaciones de pruebas
independientes en las que usamos una opción de consulta diferente para seleccionar todos los productos cuyo
nombre de categoría sea "bebidas". Cada iteración incluye el costo de crear el contexto y el costo de materializar
todas las entidades devueltas. 10 iteraciones se ejecutan de vez en cuando, antes de tomar la suma de 1000
iteraciones con tiempo. Los resultados que se muestran son el promedio de ejecución de 5 ejecuciones de cada
prueba. Para obtener más información, vea el Apéndice B, que incluye el código de la prueba.

EF P RUEB A T IEM P O ( M S) M EM O RIA

EF5 ObjectContext (comando de 621 39350272


entidad)

EF5 Consulta SQL DbContext en 825 37519360


la base de datos

EF5 Consulta del almacén de 878 39460864


ObjectContext

EF5 No seguimiento de 969 38293504


consultas LINQ de
ObjectContext

EF5 ObjectContext Entity SQL 1089 38981632


mediante consulta de
objeto

EF5 Consulta compilada de 1099 38682624


ObjectContext

EF5 Consulta de ObjectContext 1152 38178816


para LINQ

EF5 No seguimiento de consulta 1208 41803776


LINQ de DbContext
EF P RUEB A T IEM P O ( M S) M EM O RIA

EF5 Consulta SQL DbContext en 1414 37982208


DbSet

EF5 Consulta LINQ de 1574 41738240


DbContext

EF6 ObjectContext (comando de 480 47247360


entidad)

EF6 Consulta del almacén de 493 46739456


ObjectContext

EF6 Consulta SQL DbContext en 614 41607168


la base de datos

EF6 No seguimiento de 684 46333952


consultas LINQ de
ObjectContext

EF6 ObjectContext Entity SQL 767 48865280


mediante consulta de
objeto

EF6 Consulta compilada de 788 48467968


ObjectContext

EF6 No seguimiento de consulta 878 47554560


LINQ de DbContext

EF6 Consulta de ObjectContext 953 47632384


para LINQ

EF6 Consulta SQL DbContext en 1023 41992192


DbSet

EF6 Consulta LINQ de 1290 47529984


DbContext
NOTE
Por integridad, se incluye una variación en la que se ejecuta una consulta de Entity SQL en un EntityCommand. Sin
embargo, dado que los resultados no se materializan para estas consultas, la comparación no es necesariamente de
manzanas a manzanas. La prueba incluye una aproximación aproximada a la materialización para intentar hacer la
comparación más justa.

En este caso de un extremo a otro, Entity Framework 6 supera Entity Framework 5 debido a las mejoras de
rendimiento realizadas en varias partes de la pila, lo que incluye una inicialización de DbContext mucho más
clara y búsquedas de MetadataCollection T más rápidas < > .

7 consideraciones de rendimiento en tiempo de diseño


7,1 estrategias de herencia
Otra consideración de rendimiento al usar Entity Framework es la estrategia de herencia que se usa. Entity
Framework admite 3 tipos básicos de herencia y sus combinaciones:
Tabla por jerarquía (TPH): donde cada conjunto de herencia se asigna a una tabla con una columna
discriminadora para indicar qué tipo determinado de la jerarquía se representa en la fila.
Tabla por tipo (TPT): cada tipo tiene su propia tabla en la base de datos; las tablas secundarias solo definen las
columnas que no contiene la tabla primaria.
Tabla por clase (TPC): cada tipo tiene su propia tabla completa en la base de datos; las tablas secundarias
definen todos sus campos, incluidos los definidos en tipos primarios.
Si el modelo utiliza la herencia de TPT, las consultas que se generan serán más complejas que las que se
generan con las otras estrategias de herencia, lo que puede dar lugar a tiempos de ejecución más largos en el
almacé[Link] lo general, se tarda más tiempo en generar consultas en un modelo TPT y materializar los objetos
resultantes.
Vea la herencia "consideraciones de rendimiento al usar TPT (tabla por tipo)" en la Entity Framework "entrada
del blog de MSDN: <[Link]
using-tpt-table-per-type-inheritance-in-the-entity-framework> .
7.1.1 evitar la aplicación de la Model First o Code First aplicaciones
Al crear un modelo sobre una base de datos existente que tiene un esquema TPT, no tiene muchas opciones.
Pero al crear una aplicación mediante Model First o Code First, debe evitar la herencia de TPT por cuestiones de
rendimiento.
Al usar Model First en el Asistente de Entity Designer, obtendrá el TPT para cualquier herencia del modelo. Si
desea cambiar a una estrategia de herencia de TPH con Model First, puede usar "Entity Designer Database
Generation Power Pack" disponible en la galería de Visual Studio (
<[Link] ).
Cuando se usa Code First para configurar la asignación de un modelo con herencia, EF usará TPH de forma
predeterminada, por lo que todas las entidades de la jerarquía de herencia se asignarán a la misma tabla.
Consulte la sección "asignación con la API fluida" del artículo "Code First en Entity Framework 4.1" en MSDN
Magazine ( [Link] ) para obtener más detalles.
7,2 actualización de EF4 para mejorar el tiempo de generación de modelos
En Entity Framework 5 y 6 se encuentra disponible una mejora específica del SQL Server para el algoritmo que
genera el nivel de almacenamiento (SSDL), y como una actualización de Entity Framework 4 cuando está
instalado Visual Studio 2010 SP1. Los resultados de pruebas siguientes muestran la mejora al generar un
modelo muy grande, en este caso el modelo de Navision. Consulte el Apéndice C para obtener más detalles
sobre él.
El modelo contiene 1005 conjuntos de entidades y conjuntos de asociaciones de 4227.

C O N F IGURA C IÓ N DESGLO SE DEL T IEM P O C O N SUM IDO

Visual Studio 2010, Entity Framework 4 Generación de SSDL: 2 h 27 min


Generación de asignaciones: 1 segundo
Generación de CSDL: 1 segundo
Generación de ObjectLayer: 1 segundo
Generación de vista: 2 h 14 min

Visual Studio 2010 SP1, Entity Framework 4 Generación de SSDL: 1 segundo


Generación de asignaciones: 1 segundo
Generación de CSDL: 1 segundo
Generación de ObjectLayer: 1 segundo
Generación de vista: 1 h 53 min

Visual Studio 2013, Entity Framework 5 Generación de SSDL: 1 segundo


Generación de asignaciones: 1 segundo
Generación de CSDL: 1 segundo
Generación de ObjectLayer: 1 segundo
Generación de vistas: 65 minutos

Visual Studio 2013, Entity Framework 6 Generación de SSDL: 1 segundo


Generación de asignaciones: 1 segundo
Generación de CSDL: 1 segundo
Generación de ObjectLayer: 1 segundo
Generación de vista: 28 segundos.

Merece la pena tener en cuenta que cuando se genera el SSDL, la carga se emplea casi por completo en el SQL
Server, mientras que el equipo de desarrollo de cliente está esperando la inactividad de los resultados para
volver del servidor. Los DBA deben apreciar especialmente esta mejora. También merece la pena tener en cuenta
que básicamente el costo completo de la generación del modelo tiene lugar en la generación de la vista ahora.
7,3 dividir modelos grandes con Database First y Model First
A medida que aumenta el tamaño del modelo, la superficie del diseñador se vuelve abarrotada y difícil de usar.
Normalmente consideramos que un modelo con más de 300 entidades es demasiado grande para usar
eficazmente el diseñador. En la siguiente entrada de blog se describen varias opciones para dividir modelos
grandes: <[Link]
part-2> .
La publicación se escribió para la primera versión de Entity Framework, pero los pasos se siguen aplicando.
7,4 consideraciones de rendimiento con el control de origen de datos de entidad
Hemos visto casos en pruebas de rendimiento y esfuerzo multiproceso en las que el rendimiento de una
aplicación web que usa el control EntityDataSource se deteriora significativamente. La causa subyacente es que
EntityDataSource llama repetidamente a MetadataWorkspace. LoadFromAssembly en los ensamblados a los
que hace referencia la aplicación web para detectar los tipos que se van a usar como entidades.
La solución consiste en establecer el valor de ContextTypeName de EntityDataSource en el nombre de tipo de la
clase derivada de ObjectContext. Esto desactiva el mecanismo que examina todos los ensamblados a los que se
hace referencia para los tipos de entidad.
Al establecer el campo ContextTypeName también se evita un problema funcional en el que EntityDataSource en
.NET 4,0 produce una excepción ReflectionTypeLoadException cuando no puede cargar un tipo de un
ensamblado a través de la reflexión. Este problema se ha corregido en .NET 4,5.
7,5 entidades POCO y servidores proxy de seguimiento de cambios
Entity Framework le permite utilizar clases de datos personalizadas junto con su modelo de datos sin realizar
ninguna modificación en las clases de datos. Esto significa que podrá utilizar objetos CLR "antiguos" (POCO),
tales como objetos de dominio existentes, con el modelo de datos. Estas clases de datos POCO (también
conocidas como objetos que ignoran la persistencia), que se asignan a las entidades que se definen en un
modelo de datos, admiten la mayoría de los mismos comportamientos de consulta, inserción, actualización y
eliminación que los tipos de entidad generados por las herramientas de Entity Data Model.
Entity Framework también puede crear clases de proxy derivadas de los tipos POCO, que se usan cuando se
desea habilitar características como la carga diferida y el seguimiento de cambios automático en entidades
POCO. Las clases POCO deben cumplir ciertos requisitos para permitir que Entity Framework use servidores
proxy, como se describe aquí: [Link] .
Los proxies de seguimiento de oportunidades enviarán una notificación al administrador de estado de objetos
cada vez que se cambie el valor de cualquiera de las propiedades de las entidades, por lo que Entity Framework
conoce el estado real de las entidades todo el tiempo. Esto se hace agregando eventos de notificación al cuerpo
de los métodos de establecedor de las propiedades y haciendo que el administrador de estado de objetos
procese dichos eventos. Tenga en cuenta que la creación de una entidad de proxy normalmente será más
costosa que crear una entidad POCO que no sea de proxy debido al conjunto agregado de eventos creados por
Entity Framework.
Cuando una entidad POCO no tiene un proxy de seguimiento de cambios, se encuentran los cambios
comparando el contenido de las entidades con una copia de un estado guardado anterior. Esta comparación
profunda se convertirá en un proceso largo cuando tenga muchas entidades en el contexto, o cuando las
entidades tengan una gran cantidad de propiedades, aunque no hayan cambiado desde que se realizó la última
comparación.
En Resumen: pagará un impacto en el rendimiento al crear el proxy de seguimiento de cambios, pero el
seguimiento de cambios le ayudará a acelerar el proceso de detección de cambios cuando las entidades tengan
muchas propiedades o cuando tenga muchas entidades en el modelo. En el caso de las entidades con un
número pequeño de propiedades en las que la cantidad de entidades no crece demasiado, es posible que los
proxies de seguimiento de cambios no tengan muchas ventajas.

8 carga de entidades relacionadas


8,1 carga diferida frente a carga diligente
Entity Framework ofrece varias maneras de cargar las entidades relacionadas con la entidad de destino. Por
ejemplo, al consultar productos, hay diferentes maneras de cargar los pedidos relacionados en el administrador
de estado de objetos. Desde el punto de vista del rendimiento, la pregunta más importante que hay que tener
en cuenta al cargar entidades relacionadas será si se va a usar la carga diferida o la carga diligente.
Cuando se usa la carga diligente, las entidades relacionadas se cargan junto con el conjunto de entidades de
destino. Use una instrucción include en la consulta para indicar qué entidades relacionadas desea incorporar.
Cuando se usa la carga diferida, la consulta inicial solo se coloca en el conjunto de entidades de destino. Pero
siempre que se tiene acceso a una propiedad de navegación, se emite otra consulta en el almacén para cargar la
entidad relacionada.
Una vez que se ha cargado una entidad, cualquier consulta adicional para la entidad la cargará directamente
desde el administrador de estado de objetos, tanto si usa la carga diferida como la carga diligente.
8,2 Cómo elegir entre la carga diferida y la carga diligente
Lo importante es que comprenda la diferencia entre la carga diferida y la carga diligente para que pueda tomar
la decisión correcta para su aplicación. Esto le ayudará a evaluar el equilibrio entre varias solicitudes en la base
de datos frente a una solicitud única que puede contener una carga grande. Puede ser adecuado usar la carga
diligente en algunas partes de la aplicación y la carga diferida en otras partes.
Como ejemplo de lo que sucede en el capó, supongamos que desea consultar los clientes que viven en el Reino
Unido y su recuento de pedidos.
Uso de la carga diligente

using (NorthwindEntities context = new NorthwindEntities())


{
var ukCustomers = [Link](c => [Link]).Where(c => [Link] == "UK");
var chosenCustomer = AskUserToPickCustomer(ukCustomers);
[Link]("Customer Id: {0} has {1} orders", [Link], [Link]);
}

Usar la carga diferida

using (NorthwindEntities context = new NorthwindEntities())


{
[Link] = true;

//Notice that the Include method call is missing in the query


var ukCustomers = [Link](c => [Link] == "UK");

var chosenCustomer = AskUserToPickCustomer(ukCustomers);


[Link]("Customer Id: {0} has {1} orders", [Link], [Link]);
}

Cuando se usa la carga diligente, se emite una consulta única que devuelve todos los clientes y todos los
pedidos. El comando de almacenamiento tiene el siguiente aspecto:
SELECT
[Project1].[C1] AS [C1],
[Project1].[CustomerID] AS [CustomerID],
[Project1].[CompanyName] AS [CompanyName],
[Project1].[ContactName] AS [ContactName],
[Project1].[ContactTitle] AS [ContactTitle],
[Project1].[Address] AS [Address],
[Project1].[City] AS [City],
[Project1].[Region] AS [Region],
[Project1].[PostalCode] AS [PostalCode],
[Project1].[Country] AS [Country],
[Project1].[Phone] AS [Phone],
[Project1].[Fax] AS [Fax],
[Project1].[C2] AS [C2],
[Project1].[OrderID] AS [OrderID],
[Project1].[CustomerID1] AS [CustomerID1],
[Project1].[EmployeeID] AS [EmployeeID],
[Project1].[OrderDate] AS [OrderDate],
[Project1].[RequiredDate] AS [RequiredDate],
[Project1].[ShippedDate] AS [ShippedDate],
[Project1].[ShipVia] AS [ShipVia],
[Project1].[Freight] AS [Freight],
[Project1].[ShipName] AS [ShipName],
[Project1].[ShipAddress] AS [ShipAddress],
[Project1].[ShipCity] AS [ShipCity],
[Project1].[ShipRegion] AS [ShipRegion],
[Project1].[ShipPostalCode] AS [ShipPostalCode],
[Project1].[ShipCountry] AS [ShipCountry]
FROM ( SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ContactTitle] AS [ContactTitle],
[Extent1].[Address] AS [Address],
[Extent1].[City] AS [City],
[Extent1].[Region] AS [Region],
[Extent1].[PostalCode] AS [PostalCode],
[Extent1].[Country] AS [Country],
[Extent1].[Phone] AS [Phone],
[Extent1].[Fax] AS [Fax],
1 AS [C1],
[Extent2].[OrderID] AS [OrderID],
[Extent2].[CustomerID] AS [CustomerID1],
[Extent2].[EmployeeID] AS [EmployeeID],
[Extent2].[OrderDate] AS [OrderDate],
[Extent2].[RequiredDate] AS [RequiredDate],
[Extent2].[ShippedDate] AS [ShippedDate],
[Extent2].[ShipVia] AS [ShipVia],
[Extent2].[Freight] AS [Freight],
[Extent2].[ShipName] AS [ShipName],
[Extent2].[ShipAddress] AS [ShipAddress],
[Extent2].[ShipCity] AS [ShipCity],
[Extent2].[ShipRegion] AS [ShipRegion],
[Extent2].[ShipPostalCode] AS [ShipPostalCode],
[Extent2].[ShipCountry] AS [ShipCountry],
CASE WHEN ([Extent2].[OrderID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM [dbo].[Customers] AS [Extent1]
LEFT OUTER JOIN [dbo].[Orders] AS [Extent2] ON [Extent1].[CustomerID] = [Extent2].[CustomerID]
WHERE N'UK' = [Extent1].[Country]
) AS [Project1]
ORDER BY [Project1].[CustomerID] ASC, [Project1].[C2] ASC

Al usar la carga diferida, emitirá la siguiente consulta inicialmente:


SELECT
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[ContactName] AS [ContactName],
[Extent1].[ContactTitle] AS [ContactTitle],
[Extent1].[Address] AS [Address],
[Extent1].[City] AS [City],
[Extent1].[Region] AS [Region],
[Extent1].[PostalCode] AS [PostalCode],
[Extent1].[Country] AS [Country],
[Extent1].[Phone] AS [Phone],
[Extent1].[Fax] AS [Fax]
FROM [dbo].[Customers] AS [Extent1]
WHERE N'UK' = [Extent1].[Country]

Y cada vez que se accede a la propiedad de navegación Orders de un cliente, se emite otra consulta similar a la
siguiente en la tienda:

exec sp_executesql N'SELECT


[Extent1].[OrderID] AS [OrderID],
[Extent1].[CustomerID] AS [CustomerID],
[Extent1].[EmployeeID] AS [EmployeeID],
[Extent1].[OrderDate] AS [OrderDate],
[Extent1].[RequiredDate] AS [RequiredDate],
[Extent1].[ShippedDate] AS [ShippedDate],
[Extent1].[ShipVia] AS [ShipVia],
[Extent1].[Freight] AS [Freight],
[Extent1].[ShipName] AS [ShipName],
[Extent1].[ShipAddress] AS [ShipAddress],
[Extent1].[ShipCity] AS [ShipCity],
[Extent1].[ShipRegion] AS [ShipRegion],
[Extent1].[ShipPostalCode] AS [ShipPostalCode],
[Extent1].[ShipCountry] AS [ShipCountry]
FROM [dbo].[Orders] AS [Extent1]
WHERE [Extent1].[CustomerID] = @EntityKeyValue1',N'@EntityKeyValue1 nchar(5)',@EntityKeyValue1=N'AROUT'

Para obtener más información, vea cargar objetos relacionados.


hoja de referencia diferida de carga 8.2.1 frente a carga diligente
No hay nada que se ajuste a todo para elegir la carga diligente frente a la carga diferida. Pruebe primero para
comprender las diferencias entre ambas estrategias, de modo que pueda tomar una decisión bien
fundamentada; Además, considere la posibilidad de que el código se ajuste a cualquiera de los siguientes
escenarios:

ESC EN A RIO N UEST RA SUGEREN C IA

¿Necesita acceder a muchas propiedades de navegación No : las dos opciones probablemente sí lo hagan. Sin
desde las entidades capturadas? embargo, si la carga de la consulta no es demasiado grande,
puede experimentar ventajas de rendimiento mediante la
carga diligente, ya que requerirá menos viajes de ida y vuelta
de red para materializar los objetos.

Sí : Si necesita tener acceso a muchas propiedades de


navegación desde las entidades, lo haría mediante el uso de
varias instrucciones include en la consulta con la carga
diligente. Cuanto más entidades incluya, mayor será la carga
que devolverá la consulta. Una vez que incluya tres o más
entidades en la consulta, considere la posibilidad de cambiar
a la carga diferida.
ESC EN A RIO N UEST RA SUGEREN C IA

¿Sabe exactamente qué datos se necesitarán en tiempo de La carga no diferida será mejor para usted. De lo contrario,
ejecución? puede acabar consultando los datos que no necesitará.

Sí : la carga diligente es probablemente su mejor apuesta;


ayuda a cargar conjuntos completos más rápido. Si la
consulta requiere la captura de una cantidad muy grande de
datos y se vuelve demasiado lenta, intente la carga diferida
en su lugar.

¿El código se ejecuta lejos de la base de datos? (mayor No : cuando la latencia de red no es un problema, el uso de
latencia de red) la carga diferida puede simplificar el código. Recuerde que la
topología de la aplicación puede cambiar, de modo que no
asuma la proximidad de la base de datos para concedido.

Sí : cuando la red es un problema, solo puede decidir cuál se


adapta mejor a su escenario. Normalmente, la carga diligente
será mejor porque requiere menos viajes de ida y vuelta.

8.2.2 problemas de rendimiento con varios includes


Cuando se escuchan preguntas de rendimiento que implican problemas en el tiempo de respuesta del servidor,
el origen del problema es con frecuencia consultas con varias instrucciones include. Aunque incluir las entidades
relacionadas en una consulta es eficaz, es importante comprender lo que sucede en segundo plano.
Tarda bastante tiempo en una consulta con varias instrucciones include en ella para pasar a través de nuestro
compilador del plan interno y generar el comando de la tienda. La mayor parte de este tiempo se dedica a
intentar optimizar la consulta resultante. El comando de almacenamiento generado contendrá una combinación
externa o una Unión para cada inclusión, en función de la asignación. En las consultas como esta se incluirán
gráficos de gran tamaño de la base de datos en una sola carga, lo que acerbate cualquier problema de ancho de
banda, especialmente cuando hay mucha redundancia en la carga (por ejemplo, cuando se usan varios niveles
de include para atravesar las asociaciones en la dirección uno a varios).
Puede comprobar los casos en los que las consultas devuelven cargas excesivamente grandes mediante el
acceso al TSQL subyacente para la consulta mediante ToTraceString y la ejecución del comando Store en SQL
Server Management Studio para ver el tamaño de la carga. En tales casos, puede intentar reducir el número de
instrucciones include en la consulta para incorporar los datos que necesita. O bien, puede dividir la consulta en
una secuencia más pequeña de subconsultas, por ejemplo:
Antes de interrumpir la consulta:

using (NorthwindEntities context = new NorthwindEntities())


{
var customers = from c in [Link](c => [Link])
where [Link](lastNameParameter)
select c;

foreach (Customer customer in customers)


{
...
}
}

Después de interrumpir la consulta:


using (NorthwindEntities context = new NorthwindEntities())
{
var orders = from o in [Link]
where [Link](lastNameParameter)
select o;

[Link]();

var customers = from c in [Link]


where [Link](lastNameParameter)
select c;

foreach (Customer customer in customers)


{
...
}
}

Esto solo funcionará en las consultas con seguimiento, ya que estamos haciendo uso de la capacidad que el
contexto tiene para realizar la resolución de identidades y la corrección de asociación automáticamente.
Al igual que con la carga diferida, el compromiso será más consultas para cargas más pequeñas. También puede
usar las proyecciones de propiedades individuales para seleccionar explícitamente los datos que necesita de
cada entidad, pero no se cargarán las entidades en este caso y las actualizaciones no se admitirán.
8.2.3 solución alternativa para obtener la carga diferida de propiedades
Actualmente, Entity Framework no admite la carga diferida de propiedades escalares o complejas. Sin embargo,
en los casos en los que tiene una tabla que incluye un objeto grande, como un BLOB, puede usar la división de
tablas para separar las propiedades grandes en una entidad independiente. Por ejemplo, supongamos que tiene
una tabla de productos que incluye una columna de foto varbinary. Si no necesita tener acceso a esta propiedad
con frecuencia en las consultas, puede usar la división de tablas para traer solo las partes de la entidad que
necesite normalmente. La entidad que representa la foto del producto solo se cargará cuando lo necesite
explícitamente.
Un buen recurso que muestra cómo habilitar la división de tablas es la entrada de blog "división de tablas en
Entity Framework" de Gil Fink: <[Link]
[Link]> .

9 otras consideraciones
Recolección de elementos no utilizados de 9,1 Server
Algunos usuarios podrían experimentar contención de recursos que limite el paralelismo que esperan cuando el
recolector de elementos no utilizados no está configurado correctamente. Siempre que se use EF en un
escenario multiproceso o en cualquier aplicación similar a un sistema de servidor, asegúrese de habilitar la
recolección de elementos no utilizados de servidor. Esto se hace a través de una configuración simple en el
archivo de configuración de la aplicación:

<?xmlversion="1.0" encoding="utf-8" ?>


<configuration>
<runtime>
<gcServer enabled="true" />
</runtime>
</configuration>

Esto debería reducir la contención del subproceso y aumentar el rendimiento hasta un 30% en escenarios
saturados de CPU. En términos generales, siempre debe probar el comportamiento de la aplicación mediante la
recolección de elementos no utilizados clásica (que está mejor optimizada para los escenarios de la interfaz de
usuario y del cliente), así como la recolección de elementos no utilizados del servidor.
9,2 AutoDetectChanges
Como se mencionó anteriormente, Entity Framework podrían mostrar problemas de rendimiento cuando la
memoria caché de objetos tiene muchas entidades. Ciertas operaciones, como agregar, quitar, buscar, entrada y
SaveChanges, desencadenan llamadas a DetectChanges que pueden consumir una gran cantidad de CPU en
función del tamaño de la memoria caché de objetos. La razón de esto es que la memoria caché de objetos y el
administrador de estado de objetos intentan permanecer sincronizados como sea posible en cada operación
realizada en un contexto para que se garantice que los datos generados son correctos en una amplia gama de
escenarios.
Por lo general, se recomienda dejar habilitada la detección automática de cambios de Entity Framework para
toda la vida de la aplicación. Si el escenario se ve afectado negativamente por el uso intensivo de la CPU y los
perfiles indican que la causa es la llamada a DetectChanges, considere la posibilidad de desactivar
temporalmente AutoDetectChanges en la parte confidencial del código:

try
{
[Link] = false;
var product = [Link](productId);
...
}
finally
{
[Link] = true;
}

Antes de desactivar AutoDetectChanges, es conveniente comprender que esto podría hacer que Entity
Framework pierda su capacidad de realizar un seguimiento de determinada información sobre los cambios que
se están llevando a cabo en las entidades. Si se trata de forma incorrecta, puede provocar incoherencias de
datos en la aplicación. Para obtener más información sobre la desactivación de AutoDetectChanges, lea
<[Link]
detectchanges/> .
9,3 contexto por solicitud
Los contextos de Entity Framework están diseñados para usarse como instancias de corta duración con el fin de
proporcionar la experiencia de rendimiento óptima. Se espera que los contextos tengan una duración corta y se
descarten, y como tal se han implementado para ser muy ligeros y reutilizar los metadatos siempre que sea
posible. En escenarios web es importante tener esto en cuenta y no tener un contexto mayor que la duración de
una única solicitud. Del mismo modo, en escenarios que no son Web, el contexto debe descartarse en función
de los distintos niveles de almacenamiento en caché en el Entity Framework. En general, debe evitar tener una
instancia de contexto a lo largo de la vida de la aplicación, así como contextos por subproceso y contextos
estáticos.
9,4 semántica nula de la base de datos
De forma predeterminada, Entity Framework generará código SQL que tiene una semántica de comparación de
C # null. Considere la consulta de ejemplo siguiente:
int? categoryId = 7;
int? supplierId = 8;
decimal? unitPrice = 0;
short? unitsInStock = 100;
short? unitsOnOrder = 20;
short? reorderLevel = null;

var q = from p [Link]


[Link] == "Beverages"
|| ([Link] == categoryId
|| [Link] == supplierId
|| [Link] == unitPrice
|| [Link] == unitsInStock
|| [Link] == unitsOnOrder
|| [Link] == reorderLevel)
select p;

var r = [Link]();

En este ejemplo, vamos a comparar un número de variables que aceptan valores NULL con las propiedades que
aceptan valores NULL en la entidad, como SupplierID y UnitPrice. El SQL generado para esta consulta le
preguntará si el valor del parámetro es el mismo que el valor de la columna, o si los valores del parámetro y de
la columna son NULL. Esto ocultará el modo en que el servidor de base de datos controla los valores NULL y
proporcionará una experiencia coherente con C # null en diferentes proveedores de bases de datos. Por otro
lado, el código generado es un poco complicado y es posible que no funcione correctamente cuando la cantidad
de comparaciones en la instrucción WHERE de la consulta aumenta a un número grande.
Una manera de tratar esta situación es usar la semántica de valores NULL de base de datos. Tenga en cuenta que
esto podría comportarse de forma diferente a la # semántica de valores NULL de C, ya que ahora Entity
Framework generará un SQL más sencillo que exponga la manera en que el motor de base de datos controla los
valores NULL. La semántica de valores NULL de base de datos se puede activar por contexto con una sola línea
de configuración en la configuración de contexto:

[Link] = true;

Las consultas de tamaño pequeño a medio no mostrarán una mejora perceptible del rendimiento al usar la
semántica de las bases de datos nulas, pero la diferencia se volverá más perceptible en las consultas con un
gran número de comparaciones de NULL potenciales.
En la consulta de ejemplo anterior, la diferencia de rendimiento era inferior al 2% en un microbenchmark que se
ejecuta en un entorno controlado.
9,5 Async
Entity Framework 6 presentó la compatibilidad con operaciones asincrónicas cuando se ejecutan en .NET 4,5 o
versiones posteriores. En su mayor parte, las aplicaciones que tienen contención relacionada con la e/s
beneficiarán al máximo el uso de las operaciones asincrónicas de consulta y guardado. Si la aplicación no se ve
afectada por la contención de e/s, el uso de Async hará que, en los mejores casos, se ejecute sincrónicamente y
devuelva el resultado en la misma cantidad de tiempo que una llamada sincrónica, o en el peor de los casos,
simplemente postergue la ejecución a una tarea asincrónica y agregue tiempo adicional a la finalización del
escenario.
Para obtener información sobre cómo funciona la programación asincrónica que le ayudará a decidir si Async
mejorará el rendimiento de la aplicación [Link] . Para obtener más
información sobre el uso de operaciones asincrónicas en Entity Framework, consulte Async Query and Save.
NGEN 9,6
Entity Framework 6 no se incluye en la instalación predeterminada de .NET Framework. Como tal, los
ensamblados de Entity Framework no son de forma predeterminada NGEN, lo que significa que todo el código
de Entity Framework está sujeto a los mismos costos de JIT'ing que cualquier otro ensamblado de MSIL. Esto
podría degradar la experiencia F5 durante el desarrollo y también el inicio en frío de la aplicación en los
entornos de producción. Con el fin de reducir los costos de CPU y memoria de JIT'ing, es aconsejable que NGEN
las imágenes de Entity Framework según corresponda. Para obtener más información sobre cómo mejorar el
rendimiento de inicio de Entity Framework 6 con NGEN, vea mejorar el rendimiento de inicio con Ngen.
9,7 Code First frente a EDMX
Entity Framework razones sobre el problema de falta de coincidencia de impedancia entre la programación
orientada a objetos y las bases de datos relacionales con una representación en memoria del modelo conceptual
(los objetos), el esquema de almacenamiento (la base de datos) y una asignación entre los dos. Estos metadatos
se denominan Entity Data Model o EDM para abreviar. Desde este EDM, Entity Framework derivarán las vistas
para los datos de ida y vuelta de los objetos de la memoria a la base de datos y viceversa.
Cuando se usa Entity Framework con un archivo EDMX que especifica formalmente el modelo conceptual, el
esquema de almacenamiento y la asignación, la fase de carga del modelo solo tiene que validar que el EDM es
correcto (por ejemplo, asegúrese de que no faltan asignaciones), generar las vistas y, a continuación, validar las
vistas y tener estos metadatos listos para su uso. Solo entonces se puede ejecutar una consulta o se pueden
guardar datos nuevos en el almacén de datos.
El enfoque Code First es, en esencia, un generador de Entity Data Model sofisticado. El Entity Framework tiene
que generar un EDM a partir del código proporcionado; lo hace mediante el análisis de las clases implicadas en
el modelo, la aplicación de convenciones y la configuración del modelo a través de la API fluida. Una vez creado
el EDM, el Entity Framework se comporta de la misma manera que tenía un archivo EDMX presente en el
proyecto. Por lo tanto, la generación del modelo a partir de Code First agrega complejidad adicional que se
traduce en un tiempo de inicio más lento para el Entity Framework en comparación con el uso de EDMX. El
costo depende por completo del tamaño y la complejidad del modelo que se está compilando.
Al elegir usar EDMX frente a Code First, es importante saber que la flexibilidad introducida por Code First
aumenta el costo de crear el modelo por primera vez. Si la aplicación puede resistir el costo de esta primera
carga, normalmente Code First será la mejor manera de ir.

10 investigación del rendimiento


10,1 uso del generador de perfiles de Visual Studio
Si tiene problemas de rendimiento con el Entity Framework, puede usar un generador de perfiles como el que
está integrado en Visual Studio para ver dónde está gastando su aplicación su tiempo. Esta es la herramienta
que se usa para generar los gráficos circulares en la entrada de blog "Exploring the Performance of the [Link]
Entity Framework-Part 1" ( <[Link]
the-ado-net-entity-framework-part-1> ) que muestra dónde Entity Framework dedica su tiempo durante las
consultas en frío y en caliente.
La entrada de blog "Entity Framework de generación de perfiles con el generador de perfiles de Visual Studio
2010 Profiler" escrita por el equipo de asesoramiento de datos y modelado de clientes muestra un ejemplo real
de cómo usaba el generador de perfiles para investigar un problema de
<[Link]
profiler> rendimiento. Esta entrada se escribió para una aplicación Windows. Si tiene que generar perfiles de
una aplicación Web, las herramientas del Windows performance Recorder (WPR) y del analizador de
rendimiento de Windows (WPA) pueden funcionar mejor que si se trabajaran desde Visual Studio. WPR y WPA
forman parte del kit de herramientas de rendimiento de Windows, que se incluye con el kit de evaluación e
implementación de Windows ( [Link] ).
10,2 generación de perfiles de aplicación/base de datos
Herramientas como el generador de perfiles integrado en Visual Studio le indica dónde dedica tiempo la
aplicació[Link] otro tipo de generador de perfiles que realiza el análisis dinámico de la aplicación en ejecución,
ya sea en producción o en preproducción en función de las necesidades, y busca errores comunes y
antipatrones de acceso a la base de datos.
Dos perfiles disponibles comercialmente son Entity Framework Profiler ( <[Link] ) y ORMProfiler (
<[Link] ).
Si la aplicación es una aplicación MVC que usa Code First, puede usar MiniProfiler de StackExchange. Scott
Hanselman describe esta herramienta en su blog en:
<[Link]
[Link]> .
Para más información sobre la generación de perfiles de la actividad de base de datos de la aplicación, consulte
el artículo de MSDN Magazine de Julie Lerman titulado actividad de base de datos de generación de perfiles en
el Entity Framework.
registrador de base de datos 10,3
Si usa Entity Framework 6, considere también el uso de la funcionalidad de registro integrada. Se puede indicar
a la propiedad de base de datos del contexto que registre su actividad a través de una sencilla configuración de
una línea:

using (var context = [Link]())


{
[Link] = [Link];
var q = [Link](p => [Link] == "Beverages");
[Link]();
}

En este ejemplo, la actividad de la base de datos se registrará en la consola, pero la propiedad log puede
configurarse para llamar a cualquier delegado de cadena de acción < > .
Si desea habilitar el registro de base de datos sin volver a compilar y usa Entity Framework 6,1 o una versión
posterior, puede hacerlo agregando un interceptor en el archivo [Link] o [Link] de la aplicación.

<interceptors>
<interceptor type="[Link], EntityFramework">
<parameters>
<parameter value="C:\Path\To\My\[Link]"/>
</parameters>
</interceptor>
</interceptors>

Para obtener más información sobre cómo agregar registro sin volver a compilar, vaya a
<[Link] .

11 Apéndice
11,1 A. entorno de prueba
Este entorno utiliza una configuración de dos equipos con la base de datos en un equipo independiente de la
aplicación cliente. Las máquinas están en el mismo bastidor, por lo que la latencia de red es relativamente baja,
pero más realista que un entorno de un solo equipo.
Servidor de aplicaciones 11.1.1
En t o r n o d e so ft w a r e d e 1 1 .1 .1 .1

Entity Framework 4 entorno de software


Nombre del sistema operativo: Windows Server 2008 R2 Enterprise SP1.
Visual Studio 2010 – Ultimate.
Visual Studio 2010 SP1 (solo para algunas comparaciones).
Entity Framework 5 y 6 entorno de software
Nombre del sistema operativo: Windows 8.1 Enterprise
Visual Studio 2013: Ultimate.
En t o r n o d e h a r d w a r e d e 1 1 .1 .1 .2

Procesador dual: Intel (R) Xeon (R) CPU L5520 W3530 @ 2,27 GHz, 2261 Mhz8 GHz, 4 núcleos, 84
procesadores lógicos.
2412 GB RamRAM.
136 GB SCSI250GB SATA 7200 RPM GB/s unidad dividida en 4 particiones.
servidor de 11.1.2 DB
En t o r n o d e so ft w a r e d e 1 1 .1 .2 .1

Nombre del sistema operativo: Windows Server 2008 R 28.1 Enterprise SP1.
SQL Server 2008 R22012.
En t o r n o d e h a r d w a r e d e 1 1 .1 .2 .2

Procesador único: Intel (R) Xeon (R) CPU L5520 @ 2,27 GHz, 2261 MhzES-1620 0 @ 3,60 GHz, 4 núcleos, 8
procesadores lógicos.
824 GB RamRAM.
465 GB ATA500GB SATA 7200 RPM 6 GB/s unidad dividida en 4 particiones.
11,2 B. pruebas de comparación de rendimiento de consultas
El modelo Northwind se utilizó para ejecutar estas pruebas. Se generó a partir de la base de datos mediante el
diseñador de Entity Framework. A continuación, se usó el código siguiente para comparar el rendimiento de las
opciones de ejecución de la consulta:

using System;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace QueryComparison
{
public partial class NorthwindEntities : ObjectContext
{
private static readonly Func<NorthwindEntities, string, IQueryable<Product>> productsForCategoryCQ =
[Link](
(NorthwindEntities context, string categoryName) =>
[Link](p => [Link] == categoryName)
);

public IQueryable<Product> InvokeProductsForCategoryCQ(string categoryName)


{
return productsForCategoryCQ(this, categoryName);
}
}

public class QueryTypePerfComparison


{
private static string entityConnectionStr =
@"metadata=res://*/[Link]|res://*/[Link]|res://*/[Link];provider=[Link]
t;provider connection string='data source=.;initial catalog=Northwind;integrated
security=True;multipleactiveresultsets=True;App=EntityFramework'";

public void LINQIncludingContextCreation()


{
{
using (NorthwindEntities context = new NorthwindEntities())
{
var q = [Link](p => [Link] == "Beverages");
[Link]();
}
}

public void LINQNoTracking()


{
using (NorthwindEntities context = new NorthwindEntities())
{
[Link] = [Link];

var q = [Link](p => [Link] == "Beverages");


[Link]();
}
}

public void CompiledQuery()


{
using (NorthwindEntities context = new NorthwindEntities())
{
var q = [Link]("Beverages");
[Link]();
}
}

public void ObjectQuery()


{
using (NorthwindEntities context = new NorthwindEntities())
{
ObjectQuery<Product> products = [Link]("[Link] =
'Beverages'");
[Link]();
}
}

public void EntityCommand()


{
using (EntityConnection eConn = new EntityConnection(entityConnectionStr))
{
[Link]();
EntityCommand cmd = [Link]();
[Link] = "Select p From [Link] As p Where
[Link] = 'Beverages'";

using (EntityDataReader reader = [Link]([Link]))


{
List<Product> productsList = new List<Product>();
while ([Link]())
{
DbDataRecord record = (DbDataRecord)[Link](0);

// 'materialize' the product by accessing each field and value. Because we are
materializing products, we won't have any nested data readers or records.
int fieldCount = [Link];

// Treat all products as Product, even if they are the subtype DiscontinuedProduct.
Product product = new Product();

[Link] = record.GetInt32(0);
[Link] = [Link](1);
[Link] = record.GetInt32(2);
[Link] = record.GetInt32(3);
[Link] = [Link](4);
[Link] = [Link](5);
[Link] = record.GetInt16(6);
[Link] = record.GetInt16(7);
[Link] = record.GetInt16(8);
[Link] = record.GetInt16(8);
[Link] = [Link](9);

[Link](product);
}
}
}
}

public void ExecuteStoreQuery()


{
using (NorthwindEntities context = new NorthwindEntities())
{
ObjectResult<Product> beverages = [Link]<Product>(
@" SELECT [Link], [Link], [Link], [Link], [Link], [Link],
[Link], [Link], [Link], [Link]
FROM Products AS P INNER JOIN Categories AS C ON [Link] = [Link]
WHERE ([Link] = 'Beverages')"
);
[Link]();
}
}

public void ExecuteStoreQueryDbContext()


{
using (var context = new [Link]())
{
var beverages = [Link]\<[Link]>(
@" SELECT [Link], [Link], [Link], [Link], [Link], [Link],
[Link], [Link], [Link], [Link]
FROM Products AS P INNER JOIN Categories AS C ON [Link] = [Link]
WHERE ([Link] = 'Beverages')"
);
[Link]();
}
}

public void ExecuteStoreQueryDbSet()


{
using (var context = new [Link]())
{
var beverages = [Link](
@" SELECT [Link], [Link], [Link], [Link], [Link], [Link],
[Link], [Link], [Link], [Link]
FROM Products AS P INNER JOIN Categories AS C ON [Link] = [Link]
WHERE ([Link] = 'Beverages')"
);
[Link]();
}
}

public void LINQIncludingContextCreationDbContext()


{
using (var context = new [Link]())
{
var q = [Link](p => [Link] == "Beverages");
[Link]();
}
}

public void LINQNoTrackingDbContext()


{
using (var context = new [Link]())
{
var q = [Link]().Where(p => [Link] == "Beverages");
[Link]();
}
}
}
}

Modelo 11,3 C. Navision


La base de datos de Navision es una base de datos grande que se usa para la demostración de Microsoft
Dynamics – NAV. El modelo conceptual generado contiene 1005 conjuntos de entidades y conjuntos de
asociaciones de 4227. El modelo usado en la prueba es "plano": no se ha agregado ninguna herencia a él.
11.3.1 consultas usadas para las pruebas de Navision
La lista de consultas utilizada con el modelo de Navision contiene tres categorías de consultas Entity SQL:
b ú sq u e d a 1 1 .3 .1 .1

Una consulta de búsqueda simple sin agregaciones


Recuento: 16232
Ejemplo:

<Query complexity="Lookup">
<CommandText>Select value distinct top(4) e.Idle_Time From [Link] as e</CommandText>
</Query>

1 1 .3 .1 .2 Si n g l e A g g r e g a t i n g

Una consulta de BI normal con varias agregaciones, pero sin subtotales (consulta única)
Recuento: 2313
Ejemplo:

<Query complexity="SingleAggregating">
<CommandText>NavisionFK.MDF_SessionLogin_Time_Max()</CommandText>
</Query>

Donde MDF _ SessionLogin _ Time _ Max () se define en el modelo como:

<Function Name="MDF_SessionLogin_Time_Max" ReturnType="Collection(DateTime)">


<DefiningExpression>SELECT VALUE [Link](E.Login_Time) FROM [Link] as
E</DefiningExpression>
</Function>

1 1 .3 .1 .3 A g g r e g a t i n g Su b t o t a l s

Una consulta de BI con agregaciones y subtotales (a través de Union All)


Recuento: 178
Ejemplo:
<Query complexity="AggregatingSubtotals">
<CommandText>
using NavisionFK;
function AmountConsumed(entities Collection([CRONUS_International_Ltd__Zone])) as
(
[Link](select value N.Block_Movement FROM entities as E, E.CRONUS_International_Ltd__Bin as N)
)
function AmountConsumed(P1 Edm.Int32) as
(
AmountConsumed(select value e from NavisionFKContext.CRONUS_International_Ltd__Zone as e where
e.Zone_Ranking = P1)
)
------------------------------------------------------------------------------------------------------------
----------
(
select top(10) Zone_Ranking, Cross_Dock_Bin_Zone, AmountConsumed(GroupPartition(E))
from NavisionFKContext.CRONUS_International_Ltd__Zone as E
where AmountConsumed(E.Zone_Ranking) > @MinAmountConsumed
group by E.Zone_Ranking, E.Cross_Dock_Bin_Zone
)
union all
(
select top(10) Zone_Ranking, Cast(null as [Link]) as P2, AmountConsumed(GroupPartition(E))
from NavisionFKContext.CRONUS_International_Ltd__Zone as E
where AmountConsumed(E.Zone_Ranking) > @MinAmountConsumed
group by E.Zone_Ranking
)
union all
{
Row(Cast(null as Edm.Int32) as P1, Cast(null as [Link]) as P2, AmountConsumed(select value E
from
NavisionFKContext.CRONUS_International_Ltd__Zone as E
where
AmountConsumed(E.Zone_Ranking) > @MinAmountConsumed))
}</CommandText>
<Parameters>
<Parameter Name="MinAmountConsumed" DbType="Int32" Value="10000" />
</Parameters>
</Query>
Mejorar el rendimiento de inicio con NGen
12/03/2021 • 10 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

El .NET Framework admite la generación de imágenes nativas para aplicaciones y bibliotecas administradas
como una manera de ayudar a las aplicaciones a iniciarse más rápido y, en algunos casos, usar menos memoria.
Las imágenes nativas se crean mediante la conversión de ensamblados de código administrado en archivos que
contienen instrucciones máquina nativas antes de que se ejecute la aplicación, lo que evita que el compilador JIT
de .NET (Just-in-Time) tenga que generar las instrucciones nativas en tiempo de ejecución de la aplicación.
Antes de la versión 6, las bibliotecas principales del tiempo de ejecución de EF formaban parte del .NET
Framework y las imágenes nativas se generaban automáticamente para ellas. A partir de la versión 6, todo el
tiempo de ejecución de EF se ha combinado en el paquete NuGet de EntityFramework. Ahora, las imágenes
nativas deben generarse con la herramienta de línea de comandos [Link] para obtener resultados similares.
Las observaciones empíricas muestran que las imágenes nativas de los ensamblados en tiempo de ejecución de
EF pueden cortar entre 1 y 3 segundos de tiempo de inicio de la aplicación.

Cómo usar [Link]


La función más básica de la herramienta [Link] es "instalar" (es decir, crear y conservar en disco) imágenes
nativas para un ensamblado y todas sus dependencias directas. Aquí se muestra cómo lograr esto:
1. Abra una ventana de símbolo del sistema como administrador.
2. Cambie el directorio de trabajo actual a la ubicación de los ensamblados para los que desea generar
imágenes nativas:

cd <*Assemblies location*>

3. En función del sistema operativo y de la configuración de la aplicación, es posible que tenga que generar
imágenes nativas para la arquitectura de 32 bits, la arquitectura de 64 bits o para ambas.
Para la ejecución de 32 bits:

%WINDIR%\[Link]\Framework\v4.0.30319\ngen install <Assembly name>

Para la ejecución de 64 bits:

%WINDIR%\[Link]\Framework64\v4.0.30319\ngen install <Assembly name>


TIP
La generación de imágenes nativas para la arquitectura equivocada es un error muy común. En caso de duda,
simplemente puede generar imágenes nativas para todas las arquitecturas que se aplican al sistema operativo instalado
en la máquina.

[Link] también admite otras funciones, como desinstalar y mostrar las imágenes nativas instaladas, poner en
cola la generación de varias imágenes, etc. Para obtener más detalles sobre el uso, lea la documentación
[Link].

Cuándo usar [Link]


Cuando se trata de decidir en qué ensamblados se van a generar imágenes nativas en una aplicación basada en
EF versión 6 o versiones posteriores, debe tener en cuenta las siguientes opciones:
El ensamblado de tiempo de ejecución de EF principal, EntityFramework .dll : una aplicación basada
en EF típica ejecuta una cantidad significativa de código de este ensamblado en el inicio o en el primer acceso
a la base de datos. Por lo tanto, la creación de imágenes nativas de este ensamblado producirá las mayores
mejoras en el rendimiento de inicio.
Cualquier ensamblado de proveedor EF usado por la aplicación : el tiempo de inicio también puede
beneficiarse ligeramente de la generación de imágenes nativas. Por ejemplo, si la aplicación utiliza el
proveedor de EF para SQL Server querrá generar una imagen nativa para [Link].
Los ensamblados de la aplicación y otras dependencias : en la documentació[Link] se tratan los
criterios generales para elegir en qué ensamblados se van a generar imágenes nativas y el impacto de las
imágenes nativas en la seguridad, opciones avanzadas como "enlace fuerte", escenarios como el uso de
imágenes nativas en escenarios de depuración y generación de perfiles, etc.

TIP
Asegúrese de medir cuidadosamente el impacto del uso de imágenes nativas en el rendimiento de inicio y el rendimiento
general de la aplicación y compararlas con los requisitos reales. Mientras que las imágenes nativas suelen ayudar a
mejorar el rendimiento de inicio y, en algunos casos, reducir el uso de memoria, no todos los escenarios se beneficiarán de
forma equitativa. Por ejemplo, en la ejecución de estado estable (es decir, una vez que todos los métodos que se usan en
la aplicación se han invocado al menos una vez), el código generado por el compilador JIT puede producir de hecho un
rendimiento ligeramente mejor que las imágenes nativas.

Usar [Link] en una máquina de desarrollo


Durante el desarrollo, el compilador JIT de .NET ofrecerá el mejor equilibrio general para el código que cambia
con frecuencia. La generación de imágenes nativas para las dependencias compiladas, como los ensamblados
en tiempo de ejecución de EF, puede ayudar a acelerar el desarrollo y las pruebas reduciendo unos pocos
segundos al principio de cada ejecución.
Un buen lugar para encontrar los ensamblados en tiempo de ejecución de EF es la ubicación del paquete NuGet
para la solución. Por ejemplo, para una aplicación que usa EF 6.0.2 con SQL Server y con .NET 4,5 o superior
como destino, puede escribir lo siguiente en una ventana del símbolo del sistema (Recuerde abrirla como
administrador):

cd <Solution directory>\packages\EntityFramework.6.0.2\lib\net45
%WINDIR%\[Link]\Framework\v4.0.30319\ngen install [Link]
%WINDIR%\[Link]\Framework64\v4.0.30319\ngen install [Link]
NOTE
Esto aprovecha el hecho de que la instalación de imágenes nativas para el proveedor de EF para SQL Server también
instalará de forma predeterminada las imágenes nativas para el ensamblado de tiempo de ejecución de EF principal. Esto
funciona porque [Link] puede detectar que [Link] es una dependencia directa del ensamblado de
[Link] ubicado en el mismo directorio.

Crear imágenes nativas durante la instalación


El kit de herramientas de WiX permite poner en cola la generación de imágenes nativas para los ensamblados
administrados durante la instalación, como se explica en esta Guía de procedimientos. Otra alternativa es crear
una tarea de instalación personalizada que ejecute el comando [Link].

Comprobando que las imágenes nativas se usan para EF


Puede comprobar que una aplicación específica usa un ensamblado nativo buscando ensamblados cargados
que tengan la extensión ".[Link]" o ".[Link]". Por ejemplo, se llamará a una imagen nativa para el ensamblado en
tiempo de ejecución principal de EF [Link]. Una manera sencilla de inspeccionar los
ensamblados .NET cargados de un proceso es usar el Explorador de procesos.

Otras cosas que debe tener en cuenta


No se debe confundir la creación de una imagen nativa de un ensamblado con el registro del
ensamblado en la GAC (caché global de ensamblados) . [Link] permite crear imágenes de
ensamblados que no se encuentran en la GAC y, de hecho, varias aplicaciones que usan una versión específica
de EF pueden compartir la misma imagen nativa. Aunque Windows 8 puede crear automáticamente imágenes
nativas para los ensamblados colocados en la GAC, el tiempo de ejecución de EF se optimiza para
implementarse junto con la aplicación y no se recomienda registrarlo en la GAC, ya que esto tiene un impacto
negativo en la resolución de ensamblados y en el mantenimiento de las aplicaciones entre otros aspectos.
Vistas de asignación generadas previamente
12/03/2021 • 8 minutes to read

Antes de que el Entity Framework pueda ejecutar una consulta o guardar cambios en el origen de datos, debe
generar un conjunto de vistas de asignación para tener acceso a la base de datos. Estas vistas de asignación son
un conjunto de Entity SQL instrucción que representa la base de datos de forma abstracta y forman parte de los
metadatos que se almacenan en caché por dominio de aplicación. Si crea varias instancias del mismo contexto
en el mismo dominio de aplicación, volverán a usar las vistas de asignación de los metadatos almacenados en
caché en lugar de volver a generarlas. Dado que la generación de la vista de asignación es una parte
significativa del costo total de ejecutar la primera consulta, el Entity Framework permite generar previamente
vistas de asignación e incluirlas en el proyecto compilado. Para obtener más información, vea consideraciones
de rendimiento (Entity Framework).

Generación de vistas de asignación con EF Power Tools Community


Edition
La manera más sencilla de generar vistas previamente es usar la edición de EF Power Tools Community Edition.
Una vez que tenga instaladas las herramientas avanzadas, tendrá una opción de menú para generar vistas,
como se indica a continuación.
En el caso de los modelos de code First , haga clic con el botón derecho en el archivo de código que
contiene la clase DbContext.
En el caso de los modelos EF Designer , haga clic con el botón derecho en el archivo EDMX.

Una vez finalizado el proceso, tendrá una clase similar a la siguiente generada
Ahora, cuando ejecute la aplicación EF, utilizará esta clase para cargar las vistas según sea necesario. Si el
modelo cambia y no vuelve a generar esta clase, EF producirá una excepción.

Generar vistas de asignación desde código-EF6 en adelante


La otra forma de generar vistas es usar las API que proporciona EF. Al usar este método, tiene la libertad de
serializar las vistas de la manera que desee, pero también debe cargar las vistas por su cuenta.

NOTE
EF6 y versiones posteriores: las API que se muestran en esta sección se introdujeron en Entity Framework 6. Si usa una
versión anterior, esta información no se aplica.

Generar vistas
Las API para generar vistas se encuentran en la clase System. Data. Entity. Core. Mapping.
StorageMappingItemCollection. Puede recuperar un StorageMappingCollection para un contexto mediante el
MetadataWorkspace de un ObjectContext. Si usa la API DbContext más reciente, puede acceder a ella con
IObjectContextAdapter como se muestra a continuación, en este código tenemos una instancia de DbContext
derivado denominada dbContext:

var objectContext = ((IObjectContextAdapter) dbContext).ObjectContext;


var mappingCollection = (StorageMappingItemCollection)[Link]

.GetItemCollection([Link]);

Una vez que tenga el StorageMappingItemCollection, puede obtener acceso a los métodos GenerateViews y
ComputeMappingHashValue.

public Dictionary<EntitySetBase, DbMappingView> GenerateViews(IList<EdmSchemaError> errors)


public string ComputeMappingHashValue()

El primer método crea un diccionario con una entrada para cada vista de la asignación de contenedor. El
segundo método calcula un valor hash para la asignación de un solo contenedor y se utiliza en tiempo de
ejecución para validar que el modelo no ha cambiado desde que se generaron previamente las vistas. Se
proporcionan invalidaciones de los dos métodos para escenarios complejos que implican varias asignaciones de
contenedor.
Al generar vistas, llamará al método GenerateViews y, a continuación, escribirá el EntitySetBase resultante y
DbMappingView. También necesitará almacenar el hash generado por el método ComputeMappingHashValue.
Cargando vistas
Para cargar las vistas generadas por el método GenerateViews, puede proporcionar EF con una clase que herede
de la clase abstracta DbMappingViewCache. DbMappingViewCache especifica dos métodos que debe
implementar:

public abstract string MappingHashValue { get; }


public abstract DbMappingView GetView(EntitySetBase extent);

La propiedad MappingHashValue debe devolver el hash generado por el método ComputeMappingHashValue.


Cuando EF vaya a pedir las vistas, primero generará y comparará el valor hash del modelo con el hash devuelto
por esta propiedad. Si no coinciden, EF producirá una excepción EntityCommandCompilationException.
El método GetView (aceptará un EntitySetBase y tendrá que devolver un DbMappingVIew que contenga el
EntitySql generado para que se asoció con el EntitySetBase determinado en el Diccionario generado por el
método GenerateViews. Si EF solicita una vista que no tiene, GetView (debe devolver null.
A continuación se muestra un extracto del DbMappingViewCache que se genera con las herramientas
avanzadas, como se ha descrito anteriormente, en él vemos una forma de almacenar y recuperar el EntitySql
necesario.

public override string MappingHashValue


{
get { return "a0b843f03dd29abee99789e190a6fb70ce8e93dc97945d437d9a58fb8e2afd2e"; }
}

public override DbMappingView GetView(EntitySetBase extent)


{
if (extent == null)
{
throw new ArgumentNullException("extent");
}

var extentName = [Link] + "." + [Link];

if (extentName == "[Link]")
{
return GetView2();
}

if (extentName == "[Link]")
{
return GetView3();
}

return null;
}

private static DbMappingView GetView2()


{
return new DbMappingView(@"
SELECT VALUE -- Constructing Blogs
[[Link]](T1.Blog_BlogId, T1.Blog_Test, T1.Blog_title, T1.Blog_Active,
T1.Blog_SomeDecimal)
FROM (
SELECT
[Link] AS Blog_BlogId,
[Link] AS Blog_Test,
[Link] AS Blog_title,
[Link] AS Blog_Active,
[Link] AS Blog_SomeDecimal,
True AS _from0
FROM [Link] AS T
) AS T1");
}

Para que EF use el DbMappingViewCache que agregue, use el DbMappingViewCacheTypeAttribute,


especificando el contexto para el que se creó. En el código siguiente se asocia el BlogContext con la clase
MyMappingViewCache.

[assembly: DbMappingViewCacheType(typeof(BlogContext), typeof(MyMappingViewCache))]

En escenarios más complejos, se pueden proporcionar las instancias de la vista de la caché de vistas mediante la
especificación de un generador de caché de vista de asignación. Esto se puede hacer implementando la clase
abstracta System. Data. Entity. Infrastructure. MappingViews. DbMappingViewCacheFactory. La instancia del
generador de caché de la vista de asignación que se usa se puede recuperar o establecer mediante
StorageMappingItemCollection. MappingViewCacheFactoryproperty.
Proveedores de Entity Framework 6
12/03/2021 • 8 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

Entity Framework ahora se desarrolla bajo una licencia de código abierto, por lo que EF6 y versiones posteriores
no se van a incluir como parte de .NET Framework. Esto ofrece muchas ventajas, pero también exige que los
proveedores de EF se vuelvan a compilar en los ensamblados de EF6. Esto significa que los proveedores de EF
para EF5 y versiones anteriores no funcionarán con EF6 hasta que se vuelvan a compilar.

Proveedores disponibles para EF6


Los proveedores conocidos que se han vuelto a compilar para EF6 incluyen:
Proveedor de Microsoft SQL Ser ver
Compilado a partir del código base fuente abierto de Entity Framework
Incluido como parte del paquete NuGet de Entity Framework
Proveedor de Microsoft SQL Ser ver Compact Edition
Compilado a partir del código base fuente abierto de Entity Framework
Incluido en el paquete NuGet de [Link]
Proveedores de datos de Devar t dotConnect
Existen proveedores de terceros de Devart para una serie de bases de datos como Oracle, MySQL,
PostgreSQL, SQLite, Salesforce, DB2 y SQL Server
Proveedores de CData Software
Existen proveedores de terceros de CData Software para una serie de almacenes de datos como
Salesforce, Azure Table Storage, MySql, etc.
Proveedor de Firebird
Disponible como paquete NuGet
Proveedor de Visual Fox Pro
Disponible como paquete NuGet
MySQL
Conector MySQL/NET para Entity Framework
PostgreSQL
Npgsql está disponible como paquete NuGet
Oracle
[Link] está disponible como paquete NuGet
SQLite
[Link] está disponible como paquete NuGet.
Tenga en cuenta que la inclusión en esta lista no indica el nivel de funcionalidad o compatibilidad de un
proveedor determinado, solo que hay una compilación para EF6 disponible.

Registro de proveedores de EF
A partir de Entity Framework 6, los proveedores de EF se pueden registrar mediante configuración basada en
código o en el archivo de configuración de la aplicación.
Registro en el archivo de configuración
El registro del proveedor de EF en [Link] o [Link] tiene el formato siguiente:

<entityFramework>
<providers>
<provider invariantName="[Link]" type="[Link], MyAssembly" />
</providers>
</entityFramework>

Tenga en cuenta que, a menudo, si el proveedor de EF se instala desde NuGet, el paquete NuGet agrega
automáticamente este registro al archivo de configuración. Si instala el paquete NuGet en un proyecto que no es
el proyecto de inicio de la aplicación, es posible que tenga que copiar el registro en el archivo de configuración
del proyecto de inicio.
El elemento "invariantName" de este registro es el mismo nombre invariable usado para identificar a un
proveedor de [Link]. Se puede encontrar como atributo "invariant" en un registro DbProviderFactories y
como atributo "providerName" en un registro de cadena de conexión. El nombre invariable que se va a usar
también debe incluirse en la documentación del proveedor. Ejemplos de nombres invariables son
"[Link]" para SQL Server y "[Link].4.0" para SQL Server Compact.
El elemento "type" de este registro es el nombre calificado con el ensamblado del tipo de proveedor que se
deriva de "[Link]". Por ejemplo, la cadena que se va a usar para
SQL Compact es "[Link],
[Link]". El tipo que se va a usar aquí debe incluirse en la documentación del
proveedor.
Registro basado en código
A partir de Entity Framework 6, la configuración de la aplicación de EF puede especificarse en el código. Para
obtener todos los detalles, vea Entity Framework Code-Based Configuration (Configuración basada en código de
Entity Framework). La forma habitual de registrar un proveedor de EF mediante configuración basada en código
es crear una nueva clase que se derive de [Link] y colocarla en el mismo
ensamblado que la clase DbContext. Luego la clase DbConfiguration debe registrar el proveedor en su
constructor. Por ejemplo, para registrar el proveedor de SQL Compact, la clase DbConfiguration tiene este
aspecto:

public class MyConfiguration : DbConfiguration


{
public MyConfiguration()
{
SetProviderServices(
[Link],
[Link]);
}
}

En este código, "[Link]" es una comodidad de la cadena de nombre


invariable del proveedor de SQL Server Compact ("[Link].4.0") y
[Link] devuelve la instancia singleton del proveedor de EF de SQL Compact.

¿Qué ocurre si el proveedor que necesito no está disponible?


Si el proveedor está disponible para versiones anteriores de EF, se le anima a ponerse en contacto con el
propietario del proveedor y pedirle que cree una versión de EF6. Debe incluir una referencia a la documentación
para el modelo de proveedor de EF6.

¿Puedo escribir un proveedor por mí mismo?


Por supuesto que es posible crear un proveedor de EF por sí mismo, aunque no se debe considerar una tarea
trivial. El vínculo anterior sobre el modelo de proveedor de EF6 es un buen punto de partida. También le puede
resultar útil usar el código para el proveedor de SQL Server y SQL CE incluido en el código base fuente abierto
de EF como punto de partida o como referencia.
Tenga en cuenta que a partir de EF6, el proveedor de EF está menos estrechamente ligado al proveedor de
[Link] subyacente. Esto facilita la escritura de un proveedor de EF sin necesidad de escribir o ajustar las clases
de [Link].
Modelo de proveedor de Entity Framework 6
12/03/2021 • 31 minutes to read

El modelo de proveedor de Entity Framework permite utilizar Entity Framework con diferentes tipos de servidor
de base de datos. Por ejemplo, se puede conectar un proveedor para permitir el uso de EF en Microsoft SQL
Server, mientras que otro proveedor puede estar conectado a para permitir que EF se use en Microsoft SQL
Server Compact Edition. Los proveedores de EF6 que somos conscientes de pueden encontrarse en la página
proveedores de Entity Framework .
Algunos cambios fueron necesarios para la manera en que EF interactúa con los proveedores para permitir que
EF se libere bajo una licencia de código abierto. Estos cambios requieren la regeneración de los proveedores de
EF en los ensamblados de EF6 junto con los nuevos mecanismos de registro del proveedor.

Regeneración
Con EF6, el código principal que anteriormente formaba parte de la .NET Framework se envía ahora como
ensamblados fuera de banda (OOB). Puede encontrar información sobre cómo compilar aplicaciones en EF6 en
la página actualizar aplicaciones para EF6 . También será necesario volver a generar los proveedores mediante
estas instrucciones.

Información general sobre tipos de proveedor


Un proveedor de EF es realmente una colección de servicios específicos del proveedor definidos por tipos CLR
que estos servicios extienden de (para una clase base) o implementan (para una interfaz). Dos de estos servicios
son fundamentales y necesarios para que EF funcione en absoluto. Otros son opcionales y solo deben
implementarse si se requiere una funcionalidad específica y/o las implementaciones predeterminadas de estos
servicios no funcionan para el servidor de base de datos específico de destino.

Tipos fundamentales de proveedor


DbProviderFactory
EF depende de tener un tipo derivado de System. Data. Common. DbProviderFactory para realizar todo el acceso
a la base de datos de bajo nivel. DbProviderFactory no es realmente parte de EF, sino que es una clase en el .NET
Framework que sirve un punto de entrada para los proveedores de [Link] que puede usarse en EF, otro o, o
directamente mediante una aplicación, para obtener instancias de conexiones, comandos, parámetros y otras
abstracciones de [Link] de un modo independiente del proveedor. Puede encontrar más información sobre
DbProviderFactory en la documentación de MSDN para [Link].
DbProviderServices
EF depende de tener un tipo derivado de DbProviderServices para proporcionar funcionalidad adicional que EF
necesita en la parte superior de la funcionalidad que ya proporciona el proveedor [Link]. En versiones
anteriores de EF la clase DbProviderServices formaba parte del .NET Framework y se encontraba en el espacio
de nombres System. Data. Common. A partir de EF6, esta clase ahora forma parte de [Link] y está
en el espacio de nombres System. Data. Entity. Core. Common.
Puede encontrar más información sobre la funcionalidad fundamental de una implementación de
DbProviderServices en MSDN. Sin embargo, tenga en cuenta que, en el momento de escribir esta información,
no se actualiza para EF6, aunque la mayoría de los conceptos siguen siendo válidos. Las implementaciones de
SQL Server y SQL Server Compact de DbProviderServices también se protegen en el código base de código
abierto y pueden servir como referencias útiles para otras implementaciones.
En versiones anteriores de EF, la implementación de DbProviderServices que se va a usar se obtuvo
directamente de un proveedor [Link]. Esto se realiza mediante la conversión de DbProviderFactory a
IServiceProvider y la llamada al método GetService. Este es el proveedor de EF estrechamente acoplado al
DbProviderFactory. Este acoplamiento ha bloqueado EF para que no se mueva fuera del .NET Framework y, por
tanto, para EF6 se ha quitado este acoplamiento estricto y ahora se ha registrado una implementación de
DbProviderServices directamente en el archivo de configuración de la aplicación o en la configuración basada
en código, tal y como se describe más detalladamente en la sección registrar DbProviderServices que se
muestra a continuación.

Servicios adicionales
Además de los servicios fundamentales descritos anteriormente, también hay muchos otros servicios que se
usan en EF, que siempre son específicos de cada proveedor. Las implementaciones predeterminadas específicas
del proveedor de estos servicios se pueden proporcionar mediante una implementación de DbProviderServices.
Las aplicaciones también pueden invalidar las implementaciones de estos servicios o proporcionar
implementaciones cuando un tipo DbProviderServices no proporciona un valor predeterminado. Esto se
describe con más detalle en la sección resolución de servicios adicionales más adelante.
A continuación se enumeran los tipos de servicio adicionales que un proveedor puede ser de interés para un
proveedor. Encontrará más detalles sobre cada uno de estos tipos de servicio en la documentación de la API.
IDbExecutionStrategy
Se trata de un servicio opcional que permite a un proveedor implementar reintentos u otro comportamiento
cuando las consultas y los comandos se ejecutan en la base de datos. Si no se proporciona ninguna
implementación, EF simplemente ejecutará los comandos y propagará las excepciones que se produzcan. Por
SQL Server este servicio se utiliza para proporcionar una directiva de reintentos que es especialmente útil
cuando se ejecuta en servidores de bases de datos basados en la nube, como SQL Azure.
IDbConnectionFactory
Se trata de un servicio opcional que permite a un proveedor crear objetos DbConnection por Convención
cuando se especifica solo un nombre de base de datos. Tenga en cuenta que aunque este servicio se puede
resolver mediante una implementación de DbProviderServices, está presente desde EF 4,1 y también se puede
establecer explícitamente en el archivo de configuración o en el código. El proveedor solo tendrá la oportunidad
de resolver este servicio si se registró como el proveedor predeterminado (vea el proveedor predeterminado a
continuación) y si no se ha establecido un generador de conexiones predeterminado en otro lugar.
DbSpatialServices
Se trata de un servicio opcional que permite a un proveedor agregar compatibilidad para los tipos espaciales
Geography y Geometry. Se debe proporcionar una implementación de este servicio para que una aplicación use
EF con tipos espaciales. DbSptialServices se solicita de dos maneras. En primer lugar, se solicitan servicios
espaciales específicos del proveedor mediante un objeto DbProviderInfo (que contiene el nombre invariable y el
token del manifiesto) como clave. En segundo lugar, se puede solicitar a DbSpatialServices sin clave. Se usa para
resolver el "proveedor espacial global" que se usa al crear tipos independientes de DbGeography o
DbGeometry.
MigrationSqlGenerator
Se trata de un servicio opcional que permite usar migraciones de EF para la generación de SQL que se usa en la
creación y modificación de esquemas de base de datos de Code First. Se requiere una implementación para
admitir las migraciones. Si se proporciona una implementación, también se utilizará cuando se creen las bases
de datos mediante inicializadores de base de datos o el método Database. Create.
FUNC<DbConnection, String, HistoryContextFactory>
Se trata de un servicio opcional que permite a un proveedor configurar la asignación de HistoryContext a la
__MigrationHistory tabla usada por las migraciones de EF. HistoryContext es un DbContext de Code First y se
puede configurar mediante la API fluida normal para cambiar aspectos como el nombre de la tabla y las
especificaciones de asignación de columnas. La implementación predeterminada de este servicio devuelta por
EF para todos los proveedores puede funcionar para un servidor de base de datos determinado si ese
proveedor admite todas las asignaciones predeterminadas de tabla y columna. En tal caso, no es necesario que
el proveedor proporcione una implementación de este servicio.
IDbProviderFactoryResolver
Se trata de un servicio opcional para obtener el DbProviderFactory correcto de un objeto DbConnection
determinado. La implementación predeterminada de este servicio devuelta por EF para todos los proveedores
está pensada para funcionar con todos los proveedores. Sin embargo, cuando se ejecuta en .NET 4, el
DbProviderFactory no es accesible públicamente desde uno si su DbConnections. Por lo tanto, EF usa algunas
heurísticas para buscar coincidencias en los proveedores registrados. Es posible que, en algunos proveedores, se
produzca un error en esta heurística y, en tales situaciones, el proveedor proporcione una nueva
implementación.

Registrando DbProviderServices
La implementación de DbProviderServices que se va a usar se puede registrar en el archivo de configuración de
la aplicación ([Link] o [Link]) o mediante la configuración basada en código. En cualquier caso, el
registro utiliza el "nombre invariable" del proveedor como clave. Esto permite registrar y usar varios
proveedores en una sola aplicación. El nombre invariable que se usa para los registros EF es el mismo que el
nombre invariable que se usa para el registro del proveedor [Link] y las cadenas de conexión. Por ejemplo,
para SQL Server se usa el nombre invariable "System. Data. SqlClient".
Registro en el archivo de configuración
El tipo DbProviderServices que se va a usar se registra como un elemento de proveedor en la lista de
proveedores de la sección entityFramework del archivo de configuración de la aplicación. Por ejemplo:

<entityFramework>
<providers>
<provider invariantName="[Link]" type="[Link], MyAssembly" />
</providers>
</entityFramework>

La cadena de tipo debe ser el nombre de tipo calificado con el ensamblado de la implementación de
DbProviderServices que se va a usar.
Registro basado en código
A partir de, los proveedores de EF6 también se pueden registrar mediante código. Esto permite usar un
proveedor EF sin ningún cambio en el archivo de configuración de la aplicación. Para usar la configuración
basada en código, una aplicación debe crear una clase DbConfiguration tal como se describe en la
documentación de configuración basada en código. El constructor de la clase DbConfiguration debería llamar a
SetProviderServices para registrar el proveedor de EF. Por ejemplo:

public class MyConfiguration : DbConfiguration


{
public MyConfiguration()
{
SetProviderServices("[Link]", new MyProviderServices());
}
}

Resolver servicios adicionales


Como se mencionó anteriormente en la sección información general de los tipos de proveedor, una clase
DbProviderServices también se puede usar para resolver servicios adicionales. Esto es posible porque
DbProviderServices implementa IDbDependencyResolver y cada tipo de DbProviderServices registrado se
agrega como una "resolución predeterminada". El mecanismo IDbDpendencyResolver se describe con más
detalle en resolución de dependencias. Sin embargo, no es necesario comprender todos los conceptos de esta
especificación para resolver servicios adicionales en un proveedor.
La forma más común de que un proveedor resuelva servicios adicionales es llamar a DbProviderServices.
AddDependencyResolver para cada servicio en el constructor de la clase DbProviderServices. Por ejemplo,
SqlProviderServices (el proveedor de EF para SQL Server) tiene un código similar a este para la inicialización:

private SqlProviderServices()
{
AddDependencyResolver(new SingletonDependencyResolver<IDbConnectionFactory>(
new SqlConnectionFactory()));

AddDependencyResolver(new ExecutionStrategyResolver<DefaultSqlExecutionStrategy>(
"[Link]", null, () => new DefaultSqlExecutionStrategy()));

AddDependencyResolver(new SingletonDependencyResolver<Func<MigrationSqlGenerator>>(
() => new SqlServerMigrationSqlGenerator(), "[Link]"));

AddDependencyResolver(new SingletonDependencyResolver<DbSpatialServices>(
[Link],
k =>
{
var asSpatialKey = k as DbProviderInfo;
return asSpatialKey == null
|| [Link] == ProviderInvariantName;
}));
}

Este constructor usa las siguientes clases auxiliares:


SingletonDependencyResolver: proporciona una manera sencilla de resolver los servicios singleton, es decir,
los servicios para los que se devuelve la misma instancia cada vez que se llama a GetService. Los servicios
transitorios suelen registrarse como un generador singleton que se usará para crear instancias transitorias a
petición.
ExecutionStrategyResolver: un solucionador específico para devolver implementaciones de
IExecutionStrategy.
En lugar de usar DbProviderServices. AddDependencyResolver, también es posible invalidar
DbProviderServices. GetService y resolver servicios adicionales directamente. Se llamará a este método cuando
EF necesite un servicio definido por un tipo determinado y, en algunos casos, para una clave determinada. El
método debe devolver el servicio si es posible o devolver null a la cancelación de la devolución del servicio y, en
su lugar, permitir que otra clase la resuelva. Por ejemplo, para resolver el generador de conexiones
predeterminado, el código de GetService podría tener un aspecto similar al siguiente:

public override object GetService(Type type, object key)


{
if (type == typeof(IDbConnectionFactory))
{
return new SqlConnectionFactory();
}
return null;
}

Orden de registro
Cuando se registran varias implementaciones de DbProviderServices en el archivo de configuración de una
aplicación, se agregarán como resoluciones secundarias en el orden en que se muestran. Puesto que los
solucionadores siempre se agregan a la parte superior de la cadena de resolución secundaria, esto significa que
el proveedor al final de la lista tendrá la oportunidad de resolver las dependencias antes de las demás. (Esto
puede parecer un poco más intuitivo al principio, pero tiene sentido si se deja que cada proveedor esté fuera de
la lista y se apile encima de los proveedores existentes).
Normalmente, esta clasificación no es importante porque la mayoría de los servicios del proveedor son
específicos del proveedor y tienen como clave el nombre invariable del proveedor. Sin embargo, para los
servicios que no están codificados por el nombre invariable del proveedor o alguna otra clave específica del
proveedor, el servicio se resolverá en función de este orden. Por ejemplo, si no se establece explícitamente de
forma distinta en otro lugar, el generador de conexiones predeterminado procederá del proveedor superior de
la cadena.

Registros de archivos de configuración adicionales


Es posible registrar explícitamente algunos de los servicios de proveedor adicionales descritos anteriormente en
el archivo de configuración de una aplicación. Cuando esto se hace, se usará el registro del archivo de
configuración en lugar de cualquier elemento devuelto por el método GetService de la implementación de
DbProviderServices.
Registro del generador de conexiones predeterminado
A partir de EF5, el paquete NuGet de EntityFramework registra automáticamente el generador de conexiones de
SQL Express o el generador de conexiones de LocalDb en el archivo de configuración.
Por ejemplo:

<entityFramework>
<defaultConnectionFactory type="[Link], EntityFramework" >
</entityFramework>

El tipo es el nombre de tipo calificado con el ensamblado para el generador de conexiones predeterminado, que
debe implementar IDbConnectionFactory.
Se recomienda que un paquete NuGet de proveedor establezca el generador de conexiones predeterminado de
este modo cuando se instale. Consulte los paquetes de NuGet para los proveedores siguientes.

Cambios adicionales del proveedor de EF6


Cambios del proveedor espacial
Los proveedores que admiten tipos espaciales ahora deben implementar algunos métodos adicionales en las
clases que derivan de DbSpatialDataReader:
public abstract bool IsGeographyColumn(int ordinal)
public abstract bool IsGeometryColumn(int ordinal)

También hay nuevas versiones asincrónicas de los métodos existentes que se recomienda invalidar como las
implementaciones predeterminadas que se delegan en los métodos sincrónicos y, por tanto, no se ejecutan de
forma asincrónica:
public virtual Task<DbGeography> GetGeographyAsync(int ordinal, CancellationToken cancellationToken)
public virtual Task<DbGeometry> GetGeometryAsync(int ordinal, CancellationToken cancellationToken)

Compatibilidad nativa con Enumerable. Contains


EF6 introduce un nuevo tipo de expresión, DbInExpression, que se ha agregado para solucionar problemas de
rendimiento relacionados con el uso de Enumerable. Contains en consultas LINQ. La clase DbProviderManifest
tiene un nuevo método virtual, SupportsInExpression, al que se llama EF para determinar si un proveedor
controla el nuevo tipo de expresión. Para ofrecer compatibilidad con las implementaciones de proveedor
existentes, el método devuelve false. Para beneficiarse de esta mejora, un proveedor de EF6 puede agregar
código para controlar DbInExpression e invalidar SupportsInExpression para que devuelva true. Se puede crear
una instancia de DbInExpression llamando al método [Link]. Una instancia de DbInExpression
se compone de un DbExpression, que normalmente representa una columna de tabla, y una lista de
DbConstantExpression para comprobar si hay coincidencias.

Paquetes NuGet para proveedores


Una manera de hacer que un proveedor de EF6 esté disponible es publicarlo como un paquete de NuGet. El uso
de un paquete NuGet presenta las siguientes ventajas:
Es fácil usar NuGet para agregar el registro del proveedor al archivo de configuración de la aplicación.
Se pueden realizar cambios adicionales en el archivo de configuración para establecer el generador de
conexiones predeterminado de modo que las conexiones realizadas por la Convención utilicen el proveedor
registrado
NuGet controla la adición de redirecciones de enlace para que el proveedor de EF6 siga funcionando incluso
después de que se publique un nuevo paquete EF.
Un ejemplo de esto es el paquete EntityFramework. Sqlservercom que se incluye en el código base de código
abierto. Este paquete proporciona una buena plantilla para crear paquetes NuGet del proveedor de EF.
Comandos de PowerShell
Cuando se instala el paquete NuGet EntityFramework, se registra un módulo de PowerShell que contiene dos
comandos que son muy útiles para los paquetes de proveedor:
Add-EFProvider agrega una nueva entidad para el proveedor en el archivo de configuración del proyecto de
destino y se asegura de que está al final de la lista de proveedores registrados.
Add-EFDefaultConnectionFactory agrega o actualiza el registro de defaultConnectionFactory en el archivo de
configuración del proyecto de destino.
Ambos comandos se encargan de agregar una sección entityFramework al archivo de configuración y agregar
una colección de proveedores si es necesario.
Está previsto que se llame a estos comandos desde el install.ps1 script de NuGet. Por ejemplo, install.ps1 para el
proveedor de SQL Compact tiene un aspecto similar al siguiente:

param($installPath, $toolsPath, $package, $project)


Add-EFDefaultConnectionFactory $project '[Link],
EntityFramework' -ConstructorArguments '[Link].4.0'
Add-EFProvider $project '[Link].4.0'
'[Link], [Link]'</pre>

Puede obtener más información acerca de estos comandos mediante Get-Help en la ventana de la consola del
administrador de paquetes.

Encapsular proveedores
Un proveedor de ajuste es un proveedor EF o [Link] que contiene un proveedor existente para extenderlo
con otras funciones, como la generación de perfiles o las capacidades de seguimiento. Los proveedores de
encapsulado se pueden registrar de la manera normal, pero a menudo es más conveniente configurar el
proveedor de ajuste en tiempo de ejecución mediante la interceptación de la resolución de servicios
relacionados con el proveedor. Para ello, se puede usar el evento estático OnLockingConfiguration en la clase
DbConfiguration.
Se llama a OnLockingConfiguration después de que EF haya determinado dónde se obtendrá la configuración
de EF para el dominio de aplicación, pero antes de que se bloquee para su uso. En el inicio de la aplicación (antes
de que se use EF), la aplicación debe registrar un controlador de eventos para este evento. (Se está considerando
la posibilidad de agregar compatibilidad para registrar este controlador en el archivo de configuración, pero aún
no se admite). A continuación, el controlador de eventos debe realizar una llamada a ReplaceService para cada
servicio que debe ajustarse.
Por ejemplo, para ajustar IDbConnectionFactory y DbProviderService, se debe registrar un controlador similar a
este:

[Link] +=
(_, a) =>
{
[Link]<DbProviderServices>(
(s, k) => new MyWrappedProviderServices(s));

[Link]<IDbConnectionFactory>(
(s, k) => new MyWrappedConnectionFactory(s));
};

El servicio que se ha resuelto y ahora debe ajustarse junto con la clave que se usó para resolver el servicio se
pasa al controlador. Después, el controlador puede encapsular este servicio y reemplazar el servicio devuelto
por la versión ajustada.

Resolver un DbProviderFactory con EF


DbProviderFactory es uno de los tipos fundamentales de proveedor que requiere EF como se describe en la
sección de información general de tipos de proveedor anterior. Como ya se mencionó, no es un tipo de EF y el
registro no suele ser parte de la configuración de EF, sino que es el registro de proveedor [Link] normal en el
archivo de configuración de la aplicación o el archivo de [Link].
A pesar de que este EF siga usando su mecanismo de resolución de dependencias normal al buscar un
DbProviderFactory para usarlo. El solucionador predeterminado usa el registro [Link] normal en los archivos
de configuración y, por lo tanto, esto suele ser transparente. Sin embargo, debido al mecanismo de resolución
de dependencias normal se usa, significa que se puede usar una IDbDependencyResolver para resolver un
DbProviderFactory incluso cuando no se ha realizado el registro de [Link] normal.
La resolución de DbProviderFactory de esta manera tiene varias implicaciones:
Una aplicación que usa la configuración basada en código puede agregar llamadas en su clase
DbConfiguration para registrar el DbProviderFactory adecuado. Esto es especialmente útil para las
aplicaciones que no quieren (o no pueden) hacer uso de cualquier configuración basada en archivos.
El servicio se puede ajustar o reemplazar mediante ReplaceService tal como se describe en la sección
anterior de los proveedores de ajuste
Teóricamente, una implementación de DbProviderServices podría resolver un DbProviderFactory.
El punto importante que hay que tener en cuenta es que solo afectará a la búsqueda de DbProviderFactory en
EF. Otro código que no sea EF podría esperar que el proveedor [Link] se registre de la forma normal y puede
producir un error si no se encuentra el registro. Por esta razón, suele ser mejor registrar un DbProviderFactory
de la manera normal de [Link].
Servicios relacionados
Si EF se usa para resolver un DbProviderFactory, debe resolver también los servicios IProviderInvariantName y
IDbProviderFactoryResolver.
IProviderInvariantName es un servicio que se usa para determinar un nombre invariable de proveedor para un
tipo determinado de DbProviderFactory. La implementación predeterminada de este servicio utiliza el registro
del proveedor [Link]. Esto significa que si el proveedor [Link] no está registrado de la manera normal
porque EF está resolviendo DbProviderFactory, también será necesario para resolver este servicio. Tenga en
cuenta que se agrega automáticamente un solucionador para este servicio cuando se usa el método
DbConfiguration. SetProviderFactory.
Tal y como se describe en la sección de información general sobre tipos de proveedores anterior,
IDbProviderFactoryResolver se usa para obtener el DbProviderFactory correcto de un objeto DbConnection
determinado. La implementación predeterminada de este servicio cuando se ejecuta en .NET 4 usa el registro
del proveedor [Link]. Esto significa que si el proveedor [Link] no está registrado de la manera normal
porque EF está resolviendo DbProviderFactory, también será necesario para resolver este servicio.
Compatibilidad del proveedor con tipos espaciales
12/03/2021 • 5 minutes to read

Entity Framework admite el trabajo con datos espaciales a través de las clases DbGeography o DbGeometry.
Estas clases se basan en la funcionalidad específica de la base de datos que ofrece el proveedor de Entity
Framework. No todos los proveedores admiten datos espaciales y los que pueden tener requisitos previos
adicionales, como la instalación de ensamblados de tipo espacial. A continuación se proporciona más
información sobre la compatibilidad del proveedor con los tipos espaciales.
Puede encontrar información adicional sobre cómo usar los tipos espaciales en una aplicación en dos tutoriales,
uno para Code First, el otro para Database First o Model First:
Tipos de datos espaciales en Code First
Tipos de datos espaciales en EF Designer

Versiones de EF que admiten tipos espaciales


La compatibilidad con los tipos espaciales se presentó en EF5. Sin embargo, en los tipos espaciales de EF5 solo
se admiten cuando la aplicación tiene como destino y se ejecuta en .NET 4,5.
A partir de EF6, se admiten los tipos espaciales para las aplicaciones que tienen como destino .NET 4 y .NET 4,5.

Proveedores de EF que admiten tipos espaciales


EF5
Los proveedores de Entity Framework para EF5 que somos conscientes de que admiten tipos espaciales son:
Proveedor de Microsoft SQL Server
Este proveedor se incluye como parte de EF5.
Este proveedor depende de algunas bibliotecas adicionales de bajo nivel que puedan ser necesarias
para instalarse; consulte a continuación para obtener más información.
Devart dotConnect para Oracle
Se trata de un proveedor de terceros de Devart.
Si conoce un proveedor de EF5 que admite tipos espaciales, póngase en contacto con y nos alegrará de
agregarlo a esta lista.
EF6
Los proveedores de Entity Framework para EF6 que somos conscientes de que admiten tipos espaciales son:
Proveedor de Microsoft SQL Server
Este proveedor se incluye como parte de EF6.
Este proveedor depende de algunas bibliotecas adicionales de bajo nivel que puedan ser necesarias
para instalarse; consulte a continuación para obtener más información.
Devart dotConnect para Oracle
Se trata de un proveedor de terceros de Devart.
Si conoce un proveedor de EF6 que admite tipos espaciales, póngase en contacto con y nos alegrará de
agregarlo a esta lista.
Requisitos previos para los tipos espaciales con Microsoft SQL Server
SQL Server compatibilidad espacial depende de los tipos de bajo nivel, específicos de SQL Server SqlGeography
y SqlGeometry. Estos tipos viven en [Link] ensamblado, y este ensamblado no se envía
como parte de EF o como parte de la .NET Framework.
Cuando se instala Visual Studio, a menudo también se instala una versión de SQL Server, lo que incluye la
instalación de la [Link].
Si SQL Server no está instalado en el equipo en el que desea usar tipos espaciales, o si los tipos espaciales se
excluyen de la instalación de SQL Server, deberá instalarlos manualmente. Los tipos se pueden instalar mediante
[Link] , que forma parte de Microsoft SQL Server Feature Pack. Los tipos espaciales son SQL
Server específicos de la versión, por lo que se recomienda Buscar "SQL Server Feature Pack" en el centro de
descarga de Microsoft y, a continuación, seleccionar y descargar la opción correspondiente a la versión de SQL
Server que se va a usar.
Trabajar con servidores proxy
12/03/2021 • 4 minutes to read

Al crear instancias de tipos de entidad POCO, Entity Framework a menudo crea instancias de un tipo derivado
generado dinámicamente que actúa como un proxy para la entidad. Este proxy invalida algunas propiedades
virtuales de la entidad para insertar enlaces para realizar acciones automáticamente cuando se tiene acceso a la
propiedad. Por ejemplo, este mecanismo se usa para admitir la carga diferida de relaciones. Las técnicas que se
muestran en este tema se aplican igualmente a los modelos creados con Code First y EF Designer.

Deshabilitar la creación de proxy


A veces resulta útil evitar que Entity Framework Cree instancias de proxy. Por ejemplo, la serialización de
instancias que no son de proxy es considerablemente más fácil que serializar instancias de proxy. La creación del
proxy se puede desactivar borrando la ProxyCreationEnabled marca. Un lugar donde podría hacer esto es en el
constructor del contexto. Por ejemplo:

public class BloggingContext : DbContext


{
public BloggingContext()
{
[Link] = false;
}

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


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

Tenga en cuenta que EF no creará servidores proxy para los tipos en los que no hay nada que pueda realizar el
proxy. Esto significa que también puede evitar servidores proxy si tiene tipos que están sellados o no tienen
ninguna propiedad virtual.

Crear explícitamente una instancia de un proxy


No se creará una instancia de proxy si crea una instancia de una entidad mediante el operador new. Esto puede
no ser un problema, pero si necesita crear una instancia de proxy (por ejemplo, para que la carga diferida o el
seguimiento de cambios de proxy funcionen), puede hacerlo mediante el Create método de DbSet . Por
ejemplo:

using (var context = new BloggingContext())


{
var blog = [Link]();
}

Se puede usar la versión genérica de Create si desea crear una instancia de un tipo de entidad derivada. Por
ejemplo:

using (var context = new BloggingContext())


{
var admin = [Link]<Administrator>();
}
Tenga en cuenta que el Create método no agrega ni adjunta la entidad creada al contexto.
Tenga en cuenta que el Create método solo creará una instancia del tipo de entidad si la creación de un tipo de
proxy para la entidad no tiene ningún valor, ya que no haría nada. Por ejemplo, si el tipo de entidad está sellado
y/o no tiene ninguna propiedad virtual, Create solo creará una instancia del tipo de entidad.

Obtener el tipo de entidad real a partir de un tipo de proxy


Los tipos de proxy tienen nombres que tienen un aspecto similar al siguiente:

[Link].Blog_5E43C6C196972BF0754973E48C9C941092D86818CD94005E9A759B70BF6E48E6

Puede encontrar el tipo de entidad para este tipo de proxy mediante el GetObjectType método de
ObjectContext . Por ejemplo:

using (var context = new BloggingContext())


{
var blog = [Link](1);
var entityType = [Link]([Link]());
}

Tenga en cuenta que si el tipo que se pasa a GetObjectType es una instancia de un tipo de entidad que no es un
tipo de proxy, el tipo de entidad se sigue devolviendo. Esto significa que siempre puede usar este método para
obtener el tipo de entidad real sin ninguna otra comprobación para ver si el tipo es un tipo de proxy.
Probar con un marco ficticio
12/03/2021 • 14 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

Al escribir pruebas para la aplicación, a menudo es conveniente evitar la llegada de la base de datos. Entity
Framework le permite conseguirlo mediante la creación de un contexto, con el comportamiento definido por las
pruebas, que hace uso de los datos en memoria.

Opciones para crear dobles de pruebas


Existen dos enfoques diferentes que se pueden usar para crear una versión en memoria del contexto.
Crear sus propios dobles de pruebas : este enfoque implica escribir su propia implementación en
memoria de su contexto y DbSets. Esto le ofrece un gran control sobre cómo se comportan las clases, pero
puede implicar la escritura y la propiedad de una cantidad de código razonable.
Usar un marco ficticio para crear dobles de pruebas : mediante un marco ficticio (como MOQ), puede
tener las implementaciones en memoria del contexto y los conjuntos creados dinámicamente en tiempo de
ejecución.
En este artículo se tratará el uso de un marco ficticio. Para crear sus propios dobles de pruebas, consulte
pruebas con los dobles de pruebas.
Para demostrar el uso de EF con un marco ficticio, vamos a usar MOQ. La forma más fácil de obtener MOQ es
instalar el paquete MOQ desde NuGet.

Pruebas con versiones anteriores a EF6


El escenario que se muestra en este artículo depende de algunos cambios realizados en DbSet en EF6. Para
realizar pruebas con EF5 y versiones anteriores, consulte pruebas con un contexto falso.

Limitaciones de los dobles de pruebas en memoria de EF


Los dobles de pruebas en memoria pueden ser una buena manera de proporcionar una cobertura de nivel de
prueba unitaria de bits de la aplicación que usa EF. Sin embargo, al hacerlo, se usa LINQ to Objects para ejecutar
consultas en los datos en memoria. Esto puede dar lugar a un comportamiento diferente al uso del proveedor
LINQ (LINQ to Entities) de EF para traducir las consultas en SQL que se ejecutan en la base de datos.
Un ejemplo de este tipo de diferencia es la carga de datos relacionados. Si crea una serie de blogs en los que
cada uno tiene elementos relacionados, al usar los datos en memoria, los envíos relacionados se cargarán
siempre para cada blog. Sin embargo, cuando se ejecuta en una base de datos, los datos solo se cargarán si se
usa el método include.
Por esta razón, se recomienda incluir siempre cierto nivel de pruebas de un extremo a otro (además de las
pruebas unitarias) para asegurarse de que la aplicación funciona correctamente en una base de datos.

Junto con este artículo


En este artículo se proporcionan listas de código completas que se pueden copiar en Visual Studio para que se
realicen a continuación, si así se desea. Es más fácil crear un proyecto de prueba unitaria y tendrá que tener
como destino .NET Framework 4,5 para completar las secciones que usan Async.

El modelo EF
El servicio que vamos a probar hace uso de un modelo EF compuesto por las clases BloggingContext y blog y
post. Este código puede haber sido generado por EF Designer o ser un modelo de Code First.

using [Link];
using [Link];

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

public class Blog


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

public virtual 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 virtual Blog Blog { get; set; }
}
}

Propiedades de DbSet virtuales con EF Designer


Tenga en cuenta que las propiedades de DbSet en el contexto se marcan como virtuales. Esto permitirá que el
marco ficticio se derive de nuestro contexto e invalide estas propiedades con una implementación ficticia.
Si usa Code First, puede editar las clases directamente. Si usa el diseñador de EF, tendrá que editar la plantilla T4
que genera el contexto. Abra el <model_name> . Archivo [Link] que está anidado en el archivo edmx,
busque el fragmento de código siguiente y agregue la palabra clave virtual como se muestra.

public string DbSet(EntitySet entitySet)


{
return [Link](
[Link],
"{0} virtual DbSet\<{1}> {2} {{ get; set; }}",
[Link](entitySet),
_typeMapper.GetTypeName([Link]),
_code.Escape(entitySet));
}

Servicio que se va a probar


Para demostrar las pruebas con los dobles de pruebas en memoria, vamos a escribir un par de pruebas para un
BlogService. El servicio es capaz de crear nuevos blogs (AddBlog) y devolver todos los blogs ordenados por
nombre (GetAllBlogs). Además de GetAllBlogs, también se proporciona un método que obtendrá de forma
asincrónica todos los blogs ordenados por nombre (GetAllBlogsAsync).

using [Link];
using [Link];
using [Link];
using [Link];

namespace TestingDemo
{
public class BlogService
{
private BloggingContext _context;

public BlogService(BloggingContext context)


{
_context = context;
}

public Blog AddBlog(string name, string url)


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

return blog;
}

public List<Blog> GetAllBlogs()


{
var query = from b in _context.Blogs
orderby [Link]
select b;

return [Link]();
}

public async Task<List<Blog>> GetAllBlogsAsync()


{
var query = from b in _context.Blogs
orderby [Link]
select b;

return await [Link]();


}
}
}

Probar escenarios que no son de consulta


Eso es todo lo que necesitamos hacer para empezar a probar los métodos que no son de consulta. La prueba
siguiente usa MOQ para crear un contexto. Después crea un DbSet <Blog> y lo conecta para que se devuelva
desde la propiedad blogs del contexto. Después, el contexto se usa para crear un nuevo BlogService que se usa
para crear un nuevo blog: mediante el método AddBlog. Por último, la prueba comprueba que el servicio agregó
un nuevo blog y se llama SaveChanges en el contexto.
using [Link];
using Moq;
using [Link];

namespace TestingDemo
{
[TestClass]
public class NonQueryTests
{
[TestMethod]
public void CreateBlog_saves_a_blog_via_context()
{
var mockSet = new Mock<DbSet<Blog>>();

var mockContext = new Mock<BloggingContext>();


[Link](m => [Link]).Returns([Link]);

var service = new BlogService([Link]);


[Link]("[Link] Blog", "[Link]

[Link](m => [Link]([Link]<Blog>()), [Link]());


[Link](m => [Link](), [Link]());
}
}
}

Probar escenarios de consulta


Para poder ejecutar consultas en nuestra prueba de DbSet doble, es necesario configurar una implementación
de IQueryable. El primer paso es crear algunos datos en memoria: usamos una lista <Blog> . A continuación,
creamos un contexto y DBSet <Blog> , a continuación, conectamos la implementación de IQueryable para
DBSet: simplemente están delegando en el LINQ to Objects proveedor que funciona con la lista <T> .
A continuación, podemos crear un BlogService basado en los dobles de pruebas y asegurarse de que los datos
que se obtienen de GetAllBlogs se ordenan por nombre.
using [Link];
using Moq;
using [Link];
using [Link];
using [Link];

namespace TestingDemo
{
[TestClass]
public class QueryTests
{
[TestMethod]
public void GetAllBlogs_orders_by_name()
{
var data = new List<Blog>
{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();

var mockSet = new Mock<DbSet<Blog>>();


[Link]<IQueryable<Blog>>().Setup(m => [Link]).Returns([Link]);
[Link]<IQueryable<Blog>>().Setup(m => [Link]).Returns([Link]);
[Link]<IQueryable<Blog>>().Setup(m => [Link]).Returns([Link]);
[Link]<IQueryable<Blog>>().Setup(m => [Link]()).Returns([Link]());

var mockContext = new Mock<BloggingContext>();


[Link](c => [Link]).Returns([Link]);

var service = new BlogService([Link]);


var blogs = [Link]();

[Link](3, [Link]);
[Link]("AAA", blogs[0].Name);
[Link]("BBB", blogs[1].Name);
[Link]("ZZZ", blogs[2].Name);
}
}
}

Pruebas con consultas asincrónicas


Entity Framework 6 presentó un conjunto de métodos de extensión que se pueden usar para ejecutar una
consulta de forma asincrónica. Entre los ejemplos de estos métodos se incluyen ToListAsync, FirstAsync,
ForEachAsync, etc.
Dado que Entity Framework consultas usan LINQ, los métodos de extensión se definen en IQueryable y
IEnumerable. Sin embargo, dado que solo están diseñados para usarse con Entity Framework puede recibir el
siguiente error si intenta utilizarlos en una consulta LINQ que no es una consulta de Entity Framework:

IQueryable de origen no implementa IDbAsyncEnumerable {0} . Solo los orígenes que implementan
IDbAsyncEnumerable se pueden usar para las operaciones asincrónicas de Entity Framework. Para obtener
más información, vea [Link] .

Mientras que los métodos asincrónicos solo se admiten cuando se ejecuta en una consulta EF, puede que desee
usarlos en la prueba unitaria cuando se ejecuta en una prueba en memoria Double de un DbSet.
Para utilizar los métodos asincrónicos, es necesario crear un DbAsyncQueryProvider en memoria para procesar
la consulta asincrónica. Aunque sería posible configurar un proveedor de consultas mediante MOQ, es mucho
más fácil crear una implementación de prueba Double en el código. El código para esta implementación es el
siguiente:
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace TestingDemo
{
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;

internal TestDbAsyncQueryProvider(IQueryProvider inner)


{
_inner = inner;
}

public IQueryable CreateQuery(Expression expression)


{
return new TestDbAsyncEnumerable<TEntity>(expression);
}

public IQueryable<TElement> CreateQuery<TElement>(Expression expression)


{
return new TestDbAsyncEnumerable<TElement>(expression);
}

public object Execute(Expression expression)


{
return _inner.Execute(expression);
}

public TResult Execute<TResult>(Expression expression)


{
return _inner.Execute<TResult>(expression);
}

public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)


{
return [Link](Execute(expression));
}

public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken


cancellationToken)
{
return [Link](Execute<TResult>(expression));
}
}

internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>


{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }

public TestDbAsyncEnumerable(Expression expression)


: base(expression)
{ }

public IDbAsyncEnumerator<T> GetAsyncEnumerator()


{
return new TestDbAsyncEnumerator<T>([Link]().GetEnumerator());
}

IDbAsyncEnumerator [Link]()
{
return GetAsyncEnumerator();
return GetAsyncEnumerator();
}

IQueryProvider [Link]
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}

internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>


{
private readonly IEnumerator<T> _inner;

public TestDbAsyncEnumerator(IEnumerator<T> inner)


{
_inner = inner;
}

public void Dispose()


{
_inner.Dispose();
}

public Task<bool> MoveNextAsync(CancellationToken cancellationToken)


{
return [Link](_inner.MoveNext());
}

public T Current
{
get { return _inner.Current; }
}

object [Link]
{
get { return Current; }
}
}
}

Ahora que tenemos un proveedor de consultas asincrónicas, podemos escribir una prueba unitaria para el
nuevo método GetAllBlogsAsync.
using [Link];
using Moq;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace TestingDemo
{
[TestClass]
public class AsyncQueryTests
{
[TestMethod]
public async Task GetAllBlogsAsync_orders_by_name()
{

var data = new List<Blog>


{
new Blog { Name = "BBB" },
new Blog { Name = "ZZZ" },
new Blog { Name = "AAA" },
}.AsQueryable();

var mockSet = new Mock<DbSet<Blog>>();


[Link]<IDbAsyncEnumerable<Blog>>()
.Setup(m => [Link]())
.Returns(new TestDbAsyncEnumerator<Blog>([Link]()));

[Link]<IQueryable<Blog>>()
.Setup(m => [Link])
.Returns(new TestDbAsyncQueryProvider<Blog>([Link]));

[Link]<IQueryable<Blog>>().Setup(m => [Link]).Returns([Link]);


[Link]<IQueryable<Blog>>().Setup(m => [Link]).Returns([Link]);
[Link]<IQueryable<Blog>>().Setup(m => [Link]()).Returns([Link]());

var mockContext = new Mock<BloggingContext>();


[Link](c => [Link]).Returns([Link]);

var service = new BlogService([Link]);


var blogs = await [Link]();

[Link](3, [Link]);
[Link]("AAA", blogs[0].Name);
[Link]("BBB", blogs[1].Name);
[Link]("ZZZ", blogs[2].Name);
}
}
}
Pruebas con sus propias dobles de pruebas
12/03/2021 • 15 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

Al escribir pruebas para la aplicación, a menudo es conveniente evitar la llegada de la base de datos. Entity
Framework le permite conseguirlo mediante la creación de un contexto, con el comportamiento definido por las
pruebas, que hace uso de los datos en memoria.

Opciones para crear dobles de pruebas


Existen dos enfoques diferentes que se pueden usar para crear una versión en memoria del contexto.
Crear sus propios dobles de pruebas : este enfoque implica escribir su propia implementación en
memoria de su contexto y DbSets. Esto le ofrece un gran control sobre cómo se comportan las clases, pero
puede implicar la escritura y la propiedad de una cantidad de código razonable.
Usar un marco ficticio para crear dobles de pruebas : mediante un marco ficticio (como MOQ), puede
tener las implementaciones en memoria de contexto y conjuntos creados dinámicamente en tiempo de
ejecución.
En este artículo se tratará la creación de su propio Double de prueba. Para obtener información sobre el uso de
un marco ficticio, vea probar con un marco ficticio.

Pruebas con versiones anteriores a EF6


El código que se muestra en este artículo es compatible con EF6. Para realizar pruebas con EF5 y versiones
anteriores, consulte pruebas con un contexto falso.

Limitaciones de los dobles de pruebas en memoria de EF


Los dobles de pruebas en memoria pueden ser una buena manera de proporcionar una cobertura de nivel de
prueba unitaria de bits de la aplicación que usa EF. Sin embargo, al hacerlo, se usa LINQ to Objects para ejecutar
consultas en los datos en memoria. Esto puede dar lugar a un comportamiento diferente al uso del proveedor
LINQ (LINQ to Entities) de EF para traducir las consultas en SQL que se ejecutan en la base de datos.
Un ejemplo de este tipo de diferencia es la carga de datos relacionados. Si crea una serie de blogs en los que
cada uno tiene elementos relacionados, al usar los datos en memoria, los envíos relacionados se cargarán
siempre para cada blog. Sin embargo, cuando se ejecuta en una base de datos, los datos solo se cargarán si se
usa el método include.
Por esta razón, se recomienda incluir siempre cierto nivel de pruebas de un extremo a otro (además de las
pruebas unitarias) para asegurarse de que la aplicación funciona correctamente en una base de datos.

Junto con este artículo


En este artículo se proporcionan listas de código completas que se pueden copiar en Visual Studio para que se
realicen a continuación, si así se desea. Es más fácil crear un proyecto de prueba unitaria y tendrá que tener
como destino .NET Framework 4,5 para completar las secciones que usan Async.

Crear una interfaz de contexto


Vamos a echar un vistazo a la prueba de un servicio que hace uso de un modelo EF. Para poder reemplazar el
contexto de EF con una versión en memoria para las pruebas, vamos a definir una interfaz que implementará el
contexto de EF (y el valor Double en memoria).
El servicio que se va a probar consultará y modificará los datos mediante las propiedades DbSet de nuestro
contexto y también llamará a SaveChanges para enviar los cambios a la base de datos. Por tanto, vamos a incluir
estos miembros en la interfaz.

using [Link];

namespace TestingDemo
{
public interface IBloggingContext
{
DbSet<Blog> Blogs { get; }
DbSet<Post> Posts { get; }
int SaveChanges();
}
}

El modelo EF
El servicio que vamos a probar hace uso de un modelo EF compuesto por las clases BloggingContext y blog y
post. Este código puede haber sido generado por EF Designer o ser un modelo de Code First.

using [Link];
using [Link];

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

public class Blog


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

public virtual 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 virtual Blog Blog { get; set; }
}
}

Implementar la interfaz de contexto con EF Designer


Tenga en cuenta que nuestro contexto implementa la interfaz IBloggingContext.
Si usa Code First, puede modificar el contexto directamente para implementar la interfaz. Si usa el diseñador de
EF, tendrá que editar la plantilla T4 que genera el contexto. Abra el <model_name> . Archivo [Link] que está
anidado en el archivo edmx, busque el fragmento de código siguiente y agréguelo en la interfaz como se
muestra.

<#=[Link](container)#> partial class <#=[Link](container)#> : DbContext,


IBloggingContext

Servicio que se va a probar


Para demostrar las pruebas con los dobles de pruebas en memoria, vamos a escribir un par de pruebas para un
BlogService. El servicio es capaz de crear nuevos blogs (AddBlog) y devolver todos los blogs ordenados por
nombre (GetAllBlogs). Además de GetAllBlogs, también se proporciona un método que obtendrá de forma
asincrónica todos los blogs ordenados por nombre (GetAllBlogsAsync).

using [Link];
using [Link];
using [Link];
using [Link];

namespace TestingDemo
{
public class BlogService
{
private IBloggingContext _context;

public BlogService(IBloggingContext context)


{
_context = context;
}

public Blog AddBlog(string name, string url)


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

return blog;
}

public List<Blog> GetAllBlogs()


{
var query = from b in _context.Blogs
orderby [Link]
select b;

return [Link]();
}

public async Task<List<Blog>> GetAllBlogsAsync()


{
var query = from b in _context.Blogs
orderby [Link]
select b;

return await [Link]();


}
}
}
Crear dobles de pruebas en memoria
Ahora que tenemos el modelo de EF real y el servicio que puede usarlo, es el momento de crear el doble de
prueba en memoria que se puede usar para las pruebas. Hemos creado un Double de prueba de TestContext
para nuestro contexto. En la prueba, se puede elegir el comportamiento que se desea para admitir las pruebas
que se van a ejecutar. En este ejemplo, vamos a capturar el número de veces que se llama a SaveChanges, pero
puede incluir la lógica que se necesita para comprobar el escenario que se está probando.
También hemos creado un TestDbSet que proporciona una implementación en memoria de DbSet. Hemos
proporcionado una implementación completa de todos los métodos de DbSet (excepto buscar), pero solo tiene
que implementar los miembros que usará el escenario de prueba.
TestDbSet usa algunas otras clases de infraestructura que hemos incluido para asegurarse de que se puedan
procesar las consultas asincrónicas.

using System;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace TestingDemo
{
public class TestContext : IBloggingContext
{
public TestContext()
{
[Link] = new TestDbSet<Blog>();
[Link] = new TestDbSet<Post>();
}

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


public DbSet<Post> Posts { get; set; }
public int SaveChangesCount { get; private set; }
public int SaveChanges()
{
[Link]++;
return 1;
}
}

public class TestDbSet<TEntity> : DbSet<TEntity>, IQueryable, IEnumerable<TEntity>,


IDbAsyncEnumerable<TEntity>
where TEntity : class
{
ObservableCollection<TEntity> _data;
IQueryable _query;

public TestDbSet()
{
_data = new ObservableCollection<TEntity>();
_query = _data.AsQueryable();
}

public override TEntity Add(TEntity item)


{
_data.Add(item);
return item;
}

public override TEntity Remove(TEntity item)


{
_data.Remove(item);
return item;
}

public override TEntity Attach(TEntity item)


{
_data.Add(item);
return item;
}

public override TEntity Create()


{
return [Link]<TEntity>();
}

public override TDerivedEntity Create<TDerivedEntity>()


{
return [Link]<TDerivedEntity>();
}

public override ObservableCollection<TEntity> Local


{
get { return _data; }
}

Type [Link]
{
get { return _query.ElementType; }
}

Expression [Link]
{
get { return _query.Expression; }
}

IQueryProvider [Link]
{
get { return new TestDbAsyncQueryProvider<TEntity>(_query.Provider); }
}

[Link] [Link]()
{
return _data.GetEnumerator();
}

IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
{
return _data.GetEnumerator();
}

IDbAsyncEnumerator<TEntity> IDbAsyncEnumerable<TEntity>.GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<TEntity>(_data.GetEnumerator());
}
}

internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider


{
private readonly IQueryProvider _inner;

internal TestDbAsyncQueryProvider(IQueryProvider inner)


{
_inner = inner;
}

public IQueryable CreateQuery(Expression expression)


{
return new TestDbAsyncEnumerable<TEntity>(expression);
return new TestDbAsyncEnumerable<TEntity>(expression);
}

public IQueryable<TElement> CreateQuery<TElement>(Expression expression)


{
return new TestDbAsyncEnumerable<TElement>(expression);
}

public object Execute(Expression expression)


{
return _inner.Execute(expression);
}

public TResult Execute<TResult>(Expression expression)


{
return _inner.Execute<TResult>(expression);
}

public Task<object> ExecuteAsync(Expression expression, CancellationToken cancellationToken)


{
return [Link](Execute(expression));
}

public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken


cancellationToken)
{
return [Link](Execute<TResult>(expression));
}
}

internal class TestDbAsyncEnumerable<T> : EnumerableQuery<T>, IDbAsyncEnumerable<T>, IQueryable<T>


{
public TestDbAsyncEnumerable(IEnumerable<T> enumerable)
: base(enumerable)
{ }

public TestDbAsyncEnumerable(Expression expression)


: base(expression)
{ }

public IDbAsyncEnumerator<T> GetAsyncEnumerator()


{
return new TestDbAsyncEnumerator<T>([Link]().GetEnumerator());
}

IDbAsyncEnumerator [Link]()
{
return GetAsyncEnumerator();
}

IQueryProvider [Link]
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}

internal class TestDbAsyncEnumerator<T> : IDbAsyncEnumerator<T>


{
private readonly IEnumerator<T> _inner;

public TestDbAsyncEnumerator(IEnumerator<T> inner)


{
_inner = inner;
}

public void Dispose()


{
_inner.Dispose();
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
return [Link](_inner.MoveNext());
}

public T Current
{
get { return _inner.Current; }
}

object [Link]
{
get { return Current; }
}
}
}

Implementación de Find
El método Find es difícil de implementar de forma genérica. Si necesita probar el código que hace uso del
método Find, es más fácil crear un DbSet de prueba para cada uno de los tipos de entidad que deben admitir la
búsqueda. Después, puede escribir lógica para buscar ese tipo de entidad concreto, como se muestra a
continuación.

using [Link];

namespace TestingDemo
{
class TestBlogDbSet : TestDbSet<Blog>
{
public override Blog Find(params object[] keyValues)
{
var id = (int)[Link]();
return [Link](b => [Link] == id);
}
}
}

Escribir algunas pruebas


Eso es todo lo que necesitamos hacer para iniciar las pruebas. La prueba siguiente crea una TestContext y luego
un servicio basado en este contexto. Después, el servicio se usa para crear un nuevo blog: mediante el método
AddBlog. Por último, la prueba comprueba que el servicio agregó un nuevo blog a la propiedad blogs del
contexto y que se llama SaveChanges en el contexto.
Este es solo un ejemplo de los tipos de cosas que puede probar con una prueba en memoria Double y puede
ajustar la lógica de los dobles de pruebas y la comprobación para satisfacer sus requisitos.
using [Link];
using [Link];

namespace TestingDemo
{
[TestClass]
public class NonQueryTests
{
[TestMethod]
public void CreateBlog_saves_a_blog_via_context()
{
var context = new TestContext();

var service = new BlogService(context);


[Link]("[Link] Blog", "[Link]

[Link](1, [Link]());
[Link]("[Link] Blog", [Link]().Name);
[Link]("[Link] [Link]().Url);
[Link](1, [Link]);
}
}
}

Este es otro ejemplo de una prueba; esta vez, una que realiza una consulta. La prueba se inicia creando un
contexto de prueba con algunos datos en su propiedad de blog. tenga en cuenta que los datos no están en
orden alfabético. A continuación, podemos crear un BlogService basado en nuestro contexto de prueba y
asegurarse de que los datos que obtenemos de GetAllBlogs estén ordenados por nombre.

using [Link];

namespace TestingDemo
{
[TestClass]
public class QueryTests
{
[TestMethod]
public void GetAllBlogs_orders_by_name()
{
var context = new TestContext();
[Link](new Blog { Name = "BBB" });
[Link](new Blog { Name = "ZZZ" });
[Link](new Blog { Name = "AAA" });

var service = new BlogService(context);


var blogs = [Link]();

[Link](3, [Link]);
[Link]("AAA", blogs[0].Name);
[Link]("BBB", blogs[1].Name);
[Link]("ZZZ", blogs[2].Name);
}
}
}

Por último, vamos a escribir una prueba más que use nuestro método asincrónico para asegurarse de que la
infraestructura asincrónica incluida en TestDbSet funciona.
using [Link];
using [Link];
using [Link];
using [Link];

namespace TestingDemo
{
[TestClass]
public class AsyncQueryTests
{
[TestMethod]
public async Task GetAllBlogsAsync_orders_by_name()
{
var context = new TestContext();
[Link](new Blog { Name = "BBB" });
[Link](new Blog { Name = "ZZZ" });
[Link](new Blog { Name = "AAA" });

var service = new BlogService(context);


var blogs = await [Link]();

[Link](3, [Link]);
[Link]("AAA", blogs[0].Name);
[Link]("BBB", blogs[1].Name);
[Link]("ZZZ", blogs[2].Name);
}
}
}
Capacidad de prueba y Entity Framework 4,0
12/03/2021 • 87 minutes to read

Scott Allen
Publicación: mayo de 2010

Introducción
En estas notas del producto se describe y se muestra cómo escribir código que se pueda probar con [Link]
Entity Framework 4,0 y Visual Studio 2010. Este documento no intenta centrarse en una metodología de prueba
específica, como el diseño basado en pruebas (TDD) o el diseño controlado por comportamientos (BDD). En su
lugar, este documento se centrará en cómo escribir código que use el Entity Framework de [Link], pero sigue
siendo fácil aislar y probar de forma automatizada. Veremos patrones de diseño comunes que facilitan las
pruebas en escenarios de acceso a datos y ven cómo aplicar esos patrones al usar el marco de trabajo. También
veremos características específicas del marco de trabajo para ver cómo estas características pueden funcionar
en código comprobable.

¿Qué es el código que se pueda probar?


La capacidad de comprobar una parte del software mediante pruebas unitarias automatizadas ofrece muchas
ventajas deseadas. Todo el mundo sabe que las buenas pruebas reducirán el número de defectos de software de
una aplicación y aumentarán la calidad de la aplicación, pero si tiene pruebas unitarias en su lugar, más allá de
buscar errores.
Un buen conjunto de pruebas unitarias permite que un equipo de desarrollo Ahorre tiempo y mantenga el
control del software que crean. Un equipo puede realizar cambios en el código existente, refactorizar, rediseñar y
reestructurar el software para cumplir los requisitos nuevos y agregar nuevos componentes a una aplicación, al
tiempo que sabe que el conjunto de pruebas puede comprobar el comportamiento de la aplicación. Las pruebas
unitarias forman parte de un ciclo de comentarios rápido para facilitar el cambio y preservar el mantenimiento
del software a medida que aumenta la complejidad.
Sin embargo, las pruebas unitarias incluyen un precio. Un equipo tiene que invertir el tiempo de creación y
mantenimiento de las pruebas unitarias. La cantidad de esfuerzo necesaria para crear estas pruebas está
directamente relacionada con la capacidad de prueba del software subyacente. ¿Es fácil probar el software? Un
equipo que diseñe software con capacidad de prueba en mente creará pruebas eficaces más rápido que el
equipo que trabaja con un software no comprobable.
Microsoft diseñó el [Link] Entity Framework 4,0 (EF4), teniendo en cuenta la capacidad de prueba. Esto no
significa que los desarrolladores vayan a escribir pruebas unitarias en el propio código del marco. En su lugar,
los objetivos de capacidad de prueba de EF4 facilitan la creación de código comprobable que se basa en el
marco de trabajo. Antes de ver ejemplos específicos, merece la pena comprender las cualidades del código
comprobable.
Las cualidades del código comprobable
El código que es fácil de probar siempre presentará al menos dos rasgos. En primer lugar, el código
comprobable se obser va fácilmente. Dado un conjunto de entradas, debe ser fácil observar la salida del código.
Por ejemplo, es fácil probar el método siguiente porque el método devuelve directamente el resultado de un
cálculo.
public int Add(int x, int y) {
return x + y;
}

Probar un método es difícil si el método escribe el valor calculado en un socket de red, una tabla de base de
datos o un archivo como el código siguiente. La prueba tiene que realizar trabajo adicional para recuperar el
valor.

public void AddAndSaveToFile(int x, int y) {


var results = [Link]("The answer is {0}", x + y);
[Link]("[Link]", results);
}

En segundo lugar, el código comprobable es fácil de aislar . Vamos a usar el siguiente pseudocódigo como
ejemplo incorrecto de código comprobable.

public int ComputePolicyValue(InsurancePolicy policy) {


using (var connection = new SqlConnection("dbConnection"))
using (var command = new SqlCommand(query, connection)) {

// business calculations omitted ...

if (totalValue > notificationThreshold) {


var message = new MailMessage();
[Link] = "Warning!";
var client = new SmtpClient();
[Link](message);
}
}
return totalValue;
}

El método es fácil de observar: podemos pasar una directiva de seguros y comprobar que el valor devuelto
coincide con un resultado esperado. Sin embargo, para probar el método, es necesario tener una base de datos
de instalada con el esquema correcto y configurar el servidor SMTP en caso de que el método intente enviar un
correo electrónico.
La prueba unitaria solo desea comprobar la lógica de cálculo dentro del método, pero puede producirse un
error en la prueba porque el servidor de correo electrónico está sin conexión o porque se ha desconectado el
servidor de base de datos. Ambos errores no están relacionados con el comportamiento que la prueba desea
comprobar. Es difícil aislar el comportamiento.
Los desarrolladores de software que se esfuerzan por escribir código comprobable a menudo esfuerzan por
mantener una separación de preocupaciones en el código que escriben. El método anterior debe centrarse en
los cálculos empresariales y delegar la base de datos y los detalles de implementación de correo electrónico a
otros componentes. Robert C. Martin llama a este principio de responsabilidad única. Un objeto debe encapsular
una única responsabilidad estrecha, como calcular el valor de una directiva. El resto de la base de datos y el
trabajo de notificación deben ser responsabilidad de algún otro objeto. El código escrito de este modo es más
fácil de aislar porque se centra en una sola tarea.
En .NET tenemos las abstracciones que necesitamos seguir el principio de responsabilidad única y conseguir el
aislamiento. Podemos usar las definiciones de interfaz y forzar que el código use la abstracción de la interfaz en
lugar de un tipo concreto. Más adelante en este documento veremos cómo un método como el ejemplo
incorrecto presentado anteriormente puede funcionar con interfaces que parecen que se comunicarán con la
base de datos. Sin embargo, en el momento de la prueba, podemos sustituir una implementación ficticia que no
se comunica con la base de datos, sino que almacena los datos en la memoria. Esta implementación ficticia
aislará el código de problemas no relacionados en el código de acceso a datos o la configuración de la base de
datos.
Existen ventajas adicionales para el aislamiento. El cálculo empresarial en el último método solo debe tardar
unos milisegundos en ejecutarse, pero la propia prueba podría ejecutarse durante varios segundos a medida
que el código se salto alrededor de la red y se comunique con varios servidores. Las pruebas unitarias se deben
ejecutar rápidamente para facilitar pequeños cambios. Las pruebas unitarias también se deben repetir y no
generar un error porque un componente no relacionado con la prueba tiene un problema. Escribir código que
sea fácil de observar y aislar significa que los desarrolladores tendrán un tiempo más sencillo escribiendo las
pruebas para el código, dedique menos tiempo a esperar a que se ejecuten las pruebas y, lo que es más
importante, dedique menos tiempo a realizar un seguimiento de los errores que no existen.
Espero que pueda apreciar las ventajas de las pruebas y comprender las cualidades que exhibe el código.
Estamos a punto de tratar cómo escribir código que funcione con EF4 para guardar datos en una base de datos,
mientras que el resto es visible y fácil aislar, pero en primer lugar limitaremos nuestro enfoque para analizar los
diseños que se pueden probar para el acceso a los datos.

Modelos de diseño para la persistencia de datos


Los dos ejemplos no válidos que se presentaron anteriormente tenían demasiadas responsabilidades. El primer
ejemplo incorrecto tenía que realizar un cálculo y escribir en un archivo. El segundo ejemplo incorrecto tenía
que leer los datos de una base de datos y realizar un cálculo empresarial y enviar un correo electrónico. Al
diseñar métodos más pequeños que separan los problemas y delegar la responsabilidad en otros componentes,
hará grandes progresos en la escritura de código comprobable. El objetivo es crear la funcionalidad mediante la
composición de acciones a partir de abstracciones pequeñas y centradas.
En cuanto a la persistencia de los datos, las abstracciones pequeñas y centradas que se buscan son tan comunes
que se han documentado como modelos de diseño. Los patrones de libros de Martin Fowler de la arquitectura
de aplicaciones empresariales fueron el primer trabajo para describir estos patrones en la impresión.
Proporcionaremos una breve descripción de estos patrones en las secciones siguientes antes de mostrar cómo
estos [Link] Entity Framework implementan y funcionan con estos patrones.
The Repository Pattern (El modelo de repositorio )
Fowler indica un repositorio "media entre las capas de asignación de datos y dominio mediante una interfaz
similar a la colección para tener acceso a los objetos de dominio". El objetivo del patrón de repositorio es aislar
el código del Minutiae de acceso a datos y, como vimos, el aislamiento anterior es un rasgo necesario para la
prueba.
La clave del aislamiento es cómo expone los objetos el repositorio mediante una interfaz similar a la de una
colección. La lógica que escriba para usar el repositorio no tiene ninguna idea de cómo el repositorio
materializará los objetos que solicite. El repositorio puede comunicarse con una base de datos o simplemente
devolver objetos de una colección en memoria. Todo el código debe saber que el repositorio parece mantener la
colección, y puede recuperar, agregar y eliminar objetos de la colección.
En las aplicaciones .NET existentes, un repositorio concreto a menudo hereda de una interfaz genérica como la
siguiente:

public interface IRepository<T> {


IEnumerable<T> FindAll();
IEnumerable<T> FindBy(Expression<Func\<T, bool>> predicate);
T FindById(int id);
void Add(T newEntity);
void Remove(T entity);
}

Realizaremos algunos cambios en la definición de interfaz cuando se proporciona una implementación para EF4,
pero el concepto básico sigue siendo el mismo. El código puede usar un repositorio concreto que implemente
esta interfaz para recuperar una entidad por su valor de clave principal, para recuperar una colección de
entidades en función de la evaluación de un predicado, o simplemente para recuperar todas las entidades
disponibles. El código también puede Agregar y quitar entidades a través de la interfaz del repositorio.
Dado un IRepository de objetos de empleado, el código puede realizar las siguientes operaciones.

var employeesNamedScott =
repository
.FindBy(e => [Link] == "Scott")
.OrderBy(e => [Link]);
var firstEmployee = [Link](1);
var newEmployee = new Employee() {/*... */};
[Link](newEmployee);

Dado que el código está usando una interfaz (IRepository de Employee), podemos proporcionar el código con
diferentes implementaciones de la interfaz. Una implementación puede ser una implementación respaldada por
EF4 y almacenar objetos en una base de datos Microsoft SQL Server. Una implementación diferente (una que
usamos durante las pruebas) puede estar respaldada por una lista en memoria de objetos de empleado. La
interfaz le ayudará a lograr el aislamiento en el código.
Observe que la < interfaz IRepository T > no expone una operación de guardar. ¿Cómo se actualizan los objetos
existentes? Puede que se encuentre entre las definiciones de IRepository que incluyen la operación de guardar, y
las implementaciones de estos repositorios deberán conservar de forma inmediata un objeto en la base de
datos. Sin embargo, en muchas aplicaciones no queremos conservar los objetos individualmente. En su lugar,
queremos traer objetos a la vida, quizás desde diferentes repositorios, modificar esos objetos como parte de
una actividad económica y, a continuación, conservar todos los objetos como parte de una única operación
atómica. Afortunadamente, hay un patrón que permite este tipo de comportamiento.
Patrón de unidad de trabajo
Fowler indica que una unidad de trabajo "mantendrá una lista de objetos afectados por una transacción
empresarial y coordina la escritura de los cambios y la resolución de los problemas de simultaneidad". Es
responsabilidad de la unidad de trabajo realizar un seguimiento de los cambios en los objetos que aportamos a
la vida desde un repositorio y que conservan los cambios realizados en los objetos cuando se indica a la unidad
de trabajo que confirme los cambios. También es responsabilidad de la unidad de trabajo realizar los nuevos
objetos que hemos agregado a todos los repositorios e insertar los objetos en una base de datos, así como
administrar la eliminación.
Si alguna vez ha realizado algún trabajo con conjuntos de valores de [Link], ya estará familiarizado con el
patrón de unidad de trabajo. Los conjuntos de datos de [Link] tenían la capacidad de realizar un seguimiento
de las actualizaciones, eliminaciones e inserción de objetos DataRow y podrían (con la ayuda de un
TableAdapter) conciliar todos los cambios en una base de datos. Sin embargo, los objetos DataSet modelan un
subconjunto desconectado de la base de datos subyacente. El patrón de unidad de trabajo exhibe el mismo
comportamiento, pero funciona con objetos de negocio y objetos de dominio que están aislados del código de
acceso a datos y sin tener en cuenta la base de datos.
Una abstracción para modelar la unidad de trabajo en código .NET podría ser similar a la siguiente:

public interface IUnitOfWork {


IRepository<Employee> Employees { get; }
IRepository<Order> Orders { get; }
IRepository<Customer> Customers { get; }
void Commit();
}

Al exponer las referencias del repositorio a partir de la unidad de trabajo, se puede asegurar de que un solo
objeto de unidad de trabajo tiene la capacidad de realizar un seguimiento de todas las entidades materializadas
durante una transacción empresarial. La implementación del método commit para una unidad de trabajo real es
donde se produce toda la instrucción mágica para conciliar los cambios en memoria con la base de datos.
Dada una referencia IUnitOfWork, el código puede realizar cambios en los objetos comerciales recuperados de
uno o varios repositorios y guardar todos los cambios mediante la operación de confirmación atómica.

var firstEmployee = [Link](1);


var firstCustomer = [Link](1);
[Link] = "Alex";
[Link] = "Christopher";
[Link]();

El modelo de carga diferida


Fowler usa el nombre Lazy LOAD para describir "un objeto que no contiene todos los datos que necesita, pero
sabe cómo obtenerlo". La carga diferida transparente es una característica importante que se debe tener al
escribir código empresarial comprobable y al trabajar con una base de datos relacional. Como ejemplo,
considere el siguiente código.

var employee = [Link](id);


// ... and later ...
foreach(var timeCard in [Link]) {
// .. manipulate the timeCard
}

¿Cómo se rellena la colección de tarjetas de horas? Hay dos posibles respuestas. Una respuesta es que el
repositorio del empleado, cuando se le pide que capture un empleado, emite una consulta para recuperar el
empleado junto con la información de la tarjeta de tiempo asociada al empleado. En las bases de datos
relacionales, esto normalmente requiere una consulta con una cláusula JOIN y puede dar lugar a la recuperación
de más información de la que necesita una aplicación. ¿Qué ocurre si la aplicación no necesita tocar la
propiedad de las tarjetas de información.
Una segunda respuesta es cargar la propiedad "a petición" de las tarjetas de horas. Esta carga diferida es
implícita y transparente para la lógica de negocios, ya que el código no invoca API especiales para recuperar la
información de la tarjeta de tiempo. El código asume que la información de la tarjeta de tiempo está presente
cuando sea necesario. Hay una especial participación en la carga diferida que generalmente implica la
interceptación en tiempo de ejecución de las invocaciones de método. El código de interceptación es
responsable de comunicarse con la base de datos y recuperar la información de la tarjeta de tiempo, a la vez que
la lógica de negocios queda libre para ser lógica empresarial. Esta magia de carga diferida permite al código de
negocio aislarse de las operaciones de recuperación de datos y da como resultado un código más comprobable.
El inconveniente de una carga diferida es que cuando una aplicación necesita la información de la tarjeta de
tiempo, el código ejecutará una consulta adicional. Esto no supone un problema para muchas aplicaciones, pero
para las aplicaciones o aplicaciones sensibles al rendimiento se repiten por un número de objetos de empleado
y la ejecución de una consulta para recuperar las tarjetas de tiempo durante cada iteración del bucle (un
problema a menudo conocido como el problema de consulta N + 1), la carga diferida es un arrastre. En estos
escenarios, es posible que una aplicación quiera cargar la información de la tarjeta de tiempo de la manera más
eficaz posible.
Afortunadamente, veremos cómo EF4 admite las cargas diferidas implícitas y las cargas diligentes eficaces a
medida que avanzamos en la siguiente sección e implementamos estos patrones.

Implementar patrones con el Entity Framework


La buena noticia es que todos los patrones de diseño que se describen en la última sección son sencillos de
implementar con EF4. Para demostrar que vamos a usar una sencilla aplicación [Link] MVC para editar y
mostrar los empleados y la información de su tarjeta de tiempo asociada. Comenzaremos usando los siguientes
"objetos CLR antiguos sin formato" (POCO).

public class Employee {


public int Id { get; set; }
public string Name { get; set; }
public DateTime HireDate { get; set; }
public ICollection<TimeCard> TimeCards { get; set; }
}

public class TimeCard {


public int Id { get; set; }
public int Hours { get; set; }
public DateTime EffectiveDate { get; set; }
}

Estas definiciones de clase cambiarán ligeramente a medida que exploramos diferentes enfoques y
características de EF4, pero el objetivo es mantener estas clases como la persistencia ignorada (PI) como sea
posible. Un objeto PI no sabe Cómo, o incluso si, el estado que contiene reside en una base de datos. PI y POCO
go están a mano con el software que se pueda probar. Los objetos que usan un enfoque POCO son menos
restrictivos, más flexibles y fáciles de probar porque pueden funcionar sin una base de datos presente.
Con los POCO en vigor, podemos crear un Entity Data Model (EDM) en Visual Studio (vea la ilustración 1). No
usaremos el EDM para generar código para nuestras entidades. En su lugar, queremos usar las entidades que
lovinglymos de forma manual. Solo usaremos el EDM para generar el esquema de la base de datos y
proporcionar los metadatos que EF4 necesita para asignar objetos a la base de datos.

Ilustración 1
Nota: Si desea desarrollar el modelo EDM en primer lugar, es posible generar código limpio y POCO a partir del
EDM. Puede hacerlo con una extensión de Visual Studio 2010 proporcionada por el equipo de programación de
datos. Para descargar la extensión, inicie el administrador de extensiones desde el menú herramientas de Visual
Studio y busque "POCO" en la galería en línea de plantillas (consulte la figura 2). Hay varias plantillas POCO
disponibles para EF. Para obtener más información sobre el uso de la plantilla, vea " Tutorial: poco plantilla para
el Entity Framework".
Ilustración 2
Desde este punto de partida POCO, exploraremos dos enfoques diferentes para el código comprobable. El
primer enfoque que se llama al enfoque EF porque aprovecha las abstracciones de la API de Entity Framework
para implementar unidades de trabajo y repositorios. En el segundo enfoque, crearemos nuestras propias
abstracciones de repositorio personalizadas y, a continuación, veremos las ventajas y desventajas de cada
enfoque. Comenzaremos explorando el enfoque de EF.
Una implementación centrada en EF
Considere la siguiente acción del controlador de un proyecto de MVC de [Link]. La acción recupera un objeto
de empleado y devuelve un resultado para mostrar una vista detallada del empleado.

public ViewResult Details(int id) {


var employee = _unitOfWork.Employees
.Single(e => [Link] == id);
return View(employee);
}

¿Se va a probar el código? Hay al menos dos pruebas que necesitamos para comprobar el comportamiento de
la acción. En primer lugar, nos gustaría comprobar que la acción devuelve la vista correcta (una prueba sencilla).
También deseamos escribir una prueba para comprobar que la acción recupera el empleado correcto y nos
gustaría hacerlo sin ejecutar código para consultar la base de datos. Recuerde que queremos aislar el código
sometido a prueba. El aislamiento garantizará que la prueba no produzca un error debido a un error en el
código de acceso a datos o la configuración de la base de datos. Si se produce un error en la prueba, se sabrá
que tenemos un error en la lógica del controlador y no en un componente del sistema de nivel inferior.
Para lograr el aislamiento, necesitamos algunas abstracciones como las interfaces que presentamos
anteriormente para los repositorios y las unidades de trabajo. Recuerde que el patrón de repositorio está
diseñado para mediar entre objetos de dominio y la capa de asignación de datos. En este escenario, EF4 es la
capa de asignación de datos y ya proporciona una abstracción similar a la del repositorio denominada
IObjectSet < T > (del espacio de nombres System. Data. Objects). La definición de la interfaz es similar a la
siguiente.
public interface IObjectSet<TEntity> :
IQueryable<TEntity>,
IEnumerable<TEntity>,
IQueryable,
IEnumerable
where TEntity : class
{
void AddObject(TEntity entity);
void Attach(TEntity entity);
void DeleteObject(TEntity entity);
void Detach(TEntity entity);
}

IObjectSet < T > cumple los requisitos de un repositorio porque se parece a una colección de objetos (a través
de IEnumerable < T > ) y proporciona métodos para agregar y quitar objetos de la colección simulada. Los
métodos Attach y detach exponen funcionalidades adicionales de la API de EF4. Para usar IObjectSet < T > como
interfaz para los repositorios, se necesita una abstracción de unidad de trabajo para enlazar repositorios juntos.

public interface IUnitOfWork {


IObjectSet<Employee> Employees { get; }
IObjectSet<TimeCard> TimeCards { get; }
void Commit();
}

Una implementación concreta de esta interfaz se comunicará con SQL Server y es fácil de crear mediante la
clase ObjectContext desde EF4. La clase ObjectContext es la unidad real de trabajo de la API de EF4.

public class SqlUnitOfWork : IUnitOfWork {


public SqlUnitOfWork() {
var connectionString =
ConfigurationManager
.ConnectionStrings[ConnectionStringName]
.ConnectionString;
_context = new ObjectContext(connectionString);
}

public IObjectSet<Employee> Employees {


get { return _context.CreateObjectSet<Employee>(); }
}

public IObjectSet<TimeCard> TimeCards {


get { return _context.CreateObjectSet<TimeCard>(); }
}

public void Commit() {


_context.SaveChanges();
}

readonly ObjectContext _context;


const string ConnectionStringName = "EmployeeDataModelContainer";
}

Llevar un IObjectSet < T > a la vida es tan sencillo como invocar el método método createobjectset del objeto
ObjectContext. En segundo plano, el marco de trabajo usará los metadatos proporcionados en el EDM para
producir un ObjectSet < T concreto > . Nos centraremos en la devolución de < la > interfaz IObjectSet T porque
le ayudará a mantener la capacidad de prueba en el código de cliente.
Esta implementación concreta es útil en producción, pero es necesario centrarnos en cómo usaremos nuestra
abstracción IUnitOfWork para facilitar las pruebas.
La prueba se duplica
Para aislar la acción del controlador, se necesita la capacidad de cambiar entre la unidad de trabajo real
(respaldada por un ObjectContext) y una unidad de trabajo de prueba doble o "falsa" (realizando operaciones en
memoria). El método común para realizar este tipo de conmutación es no permitir que el controlador de MVC
cree una instancia de una unidad de trabajo, sino que en su lugar pasa la unidad de trabajo al controlador como
un parámetro de constructor.

class EmployeeController : Controller {


publicEmployeeController(IUnitOfWork unitOfWork) {
_unitOfWork = unitOfWork;
}
...
}

El código anterior es un ejemplo de inserción de dependencias. No permitimos que el controlador cree su


dependencia (la unidad de trabajo), sino que inserte la dependencia en el controlador. En un proyecto de MVC,
es habitual usar un generador de controlador personalizado en combinación con un contenedor de inversión de
control (IoC) para automatizar la inserción de dependencias. Estos temas están fuera del ámbito de este artículo,
pero puede leer más mediante las referencias que se indican al final de este artículo.
Una implementación de unidad de trabajo falsa que se puede usar para las pruebas podría ser similar a la
siguiente.

public class InMemoryUnitOfWork : IUnitOfWork {


public InMemoryUnitOfWork() {
Committed = false;
}
public IObjectSet<Employee> Employees {
get;
set;
}

public IObjectSet<TimeCard> TimeCards {


get;
set;
}

public bool Committed { get; set; }


public void Commit() {
Committed = true;
}
}

Observe que la unidad de trabajo falsa expone una propiedad confirmada. A veces resulta útil agregar
características a una clase falsa que facilitan las pruebas. En este caso, es fácil observar si el código confirma una
unidad de trabajo mediante la comprobación de la propiedad confirmada.
También se necesitará una IObjectSet falsa < > para almacenar los objetos de los empleados y las tarjetas de la
tarjeta de la memoria. Se puede proporcionar una implementación única mediante genéricos.
public class InMemoryObjectSet<T> : IObjectSet<T> where T : class
public InMemoryObjectSet()
: this([Link]<T>()) {
}
public InMemoryObjectSet(IEnumerable<T> entities) {
_set = new HashSet<T>();
foreach (var entity in entities) {
_set.Add(entity);
}
_queryableSet = _set.AsQueryable();
}
public void AddObject(T entity) {
_set.Add(entity);
}
public void Attach(T entity) {
_set.Add(entity);
}
public void DeleteObject(T entity) {
_set.Remove(entity);
}
public void Detach(T entity) {
_set.Remove(entity);
}
public Type ElementType {
get { return _queryableSet.ElementType; }
}
public Expression Expression {
get { return _queryableSet.Expression; }
}
public IQueryProvider Provider {
get { return _queryableSet.Provider; }
}
public IEnumerator<T> GetEnumerator() {
return _set.GetEnumerator();
}
IEnumerator [Link]() {
return GetEnumerator();
}

readonly HashSet<T> _set;


readonly IQueryable<T> _queryableSet;
}

Esta prueba Double delega la mayor parte de su trabajo en un < objeto HashSet T subyacente > . Tenga en
cuenta que IObjectSet < T > requiere una restricción genérica que aplique t como clase (un tipo de referencia) y
también nos obliga a implementar IQueryable < T > . Es fácil hacer que una colección en memoria aparezca
como IQueryable < T > mediante el operador estándar LINQ que se pueda consultar.
Las pruebas
Las pruebas unitarias tradicionales usarán una sola clase de prueba para contener todas las pruebas de todas las
acciones en un solo controlador de MVC. Podemos escribir estas pruebas, o cualquier tipo de prueba unitaria,
mediante el uso de las falsificaciones de memoria que hemos creado. Sin embargo, en este artículo se evitará el
enfoque de la clase de prueba monolítica y, en su lugar, se agruparán las pruebas para centrarse en una parte
específica de la [Link] ejemplo, "crear nuevo empleado" podría ser la funcionalidad que queremos
probar, por lo que usaremos una sola clase de prueba para comprobar la acción de controlador único
responsable de crear un nuevo empleado.
Hay algún código de instalación común que necesitamos para todas estas clases de prueba concretas. Por
ejemplo, siempre tenemos que crear los repositorios en memoria y la unidad de trabajo falsa. También se
necesita una instancia del controlador de empleados con la unidad falsa de trabajo insertada. Se compartirá este
código de instalación común en todas las clases de prueba mediante una clase base.
public class EmployeeControllerTestBase {
public EmployeeControllerTestBase() {
_employeeData = [Link]()
.ToList();
_repository = new InMemoryObjectSet<Employee>(_employeeData);
_unitOfWork = new InMemoryUnitOfWork();
_unitOfWork.Employees = _repository;
_controller = new EmployeeController(_unitOfWork);
}

protected IList<Employee> _employeeData;


protected EmployeeController _controller;
protected InMemoryObjectSet<Employee> _repository;
protected InMemoryUnitOfWork _unitOfWork;
}

El "objeto madre" que usamos en la clase base es un patrón común para crear datos de prueba. Un objeto
Mother contiene métodos de generador para crear instancias de entidades de prueba para su uso en varios
extras de prueba.

public static class EmployeeObjectMother {


public static IEnumerable<Employee> CreateEmployees() {
yield return new Employee() {
Id = 1, Name = "Scott", HireDate=new DateTime(2002, 1, 1)
};
yield return new Employee() {
Id = 2, Name = "Poonam", HireDate=new DateTime(2001, 1, 1)
};
yield return new Employee() {
Id = 3, Name = "Simon", HireDate=new DateTime(2008, 1, 1)
};
}
// ... more fake data for different scenarios
}

Podemos usar EmployeeControllerTestBase como la clase base para una serie de accesorios de prueba (vea la
figura 3). Cada accesorio de prueba probará una acción de controlador específica. Por ejemplo, un accesorio de
prueba se centrará en probar la acción de creación que se usa durante una solicitud GET de HTTP (para mostrar
la vista de creación de un empleado) y un accesorio diferente se centrará en la acción de creación usada en una
solicitud HTTP POST (para que el usuario envíe la información para crear un empleado). Cada clase derivada
solo es responsable de la configuración necesaria en su contexto específico y de proporcionar las aserciones
necesarias para comprobar los resultados de su contexto de prueba específico.
Ilustración 3
La Convención de nomenclatura y el estilo de prueba presentados aquí no son necesarios para el código
comprobable, sino solo un enfoque. En la figura 4 se muestran las pruebas que se ejecutan en el complemento
del Ejecutor de pruebas de jet cerebro ReSharper para Visual Studio 2010.

Figura 4
Con una clase base para controlar el código compartido de la instalación, las pruebas unitarias para cada acción
de controlador son pequeñas y fáciles de escribir. Las pruebas se ejecutarán rápidamente (dado que se están
realizando operaciones en memoria) y no se debería producir un error debido a la infraestructura no
relacionada o a problemas ambientales (porque hemos aislado la unidad en pruebas).

[TestClass]
public class EmployeeControllerCreateActionPostTests
: EmployeeControllerTestBase {
[TestMethod]
public void ShouldAddNewEmployeeToRepository() {
_controller.Create(_newEmployee);
[Link](_repository.Contains(_newEmployee));
}
[TestMethod]
public void ShouldCommitUnitOfWork() {
_controller.Create(_newEmployee);
[Link](_unitOfWork.Committed);
}
// ... more tests

Employee _newEmployee = new Employee() {


Name = "NEW EMPLOYEE",
HireDate = new [Link](2010, 1, 1)
};
}

En estas pruebas, la clase base realiza la mayor parte del trabajo de configuración. Recuerde que el constructor
de clase base crea el repositorio en memoria, una unidad de trabajo falsa y una instancia de la clase
EmployeeController. La clase de prueba se deriva de esta clase base y se centra en los detalles de la prueba del
método Create. En este caso, las características específicas se reducen a los pasos "Arrange, Act y Assert" que
verá en cualquier procedimiento de prueba unitaria:
Cree un objeto newEmployee para simular los datos entrantes.
Invocar la acción de creación de EmployeeController y pasar newEmployee.
Compruebe que la acción crear produce los resultados esperados (el empleado aparece en el repositorio).
Lo que hemos creado nos permite probar cualquiera de las acciones de EmployeeController. Por ejemplo,
cuando se escriben pruebas para la acción de índice del controlador de empleado, se puede heredar de la clase
base de prueba para establecer la misma configuración base para nuestras pruebas. De nuevo, la clase base
creará el repositorio en memoria, la unidad de trabajo falsa y una instancia de EmployeeController. Las pruebas
para la acción de índice solo deben centrarse en la invocación de la acción de índice y en la prueba de las
cualidades del modelo que devuelve la acción.

[TestClass]
public class EmployeeControllerIndexActionTests
: EmployeeControllerTestBase {
[TestMethod]
public void ShouldBuildModelWithAllEmployees() {
var result = _controller.Index();
var model = [Link]
as IEnumerable<Employee>;
[Link]([Link]() == _employeeData.Count);
}
[TestMethod]
public void ShouldOrderModelByHiredateAscending() {
var result = _controller.Index();
var model = [Link]
as IEnumerable<Employee>;
[Link]([Link](
_employeeData.OrderBy(e => [Link])));
}
// ...
}

Las pruebas que creamos con las falsificaciones en memoria están orientadas a probar el Estado del software.
Por ejemplo, al probar la acción de creación, queremos inspeccionar el estado del repositorio después de que se
ejecute la acción de creación: ¿el repositorio mantiene el nuevo empleado?

[TestMethod]
public void ShouldAddNewEmployeeToRepository() {
_controller.Create(_newEmployee);
[Link](_repository.Contains(_newEmployee));
}

Más adelante veremos las pruebas basadas en la interacción. Las pruebas basadas en interacción le preguntarán
si el código sometido a prueba invocó los métodos adecuados en nuestros objetos y pasa los parámetros
correctos. Por ahora, pasaremos a la portada otro patrón de diseño: la carga diferida.

Carga diligente y carga diferida


En algún momento de la aplicación web MVC de [Link] podríamos querer Mostrar la información de un
empleado e incluir las tarjetas de tiempo asociadas del empleado. Por ejemplo, es posible que tengamos una
pantalla de Resumen de tarjeta de tiempo que muestre el nombre del empleado y el número total de tarjetas de
tiempo del sistema. Existen varios enfoques que se pueden seguir para implementar esta característica.
Proyección
Un enfoque sencillo para crear el resumen es construir un modelo dedicado a la información que queremos
mostrar en la vista. En este escenario, el modelo podría ser similar al siguiente.
public class EmployeeSummaryViewModel {
public string Name { get; set; }
public int TotalTimeCards { get; set; }
}

Tenga en cuenta que EmployeeSummaryViewModel no es una entidad; es decir, no es algo que queremos
conservar en la base de datos. Solo vamos a usar esta clase para ordenar los datos en la vista de forma
fuertemente tipada. El modelo de vista es como un objeto de transferencia de datos (DTO) porque no contiene
ningún comportamiento (sin métodos): solo propiedades. Las propiedades contendrán los datos que
necesitamos trasladar. Es fácil crear instancias de este modelo de vista mediante el operador de proyección
estándar de LINQ: el operador Select.

public ViewResult Summary(int id) {


var model = _unitOfWork.Employees
.Where(e => [Link] == id)
.Select(e => new EmployeeSummaryViewModel
{
Name = [Link],
TotalTimeCards = [Link]()
})
.Single();
return View(model);
}

Hay dos características importantes para el código anterior. En primer lugar, el código es fácil de probar porque
todavía es fácil de observar y aislar. El operador Select funciona igual que en las falsificaciones en memoria que
en la unidad de trabajo real.

[TestClass]
public class EmployeeControllerSummaryActionTests
: EmployeeControllerTestBase {
[TestMethod]
public void ShouldBuildModelWithCorrectEmployeeSummary() {
var id = 1;
var result = _controller.Summary(id);
var model = [Link] as EmployeeSummaryViewModel;
[Link]([Link] == 3);
}
// ...
}

La segunda característica importante es la forma en que el código permite a EF4 generar una consulta única y
eficaz para ensamblar la información de los empleados y la tarjeta de tiempo conjuntamente. Hemos cargado
información de empleados e información de la tarjeta de tiempo en el mismo objeto sin usar ninguna API
especial. El código simplemente expresó la información necesaria para usar operadores LINQ estándar que
funcionan con orígenes de datos en memoria y con orígenes de datos remotos. EF4 pudo traducir los árboles de
expresión generados por la consulta LINQ y el # compilador de C en una consulta T-SQL eficaz y única.
SELECT
[Limit1].[Id] AS [Id],
[Limit1].[Name] AS [Name],
[Limit1].[C1] AS [C1]
FROM (SELECT TOP (2)
[Project1].[Id] AS [Id],
[Project1].[Name] AS [Name],
[Project1].[C1] AS [C1]
FROM (SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
(SELECT COUNT(1) AS [A1]
FROM [dbo].[TimeCards] AS [Extent2]
WHERE [Extent1].[Id] =
[Extent2].[EmployeeTimeCard_TimeCard_Id]) AS [C1]
FROM [dbo].[Employees] AS [Extent1]
WHERE [Extent1].[Id] = @p__linq__0
) AS [Project1]
) AS [Limit1]

Hay otras ocasiones en las que no se desea trabajar con un modelo de vista o un objeto DTO, sino con entidades
reales. Cuando sabemos que necesitamos un empleado y las tarjetas de tiempo del empleado, podemos cargar
diligentemente los datos relacionados de manera discreta y eficaz.
Carga diligente explícita
Cuando queremos cargar diligentemente la información relacionada de la entidad, necesitamos algún
mecanismo para la lógica de negocios (o en este escenario, la lógica de acción del controlador) para expresar su
deseo en el repositorio. La clase EF4 ObjectQuery < T > define un método include para especificar los objetos
relacionados que se van a recuperar durante una consulta. Recuerde que EF4 ObjectContext expone entidades a
través de la < clase de ObjectSet t concreta, > que hereda de ObjectQuery < t > .Si usamos < referencias de
ObjectSet T > en nuestra acción del controlador, podríamos escribir el código siguiente para especificar una
carga diligente de información de la tarjeta de tiempo para cada empleado.

_employees.Include("TimeCards")
.Where(e => [Link] > 2009);

Sin embargo, puesto que estamos intentando mantener el código comprobable, no exponemos < el ObjectSet T
> desde fuera de la clase real de la unidad de trabajo. En su lugar, confiamos en la < interfaz IObjectSet T > , que
es más fácil de falsificar, pero IObjectSet < T > no define un método include. La belleza de LINQ es que podemos
crear nuestro propio operador include.

public static class QueryableExtensions {


public static IQueryable<T> Include<T>
(this IQueryable<T> sequence, string path) {
var objectQuery = sequence as ObjectQuery<T>;
if(objectQuery != null)
{
return [Link](path);
}
return sequence;
}
}

Observe que este operador include se define como un método de extensión para IQueryable < t > en lugar de
IObjectSet < T > . Esto nos da la posibilidad de usar el método con una gama más amplia de tipos posibles,
incluidos IQueryable < t > , IObjectSet < t > , ObjectQuery < t > y ObjectSet < t > . En el caso de que la
secuencia subyacente no sea una copia de EF4 original de ObjectQuery < T > , no habrá ningún daño y el
operador include es una operación no operativa. Si la secuencia subyacente es un ObjectQuery < t > (o se
deriva de OBJECTQUERY < t > ), EF4 verá nuestro requisito de datos adicionales y formulará la consulta SQL
adecuada.
Con este nuevo operador en su lugar, podemos solicitar explícitamente una carga diligente de información de la
tarjeta de tiempo del repositorio.

public ViewResult Index() {


var model = _unitOfWork.Employees
.Include("TimeCards")
.OrderBy(e => [Link]);
return View(model);
}

Cuando se ejecuta en un ObjectContext real, el código genera la siguiente consulta única. La consulta recopila
suficiente información de la base de datos en un recorrido para materializar los objetos de empleado y rellenar
completamente su propiedad de tarjetas de seguridad.

SELECT
[Project1].[Id] AS [Id],
[Project1].[Name] AS [Name],
[Project1].[HireDate] AS [HireDate],
[Project1].[C1] AS [C1],
[Project1].[Id1] AS [Id1],
[Project1].[Hours] AS [Hours],
[Project1].[EffectiveDate] AS [EffectiveDate],
[Project1].[EmployeeTimeCard_TimeCard_Id] AS [EmployeeTimeCard_TimeCard_Id]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Name] AS [Name],
[Extent1].[HireDate] AS [HireDate],
[Extent2].[Id] AS [Id1],
[Extent2].[Hours] AS [Hours],
[Extent2].[EffectiveDate] AS [EffectiveDate],
[Extent2].[EmployeeTimeCard_TimeCard_Id] AS
[EmployeeTimeCard_TimeCard_Id],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int)
ELSE 1 END AS [C1]
FROM [dbo].[Employees] AS [Extent1]
LEFT OUTER JOIN [dbo].[TimeCards] AS [Extent2] ON [Extent1].[Id] = [Extent2].
[EmployeeTimeCard_TimeCard_Id]
) AS [Project1]
ORDER BY [Project1].[HireDate] ASC,
[Project1].[Id] ASC, [Project1].[C1] ASC

La gran noticia es que el código dentro del método de acción sigue siendo totalmente comprobable. No es
necesario proporcionar ninguna característica adicional para nuestras falsificaciones para admitir el operador
include. La mala noticia es que teníamos que usar el operador include en el código que deseamos para
mantener la persistencia. Este es un buen ejemplo del tipo de inconvenientes que debe evaluar al compilar
código comprobable. Hay ocasiones en las que es necesario dejar que la persistencia tenga pérdidas fuera de la
abstracción del repositorio para satisfacer los objetivos de rendimiento.
La alternativa a la carga diligente es la carga diferida. La carga diferida significa que no es necesario que nuestro
código de negocio anuncie explícitamente el requisito de los datos asociados. En su lugar, usamos nuestras
entidades en la aplicación y si se necesitan datos adicionales Entity Framework cargarán los datos a petición.
Carga diferida
Es fácil imaginar un escenario en el que no se sepa qué datos necesitará una lógica de negocios. Podríamos
saber que la lógica necesita un objeto de empleado, pero podemos crear una rama en diferentes rutas de
ejecución, donde algunas de esas rutas requieren información de tarjeta de tiempo del empleado y otras no. Los
escenarios como este son idóneos para la carga diferida implícita porque los datos aparecen de forma mágica
según sea necesario.
La carga diferida, también conocida como carga aplazada, impone algunos requisitos en nuestros objetos
entidad. Un POCO con persistencia real omisión no tendría ningún requisito de la capa de persistencia, pero la
persistencia real omisión es prácticamente imposible de [Link] su lugar, medimos la persistencia omisión en
grados relativos. Sería desafortunable si era necesario heredar de una clase base orientada a la persistencia o
usar una colección especializada para lograr la carga diferida en POCO. Afortunadamente, EF4 tiene una
solución menos intrusiva.
Prácticamente no detectable
Cuando se usan objetos POCO, EF4 puede generar dinámicamente servidores proxy en tiempo de ejecución
para las entidades. Estos proxies invisiblemente encapsulan los POCO materializados y proporcionan servicios
adicionales interceptando cada operación get y set de cada propiedad para realizar trabajo adicional. Uno de
estos servicios es la característica de carga diferida que estamos buscando. Otro servicio es un mecanismo de
seguimiento de cambios eficaz que puede grabar cuando el programa cambia los valores de propiedad de una
entidad. El ObjectContext usa la lista de cambios durante el método SaveChanges para conservar las entidades
modificadas mediante comandos UPDATE.
Sin embargo, para que estos proxies funcionen, necesitan una manera de enlazar las operaciones GET y set de la
propiedad en una entidad, y los proxies logran este objetivo mediante la invalidación de los miembros virtuales.
Por lo tanto, si queremos tener una carga diferida implícita y un seguimiento de cambios eficaz, es necesario
volver a las definiciones de clase POCO y marcar las propiedades como virtuales.

public class Employee {


public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual DateTime HireDate { get; set; }
public virtual ICollection<TimeCard> TimeCards { get; set; }
}

Todavía podemos decir que la entidad Employee es la que ignora la persistencia. El único requisito es usar
miembros virtuales y esto no afecta a la capacidad de prueba del código. No es necesario derivar de ninguna
clase base especial ni siquiera usar una colección especial dedicada a la carga diferida. Como se muestra en el
código, cualquier clase que implemente ICollection < T > está disponible para contener entidades relacionadas.
También hay un pequeño cambio que necesitamos hacer dentro de nuestra unidad de trabajo. La carga diferida
está desactivada de forma predeterminada cuando se trabaja directamente con un objeto ObjectContext. Hay
una propiedad que se puede establecer en la propiedad ContextOptions para habilitar la carga aplazada y
podemos establecer esta propiedad dentro de nuestra unidad real de trabajo si queremos habilitar la carga
diferida en todas partes.

public class SqlUnitOfWork : IUnitOfWork {


public SqlUnitOfWork() {
// ...
_context = new ObjectContext(connectionString);
_context.[Link] = true;
}
// ...
}

Con la carga diferida implícita habilitada, el código de aplicación puede usar un empleado y las tarjetas de
tiempo asociadas del empleado, mientras que el resto de completamente no es consciente del trabajo necesario
para que EF cargue los datos adicionales.
var employee = _unitOfWork.Employees
.Single(e => [Link] == id);
foreach (var card in [Link]) {
// ...
}

La carga diferida facilita la escritura del código de la aplicación y, con el proxy mágico, el código sigue siendo
totalmente comprobable. Las falsificaciones en memoria de la unidad de trabajo pueden simplemente precargar
las entidades falsas con datos asociados cuando sea necesario durante una prueba.
Llegados a este punto, nos centraremos en la creación de repositorios con IObjectSet < T > y veremos las
abstracciones para ocultar todos los signos del marco de persistencia.

Repositorios personalizados
La primera vez que se presentó el patrón de diseño de unidad de trabajo en este artículo se proporciona código
de ejemplo para el aspecto que podría tener la unidad de trabajo. Vamos a presentar esta idea original con el
escenario de tarjeta de tiempo empleado y empleado con el que hemos trabajado.

public interface IUnitOfWork {


IRepository<Employee> Employees { get; }
IRepository<TimeCard> TimeCards { get; }
void Commit();
}

La principal diferencia entre esta unidad de trabajo y la unidad de trabajo que creamos en la última sección es
cómo esta unidad de trabajo no usa ninguna abstracción del marco de EF4 (no hay IObjectSet < T > ). IObjectSet
< T > funciona bien como una interfaz de repositorio, pero es posible que la API que expone no se alinee
perfectamente con las necesidades de la aplicación. En este próximo enfoque, se representarán los repositorios
con una < abstracción de IRepository T personalizada > .
Muchos desarrolladores que siguen el diseño basado en pruebas, el diseño controlado por el comportamiento y
las metodologías controladas por el dominio prefieren el < enfoque IRepository T > por varias razones. En
primer lugar, < la > interfaz IRepository T representa una capa "contra daños". Tal como se describe en Eric
Evans en su libro de diseño controlado por dominios, una capa contra daños mantiene el código de dominio
fuera de las API de infraestructura, como una API de persistencia. En segundo lugar, los desarrolladores pueden
crear métodos en el repositorio que satisfagan las necesidades exactas de una aplicación (como se detectó al
escribir pruebas). Por ejemplo, con frecuencia es necesario buscar una sola entidad con un valor de identificador,
por lo que podemos agregar un método FindById a la interfaz de [Link] < > definición de
IRepository T tendrá un aspecto similar al siguiente.

public interface IRepository<T>


where T : class, IEntity {
IQueryable<T> FindAll();
IQueryable<T> FindWhere(Expression<Func\<T, bool>> predicate);
T FindById(int id);
void Add(T newEntity);
void Remove(T entity);
}

Observe que volveremos a usar una interfaz IQueryable < T > para exponer las colecciones de entidades.
IQueryable < T > permite que los árboles de expresión LINQ fluyan al proveedor EF4 y proporcione al
proveedor una vista holística de la consulta. Una segunda opción sería devolver IEnumerable < T > , lo que
significa que el proveedor EF4 LINQ solo verá las expresiones compiladas dentro del repositorio. Cualquier
agrupación, ordenación y proyección realizadas fuera del repositorio no se creará en el comando SQL enviado a
la base de datos, lo que puede afectar negativamente al rendimiento. Por otro lado, un repositorio que devuelva
solo < los resultados de IEnumerable T > nunca le sorprenderá con un nuevo comando SQL. Ambos enfoques
funcionarán y ambos enfoques seguirán siendo comprobables.
Es sencillo proporcionar una única implementación de la < interfaz IRepository T > mediante genéricos y la API
de OBJECTCONTEXT de EF4.

public class SqlRepository<T> : IRepository<T>


where T : class, IEntity {
public SqlRepository(ObjectContext context) {
_objectSet = [Link]<T>();
}
public IQueryable<T> FindAll() {
return _objectSet;
}
public IQueryable<T> FindWhere(
Expression<Func\<T, bool>> predicate) {
return _objectSet.Where(predicate);
}
public T FindById(int id) {
return _objectSet.Single(o => [Link] == id);
}
public void Add(T newEntity) {
_objectSet.AddObject(newEntity);
}
public void Remove(T entity) {
_objectSet.DeleteObject(entity);
}
protected ObjectSet<T> _objectSet;
}

El < enfoque IRepository T > nos proporciona un control adicional sobre nuestras consultas porque un cliente
tiene que invocar un método para llegar a una entidad. Dentro del método, podríamos proporcionar
comprobaciones adicionales y operadores LINQ para aplicar las restricciones de la aplicación. Observe que la
interfaz tiene dos restricciones en el parámetro de tipo genérico. La primera restricción es la clase cons que
requiere el ObjectSet < T > y la segunda restricción obliga a nuestras entidades a implementar IEntity: una
abstracción creada para la aplicación. La interfaz IEntity fuerza a las entidades a tener una propiedad de
identificador legible y, a continuación, podemos usar esta propiedad en el método FindById. IEntity se define con
el código siguiente.

public interface IEntity {


int Id { get; }
}

IEntity podría considerarse una pequeña infracción de la persistencia omisión, ya que las entidades necesitan
implementar esta interfaz. Recuerde que la persistencia omisión se refiere a las ventajas y, para muchas de las
funciones de FindById, la restricción impuesta por la interfaz. La interfaz no tiene ningún impacto en la
capacidad de prueba.
La creación de una instancia < de Live IRepository T > requiere un OBJECTCONTEXT de EF4, por lo que una
implementación concreta de la unidad de trabajo debe administrar la creación de instancias.
public class SqlUnitOfWork : IUnitOfWork {
public SqlUnitOfWork() {
var connectionString =
ConfigurationManager
.ConnectionStrings[ConnectionStringName]
.ConnectionString;

_context = new ObjectContext(connectionString);


_context.[Link] = true;
}

public IRepository<Employee> Employees {


get {
if (_employees == null) {
_employees = new SqlRepository<Employee>(_context);
}
return _employees;
}
}

public IRepository<TimeCard> TimeCards {


get {
if (_timeCards == null) {
_timeCards = new SqlRepository<TimeCard>(_context);
}
return _timeCards;
}
}

public void Commit() {


_context.SaveChanges();
}

SqlRepository<Employee> _employees = null;


SqlRepository<TimeCard> _timeCards = null;
readonly ObjectContext _context;
const string ConnectionStringName = "EmployeeDataModelContainer";
}

Uso del repositorio personalizado


El uso de nuestro repositorio personalizado no es significativamente diferente del uso del repositorio basado en
IObjectSet < T > . En lugar de aplicar operadores LINQ directamente a una propiedad, primero debemos invocar
los métodos del repositorio para obtener una < referencia IQueryable T > .

public ViewResult Index() {


var model = _repository.FindAll()
.Include("TimeCards")
.OrderBy(e => [Link]);
return View(model);
}

Observe que el operador de inclusión personalizado implementado anteriormente funcionará sin cambios. El
método FindById del repositorio quita la lógica duplicada de acciones que intentan recuperar una sola entidad.

public ViewResult Details(int id) {


var model = _repository.FindById(id);
return View(model);
}

No hay ninguna diferencia significativa en la capacidad de prueba de los dos métodos que hemos examinado.
Podríamos proporcionar implementaciones falsas de IRepository < T > mediante la creación de clases concretas
respaldadas por < > un empleado de HashSet, exactamente igual que en la última sección. Sin embargo,
algunos desarrolladores prefieren usar objetos ficticios y marcos de objetos ficticios en lugar de crear
simulaciones. Veremos el uso de simulacros para probar nuestra implementación y analizar las diferencias entre
los simulacros y las simulaciones en la sección siguiente.
Pruebas con simulacros
Hay diferentes enfoques para crear lo que Martin Fowler llama "Double de prueba". Una prueba Double (como
una película Stunt Double) es un objeto que se compila en "out" para objetos reales de producción durante las
pruebas. Los repositorios en memoria creados son dobles de pruebas para los repositorios que se comunican
con SQL Server. Hemos visto cómo usar estas pruebas dobles durante las pruebas unitarias para aislar el código
y mantener la ejecución rápida de las pruebas.
Los dobles de pruebas que hemos creado son implementaciones reales y en funcionamiento. En segundo plano,
cada uno almacena una colección concreta de objetos y agrega y quita objetos de esta colección mientras se
manipula el repositorio durante una prueba. Algunos desarrolladores como para compilar la prueba se duplican
de esta manera: con código real y implementaciones de [Link] dobles de pruebas son lo que llamamos
imitaciones. Tienen implementaciones en funcionamiento, pero no son lo suficientemente reales para su uso en
producción. El repositorio falso no escribe realmente en la base de datos. El servidor SMTP falso no envía un
mensaje de correo electrónico a través de la red.
Simuladores frente a falsificaciones
Hay otro tipo de prueba doble conocido como ficticio. Mientras que las simulaciones tienen implementaciones
en funcionamiento, los simulacros se incluyen sin implementación. Con la ayuda de un marco de objeto ficticio,
construimos estos objetos ficticios en tiempo de ejecución y los usamos como dobles de pruebas. En esta
sección vamos a usar el marco de trabajo ficticio de código abierto MOQ. Este es un ejemplo sencillo del uso de
MOQ para crear dinámicamente una prueba Double para un repositorio de empleados.

Mock<IRepository<Employee>> mock =
new Mock<IRepository<Employee>>();
IRepository<Employee> repository = [Link];
[Link](new Employee());
var employee = [Link](1);

Solicitamos MOQ para una implementación de empleados de IRepository < > y creamos una dinámicamente.
Podemos obtener el objeto que implementa IRepository < Employee > mediante el acceso a la propiedad Object
del < objeto ficticio T > . Es este objeto interno que se puede pasar a nuestros controladores y no sabrá si se
trata de un doble de prueba o del repositorio real. Se pueden invocar métodos en el objeto del mismo modo
que se invocan los métodos en un objeto con una implementación real.
Debe preguntarse lo que hará el repositorio ficticio cuando se invoque el método Add. Dado que no hay
ninguna implementación detrás del objeto ficticio, Add no hace nada. No hay ninguna colección concreta en
segundo plano como teníamos con las falsificaciones que escribimos, por lo que el empleado se descarta. ¿Qué
ocurre con el valor devuelto de FindById? En este caso, el objeto ficticio hace lo único que puede hacer, que
devuelve un valor predeterminado. Dado que se devuelve un tipo de referencia (un empleado), el valor devuelto
es un valor null.
Los simulacros podrían parecer no útil; sin embargo, hay dos características más de los simulacros que no
hemos hablado. En primer lugar, el marco MOQ registra todas las llamadas realizadas en el objeto ficticio. Más
adelante en el código, se puede preguntar a MOQ si alguien invocó el método Add o si alguien invocó el
método FindById. Más adelante veremos cómo podemos usar esta característica de grabación de "caja negra"
en las pruebas.
La segunda característica excelente es cómo se puede usar MOQ para programar un objeto ficticio con las
expectativas. Una expectativa indica al objeto ficticio cómo responder a cualquier interacción determinada. Por
ejemplo, se puede programar una expectativa en nuestro simulacro e indicar a que devuelva un objeto de
empleado cuando alguien invoque FindById. El marco de trabajo de MOQ usa una API de instalación y
expresiones lambda para programar estas expectativas.

[TestMethod]
public void MockSample() {
Mock<IRepository<Employee>> mock =
new Mock<IRepository<Employee>>();
[Link](m => [Link](5))
.Returns(new Employee {Id = 5});
IRepository<Employee> repository = [Link];
var employee = [Link](5);
[Link]([Link] == 5);
}

En este ejemplo, se pide a MOQ que cree de forma dinámica un repositorio y, a continuación, programemos el
repositorio con una expectativa. La expectativa indica al objeto ficticio que devuelva un nuevo objeto de
empleado con un valor de identificador de 5 cuando alguien invoque el método FindById pasando un valor de 5.
Esta prueba se supera y no es necesario crear una implementación completa para falsificar IRepository < t > .
Vamos a revisar las pruebas que hemos escrito anteriormente y a trabajar con ellas para usar simulacros en
lugar de simulaciones. Al igual que antes, usaremos una clase base para configurar los componentes comunes
de la infraestructura que necesitamos para todas las pruebas del controlador.

public class EmployeeControllerTestBase {


public EmployeeControllerTestBase() {
_employeeData = [Link]()
.AsQueryable();
_repository = new Mock<IRepository<Employee>>();
_unitOfWork = new Mock<IUnitOfWork>();
_unitOfWork.Setup(u => [Link])
.Returns(_repository.Object);
_controller = new EmployeeController(_unitOfWork.Object);
}

protected IQueryable<Employee> _employeeData;


protected Mock<IUnitOfWork> _unitOfWork;
protected EmployeeController _controller;
protected Mock<IRepository<Employee>> _repository;
}

El código de instalación se mantiene principalmente igual. En lugar de usar falsificaciones, usaremos MOQ para
construir objetos ficticios. La clase base organiza la unidad de trabajo simulada para devolver un repositorio
ficticio cuando el código invoca la propiedad Employees. El resto de la configuración del simulacro tendrá lugar
dentro de los extras de prueba dedicados a cada escenario específico. Por ejemplo, el accesorio de prueba para
la acción de índice configurará el repositorio ficticio para devolver una lista de empleados cuando la acción
invoca el método FindAll del repositorio ficticio.
[TestClass]
public class EmployeeControllerIndexActionTests
: EmployeeControllerTestBase {
public EmployeeControllerIndexActionTests() {
_repository.Setup(r => [Link]())
.Returns(_employeeData);
}
// .. tests
[TestMethod]
public void ShouldBuildModelWithAllEmployees() {
var result = _controller.Index();
var model = [Link]
as IEnumerable<Employee>;
[Link]([Link]() == _employeeData.Count());
}
// .. and more tests
}

A excepción de las expectativas, nuestras pruebas son similares a las pruebas que teníamos antes. Sin embargo,
con la capacidad de grabación de un marco ficticio, podemos enfocar las pruebas desde un ángulo diferente.
Veremos esta nueva perspectiva en la sección siguiente.
Pruebas de estado frente a interacción
Hay distintas técnicas que puede usar para probar el software con objetos ficticios. Un enfoque consiste en usar
pruebas basadas en el estado, que es lo que hemos hecho en este documento hasta ahora. Las pruebas basadas
en el estado realizan aserciones sobre el estado del software. En la última prueba, se ha invocado un método de
acción en el controlador y se ha realizado una aserción sobre el modelo que debe compilar. Estos son algunos
ejemplos de estado de prueba:
Compruebe que el repositorio contiene el nuevo objeto de empleado después de que se ejecute Create.
Compruebe que el modelo contiene una lista de todos los empleados después de que se ejecute el índice.
Compruebe que el repositorio no contiene un empleado determinado después de que se ejecute la
eliminación.
Otro enfoque que verá con objetos ficticios es comprobar las interacciones. Mientras que las pruebas basadas
en el estado realizan aserciones sobre el estado de los objetos, las pruebas basadas en la interacción realizan
aserciones sobre cómo interactúan los objetos. Por ejemplo:
Compruebe que el controlador invoca el método Add del repositorio cuando se ejecuta Create.
Compruebe que el controlador invoca el método FindAll del repositorio cuando se ejecuta el índice.
Compruebe que el controlador invoca el método commit de la unidad del trabajo para guardar los cambios
cuando se ejecuta la edición.
A menudo, las pruebas de interacción requieren menos datos de prueba, porque no se Poking dentro de las
colecciones y se comprueban los recuentos. Por ejemplo, si sabemos que la acción de detalles invoca el método
FindById de un repositorio con el valor correcto, es probable que la acción se comporte correctamente.
Podemos comprobar este comportamiento sin configurar ningún dato de prueba para que se devuelva desde
FindById.
[TestClass]
public class EmployeeControllerDetailsActionTests
: EmployeeControllerTestBase {
// ...
[TestMethod]
public void ShouldInvokeRepositoryToFindEmployee() {
var result = _controller.Details(_detailsId);
_repository.Verify(r => [Link](_detailsId));
}
int _detailsId = 1;
}

La única configuración necesaria en el accesorio de prueba anterior es la que proporciona la clase base. Cuando
se invoca la acción del controlador, MOQ registrará las interacciones con el repositorio ficticio. Mediante la
comprobación de la API de MOQ, podemos preguntar a MOQ si el controlador invocó FindById con el valor de
ID. adecuado. Si el controlador no invocó el método o invocó el método con un valor de parámetro inesperado,
el método verify producirá una excepción y se producirá un error en la prueba.
Este es otro ejemplo para comprobar que la acción de creación invoca la confirmación en la unidad de trabajo
actual.

[TestMethod]
public void ShouldCommitUnitOfWork() {
_controller.Create(_newEmployee);
_unitOfWork.Verify(u => [Link]());
}

Un peligro con las pruebas de interacción es la tendencia de especificar las interacciones. La capacidad del
objeto ficticio de registrar y comprobar cada interacción con el objeto ficticio no significa que la prueba debe
intentar comprobar cada interacción. Algunas interacciones son detalles de la implementación y solo se deben
comprobar las interacciones necesarias para satisfacer la prueba actual.
La elección entre simulacros o falsificaciones depende en gran medida del sistema que se está probando y de
sus preferencias personales (o de equipo). Los objetos ficticios pueden reducir drásticamente la cantidad de
código que necesita para implementar los dobles de pruebas, pero no todos se sienten cómodos con la
programación de las expectativas y la comprobación de las interacciones.

Conclusiones
En este documento hemos mostrado varios enfoques para crear código comprobable al usar el Entity
Framework [Link] para la persistencia de datos. Podemos aprovechar las abstracciones integradas como
IObjectSet < t > o crear nuestras propias abstracciones como IRepository < t > .En ambos casos, la
compatibilidad POCO en la Entity Framework 4,0 de [Link] permite a los consumidores de estas
abstracciones seguir siendo ignorables y muy comprobables. Características adicionales de EF4 como la carga
diferida implícita permite que el código del servicio de aplicaciones y del negocio funcione sin preocuparse por
los detalles de un almacén de datos relacional. Por último, las abstracciones que creamos son fáciles de simular
o falsificar dentro de las pruebas unitarias, y podemos usar estos dobles de pruebas para lograr pruebas de
ejecución rápida, muy aisladas y confiables.
Recursos adicionales
Robert C. Martin, " el principio de responsabilidad única"
Martin Fowler, Catálogo de patrones de patrones de arquitectura de aplicaciones empresariales
Griffin Caprio, " inyección de dependencia"
Blog de programación de datos, " Tutorial: desarrollo controlado por pruebas con el Entity Framework 4,0".
Blog de programación de datos, " uso de patrones de repositorio y unidad de trabajo con Entity Framework
4,0"
Aaron Jensen, " Introduccióna las especificaciones de la máquina"
Eric Lee, " BDD con MSTest"
Eric Evans, " diseño controlado por dominios"
Martin Fowler, "los simulacros no son stubs"
Martin Fowler, " Test Double"
Moq
Biografía
Scott Allen es miembro del personal técnico de Pluralsight y el fundador de [Link]. En 15 años de
desarrollo de software comercial, Scott ha trabajado en soluciones para todo, desde dispositivos incrustados de
8 bits hasta aplicaciones Web [Link] altamente escalables. Puede ponerse en contacto con Scott en su blog en
OdeToCode o en Twitter en [Link] .
Creación de un modelo
12/03/2021 • 5 minutes to read

Un modelo de EF almacena los detalles sobre cómo se asignan las propiedades y las clases de la aplicación a las
columnas y las tablas de la base de datos. Hay dos formas principales de crear un modelo de EF:
Uso de Code First : el desarrollador escribe código para especificar el modelo. EF genera los modelos y
las asignaciones en tiempo de ejecución según las clases de entidad y la configuración de modelo
adicional proporcionados por el desarrollador.
Uso de EF Designer : el desarrollador dibuja cuadros y líneas para especificar el modelo con EF
Designer. El modelo resultante se almacena como XML en un archivo con la extensión EDMX. Los objetos
de dominio de la aplicación normalmente se generan de forma automática desde el modelo conceptual.

Flujos de trabajo de EF
Estos dos enfoques pueden usarse para establecer como destino una base de datos existente o para crear una
nueva base de datos, lo que da lugar a cuatro flujos de trabajo diferentes. Descubra cuál es el más adecuado en
su caso:

SO LO Q UIERO ESC RIB IR C Ó DIGO. . . Q UIERO USA R UN DISEÑ A DO R. . .

Voy a crear una nueva base de Use Code First para definir el modelo Use Model First para definir el
datos en el código y luego generar una base modelo mediante cuadros y líneas y
de datos. luego generar una base de datos.

Necesito acceder a una base de Use Code First para crear un modelo Use Database First para crear un
datos existente basado en código que se asigne a una modelo de cuadros y líneas que se
base de datos existente. asigne a una base de datos existente.

Vea el vídeo: What EF workflow should I use? (Qué flujo de trabajo de EF debo usar)
En este breve vídeo se explican las diferencias y cómo encontrar el adecuado para usted.
Presentado por : Rowan Miller

(Consejos para elegir un flujo de trabajo) WMV | MP4 | WMV (ZIP)


Si después de ver el vídeo todavía no se siente cómodo para decidir si quiere usar EF Designer o Code First,
obtenga información sobre ambos.

Aspectos técnicos
Independientemente de si usa Code First o EF Designer, un modelo de EF siempre tiene varios componentes:
Los objetos de dominio de la aplicación o los propios tipos de entidad. Esto se suele denominar la capa
de objeto
Un modelo conceptual que consta de tipos de entidad específicos de dominio y relaciones, que se
describen mediante Entity Data Model. Se suele hacer referencia a esta capa con la letra "C", por
conceptual.
Un modelo de almacenamiento que representa las tablas, las columnas y las relaciones según se definen
en la base de datos. Se suele hacer referencia a esta capa con la letra "S", por storage (almacenamiento en
inglés).
Una asignación entre el modelo conceptual y el esquema de base de datos. Esta asignación se suele
conocer como asignación "C-S".
El motor de asignación de EF aprovecha la asignación "C-S" para transformar las operaciones en entidades (por
ejemplo, crear, leer, actualizar y eliminar) en operaciones equivalentes en las tablas de la base de datos.
La asignación entre el modelo conceptual y los objetos de la aplicación se suele conocer como asignación "O-C".
En comparación con la asignación "C-S", la asignación "O-C" es implícita y de uno a uno: las entidades,
propiedades y relaciones definidas en el modelo conceptual tienen que coincidir con las formas y los tipos de
los objetos .NET. A partir de EF4 y versiones posteriores, la capa de objetos puede componerse de objetos
simples con propiedades sin dependencias en EF. Estos se conocen normalmente como objetos CLR estándar
(POCO) y la asignación de tipos y propiedades se realiza según las convenciones de coincidencia de nombres.
Anteriormente, en EF 3.5, había restricciones específicas para la capa de objeto, por ejemplo, las entidades tenían
que derivar de la clase EntityObject e incluir atributos de EF para implementar la asignación "O-C".
Code First en una nueva base de datos
12/03/2021 • 20 minutes to read

Este vídeo y el tutorial paso a paso proporcionan una introducción al desarrollo de Code First que tiene como
destino una nueva base de datos. Este escenario incluye establecer como destino una base de datos que no
existe y Code First creará, o una base de datos vacía a la que Code First agregará nuevas tablas. Code First
permite definir el modelo mediante # las clases C o [Link]. Opcionalmente, se puede realizar una configuración
adicional mediante atributos en las clases y propiedades o mediante una API fluida.

Visualización del vídeo


Este vídeo proporciona una introducción al desarrollo de Code First orientado a una nueva base de datos. Este
escenario incluye establecer como destino una base de datos que no existe y Code First creará, o una base de
datos vacía a la que Code First agregará nuevas tablas. Code First permite definir el modelo mediante clases de
C# o [Link]. Opcionalmente, se puede realizar una configuración adicional mediante atributos en las clases y
propiedades o mediante una API fluida.
Presentado por : Rowan Miller
Vídeo : WMV | MP4 | WMV (zip)

Requisitos previos
Debe tener instalado al menos Visual Studio 2010 o Visual Studio 2012 para completar este tutorial.
Si usa Visual Studio 2010, también debe tener instalado NuGet .

1. crear la aplicación
Para simplificar las cosas, vamos a crear una aplicación de consola básica que use Code First para realizar el
acceso a los datos.
Apertura de Visual Studio
Archivo- > nuevo- > proyecto...
Seleccionar ventanas en el menú izquierdo y en la aplicación de consola
Escriba CodeFirstNewDatabaseSample como nombre
Seleccione Aceptar .

2. crear el modelo
Vamos a definir un modelo muy sencillo mediante clases. Simplemente los definimos en el archivo [Link],
pero en una aplicación real dividiría las clases en archivos independientes y potencialmente un proyecto
independiente.
Debajo de la definición de clase de programa en [Link], agregue las dos clases siguientes.
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }

public virtual 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 virtual Blog Blog { get; set; }
}

Observará que vamos a hacer que las dos propiedades de navegación (blog. posts y post. blog) sean virtuales.
Esto habilita la característica de carga diferida de Entity Framework. La carga diferida significa que el contenido
de estas propiedades se cargará automáticamente desde la base de datos cuando intente obtener acceso a ellas.

3. crear un contexto
Ahora es el momento de definir un contexto derivado, que representa una sesión con la base de datos, lo que
nos permite consultar y guardar datos. Definimos un contexto que se deriva de System. Data. Entity. DbContext y
expone una carpa DbSet con tipo < > para cada clase de nuestro modelo.
Ahora estamos empezando a usar los tipos del Entity Framework por lo que necesitamos agregar el paquete
NuGet EntityFramework.
Proyecto: > administrar paquetes NuGet... Nota: Si no tiene la Administración de paquetes de
NuGet.. . opción debe instalar la versión más reciente de NuGet
Seleccione la pestaña en línea
Seleccione el paquete EntityFramework
Haz clic en Instalar
Agregue una instrucción using para System. Data. Entity en la parte superior de [Link].

using [Link];

Debajo de la clase post de [Link], agregue el siguiente contexto derivado.

public class BloggingContext : DbContext


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

Esta es una lista completa de lo que [Link] debería contener ahora.


using System;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace CodeFirstNewDatabaseSample
{
class Program
{
static void Main(string[] args)
{
}
}

public class Blog


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

public virtual 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 virtual Blog Blog { get; set; }
}

public class BloggingContext : DbContext


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

Eso es todo el código que necesitamos para empezar a almacenar y recuperar los datos. Obviamente, hay un
poco más en segundo plano y echaremos un vistazo a eso en un momento, pero primero lo veremos en acción.

4. leer & escribir datos


Implemente el método Main en [Link] como se muestra a continuación. Este código crea una nueva
instancia de nuestro contexto y, a continuación, la usa para insertar un nuevo blog. A continuación, usa una
consulta LINQ para recuperar todos los blogs de la base de datos ordenados alfabéticamente por título.
class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
[Link]("Enter a name for a new Blog: ");
var name = [Link]();

var blog = new Blog { Name = name };


[Link](blog);
[Link]();

// Display all Blogs from the database


var query = from b in [Link]
orderby [Link]
select b;

[Link]("All blogs in the database:");


foreach (var item in query)
{
[Link]([Link]);
}

[Link]("Press any key to exit...");


[Link]();
}
}
}

Ahora puede ejecutar la aplicación y probarla.

Enter a name for a new Blog: [Link] Blog


All blogs in the database:
[Link] Blog
Press any key to exit...

¿Dónde están mis datos?


Por Convención DbContext ha creado una base de datos automáticamente.
Si una instancia local de SQL Express está disponible (se instala de forma predeterminada con Visual Studio
2010), Code First ha creado la base de datos en esa instancia.
Si SQL Express no está disponible, Code First probará y usará LocalDB (instalado de forma predeterminada
con Visual Studio 2012).
La base de datos se denomina después del nombre completo del contexto derivado, en nuestro caso,
CodeFirstNewDatabaseSample. BloggingContext
Estas son solo las convenciones predeterminadas y hay varias maneras de cambiar la base de datos que usa
Code First. hay más información disponible en la sección cómo el DbContext detecta el modelo y la
conexión a la base de datos . Puede conectarse a esta base de datos mediante Explorador de servidores en
Visual Studio
Vista > Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos y seleccione Agregar conexión.. .
Si no se ha conectado a una base de datos desde Explorador de servidores antes de que tenga que
seleccionar Microsoft SQL Server como origen de datos
Conéctese a LocalDB o a SQL Express, en función de la que haya instalado.
Ahora podemos inspeccionar el esquema que Code First creado.

DbContext ha trabajado en las clases que se van a incluir en el modelo examinando las propiedades de DbSet
que hemos definido. A continuación, usa el conjunto predeterminado de convenciones de Code First para
determinar los nombres de tabla y columna, determinar los tipos de datos, buscar claves principales, etc. Más
adelante en este tutorial veremos cómo puede invalidar estas convenciones.

5. tratar con cambios en el modelo


Ahora es el momento de realizar algunos cambios en nuestro modelo, cuando realizamos estos cambios
también necesitamos actualizar el esquema de la base de datos. Para ello, vamos a usar una característica
denominada Migraciones de Code First o migraciones para abreviar.
Las migraciones nos permiten tener un conjunto ordenado de pasos que describen cómo actualizar (y degradar)
el esquema de la base de datos. Cada uno de estos pasos, conocido como migración, contiene código que
describe los cambios que se van a aplicar.
El primer paso es habilitar Migraciones de Code First para nuestro BloggingContext.
Herramientas- > Administrador de paquetes de la biblioteca- > consola del administrador
de paquetes
Ejecute el comando Enable-Migrations en la consola del Administrador de paquetes
Se ha agregado una nueva carpeta Migrations al proyecto que contiene dos elementos:
[Link] : este archivo contiene la configuración que las migraciones usarán para migrar
BloggingContext. No es necesario cambiar nada para este tutorial, pero aquí es donde se pueden
especificar los datos de inicialización, registrar proveedores para otras bases de datos, cambiar el
espacio de nombres que se generan en las migraciones, etc.
** < timestamp > _ [Link]** : esta es la primera migración, que representa los cambios que
ya se han aplicado a la base de datos para que no sea una base de datos vacía a la que se incluyan las
tablas blogs y posts. Aunque se permite a Code First crear automáticamente estas tablas, ahora que
hemos optado por las migraciones que se han convertido en una migración. Code First también se ha
registrado en la base de datos local que ya se ha aplicado esta migración. La marca de tiempo en el
nombre de archivo se usa para la ordenación.
Ahora vamos a hacer un cambio en nuestro modelo, agregaremos una propiedad de dirección URL a la
clase de blog:

public class Blog


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

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


}

Ejecute el comando Add-Migration AddUrl en la consola del administrador de paquetes. El comando Add-
Migration comprueba los cambios desde la última migración y scaffolding una nueva migración con los
cambios que se hayan encontrado. Podemos dar un nombre a las migraciones. en este caso, se llama a la
migración ' AddUrl '. El código con scaffolding está diciendo que necesitamos agregar una columna de
dirección URL, que puede contener datos de cadena, a DBO. Tabla de blogs. Si es necesario, podríamos editar
el código con scaffolding, pero esto no es necesario en este caso.

namespace [Link]
{
using System;
using [Link];

public partial class AddUrl : DbMigration


{
public override void Up()
{
AddColumn("[Link]", "Url", c => [Link]());
}

public override void Down()


{
DropColumn("[Link]", "Url");
}
}
}

Ejecute el comando Update-Database en la consola del administrador de paquetes. Este comando aplicará
las migraciones pendientes a la base de datos. La migración de InitialCreate ya se ha aplicado, por lo que las
migraciones solo aplicarán la nueva migración de AddUrl. Sugerencia: puede usar el modificador – verbose
al llamar a Update-Database para ver el SQL que se ejecuta en la base de datos.
La nueva columna URL se agrega ahora a la tabla blogs en la base de datos:
6. anotaciones de datos
Hasta ahora, simplemente se permite a EF detectar el modelo con sus convenciones predeterminadas, pero
habrá ocasiones en las que nuestras clases no sigan las convenciones y necesitamos poder realizar una
configuración adicional. Hay dos opciones para ello: veremos las anotaciones de datos de esta sección y, a
continuación, la API fluida en la sección siguiente.
Vamos a agregar una clase de usuario a nuestro modelo

public class User


{
public string Username { get; set; }
public string DisplayName { get; set; }
}

También necesitamos agregar un conjunto a nuestro contexto derivado.

public class BloggingContext : DbContext


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

Si intentamos agregar una migración, se obtendría un error que indica que "elusuario de EntityType" no tiene
ninguna clave definida. Defina la clave para este EntityType ". como EF no tiene forma de saber que el
nombre de usuario debe ser la clave principal del usuario.
Ahora vamos a usar anotaciones de datos, por lo que necesitamos agregar una instrucción using en la parte
superior de [Link]

using [Link];

Ahora, anote la propiedad username para identificar que es la clave principal.

public class User


{
[Key]
public string Username { get; set; }
public string DisplayName { get; set; }
}

Usar el comando Add-Migration adduser para aplicar scaffolding a una migración para aplicar estos
cambios a la base de datos
Ejecutar el comando Update-Database para aplicar la nueva migración a la base de datos
La nueva tabla se agrega ahora a la base de datos:

La lista completa de anotaciones admitidas por EF es:


KeyAttribute
StringLengthAttribute
MaxLengthAttribute
ConcurrencyCheckAttribute
RequiredAttribute
TimestampAttribute
ComplexTypeAttribute
ColumnAttribute
TableAttribute
InversePropertyAttribute
ForeignKeyAttribute
DatabaseGeneratedAttribute
NotMappedAttribute

7. API fluida
En la sección anterior, analizamos el uso de anotaciones de datos para complementar o invalidar lo que detectó
la Convención. La otra forma de configurar el modelo es a través de la API fluida de Code First.
La mayoría de la configuración del modelo se puede realizar con anotaciones de datos simples. La API fluida es
una forma más avanzada de especificar la configuración del modelo que abarca todo lo que pueden hacer las
anotaciones de datos además de algunas configuraciones más avanzadas que no son posibles con las
anotaciones de datos. Las anotaciones de datos y la API fluida se pueden usar juntas.
Para acceder a la API fluida, invalide el método OnModelCreating en DbContext. Supongamos que deseamos
cambiar el nombre de la columna en la que se almacena User. DisplayName para mostrar el _ nombre.
Invalide el método OnModelCreating en BloggingContext con el siguiente código
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<User> Users { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link]<User>()
.Property(u => [Link])
.HasColumnName("display_name");
}
}

Use el comando Add-Migration ChangeDisplayName para aplicar scaffolding a una migración para
aplicar estos cambios a la base de datos.
Ejecute el comando Update-Database para aplicar la nueva migración a la base de datos.
Ahora se ha cambiado el nombre de la columna DisplayName a nombre para mostrar _ :

Resumen
En este tutorial, hemos examinado Code First desarrollo con una nueva base de datos. Definimos un modelo
mediante clases y luego usaba ese modelo para crear una base de datos y almacenar y recuperar datos. Una vez
creada la base de datos, usamos Migraciones de Code First para cambiar el esquema a medida que nuestro
modelo evolucionó. También vimos cómo configurar un modelo con anotaciones de datos y la API fluida.
Code First a una base de datos existente
12/03/2021 • 10 minutes to read

Este vídeo y el tutorial paso a paso proporcionan una introducción al desarrollo de Code First que tiene como
destino una base de datos existente. Code First permite definir el modelo mediante # las clases C o [Link].
Opcionalmente, se puede realizar una configuración adicional mediante atributos en las clases y propiedades o
mediante una API fluida.

Visualización del vídeo


Este vídeo ahora está disponible en Channel 9.

Requisitos previos
Para completar este tutorial, necesitará tener Visual Studio 2012 o Visual Studio 2013 instalado.
También necesitará la versión 6,1 (o posterior) de la Entity Framework Tools para Visual Studio instalado.
Consulte obtener Entity Framework para obtener información sobre la instalación de la versión más reciente del
Entity Framework Tools.

1. crear una base de datos existente


Normalmente, cuando el destino es una base de datos existente, ya se creará, pero para este tutorial es
necesario crear una base de datos para tener acceso a.
Vamos a generar la base de datos.
Apertura de Visual Studio
Vista > Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos: > Agregar conexión...
Si no se ha conectado a una base de datos desde Explorador de ser vidores antes de que tenga que
seleccionar Microsoft SQL Ser ver como origen de datos

Conéctese a la instancia de LocalDB y escriba blog como nombre de la base de datos.


Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .

La nueva base de datos aparecerá ahora en Explorador de servidores, haga clic con el botón derecho en
ella y seleccione nueva consulta .
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
CREATE TABLE [dbo].[Blogs] (
[BlogId] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (200) NULL,
[Url] NVARCHAR (200) NULL,
CONSTRAINT [PK_dbo.Blogs] PRIMARY KEY CLUSTERED ([BlogId] ASC)
);

CREATE TABLE [dbo].[Posts] (


[PostId] INT IDENTITY (1, 1) NOT NULL,
[Title] NVARCHAR (200) NULL,
[Content] NTEXT NULL,
[BlogId] INT NOT NULL,
CONSTRAINT [PK_dbo.Posts] PRIMARY KEY CLUSTERED ([PostId] ASC),
CONSTRAINT [FK_dbo.Posts_dbo.Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [dbo].[Blogs] ([BlogId]) ON
DELETE CASCADE
);

INSERT INTO [dbo].[Blogs] ([Name],[Url])


VALUES ('The Visual Studio Blog', '[Link]

INSERT INTO [dbo].[Blogs] ([Name],[Url])


VALUES ('.NET Framework Blog', '[Link]

2. crear la aplicación
Para simplificar las cosas, crearemos una aplicación de consola básica que use Code First para realizar el acceso
a los datos:
Apertura de Visual Studio
Archivo- > nuevo- > proyecto...
Seleccionar ventanas en el menú izquierdo y en la aplicación de consola
Escriba CodeFirstExistingDatabaseSample como nombre
Seleccione Aceptar .

3. modelo de ingeniería inversa


Usaremos el Entity Framework Tools para Visual Studio, que nos ayudará a generar código inicial para asignarlo
a la base de datos. Estas herramientas solo generan código que también podría escribir manualmente si lo
prefiere.
Proyecto- > Agregar nuevo elemento...
Seleccione datos en el menú de la izquierda y, a continuación, [Link] Entity Data Model
Escriba BloggingContext como nombre y haga clic en Aceptar .
Se iniciará el Asistente para Entity Data Model
Seleccione code First desde la base de datos y haga clic en siguiente .
Seleccione la conexión a la base de datos creada en la primera sección y haga clic en siguiente .

Haga clic en la casilla situada junto a tablas para importar todas las tablas y haga clic en Finalizar .
Una vez que se complete el proceso de ingeniería inversa, se agregará al proyecto un número de elementos,
echemos un vistazo a lo que se ha agregado.
Archivo de configuración
Se ha agregado un archivo de [Link] al proyecto, este archivo contiene la cadena de conexión a la base de
datos existente.

<connectionStrings>
<add
name="BloggingContext"
connectionString="data source=(localdb)\mssqllocaldb;initial catalog=Blogging;integrated
security=True;MultipleActiveResultSets=True;App=EntityFramework"
providerName="[Link]" />
</connectionStrings>

Observará también algunas otras opciones del archivo de configuración, que son valores de EF
predeterminados que indican Code First dónde crear las bases de datos. Dado que estamos asignando a una
base de datos existente, esta configuración se omitirá en nuestra aplicación.
Contexto derivado
Se ha agregado una clase BloggingContext al proyecto. El contexto representa una sesión con la base de
datos, lo que nos permite consultar y guardar datos. El contexto expone una DbSet < > para cada tipo de
nuestro modelo. También observará que el constructor predeterminado llama a un constructor base mediante la
sintaxis Name = . Esto indica Code First que la cadena de conexión que se va a utilizar para este contexto se
debe cargar desde el archivo de configuración.
public partial class BloggingContext : DbContext
{
public BloggingContext()
: base("name=BloggingContext")
{
}

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


public virtual DbSet<Post> Posts { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
}
}

Siempre debe usar la sintaxis Name = cuando se usa una cadena de conexión en el archivo de configuración.
Esto garantiza que, si la cadena de conexión no está presente, se producirá Entity Framework en lugar de crear
una nueva base de datos por Convención.
Clases de modelo
Por último, también se han agregado al proyecto una clase de blog y publicación . Estas son las clases de
dominio que componen nuestro modelo. Verá que las anotaciones de datos se aplican a las clases para
especificar la configuración en la que las convenciones de Code First no se alinearán con la estructura de la base
de datos existente. Por ejemplo, verá la anotación StringLength en [Link] y blog. URL , ya que tienen
una longitud máxima de 200 en la base de datos (la Code First predeterminada es usar la longitud máximo que
admite el proveedor de base de datos- nvarchar (Max) en SQL Server).

public partial class Blog


{
public Blog()
{
Posts = new HashSet<Post>();
}

public int BlogId { get; set; }

[StringLength(200)]
public string Name { get; set; }

[StringLength(200)]
public string Url { get; set; }

public virtual ICollection<Post> Posts { get; set; }


}

4. leer & escribir datos


Ahora que tenemos un modelo, es el momento de usarlo para tener acceso a algunos datos. Implemente el
método Main en [Link] como se muestra a continuación. Este código crea una nueva instancia de
nuestro contexto y, a continuación, la usa para insertar un nuevo blog . A continuación, usa una consulta LINQ
para recuperar todos los blogs de la base de datos ordenados alfabéticamente por título .
class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
[Link]("Enter a name for a new Blog: ");
var name = [Link]();

var blog = new Blog { Name = name };


[Link](blog);
[Link]();

// Display all Blogs from the database


var query = from b in [Link]
orderby [Link]
select b;

[Link]("All blogs in the database:");


foreach (var item in query)
{
[Link]([Link]);
}

[Link]("Press any key to exit...");


[Link]();
}
}
}

Ahora puede ejecutar la aplicación y probarla.

Enter a name for a new Blog: [Link] Blog


All blogs in the database:
.NET Framework Blog
[Link] Blog
The Visual Studio Blog
Press any key to exit...

¿Qué ocurre si mi base de datos cambia?


El Asistente para Code First a base de datos está diseñado para generar un conjunto de puntos de partida de
clases que se pueden retocar y modificar. Si cambia el esquema de la base de datos, puede editar manualmente
las clases o realizar otro ingeniero inverso para sobrescribir las clases.

Usar Migraciones de Code First en una base de datos existente


Si desea usar Migraciones de Code First con una base de datos existente, vea migraciones de Code First a una
base de datos existente.

Resumen
En este tutorial, hemos examinado Code First desarrollo con una base de datos existente. Usamos el Entity
Framework Tools de Visual Studio para aplicar ingeniería inversa a un conjunto de clases que se asignan a la
base de datos y que podrían usarse para almacenar y recuperar datos.
Anotaciones de datos de Code First
12/03/2021 • 30 minutes to read

NOTE
EF 4.1 en adelante solo : las características, las API, etc. que se describen en esta página se introdujeron en Entity
Framework 4,1. Si usa una versión anterior, no se aplicará parte o toda esta información.

El contenido de esta página se adapta a un artículo escrito originalmente por Julia Lerman (
<[Link] ).
Entity Framework Code First le permite usar sus propias clases de dominio para representar el modelo en el que
se basa EF para realizar consultas, seguimiento de cambios y funciones de actualización. Code First aprovecha
un patrón de programación denominado "Convención sobre configuración". Code First asumirá que las clases
siguen las convenciones de Entity Framework y, en ese caso, se descargará automáticamente de cómo realizar
su trabajo. Sin embargo, si las clases no siguen esas convenciones, tiene la posibilidad de agregar
configuraciones a las clases para proporcionar a EF la información necesaria.
Code First ofrece dos maneras de agregar estas configuraciones a las clases. Uno es el uso de atributos simples
denominado DataAnnotations y el segundo es el uso de la API fluida de Code First, que proporciona una manera
de describir las configuraciones de forma imperativa, en el código.
En este artículo se centrará en el uso de DataAnnotations (en el espacio de nombres System. ComponentModel.
DataAnnotations) para configurar las clases, resaltando las configuraciones más necesarias. Las anotaciones
también se entienden en una serie de aplicaciones .NET, como [Link] MVC, que permite a estas aplicaciones
aprovechar las mismas anotaciones para las validaciones del lado cliente.

El modelo
Mostraré Code First DataAnnotations con un par sencillo de clases: blog y publicación.

public class Blog


{
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { get; set;}
public virtual ICollection<Post> Posts { get; set; }
}

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public ICollection<Comment> Comments { get; set; }
}

Como están, las clases blog y post siguen la Convención Code First de manera cómoda y no requieren ajustes
para habilitar la compatibilidad con EF. Sin embargo, también puede usar las anotaciones para proporcionar
más información a EF sobre las clases y la base de datos a las que se asignan.
Clave
Entity Framework se basa en cada entidad que tiene un valor de clave que se usa para el seguimiento de
entidades. Una Convención de Code First son las propiedades de clave IMPLÍCITAS; Code First buscará una
propiedad denominada "ID" o una combinación de nombre de clase e "ID", como "BlogId". Esta propiedad se
asignará a una columna de clave principal en la base de datos.
Las clases blog y post siguen esta Convención. ¿Qué ocurre si no? ¿Qué ocurre si el blog usa el nombre
PrimaryTrackingKey en su lugar, o incluso foo? Si Code First no encuentra una propiedad que coincida con esta
Convención, producirá una excepción debido al requisito de Entity Framework que debe tener una propiedad de
clave. Puede usar la anotación de clave para especificar qué propiedad se va a usar como EntityKey.

public class Blog


{
[Key]
public int PrimaryTrackingKey { get; set; }
public string Title { get; set; }
public string BloggerName { get; set;}
public virtual ICollection<Post> Posts { get; set; }
}

Si usa la característica de generación de bases de datos de Code First, la tabla de blog tendrá una columna de
clave principal denominada PrimaryTrackingKey, que también se define como Identity de forma predeterminada.

Claves compuestas
Entity Framework admite claves compuestas: las claves principales que se componen de más de una propiedad.
Por ejemplo, podría tener una clase de Passport cuya clave principal sea una combinación de PassportNumber y
IssuingCountry.

public class Passport


{
[Key]
public int PassportNumber { get; set; }
[Key]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}

Si se intenta usar la clase anterior en el modelo EF, se produciría una InvalidOperationException :


No se puede determinar el orden de claves principales compuesto para el tipo ' Passport '. Use el método
ColumnAttribute o Haskey (para especificar un orden para las claves principales compuestas.
Para usar las claves compuestas, Entity Framework requiere definir un orden para las propiedades de clave.
Puede hacerlo mediante la anotación de columna para especificar un orden.
NOTE
El valor del orden es relativo (en lugar de basado en índice), por lo que se pueden usar los valores. Por ejemplo, 100 y 200
serían aceptables en lugar de 1 y 2.

public class Passport


{
[Key]
[Column(Order=1)]
public int PassportNumber { get; set; }
[Key]
[Column(Order = 2)]
public string IssuingCountry { get; set; }
public DateTime Issued { get; set; }
public DateTime Expires { get; set; }
}

Si tiene entidades con claves externas compuestas, debe especificar la misma ordenación de columnas que usó
para las propiedades de clave principal correspondientes.
Solo el orden relativo dentro de las propiedades de clave externa debe ser el mismo, no es necesario que
coincidan los valores exactos asignados al pedido . Por ejemplo, en la clase siguiente, se podría usar 3 y 4 en
lugar de 1 y 2.

public class PassportStamp


{
[Key]
public int StampId { get; set; }
public DateTime Stamped { get; set; }
public string StampingCountry { get; set; }

[ForeignKey("Passport")]
[Column(Order = 1)]
public int PassportNumber { get; set; }

[ForeignKey("Passport")]
[Column(Order = 2)]
public string IssuingCountry { get; set; }

public Passport Passport { get; set; }


}

Requerido
La Required anotación indica a EF que se requiere una propiedad determinada.
Agregar obligatorio a la propiedad title forzará a EF (y MVC) a asegurarse de que la propiedad contiene datos.

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

Sin ningún cambio de código o marcado adicional en la aplicación, una aplicación MVC realizará la validación
del lado cliente, incluso generando dinámicamente un mensaje usando los nombres de la propiedad y de las
anotaciones.
El atributo required también afectará a la base de datos generada haciendo que la propiedad asignada no acepte
valores NULL. Observe que el campo title ha cambiado a "not null".

NOTE
En algunos casos, es posible que no sea posible que la columna de la base de datos no acepte valores NULL, aunque se
requiera la propiedad. Por ejemplo, cuando se usa un dato de estrategia de herencia de TPH para varios tipos, se
almacena en una sola tabla. Si un tipo derivado incluye una propiedad necesaria, no se puede hacer que la columna no
acepte valores NULL, ya que no todos los tipos de la jerarquía tendrán esta propiedad.

MaxLength y MinLength
Los MaxLength MinLength atributos y permiten especificar validaciones de propiedades adicionales, tal como se
hizo con Required .
Este es el BloggerName con los requisitos de longitud. En el ejemplo también se muestra cómo combinar
atributos.

[MaxLength(10),MinLength(5)]
public string BloggerName { get; set; }

La anotación MaxLength afectará a la base de datos estableciendo la longitud de la propiedad en 10.

La anotación del lado cliente de MVC y la anotación del lado servidor EF 4,1 respetarán esta validación y, de
nuevo, se generará dinámicamente un mensaje de error: "el campo BloggerName debe ser una cadena o un tipo
de matriz con una longitud máxima de ' 10 '". Ese mensaje es un poco largo. Muchas anotaciones permiten
especificar un mensaje de error con el atributo ErrorMessage.
[MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]
public string BloggerName { get; set; }

También puede especificar ErrorMessage en la anotación requerida.

NotMapped
La Convención Code First dicta que cada propiedad que es de un tipo de datos admitido se representa en la
base de datos. Pero esto no siempre es el caso en las aplicaciones. Por ejemplo, podría tener una propiedad en la
clase de blog que crea un código basado en los campos título y BloggerName. Esa propiedad se puede crear
dinámicamente y no es necesario almacenarla. Puede marcar las propiedades que no se asignan a la base de
datos con la anotación NotMapped como esta propiedad BlogCode.

[NotMapped]
public string BlogCode
{
get
{
return [Link](0, 1) + ":" + [Link](0, 1);
}
}

ComplexType
No es raro describir las entidades de dominio en un conjunto de clases y, a continuación, crear capas de esas
clases para describir una entidad completa. Por ejemplo, puede Agregar una clase denominada BlogDetails al
modelo.

public class BlogDetails


{
public DateTime? DateCreated { get; set; }

[MaxLength(250)]
public string Description { get; set; }
}

Tenga en cuenta que no BlogDetails tiene ningún tipo de propiedad de clave. En el diseño controlado por
dominios, BlogDetails se conoce como objeto de valor. Entity Framework hace referencia a los objetos de valor
como tipos [Link] se puede realizar un seguimiento de los tipos complejos por su cuenta.
Sin embargo, como una propiedad de la Blog clase, se BlogDetails realizará un seguimiento como parte de
un Blog objeto. Para que Code First lo reconozca, debe marcar la BlogDetails clase como ComplexType .

[ComplexType]
public class BlogDetails
{
public DateTime? DateCreated { get; set; }

[MaxLength(250)]
public string Description { get; set; }
}

Ahora puede Agregar una propiedad en la Blog clase para representar el BlogDetails para ese blog.

public BlogDetails BlogDetail { get; set; }

En la base de datos, la tabla contendrá Blog todas las propiedades del blog, incluidas las propiedades
contenidas en su BlogDetail propiedad. De forma predeterminada, cada uno está precedido por el nombre del
tipo complejo, "BlogDetail".

ConcurrencyCheck
La ConcurrencyCheck anotación permite marcar una o más propiedades que se usarán para la comprobación de
simultaneidad en la base de datos cuando un usuario edita o elimina una entidad. Si ha estado trabajando con
EF Designer, esto se alinea con el establecimiento de la propiedad ConcurrencyMode en Fixed .
Veamos cómo ConcurrencyCheck funciona agregándolo a la BloggerName propiedad.

[ConcurrencyCheck, MaxLength(10, ErrorMessage="BloggerName must be 10 characters or less"),MinLength(5)]


public string BloggerName { get; set; }

Cuando SaveChanges se llama a, debido a la ConcurrencyCheck anotación en el BloggerName campo, se usará el


valor original de dicha propiedad en la actualización. El comando intentará buscar la fila correcta filtrando no
solo en el valor de clave, sino también en el valor original de BloggerName .Estas son las partes fundamentales
del comando UPDATE que se envía a la base de datos, donde puede ver que el comando actualizará la fila que
tiene el PrimaryTrackingKey valor 1 y un BloggerName de "Julia", que era el valor original cuando el blog se
recuperó de la base de datos.

where (([PrimaryTrackingKey] = @4) and ([BloggerName] = @5))


@4=1,@5=N'Julie'

Si un usuario ha cambiado el nombre de blogger para ese blog mientras tanto, se producirá un error en esta
actualización y obtendrá un DbUpdateConcurrencyException que deberá controlar.

TimeStamp
Es más común usar los campos rowversion o TIMESTAMP para la comprobación de simultaneidad. Pero en lugar
de utilizar la ConcurrencyCheck anotación, puede usar la anotación más específica siempre que TimeStamp el
tipo de la propiedad sea una matriz de bytes. Code First tratará las Timestamp Propiedades igual que
ConcurrencyCheck las propiedades, pero también se asegurará de que el campo de base de datos generado por
Code First no admita valores NULL. Solo puede tener una propiedad timestamp en una clase determinada.
Agregando la siguiente propiedad a la clase de blog:

[Timestamp]
public Byte[] TimeStamp { get; set; }

en primer lugar, el código crea una columna de marca de tiempo que no acepta valores NULL en la tabla de base
de datos.

Tabla y columna
Si va a permitir que Code First cree la base de datos, puede que desee cambiar el nombre de las tablas y
columnas que está creando. También puede usar Code First con una base de datos existente. Pero no siempre es
el caso de que los nombres de las clases y propiedades de su dominio coincidan con los nombres de las tablas y
columnas de la base de datos.
Mi clase se denomina Blog y por Convención, Code First presupone que se asignará a una tabla denominada
Blogs . Si no es así, puede especificar el nombre de la tabla con el Table atributo. Aquí, por ejemplo, la
anotación está especificando que el nombre de la tabla es InternalBlogs .

[Table("InternalBlogs")]
public class Blog

La Column anotación es más bien para especificar los atributos de una columna asignada. Puede estipular un
nombre, un tipo de datos o incluso el orden en el que aparece una columna en la tabla. Este es un ejemplo del
Column atributo.

[Column("BlogDescription", TypeName="ntext")]
public String Description {get;set;}

No confunda TypeName el atributo de la columna con la anotación DataType. DataType es una anotación que se
usa para la interfaz de usuario y Code First omitirá.
Esta es la tabla una vez regenerada. El nombre de la tabla ha cambiado a InternalBlogs y Description la
columna del tipo complejo es ahora BlogDescription . Dado que el nombre se especificó en la anotación, Code
First no usará la Convención de inicio del nombre de columna con el nombre del tipo complejo.
DatabaseGenerated
Una característica importante de las bases de datos es la capacidad de tener propiedades calculadas. Si va a
asignar las clases de Code First a tablas que contienen columnas calculadas, no desea que Entity Framework
intente actualizar esas columnas. Pero quiere que EF devuelva los valores de la base de datos después de
insertar o actualizar los datos. Puede usar la DatabaseGenerated anotación para marcar esas propiedades en la
clase junto con la Computed enumeración. Otras enumeraciones son None y Identity .

[DatabaseGenerated([Link])]
public DateTime DateCreated { get; set; }

Puede usar la base de datos generada en columnas de tipo byte o TIMESTAMP cuando Code First está
generando la base de datos; de lo contrario, solo debería usar esta al apuntar a bases de datos existentes porque
Code First no podrá determinar la fórmula de la columna calculada.
Antes de que lea de forma predeterminada, una propiedad clave que es un entero se convertirá en una clave de
identidad en la base de datos. Es lo mismo que establecer DatabaseGenerated en
[Link] . Si no desea que sea una clave de identidad, puede establecer el valor en
[Link] .

Índice
NOTE
EF 6.1 en adelante solo : el Index atributo se presentó en Entity Framework 6,1. Si usa una versión anterior, no se
aplica la información de esta sección.

Puede crear un índice en una o más columnas mediante IndexAttribute . Si se agrega el atributo a una o varias
propiedades, EF creará el índice correspondiente en la base de datos al crear la base de datos, o scaffolding, las
llamadas CreateIndex correspondientes si usa migraciones de Code First.
Por ejemplo, el código siguiente generará un índice que se creará en la Rating columna de la Posts tabla de la
base de datos.

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Index]
public int Rating { get; set; }
public int BlogId { get; set; }
}
De forma predeterminada, el índice se denominará ** _ < nombre > de propiedad IX** (clasificación IX _ en el
ejemplo anterior). No obstante, también puede especificar un nombre para el índice. En el ejemplo siguiente se
especifica que el índice se debe denominar PostRatingIndex .

[Index("PostRatingIndex")]
public int Rating { get; set; }

De forma predeterminada, los índices no son únicos, pero puede usar el IsUnique parámetro con nombre para
especificar que un índice debe ser único. En el ejemplo siguiente se presenta un índice único en el User nombre
de inicio de sesión de.

public class User


{
public int UserId { get; set; }

[Index(IsUnique = true)]
[StringLength(200)]
public string Username { get; set; }

public string DisplayName { get; set; }


}

Índices de Multiple -Column


Los índices que abarcan varias columnas se especifican utilizando el mismo nombre en varias anotaciones de
índice para una tabla determinada. Al crear índices de varias columnas, debe especificar un orden para las
columnas en el índice. Por ejemplo, el código siguiente crea un índice de varias columnas en Rating y BlogId
denominado IX _ BlogIdAndRating . BlogId es la primera columna del índice y Rating es el segundo.

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
[Index("IX_BlogIdAndRating", 2)]
public int Rating { get; set; }
[Index("IX_BlogIdAndRating", 1)]
public int BlogId { get; set; }
}

Atributos de relación: InverseProperty y ForeignKey


NOTE
En esta página se proporciona información sobre cómo configurar las relaciones en el modelo de Code First con
anotaciones de datos. Para obtener información general sobre las relaciones en EF y cómo obtener acceso a los datos y
manipularlos mediante relaciones, vea relaciones & propiedades de navegación. *

La Convención Code First se encargará de las relaciones más comunes del modelo, pero hay algunos casos en
los que necesita ayuda.
Cambiar el nombre de la propiedad de clave en la Blog clase creó un problema con su relación con Post .
Al generar la base de datos, Code First ve la BlogId propiedad en la clase post y la reconoce, por la Convención
que coincide con un nombre de clase más ID , como una clave externa a la Blog clase. Pero no hay ninguna
BlogId propiedad en la clase de blog. La solución para esto es crear una propiedad de navegación en Post y
utilizar la anotación de datos ForeignKey para que el código de ayuda comprenda primero cómo crear la
relación entre las dos clases (mediante la [Link] propiedad) y cómo especificar restricciones en la base de
datos.

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
[ForeignKey("BlogId")]
public Blog Blog { get; set; }
public ICollection<Comment> Comments { get; set; }
}

La restricción en la base de datos muestra una relación entre [Link] y [Link]


.

InverseProperty Se usa cuando se tienen varias relaciones entre las clases.


En la Post clase, puede que desee realizar un seguimiento de quién escribió una entrada de blog y quién la
editó. A continuación se muestran dos nuevas propiedades de navegación para la clase post.

public Person CreatedBy { get; set; }


public Person UpdatedBy { get; set; }

También necesitará agregar en la clase a la Person que hacen referencia estas propiedades. La Person clase
tiene propiedades de navegación de vuelta a Post , una para todas las publicaciones escritas por la persona y
otra para todas las entradas actualizadas por esa persona.

public class Person


{
public int Id { get; set; }
public string Name { get; set; }
public List<Post> PostsWritten { get; set; }
public List<Post> PostsUpdated { get; set; }
}

Code First no puede hacer coincidir las propiedades de las dos clases por sí mismas. La tabla de base de datos
de Posts debe tener una clave externa para la CreatedBy persona y otra para la UpdatedBy persona, pero Code
First creará cuatro propiedades de clave externa: Person _ ID , Person _ Id1 , CreatedBy _ ID e UpdatedBy _
ID .
Para corregir estos problemas, puede usar la InverseProperty anotación para especificar la alineación de las
propiedades.

[InverseProperty("CreatedBy")]
public List<Post> PostsWritten { get; set; }

[InverseProperty("UpdatedBy")]
public List<Post> PostsUpdated { get; set; }

Dado PostsWritten que la propiedad en persona sabe que esto hace referencia al Post tipo, creará la relación
con [Link] . Del mismo modo, se PostsUpdated conectará a [Link] . Y Code First no crearán
las claves externas adicionales.

Resumen
Las DataAnnotations no solo permiten describir la validación del lado cliente y del servidor en las clases Code
First, sino que también permiten mejorar e incluso corregir las suposiciones que Code First realizará sobre las
clases en función de sus convenciones. Con DataAnnotations no solo puede controlar la generación de
esquemas de base de datos, sino que también puede asignar las clases Code First a una base de datos existente
previamente.
Aunque son muy flexibles, tenga en cuenta que las anotaciones solo proporcionan los cambios de configuración
más necesarios que se pueden realizar en las clases Code First. Para configurar las clases para algunos de los
casos extremos, debe buscar el mecanismo de configuración alternativo, la API fluida de Code First.
Definir DbSets
12/03/2021 • 3 minutes to read

Al desarrollar con el flujo de trabajo de Code First se define un DbContext derivado que representa la sesión con
la base de datos y expone un DbSet para cada tipo del modelo. En este tema se tratan las distintas formas en
que se pueden definir las propiedades de DbSet.

DbContext con propiedades DbSet


El caso común que se muestra en Code First ejemplos es tener un DbContext con propiedades DbSet
automáticas públicas para los tipos de entidad del modelo. Por ejemplo:

public class BloggingContext : DbContext


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

Cuando se usa en modo de Code First, se configurarán los blogs y los envíos como tipos de entidad, así como la
configuración de otros tipos accesibles desde ellos. Además, DbContext llamará automáticamente al
establecedor para cada una de estas propiedades con el fin de establecer una instancia del DbSet adecuado.

DbContext con propiedades IDbSet


Existen situaciones, como cuando se crean simulacros o simulaciones, donde resulta más útil declarar las
propiedades set mediante una interfaz. En tales casos, se puede usar la interfaz IDbSet en lugar de DbSet. Por
ejemplo:

public class BloggingContext : DbContext


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

Este contexto funciona exactamente de la misma manera que el contexto que usa la clase DbSet para sus
propiedades set.

DbContext con propiedades set de solo lectura


Si no desea exponer establecedores públicos para las propiedades DbSet o IDbSet, puede crear propiedades de
solo lectura y crear las instancias de conjunto. Por ejemplo:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs
{
get { return Set<Blog>(); }
}

public DbSet<Post> Posts


{
get { return Set<Post>(); }
}
}

Tenga en cuenta que DbContext almacena en caché la instancia de DbSet devuelta por el método Set para que
cada una de estas propiedades devuelva la misma instancia cada vez que se llame a.
La detección de tipos de entidad para Code First funciona de la misma manera que en las propiedades con
captadores y establecedores públicos.
Compatibilidad con enum-Code First
12/03/2021 • 8 minutes to read

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

Este tutorial de vídeo y paso a paso muestra cómo utilizar tipos de enumeración con Entity Framework Code
First. También se muestra cómo usar las enumeraciones en una consulta LINQ.
En este tutorial se utilizará Code First para crear una nueva base de datos, pero también puede usar code First
para asignarla a una base de datos existente.
La compatibilidad con la enumeración se presentó en Entity Framework 5. Para usar las nuevas características,
como las enumeraciones, los tipos de datos espaciales y las funciones con valores de tabla, debe tener como
destino .NET Framework 4,5. Visual Studio 2012 tiene como destino .NET 4,5 de forma predeterminada.
En Entity Framework, una enumeración puede tener los siguientes tipos subyacentes: byte , Int16 , Int32 , Int64
o SByte .

Visualización del vídeo


En este vídeo se muestra cómo utilizar tipos de enumeración con Code First de Entity Framework. También se
muestra cómo usar las enumeraciones en una consulta LINQ.
Presentada por : Julia Kornich
Vídeo : WMV | MP4 | WMV (zip)

Requisitos previos
Deberá tener instalado Visual Studio 2012, Ultimate, Premium, Professional o Web Express Edition para
completar este tutorial.

Configurar el proyecto
1. Abra Visual Studio 2012
2. En el menú archivo , seleccione nuevo y, a continuación, haga clic en proyecto .
3. En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
4. Escriba EnumCodeFirst como nombre del proyecto y haga clic en Aceptar .

Definir un nuevo modelo mediante Code First


Al usar Code First desarrollo, normalmente comienza por escribir .NET Framework clases que definen el modelo
conceptual (de dominio). El código siguiente define la clase Department.
El código también define la enumeración DepartmentNames. De forma predeterminada, la enumeración es de
tipo int . La propiedad Name de la clase Department es del tipo DepartmentNames.
Abra el archivo [Link] y pegue las siguientes definiciones de clase.

public enum DepartmentNames


{
English,
Math,
Economics
}

public partial class Department


{
public int DepartmentID { get; set; }
public DepartmentNames Name { get; set; }
public decimal Budget { get; set; }
}

Definir el tipo derivado de DbContext


Además de definir entidades, debe definir una clase que derive de DbContext y exponga las propiedades
DbSet<TEntity>. Las propiedades DbSet<TEntity> permiten que el contexto sepa qué tipos desea incluir en el
modelo.
Una instancia del tipo derivado de DbContext administra los objetos de entidad durante el tiempo de ejecución,
lo que incluye rellenar los objetos con datos de una base de datos, el seguimiento de cambios y la persistencia
de datos en la base de datos.
Los tipos DbContext y DbSet se definen en el ensamblado EntityFramework. Se agregará una referencia a este
archivo DLL mediante el paquete NuGet EntityFramework.
1. En Explorador de soluciones, haga clic con el botón derecho en el nombre del proyecto.
2. Seleccione administrar paquetes NuGet.. .
3. En el cuadro de diálogo administrar paquetes NuGet, seleccione la pestaña en línea y elija el paquete
EntityFramework .
4. Haz clic en Instalar
Tenga en cuenta que, además del ensamblado EntityFramework, también se agregan las referencias a los
ensamblados System. ComponentModel. DataAnnotations y System. Data. Entity.
En la parte superior del archivo [Link], agregue la siguiente instrucción using:

using [Link];

En [Link], agregue la definición de contexto.

public partial class EnumTestContext : DbContext


{
public DbSet<Department> Departments { get; set; }
}

Conservar y recuperar datos


Abra el archivo [Link] en el que se define el método Main. Agregue el código siguiente a la función main. El
código agrega un nuevo objeto Department al contexto. Después guarda los datos. El código también ejecuta
una consulta LINQ que devuelve un departamento en el que el nombre es DepartmentNames. English.

using (var context = new EnumTestContext())


{
[Link](new Department { Name = [Link] });

[Link]();

var department = (from d in [Link]


where [Link] == [Link]
select d).FirstOrDefault();

[Link](
"DepartmentID: {0} Name: {1}",
[Link],
[Link]);
}

Compile y ejecute la aplicación. El programa produce el siguiente resultado:

DepartmentID: 1 Name: English

Ver la base de datos generada


Al ejecutar la aplicación por primera vez, el Entity Framework crea una base de datos automáticamente. Dado
que tenemos instalado Visual Studio 2012, la base de datos se creará en la instancia de LocalDB. De forma
predeterminada, el Entity Framework nombra la base de datos después del nombre completo del contexto
derivado (para este ejemplo, que es EnumCodeFirst. EnumTestContext ). Las veces posteriores en las que se
utilizará la base de datos existente.
Tenga en cuenta que si realiza cambios en el modelo una vez creada la base de datos, debe utilizar Migraciones
de Code First para actualizar el esquema de la base de datos. Vea code First en una nueva base de datos para
obtener un ejemplo del uso de las migraciones.
Para ver la base de datos y los datos, haga lo siguiente:
1. En el menú principal de Visual Studio 2012, seleccione Ver - > Explorador de objetos de SQL Ser ver .
2. Si LocalDB no está en la lista de servidores, haga clic con el botón secundario del mouse en SQL Ser ver y
seleccione Agregar SQL Ser ver usar la autenticación de Windows predeterminada para conectarse a la
instancia de LocalDB.
3. Expandir el nodo LocalDB
4. Desabra la carpeta bases de datos para ver la nueva base de datos y vaya a la tabla Depar tment . tenga en
cuenta que Code First no crea una tabla que se asigne al tipo de enumeración
5. Para ver los datos, haga clic con el botón derecho en la tabla y seleccione ver datos .

Resumen
En este tutorial, hemos visto cómo usar los tipos de enumeración con Entity Framework Code First.
Code First espacial
12/03/2021 • 9 minutes to read

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

En el tutorial de vídeo y paso a paso se muestra cómo asignar tipos espaciales con Entity Framework Code First.
También se muestra cómo usar una consulta LINQ para buscar una distancia entre dos ubicaciones.
En este tutorial se utilizará Code First para crear una nueva base de datos, pero también puede usar code First a
una base de datos existente.
La compatibilidad con tipos espaciales se presentó en Entity Framework 5. Tenga en cuenta que para usar las
nuevas características, como el tipo espacial, las enumeraciones y las funciones con valores de tabla, debe tener
como destino .NET Framework 4,5. Visual Studio 2012 tiene como destino .NET 4,5 de forma predeterminada.
Para usar los tipos de datos espaciales, también debe usar un proveedor de Entity Framework que tenga
compatibilidad espacial. Consulte compatibilidad con proveedores para tipos espaciales para obtener más
información.
Hay dos tipos de datos espaciales principales: Geography y Geometry. El tipo de datos Geography almacena los
datos de datos elipsoidales (por ejemplo, las coordenadas de latitud y longitud de GPS). El tipo de datos
Geometry representa el sistema de coordenadas euclidiana (plano).

Visualización del vídeo


Este vídeo muestra cómo asignar tipos espaciales con Entity Framework Code First. También se muestra cómo
usar una consulta LINQ para buscar una distancia entre dos ubicaciones.
Presentada por : Julia Kornich
Vídeo : WMV | MP4 | WMV (zip)

Requisitos previos
Deberá tener instalado Visual Studio 2012, Ultimate, Premium, Professional o Web Express Edition para
completar este tutorial.

Configurar el proyecto
1. Abra Visual Studio 2012
2. En el menú archivo , seleccione nuevo y, a continuación, haga clic en proyecto .
3. En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
4. Escriba SpatialCodeFirst como nombre del proyecto y haga clic en Aceptar .

Definir un nuevo modelo mediante Code First


Al usar Code First desarrollo, normalmente comienza por escribir .NET Framework clases que definen el modelo
conceptual (de dominio). El código siguiente define la clase University.
La Universidad tiene la propiedad Location del tipo DbGeography. Para usar el tipo DbGeography, debe agregar
una referencia al ensamblado System. Data. Entity y también agregar la instrucción using System. Data. Spatial.
Abra el archivo [Link] y pegue las siguientes instrucciones Using en la parte superior del archivo:

using [Link];

Agregue la siguiente definición de clase University al archivo [Link].

public class University


{
public int UniversityID { get; set; }
public string Name { get; set; }
public DbGeography Location { get; set; }
}

Definir el tipo derivado de DbContext


Además de definir entidades, debe definir una clase que derive de DbContext y exponga las propiedades
DbSet<TEntity>. Las propiedades DbSet<TEntity> permiten que el contexto sepa qué tipos desea incluir en el
modelo.
Una instancia del tipo derivado de DbContext administra los objetos de entidad durante el tiempo de ejecución,
lo que incluye rellenar los objetos con datos de una base de datos, el seguimiento de cambios y la persistencia
de datos en la base de datos.
Los tipos DbContext y DbSet se definen en el ensamblado EntityFramework. Se agregará una referencia a este
archivo DLL mediante el paquete NuGet EntityFramework.
1. En Explorador de soluciones, haga clic con el botón derecho en el nombre del proyecto.
2. Seleccione administrar paquetes NuGet.. .
3. En el cuadro de diálogo administrar paquetes NuGet, seleccione la pestaña en línea y elija el paquete
EntityFramework .
4. Haz clic en Instalar
Tenga en cuenta que, además del ensamblado EntityFramework, también se agrega una referencia al
ensamblado System. ComponentModel. DataAnnotations.
En la parte superior del archivo [Link], agregue la siguiente instrucción using:

using [Link];

En [Link], agregue la definición de contexto.

public partial class UniversityContext : DbContext


{
public DbSet<University> Universities { get; set; }
}

Conservar y recuperar datos


Abra el archivo [Link] en el que se define el método Main. Agregue el código siguiente a la función main.
El código agrega dos nuevos objetos universitarios al contexto. Las propiedades espaciales se inicializan
mediante el método DbGeography. FromText. El punto de geografía representado como WellKnownText se pasa
al método. Después, el código guarda los datos. A continuación, se crea y se ejecuta la consulta LINQ que
devuelve un objeto University donde su ubicación es más cercana a la ubicación especificada.

using (var context = new UniversityContext ())


{
[Link](new University()
{
Name = "Graphic Design Institute",
Location = [Link]("POINT(-122.336106 47.605049)"),
});

context. [Link](new University()


{
Name = "School of Fine Art",
Location = [Link]("POINT(-122.335197 47.646711)"),
});

[Link]();

var myLocation = [Link]("POINT(-122.296623 47.640405)");

var university = (from u in [Link]


orderby [Link](myLocation)
select u).FirstOrDefault();

[Link](
"The closest University to you is: {0}.",
[Link]);
}

Compile y ejecute la aplicación. El programa produce el siguiente resultado:

The closest University to you is: School of Fine Art.

Ver la base de datos generada


Al ejecutar la aplicación por primera vez, el Entity Framework crea una base de datos automáticamente. Dado
que tenemos instalado Visual Studio 2012, la base de datos se creará en la instancia de LocalDB. De forma
predeterminada, el Entity Framework nombra la base de datos después del nombre completo del contexto
derivado (en este ejemplo, que es SpatialCodeFirst. UniversityContext ). Las veces posteriores en las que se
utilizará la base de datos existente.
Tenga en cuenta que si realiza cambios en el modelo una vez creada la base de datos, debe utilizar Migraciones
de Code First para actualizar el esquema de la base de datos. Vea code First en una nueva base de datos para
obtener un ejemplo del uso de las migraciones.
Para ver la base de datos y los datos, haga lo siguiente:
1. En el menú principal de Visual Studio 2012, seleccione Ver - > Explorador de objetos de SQL Ser ver .
2. Si LocalDB no está en la lista de servidores, haga clic con el botón secundario del mouse en SQL Ser ver y
seleccione Agregar SQL Ser ver usar la autenticación de Windows predeterminada para conectarse a la
instancia de LocalDB.
3. Expandir el nodo LocalDB
4. Desdoblar la carpeta bases de datos para ver la nueva base de datos y examinar la tabla universidades
5. Para ver los datos, haga clic con el botón derecho en la tabla y seleccione ver datos .
Resumen
En este tutorial, hemos visto cómo usar los tipos espaciales con Entity Framework Code First.
Convenciones de Code First
12/03/2021 • 10 minutes to read

Code First le permite describir un modelo mediante el uso de clases de C# o Visual Basic .NET. La forma básica
del modelo se detecta mediante el uso de convenciones. Las convenciones son conjuntos de reglas que se
utilizan para configurar automáticamente un modelo conceptual basado en definiciones de clase al trabajar con
Code First. Las convenciones se definen en el espacio de nombres System. Data. Entity. ModelConfiguration.
Conventions.
Puede seguir configurando el modelo con anotaciones de datos o la API fluida. Se da prioridad a la
configuración a través de la API fluida, seguida de las anotaciones de datos y, a continuación, las convenciones.
Para obtener más información, vea anotaciones de datos, API fluidas, relaciones, tipos de API fluidas &
propiedades y API fluida con [Link].
Puede encontrar una lista detallada de las convenciones de Code First en la documentación de la API. En este
tema se proporciona información general sobre las convenciones utilizadas por Code First.

Detección de tipos
Al usar Code First desarrollo, normalmente comienza por escribir .NET Framework clases que definen el modelo
conceptual (de dominio). Además de definir las clases, también debe dejar que DbContext sepa qué tipos
quiere incluir en el modelo. Para ello, defina una clase de contexto que derive de DbContext y exponga
propiedades DbSet para los tipos que desea que formen parte del modelo. Code First incluirá estos tipos y
también extraerá todos los tipos a los que se hace referencia, aunque los tipos a los que se hace referencia se
hayan definido en un ensamblado diferente.
Si los tipos participan en una jerarquía de herencia, basta con definir una propiedad DbSet para la clase base y
los tipos derivados se incluirán automáticamente, si están en el mismo ensamblado que la clase base.
En el ejemplo siguiente, solo hay una propiedad DbSet definida en la clase SchoolEntities (depar tments ).
Code First usa esta propiedad para detectar y extraer todos los tipos a los que se hace referencia.
public class SchoolEntities : DbContext
{
public DbSet<Department> Departments { get; set; }
}

public class Department


{
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }

// Navigation property
public virtual ICollection<Course> Courses { get; set; }
}

public class Course


{
// Primary key
public int CourseID { get; set; }

public string Title { get; set; }


public int Credits { get; set; }

// Foreign key
public int DepartmentID { get; set; }

// Navigation properties
public virtual Department Department { get; set; }
}

public partial class OnlineCourse : Course


{
public string URL { get; set; }
}

public partial class OnsiteCourse : Course


{
public string Location { get; set; }
public string Days { get; set; }
public [Link] Time { get; set; }
}

Si desea excluir un tipo del modelo, use el atributo NotMapped o la API fluida DbModelBuilder. ignore .

[Link]<Department>();

Convención de clave principal


Code First infiere que una propiedad es una clave principal si una propiedad de una clase se denomina "ID" (no
distingue mayúsculas de minúsculas) o el nombre de clase seguido de "ID". Si el tipo de la propiedad de clave
principal es numérico o GUID, se configurará como una columna de identidad.

public class Department


{
// Primary key
public int DepartmentID { get; set; }

. . .

}
Convención de relación
En Entity Framework, las propiedades de navegación proporcionan una manera de navegar por una relación
entre dos tipos de entidad. Cada objeto puede tener una propiedad de navegación para cada relación en la que
participa. Las propiedades de navegación permiten navegar y administrar las relaciones en ambas direcciones,
devolviendo un objeto de referencia (si la multiplicidad es uno o cero o uno) o una colección (si la multiplicidad
es muchas). Code First deduce las relaciones basadas en las propiedades de navegación definidas en los tipos.
Además de las propiedades de navegación, se recomienda incluir las propiedades de clave externa en los tipos
que representan los objetos dependientes. Cualquier propiedad con el mismo tipo de datos que la propiedad de
clave principal principal y con un nombre que sigue a uno de los siguientes formatos representa una clave
externa para la relación: ' <navigation property name> <principal primary key property name> ', ' <principal
class name> <primary key property name> ' o ' <principal primary key property name> '. Si se encuentran
varias coincidencias, se da prioridad en el orden indicado anteriormente. La detección de clave externa no
distingue entre mayúsculas y minúsculas. Cuando se detecta una propiedad de clave externa, Code First deduce
la multiplicidad de la relación en función de la nulabilidad de la clave externa. Si la propiedad admite valores
NULL, la relación se registra como opcional; de lo contrario, la relación se registra según sea necesario.
Si una clave externa de la entidad dependiente no admite valores NULL, Code First establece Cascade delete en
la relación. Si una clave externa de la entidad dependiente admite valores NULL, Code First no establece Cascade
delete en la relación y, cuando se elimina la entidad de seguridad, la clave externa se establecerá en NULL. El
comportamiento de la multiplicidad y la eliminación en cascada detectados por la Convención se pueden
invalidar mediante la API fluida.
En el ejemplo siguiente se usan las propiedades de navegación y una clave externa para definir la relación entre
las clases Department y Course.

public class Department


{
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }

// Navigation property
public virtual ICollection<Course> Courses { get; set; }
}

public class Course


{
// Primary key
public int CourseID { get; set; }

public string Title { get; set; }


public int Credits { get; set; }

// Foreign key
public int DepartmentID { get; set; }

// Navigation properties
public virtual Department Department { get; set; }
}

NOTE
Si tiene varias relaciones entre los mismos tipos (por ejemplo, supongamos que define las clases Person y book , donde
la clase Person contiene las propiedades de navegación ReviewedBooks y AuthoredBooks y la clase book contiene
las propiedades de navegación autor y Revisor ), debe configurar manualmente las relaciones mediante anotaciones de
datos o la API fluida. Para obtener más información, vea anotaciones de datos: relaciones y API fluida.
Convención de tipos complejos
Cuando Code First detecta una definición de clase en la que no se puede inferir una clave principal y no se
registra ninguna clave principal a través de anotaciones de datos o la API fluida, el tipo se registra
automáticamente como un tipo complejo. La detección de tipos complejos también requiere que el tipo no
tenga propiedades que hagan referencia a los tipos de entidad y no se haga referencia a ellos desde una
propiedad de colección en otro tipo. Dadas las siguientes definiciones de clase Code First deduciría que los
detalles son un tipo complejo porque no tiene ninguna clave principal.

public partial class OnsiteCourse : Course


{
public OnsiteCourse()
{
Details = new Details();
}

public Details Details { get; set; }


}

public class Details


{
public [Link] Time { get; set; }
public string Location { get; set; }
public string Days { get; set; }
}

Convención de cadena de conexión


Para obtener información sobre las convenciones que usa DbContext para detectar la conexión que se va a usar ,
consulte conexiones y modelos.

Quitar convenciones
Puede quitar cualquiera de las convenciones definidas en el espacio de nombres System. Data. Entity.
ModelConfiguration. Conventions. En el ejemplo siguiente se quita PluralizingTableNameConvention .

public class SchoolEntities : DbContext


{
. . .

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
// Configure Code First to ignore PluralizingTableName convention
// If you keep this convention, the generated tables
// will have pluralized names.
[Link]<PluralizingTableNameConvention>();
}
}

Convenciones personalizadas
Las convenciones personalizadas se admiten en EF6 en adelante. Para obtener más información, vea
convenciones de Code First personalizadas.
Convenciones de Code First personalizadas
12/03/2021 • 21 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

Al utilizar Code First el modelo se calcula a partir de las clases mediante un conjunto de convenciones. Las
convenciones de Code First predeterminadas determinan aspectos como la propiedad que se convierte en la
clave principal de una entidad, el nombre de la tabla a la que se asigna una entidad y la precisión y escala que
una columna decimal tiene de forma predeterminada.
A veces, estas convenciones predeterminadas no son ideales para el modelo y debe solucionarse configurando
muchas entidades individuales mediante anotaciones de datos o la API fluida. Las convenciones de Code First
personalizadas le permiten definir sus propias convenciones que proporcionan valores predeterminados de
configuración para el modelo. En este tutorial, exploraremos los distintos tipos de convenciones personalizadas
y cómo crear cada una de ellas.

Convenciones de Model-Based
En esta página se tratan las convenciones personalizadas de la API de DbModelBuilder. Esta API debe ser
suficiente para la creación de la mayoría de las convenciones personalizadas. Sin embargo, también existe la
posibilidad de crear convenciones basadas en modelos: convenciones que manipulan el modelo final una vez
que se crea: para controlar escenarios avanzados. Para obtener más información, vea convenciones basadas en
modelos.

Nuestro modelo
Comencemos por definir un modelo simple que podamos usar con nuestras convenciones. Agregue las clases
siguientes al proyecto.
using System;
using [Link];
using [Link];
using [Link];

public class ProductContext : DbContext


{
static ProductContext()
{
[Link](new DropCreateDatabaseIfModelChanges<ProductContext>());
}

public DbSet<Product> Products { get; set; }


}

public class Product


{
public int Key { get; set; }
public string Name { get; set; }
public decimal? Price { get; set; }
public DateTime? ReleaseDate { get; set; }
public ProductCategory Category { get; set; }
}

public class ProductCategory


{
public int Key { get; set; }
public string Name { get; set; }
public List<Product> Products { get; set; }
}

Introducción a las convenciones personalizadas


Vamos a escribir una Convención que configura cualquier propiedad denominada clave para que sea la clave
principal de su tipo de entidad.
Las convenciones se habilitan en el generador de modelos, al que se puede tener acceso invalidando
OnModelCreating en el contexto. Actualice la clase ProductContext como se indica a continuación:

public class ProductContext : DbContext


{
static ProductContext()
{
[Link](new DropCreateDatabaseIfModelChanges<ProductContext>());
}

public DbSet<Product> Products { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link]()
.Where(p => [Link] == "Key")
.Configure(p => [Link]());
}
}

Ahora, cualquier propiedad de nuestro modelo denominado Key se configurará como la clave principal de la
entidad de la que forma parte.
También podríamos hacer que nuestras convenciones sean más específicas filtrando por el tipo de propiedad
que vamos a configurar:

[Link]<int>()
.Where(p => [Link] == "Key")
.Configure(p => [Link]());

Esto configurará todas las propiedades que se llaman clave como la clave principal de su entidad, pero solo si
son un entero.
Una característica interesante del método IsKey es que es aditiva. Lo que significa que si llama a IsKey en varias
propiedades y se convertirán en parte de una clave compuesta. La única ADVERTENCIA es que, cuando se
especifican varias propiedades para una clave, también se debe especificar un orden para esas propiedades.
Para ello, puede llamar al método HasColumnOrder como se indica a continuación:

[Link]<int>()
.Where(x => [Link] == "Key")
.Configure(x => [Link]().HasColumnOrder(1));

[Link]()
.Where(x => [Link] == "Name")
.Configure(x => [Link]().HasColumnOrder(2));

Este código configurará los tipos de nuestro modelo para tener una clave compuesta que consta de la columna
de clave int y la columna de nombre de cadena. Si vemos el modelo en el diseñador, tendría el siguiente aspecto:

Otro ejemplo de convenciones de propiedad consiste en configurar todas las propiedades de fecha y hora de mi
modelo para que se asignen al tipo datetime2 en SQL Server en lugar de DateTime. Puede conseguirlo con lo
siguiente:

[Link]<DateTime>()
.Configure(c => [Link]("datetime2"));

Clases de Convención
Otra forma de definir convenciones es usar una clase de Convención para encapsular la Convención. Cuando se
usa una clase de Convención, se crea un tipo que hereda de la clase de Convención en el espacio de nombres
System. Data. Entity. ModelConfiguration. Conventions.
Se puede crear una clase de Convención con la Convención datetime2 que se mostró anteriormente haciendo lo
siguiente:
public class DateTime2Convention : Convention
{
public DateTime2Convention()
{
[Link]<DateTime>()
.Configure(c => [Link]("datetime2"));
}
}

Para indicar a EF que use esta Convención, agréguelo a la colección de convenciones en OnModelCreating, que
si ha estado siguiendo con el tutorial tendrá el siguiente aspecto:

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link]<int>()
.Where(p => [Link]("Key"))
.Configure(p => [Link]());

[Link](new DateTime2Convention());
}

Como puede ver, agregamos una instancia de nuestra Convención a la colección de convenciones. Heredar de la
Convención proporciona una manera cómoda de agrupar y compartir convenciones entre equipos o proyectos.
Por ejemplo, podría tener una biblioteca de clases con un conjunto común de convenciones que usan todos los
proyectos de la organización.

Atributos personalizados
Otro gran uso de convenciones es habilitar nuevos atributos que se van a usar al configurar un modelo. Para
ilustrar esto, vamos a crear un atributo que se puede usar para marcar las propiedades de cadena como no
Unicode.

[AttributeUsage([Link], AllowMultiple = false)]


public class NonUnicode : Attribute
{
}

Ahora, vamos a crear una Convención para aplicar este atributo a nuestro modelo:

[Link]()
.Where(x => [Link](false).OfType<NonUnicode>().Any())
.Configure(c => [Link](false));

Con esta Convención, podemos agregar el atributo NonUnicode a cualquiera de nuestras propiedades de
cadena, lo que significa que la columna de la base de datos se almacenará como varchar en lugar de nvarchar.
Una cuestión que hay que tener en cuenta sobre esta Convención es que si coloca el atributo NonUnicode en un
valor distinto de una propiedad de cadena, se producirá una excepción. Esto se debe a que no se puede
configurar IsUnicode en ningún tipo que no sea una cadena. Si esto ocurre, puede hacer que la Convención sea
más específica, de modo que filtre cualquier cosa que no sea una cadena.
Aunque la Convención anterior funciona para definir atributos personalizados, hay otra API que puede ser
mucho más fácil de usar, especialmente cuando se desea usar propiedades de la clase de atributo.
En este ejemplo, vamos a actualizar el atributo y cambiarlo a un atributo IsUnicode, de modo que tenga este
aspecto:

[AttributeUsage([Link], AllowMultiple = false)]


internal class IsUnicode : Attribute
{
public bool Unicode { get; set; }

public IsUnicode(bool isUnicode)


{
Unicode = isUnicode;
}
}

Una vez hecho esto, podemos establecer un valor bool en nuestro atributo para indicar a la Convención si una
propiedad debe ser Unicode o no. Podríamos hacer esto en la Convención que ya hemos tenido acceso a la
ClrProperty de la clase de configuración de la siguiente manera:

[Link]()
.Where(x => [Link](false).OfType<IsUnicode>().Any())
.Configure(c => [Link]([Link]<IsUnicode>().Unicode));

Esto es bastante sencillo, pero hay una manera más concisa de lograrlo mediante el uso del método Having de
la API de convenciones. El método having tiene un parámetro de tipo FUNC < PropertyInfo, T > que acepta la
PropertyInfo igual que el método Where, pero se espera que devuelva un objeto. Si el objeto devuelto es null, la
propiedad no se configurará, lo que significa que puede filtrar las propiedades con ella como Where, pero es
diferente en que también capturará el objeto devuelto y lo pasará al método configure. Esto funciona de la
siguiente manera:

[Link]()
.Having(x => [Link](false).OfType<IsUnicode>().FirstOrDefault())
.Configure((config, att) => [Link]([Link]));

Los atributos personalizados no son la única razón para usar el método Having, es útil en cualquier lugar en el
que sea necesario saber sobre algo que está filtrando al configurar los tipos o las propiedades.

Configuración de tipos
Hasta ahora, todas nuestras convenciones se han realizado para propiedades, pero hay otra área de la API de
convenciones para configurar los tipos en el modelo. La experiencia es similar a las convenciones que hemos
encontrado hasta ahora, pero las opciones incluidas en configurar estarán en la entidad en lugar de en el nivel
de propiedad.
Una de las cosas que las convenciones de nivel de tipo pueden ser realmente útiles para es cambiar la
Convención de nomenclatura de tablas, ya sea para asignar a un esquema existente que difiere del valor
predeterminado de EF o para crear una nueva base de datos con una Convención de nomenclatura diferente.
Para ello, primero necesitamos un método que pueda aceptar TypeInfo para un tipo en nuestro modelo y que
devuelva el nombre de tabla de ese tipo:
private string GetTableName(Type type)
{
var result = [Link]([Link], ".[A-Z]", m => [Link][0] + "_" + [Link][1]);

return [Link]();
}

Este método toma un tipo y devuelve una cadena que usa minúsculas con carácter de subrayado en lugar de
CamelCase. En nuestro modelo, esto significa que la clase ProductCategory se asignará a una tabla denominada
Product _ Category en lugar de ProductCategories.
Una vez que tenemos ese método, podemos llamarlo en una Convención similar a la siguiente:

[Link]()
.Configure(c => [Link](GetTableName([Link])));

Esta Convención configura todos los tipos de nuestro modelo para que se asignen al nombre de tabla que se
devuelve desde nuestro método GetTableName. Esta Convención es equivalente a llamar al método ToTable para
cada entidad del modelo mediante la API fluida.
Un aspecto que se debe tener en cuenta es que, cuando se llama a ToTable EF, toma la cadena que se
proporciona como el nombre exacto de la tabla, sin la pluralización que normalmente haría al determinar los
nombres de tabla. Esta es la razón por la que el nombre de la tabla de nuestra Convención es la _ categoría de
producto en lugar de las categorías de productos _ . Podemos resolver esto en nuestra Convención realizando
una llamada al servicio de pluralización por nuestra parte.
En el código siguiente, usaremos la característica de resolución de dependencias agregada en EF6 para
recuperar el servicio de pluralización que EF habría usado y pluralando el nombre de la tabla.

private string GetTableName(Type type)


{
var pluralizationService = [Link]<IPluralizationService>();

var result = [Link]([Link]);

result = [Link](result, ".[A-Z]", m => [Link][0] + "_" + [Link][1]);

return [Link]();
}

NOTE
La versión genérica de GetService es un método de extensión en el espacio de nombres System. Data. Entity.
Infrastructure. DependencyResolution; tendrá que agregar una instrucción using al contexto para poder usarlo.

ToTable y herencia
Otro aspecto importante de ToTable es que si asigna explícitamente un tipo a una tabla determinada, puede
modificar la estrategia de asignación que utilizará EF. Si llama a ToTable para cada tipo de una jerarquía de
herencia, pasando el nombre de tipo como el nombre de la tabla como hicimos anteriormente, cambiará la
estrategia de asignación predeterminada de tabla por jerarquía (TPH) a tabla por tipo (TPT). La mejor manera de
describir esto es whith un ejemplo concreto:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}

public class Manager : Employee


{
public string SectionManaged { get; set; }
}

De forma predeterminada, tanto el empleado como el administrador están asignados a la misma tabla
(empleados) en la base de datos. La tabla contendrá empleados y directivos con una columna de discriminador
que le indicará qué tipo de instancia se almacena en cada fila. Esta es la asignación TPH, ya que hay una sola
tabla para la jerarquía. Sin embargo, si llama a ToTable en ambos Classe, cada tipo se asignará a su propia tabla,
también conocida como TPT, ya que cada tipo tiene su propia tabla.

[Link]()
.Configure(c=>[Link]([Link]));

El código anterior se asignará a una estructura de tabla similar a la siguiente:

Puede evitar esto y mantener la asignación de TPH predeterminada de dos maneras:


1. Llame a ToTable con el mismo nombre de tabla para cada tipo de la jerarquía.
2. Llame a ToTable solo en la clase base de la jerarquía, en el ejemplo que sería Employee.

Orden de ejecución
Las convenciones funcionan en la última manera de WINS, igual que la API fluida. Esto significa que, si escribe
dos convenciones que configuran la misma opción de la misma propiedad, la última que se ejecuta gana. Por
ejemplo, en el código que aparece debajo de la longitud máxima de todas las cadenas se establece en 500, pero
después se configuran todas las propiedades denominadas nombre del modelo para que tengan una longitud
máxima de 250.
[Link]<string>()
.Configure(c => [Link](500));

[Link]<string>()
.Where(x => [Link] == "Name")
.Configure(c => [Link](250));

Dado que la Convención para establecer la longitud máxima en 250 es después de la que establece todas las
cadenas en 500, todas las propiedades denominadas Name en nuestro modelo tendrán una MaxLength de 250
mientras que cualquier otra cadena, como descripciones, sería 500. El uso de convenciones de esta manera
significa que puede proporcionar una Convención General para tipos o propiedades en el modelo y, a
continuación, reemplazarlos para subconjuntos que son diferentes.
La API fluida y las anotaciones de datos también se pueden usar para invalidar una Convención en casos
concretos. En el ejemplo anterior, si hubiéramos usado la API fluida para establecer la longitud máxima de una
propiedad, podríamos haber colocado antes o después de la Convención, ya que la API fluida más específica
ganará más allá de la Convención de configuración más general.

Convenciones integradas
Dado que las convenciones personalizadas podrían verse afectadas por las convenciones de Code First
predeterminadas, puede ser útil agregar convenciones para que se ejecuten antes o después de otra convención.
Para ello, puede usar los métodos AddBefore y AddAfter de la colección Conventions en su DbContext derivado.
El código siguiente agregaría la clase de Convención que hemos creado anteriormente para que se ejecute antes
de la Convención de detección de claves integrada.

[Link]<IdKeyDiscoveryConvention>(new DateTime2Convention());

Este será el más utilizado al agregar convenciones que deban ejecutarse antes o después de las convenciones
integradas. puede encontrar una lista de las convenciones integradas aquí: System. Data. Entity.
ModelConfiguration. Conventions (espacio de nombres).
También puede quitar las convenciones que no desea que se apliquen al modelo. Para quitar una Convención,
use el método Remove. Este es un ejemplo de cómo quitar el PluralizingTableNameConvention.

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link]<PluralizingTableNameConvention>();
}
Convenciones de Model-Based
12/03/2021 • 10 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

Las convenciones basadas en modelos son un método avanzado de configuración del modelo basado en
convenciones. En la mayoría de los casos, se debe usar la API de la Convención de Code First personalizada en
DbModelBuilder . Antes de usar las convenciones basadas en modelos, se recomienda comprender la API de
DbModelBuilder para las convenciones.
Las convenciones basadas en modelos permiten la creación de convenciones que afectan a las propiedades y
tablas que no se pueden configurar a través de convenciones estándar. Algunos ejemplos son las columnas de
discriminador en los modelos de tabla por jerarquía y las columnas de asociación independientes.

Crear una Convención


El primer paso para crear una Convención basada en el modelo es elegir cuándo debe aplicarse la Convención
en el modelo. Hay dos tipos de convenciones del modelo: conceptual (espacio C) y almacén (espacio de S). Se
aplica una Convención de espacio C al modelo que se compila en la aplicación, mientras que una Convención de
espacio S se aplica a la versión del modelo que representa la base de datos y controla aspectos como el nombre
de las columnas generadas automáticamente.
Una Convención de modelo es una clase que se extiende desde IConceptualModelConvention o
IStoreModelConvention. Estas interfaces aceptan un tipo genérico que puede ser de tipo MetadataItem, que se
usa para filtrar el tipo de datos al que se aplica la Convención.

Agregar una Convención


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

using [Link];
using [Link];
using [Link];
using [Link];

public class BlogContext : DbContext


{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


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

También se puede Agregar una Convención en relación con otra Convención mediante los métodos
Conventions. AddBefore <> o Conventions. AddAfter <> . Para obtener más información acerca de las
convenciones que se aplican Entity Framework consulte la sección Notas.

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link]<IdKeyDiscoveryConvention>(new MyModelBasedConvention());
}

Ejemplo: Convención del modelo de discriminador


Cambiar el nombre de las columnas generadas por EF es un ejemplo de algo que no se puede hacer con las
otras API de convenciones. Se trata de una situación en la que el uso de convenciones de modelos es la única
opción.
Un ejemplo de cómo usar una Convención basada en modelo para configurar las columnas generadas es
personalizar la forma en que se denominan las columnas de discriminador. A continuación se muestra un
ejemplo de una Convención basada en un modelo simple que cambia el nombre de todas las columnas del
modelo denominado "Discriminator" a "EntityType". Esto incluye las columnas que el programador simplemente
denomina "Discriminator". Dado que la columna "discriminadora" es una columna generada, esto debe
ejecutarse en el espacio de S.

using [Link];
using [Link];
using [Link];
using [Link];

class DiscriminatorRenamingConvention : IStoreModelConvention<EdmProperty>


{
public void Apply(EdmProperty property, DbModel model)
{
if ([Link] == "Discriminator")
{
[Link] = "EntityType";
}
}
}

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


Otro ejemplo más complicado de convenciones basadas en modelos en acción consiste en configurar el modo
en que se asignan nombres a las asociaciones independientes (IAs). Se trata de una situación en la que se
pueden aplicar las convenciones de modelo, ya que las generadas por EF y no están presentes en el modelo al
que puede tener acceso la API de DbModelBuilder.
Cuando EF genera un IA, crea una columna denominada EntityType_KeyName. Por ejemplo, para una asociación
denominada Customer con una columna de clave denominada CustomerId, se generaría una columna
denominada Customer_CustomerId. La Convención siguiente quita el _ carácter ' ' del nombre de columna que
se genera para el IA.
using [Link];
using [Link];
using [Link];
using [Link];

// Provides a convention for fixing the independent association (IA) foreign key column names.
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{

public void Apply(AssociationType association, DbModel model)


{
// Identify ForeignKey properties (including IAs)
if ([Link])
{
// rename FK columns
var constraint = [Link];
if (DoPropertiesHaveDefaultNames([Link], [Link],
[Link]))
{
NormalizeForeignKeyProperties([Link]);
}
if (DoPropertiesHaveDefaultNames([Link], [Link],
[Link]))
{
NormalizeForeignKeyProperties([Link]);
}
}
}

private bool DoPropertiesHaveDefaultNames(ReadOnlyMetadataCollection<EdmProperty> properties, string


roleName, ReadOnlyMetadataCollection<EdmProperty> otherEndProperties)
{
if ([Link] != [Link])
{
return false;
}

for (int i = 0; i < [Link]; ++i)


{
if (!properties[i].[Link]("_" + otherEndProperties[i].Name))
{
return false;
}
}
return true;
}

private void NormalizeForeignKeyProperties(ReadOnlyMetadataCollection<EdmProperty> properties)


{
for (int i = 0; i < [Link]; ++i)
{
int underscoreIndex = properties[i].[Link]('_');
if (underscoreIndex > 0)
{
properties[i].Name = properties[i].[Link](underscoreIndex, 1);
}
}
}
}

Extender las convenciones existentes


Si tiene que escribir una Convención similar a una de las convenciones que Entity Framework ya se aplican a su
modelo, siempre puede ampliar esa Convención para evitar tener que volver a escribirla desde el principio. Un
ejemplo de esto es reemplazar la Convención de coincidencia de ID. existente por una personalizada. Una
ventaja adicional para reemplazar la Convención de claves es que solo se llamará al método invalidado si no hay
ninguna clave ya detectada o configurada explícitamente. Aquí encontrará una lista de las convenciones
utilizadas por Entity Framework:
[Link] .

using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

// Convention to detect primary key properties.


// Recognized naming patterns in order of precedence are:
// 1. 'Key'
// 2. [type name]Key
// Primary key detection is case insensitive.
public class CustomKeyDiscoveryConvention : KeyDiscoveryConvention
{
private const string Id = "Key";

protected override IEnumerable<EdmProperty> MatchKeyProperty(


EntityType entityType, IEnumerable<EdmProperty> primitiveProperties)
{
[Link](entityType != null);
[Link](primitiveProperties != null);

var matches = primitiveProperties


.Where(p => [Link]([Link], [Link]));

if (![Link]())
{
matches = primitiveProperties
.Where(p => ([Link] + Id).Equals([Link], [Link]));
}

// If the number of matches is more than one, then multiple properties matched differing only by
// case--for example, "Key" and "key".
if ([Link]() > 1)
{
throw new InvalidOperationException("Multiple properties match the key convention");
}

return matches;
}
}

A continuación, necesitamos agregar la nueva Convención antes de la Convención de claves existente. Después
de agregar el CustomKeyDiscoveryConvention, podemos quitar el IdKeyDiscoveryConvention. Si no quitamos el
IdKeyDiscoveryConvention existente, esta Convención tendría prioridad sobre la Convención de detección de
identificadores, ya que se ejecuta en primer lugar, pero en caso de que no se encuentre ninguna propiedad
"clave", se ejecutará la Convención "ID". Vemos este comportamiento porque cada Convención Ve el modelo tal y
como lo ha actualizado la Convención anterior (en lugar de trabajar en él de forma independiente y todo
combinado conjuntamente) de modo que, si, por ejemplo, una Convención anterior actualizara un nombre de
columna para que coincida con algo de interés con la Convención personalizada (cuando antes de que el
nombre no fuera de interés) se aplique a esa columna
public class BlogContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link]<IdKeyDiscoveryConvention>(new CustomKeyDiscoveryConvention());
[Link]<IdKeyDiscoveryConvention>();
}
}

Notas
Una lista de las convenciones aplicadas actualmente por Entity Framework está disponible en la documentación
de MSDN aquí: [Link] . Esta
lista se extrae directamente del código fuente. El código fuente de Entity Framework 6 está disponible en GitHub
y muchas de las convenciones utilizadas por Entity Framework son buenos puntos de partida para las
convenciones basadas en modelos personalizados.
API fluidas: relaciones
12/03/2021 • 11 minutes to read

NOTE
En esta página se proporciona información sobre cómo configurar las relaciones en el modelo de Code First mediante la
API fluida. Para obtener información general sobre las relaciones en EF y cómo obtener acceso a los datos y manipularlos
mediante relaciones, vea relaciones & propiedades de navegación.

Cuando se trabaja con Code First, se define el modelo definiendo las clases CLR del dominio. De forma
predeterminada, Entity Framework utiliza las convenciones de Code First para asignar las clases al esquema de
la base de datos. Si utiliza las convenciones de nomenclatura de Code First, en la mayoría de los casos puede
confiar en Code First para configurar las relaciones entre las tablas en función de las claves externas y las
propiedades de navegación que defina en las clases. Si no sigue las convenciones al definir las clases, o si desea
cambiar la forma en que funcionan las convenciones, puede usar la API fluida o las anotaciones de datos para
configurar las clases de modo que Code First pueda asignar las relaciones entre las tablas.

Introducción
Al configurar una relación con la API fluida, empiece con la instancia de EntityTypeConfiguration y, a
continuación, use el método HasRequired, HasOptional o HasMany para especificar el tipo de relación en la que
participa esta entidad. Los métodos HasRequired y HasOptional toman una expresión lambda que representa
una propiedad de navegación de referencia. El método HasMany toma una expresión lambda que representa
una propiedad de navegación de colección. Después, puede configurar una propiedad de navegación inversa
mediante los métodos WithRequired, WithOptional y WithMany. Estos métodos tienen sobrecargas que no
toman argumentos y se pueden usar para especificar la cardinalidad con navegaciones unidireccionales.
Después, puede configurar las propiedades de clave externa mediante el método HasForeignKey. Este método
toma una expresión lambda que representa la propiedad que se va a usar como clave externa.

Configuración de una relación requerida-to-Optional (uno a cero o


uno)
En el ejemplo siguiente se configura una relación de uno a cero o uno. OfficeAssignment tiene la propiedad
InstructorID que es una clave principal y una clave externa, ya que el nombre de la propiedad no sigue la
Convención que el método Haskey (utiliza para configurar la clave principal.

// Configure the primary key for the OfficeAssignment


[Link]<OfficeAssignment>()
.HasKey(t => [Link]);

// Map one-to-zero or one relationship


[Link]<OfficeAssignment>()
.HasRequired(t => [Link])
.WithOptional(t => [Link]);

Configurar una relación en la que se requieren ambos extremos (uno


a uno)
En la mayoría de los casos Entity Framework puede deducir qué tipo es el dependiente y cuál es la entidad de
seguridad de una relación. Sin embargo, cuando se requieren ambos extremos de la relación o ambos lados son
opcionales Entity Framework no puede identificar el dependiente y el principal. Cuando se requieran ambos
extremos de la relación, use WithRequiredPrincipal o WithRequiredDependent después del método
HasRequired. Cuando ambos extremos de la relación sean opcionales, use WithOptionalPrincipal o
WithOptionalDependent después del método HasOptional.

// Configure the primary key for the OfficeAssignment


[Link]<OfficeAssignment>()
.HasKey(t => [Link]);

[Link]<Instructor>()
.HasRequired(t => [Link])
.WithRequiredPrincipal(t => [Link]);

Configuración de una relación de varios a varios


El siguiente código configura una relación de varios a varios entre los tipos Course y instructor. En el ejemplo
siguiente, se usan las convenciones de Code First predeterminadas para crear una tabla de combinación. Como
resultado, la tabla CourseInstructor se crea con Course_CourseID y Instructor_InstructorID columnas.

[Link]<Course>()
.HasMany(t => [Link])
.WithMany(t => [Link])

Si desea especificar el nombre de la tabla de combinación y los nombres de las columnas de la tabla, debe
realizar una configuración adicional mediante el método map. El código siguiente genera la tabla
CourseInstructor con las columnas CourseID y InstructorID.

[Link]<Course>()
.HasMany(t => [Link])
.WithMany(t => [Link])
.Map(m =>
{
[Link]("CourseInstructor");
[Link]("CourseID");
[Link]("InstructorID");
});

Configurar una relación con una propiedad de navegación


Una relación unidireccional (también denominada unidireccional) es cuando se define una propiedad de
navegación solo en uno de los extremos de la relación y no en ambos. Por Convención, Code First siempre
interpreta una relación unidireccional como uno a varios. Por ejemplo, si desea una relación de uno a uno entre
instructor y OfficeAssignment, donde tiene una propiedad de navegación solo en el tipo de instructor, debe usar
la API fluida para configurar esta relación.

// Configure the primary Key for the OfficeAssignment


[Link]<OfficeAssignment>()
.HasKey(t => [Link]);

[Link]<Instructor>()
.HasRequired(t => [Link])
.WithRequiredPrincipal();
Habilitar la eliminación en cascada
Puede configurar la eliminación en cascada en una relación mediante el método WillCascadeOnDelete. Si una
clave externa de la entidad dependiente no admite valores NULL, Code First establece Cascade delete en la
relación. Si una clave externa de la entidad dependiente admite valores NULL, Code First no establece Cascade
delete en la relación y, cuando se elimina la entidad de seguridad, la clave externa se establecerá en NULL.
Puede quitar estas convenciones de eliminación en cascada mediante:
modelBuilder. Conventions. Remove <OneToManyCascadeDeleteConvention> ()
modelBuilder. Conventions. Remove <ManyToManyCascadeDeleteConvention> ()
El código siguiente configura la relación para que sea necesaria y, a continuación, deshabilita la eliminación en
cascada.

[Link]<Course>()
.HasRequired(t => [Link])
.WithMany(t => [Link])
.HasForeignKey(d => [Link])
.WillCascadeOnDelete(false);

Configurar una clave externa compuesta


Si la clave principal del tipo de departamento consta de las propiedades DepartmentID y Name, debe configurar
la clave principal para el Departamento y la clave externa en los tipos de curso como se indica a continuación:

// Composite primary key


[Link]<Department>()
.HasKey(d => new { [Link], [Link] });

// Composite foreign key


[Link]<Course>()
.HasRequired(c => [Link])
.WithMany(d => [Link])
.HasForeignKey(d => new { [Link], [Link] });

Cambiar el nombre de una clave externa que no está definida en el


modelo
Si decide no definir una clave externa en el tipo CLR, pero desea especificar el nombre que debe tener en la base
de datos, haga lo siguiente:

[Link]<Course>()
.HasRequired(c => [Link])
.WithMany(t => [Link])
.Map(m => [Link]("ChangedDepartmentID"));

Configuración de un nombre de clave externa que no sigue la


Convención de Code First
Si la propiedad de clave externa de la clase Course se llama SomeDepartmentID en lugar de DepartmentID,
deberá hacer lo siguiente para especificar que SomeDepartmentID será la clave externa:
[Link]<Course>()
.HasRequired(c => [Link])
.WithMany(d => [Link])
.HasForeignKey(c => [Link]);

Modelo usado en los ejemplos


El siguiente modelo de Code First se usa para los ejemplos de esta página.

using [Link];
using [Link];
// add a reference to [Link] DLL
using [Link];
using [Link];
using System;

public class SchoolEntities : DbContext


{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
// Configure Code First to ignore PluralizingTableName convention
// If you keep this convention then the generated tables will have pluralized names.
[Link]<PluralizingTableNameConvention>();
}
}

public class Department


{
public Department()
{
[Link] = new HashSet<Course>();
}
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
public decimal Budget { get; set; }
public [Link] StartDate { get; set; }
public int? Administrator { get; set; }

// Navigation property
public virtual ICollection<Course> Courses { get; private set; }
}

public class Course


{
public Course()
{
[Link] = new HashSet<Instructor>();
}
// Primary key
public int CourseID { get; set; }

public string Title { get; set; }


public int Credits { get; set; }

// Foreign key
public int DepartmentID { get; set; }

// Navigation properties
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; private set; }
}

public partial class OnlineCourse : Course


{
public string URL { get; set; }
}

public partial class OnsiteCourse : Course


{
public OnsiteCourse()
{
Details = new Details();
}

public Details Details { get; set; }


}

public class Details


{
public [Link] Time { get; set; }
public string Location { get; set; }
public string Days { get; set; }
}

public class Instructor


{
public Instructor()
{
[Link] = new List<Course>();
}

// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public [Link] HireDate { get; set; }

// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}

public class OfficeAssignment


{
// Specifying InstructorID as a primary
[Key()]
public Int32 InstructorID { get; set; }

public string Location { get; set; }

// When Entity Framework sees Timestamp attribute


// it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
[Timestamp]
public Byte[] Timestamp { get; set; }

// Navigation property
public virtual Instructor Instructor { get; set; }
}
API fluida: configuración y asignación de
propiedades y tipos
12/03/2021 • 21 minutes to read

Al trabajar con Entity Framework Code First el comportamiento predeterminado es asignar las clases POCO a
las tablas mediante un conjunto de convenciones incorporadas en EF. Sin embargo, a veces no puede o no desea
seguir estas convenciones y debe asignar entidades a un valor distinto del que dictan las convenciones.
Hay dos formas principales de configurar EF para que use un elemento que no sea convenciones, como
anotaciones o API fluida de EFS. Las anotaciones solo cubren un subconjunto de la funcionalidad de la API fluida,
por lo que hay escenarios de asignación que no se pueden lograr mediante anotaciones. Este artículo está
diseñado para demostrar cómo usar la API fluida para configurar las propiedades.
Normalmente, se tiene acceso a la API fluida de Code First mediante la invalidación del método
OnModelCreating en DbContextderivado. Los ejemplos siguientes están diseñados para mostrar cómo realizar
varias tareas con la API fluida y permiten copiar el código y personalizarlo para que se adapte a su modelo; si
desea ver el modelo con el que se pueden usar tal cual, se proporciona al final de este artículo.

Configuración de Model-Wide
Esquema predeterminado (EF6 en adelante )
A partir de EF6, puede usar el método HasDefaultSchema en DbModelBuilder para especificar el esquema de
base de datos que se va a usar para todas las tablas, procedimientos almacenados, etc. Esta configuración
predeterminada se invalidará para todos los objetos para los que se configure explícitamente un esquema
diferente.

[Link]("sales");

Convenciones personalizadas (EF6 en adelante )


A partir de EF6, puede crear sus propias convenciones para complementar las incluidas en Code First. Para
obtener más información, vea convenciones de Code First personalizadas.

Asignación de propiedades
El método Property se utiliza para configurar los atributos de cada propiedad que pertenece a una entidad o un
tipo complejo. El método de propiedad se utiliza para obtener un objeto de configuración para una propiedad
determinada. Las opciones del objeto de configuración son específicas del tipo que se está configurando;
IsUnicode solo está disponible en las propiedades de cadena, por ejemplo.
Configuración de una clave principal
La Convención de Entity Framework para las claves principales es:
1. La clase define una propiedad cuyo nombre es "ID" o "ID"
2. o un nombre de clase seguido de "ID" o "ID"
Para establecer explícitamente una propiedad como clave principal, puede usar el método Haskey (. En el
ejemplo siguiente, se usa el método Haskey (para configurar la clave principal InstructorID en el tipo
OfficeAssignment.
[Link]<OfficeAssignment>().HasKey(t => [Link]);

Configuración de una clave principal compuesta


En el ejemplo siguiente se configuran las propiedades DepartmentID y Name para que sean la clave principal
compuesta del tipo Department.

[Link]<Department>().HasKey(t => new { [Link], [Link] });

Desactivar la identidad de las claves principales numéricas


En el ejemplo siguiente se establece la propiedad DepartmentID en System. ComponentModel.
DataAnnotations. DatabaseGeneratedOption. None para indicar que la base de datos no generará el valor.

[Link]<Department>().Property(t => [Link])


.HasDatabaseGeneratedOption([Link]);

Especificar la longitud máxima de una propiedad


En el ejemplo siguiente, la propiedad Name no debe tener más de 50 caracteres. Si hace que el valor supere los
50 caracteres, recibirá una excepción DbEntityValidationException . Si Code First crea una base de datos a partir
de este modelo, también establecerá la longitud máxima de la columna de nombre en 50 caracteres.

[Link]<Department>().Property(t => [Link]).HasMaxLength(50);

Configuración de la propiedad para que sea necesaria


En el ejemplo siguiente, se requiere la propiedad Name. Si no especifica el nombre, obtendrá una excepción
DbEntityValidationException. Si Code First crea una base de datos a partir de este modelo, la columna utilizada
para almacenar esta propiedad no suele ser que acepte valores NULL.

NOTE
En algunos casos, es posible que no sea posible que la columna de la base de datos no acepte valores NULL, aunque se
requiera la propiedad. Por ejemplo, cuando se usa un dato de estrategia de herencia de TPH para varios tipos, se
almacena en una sola tabla. Si un tipo derivado incluye una propiedad necesaria, no se puede hacer que la columna no
acepte valores NULL, ya que no todos los tipos de la jerarquía tendrán esta propiedad.

[Link]<Department>().Property(t => [Link]).IsRequired();

Configuración de un índice en una o más propiedades

NOTE
EF 6.1 en adelante solo : el atributo de índice se presentó en Entity Framework 6,1. Si usa una versión anterior, no se
aplica la información de esta sección.

La creación de índices no es compatible de forma nativa con la API fluida, pero puede usar la compatibilidad con
IndexAttribute a través de la API fluida. Los atributos de índice se procesan mediante la inclusión de una
anotación de modelo en el modelo que luego se convierte en un índice de la base de datos más adelante en la
canalización. Puede Agregar manualmente estas mismas anotaciones mediante la API fluida.
La forma más fácil de hacerlo es crear una instancia de IndexAttribute que contenga todos los valores para el
nuevo índice. Después, puede crear una instancia de IndexAnnotation , que es un tipo específico de EF que
convertirá la configuración de IndexAttribute en una anotación de modelo que se puede almacenar en el
modelo de EF. A continuación, se pueden pasar al método HasColumnAnnotation en la API fluida,
especificando el Índice de nombre de la anotación.

modelBuilder
.Entity<Department>()
.Property(t => [Link])
.HasColumnAnnotation("Index", new IndexAnnotation(new IndexAttribute()));

Para obtener una lista completa de los valores disponibles en IndexAttribute , consulte la sección Índice de
code First anotaciones de datos. Esto incluye la personalización del nombre del índice, la creación de índices
únicos y la creación de índices de varias columnas.
Puede especificar varias anotaciones de índice en una sola propiedad pasando una matriz de IndexAttribute al
constructor de IndexAnnotation .

modelBuilder
.Entity<Department>()
.Property(t => [Link])
.HasColumnAnnotation(
"Index",
new IndexAnnotation(new[]
{
new IndexAttribute("Index1"),
new IndexAttribute("Index2") { IsUnique = true }
})));

Especificar no asignar una propiedad CLR a una columna en la base de datos


En el ejemplo siguiente se muestra cómo especificar que una propiedad de un tipo CLR no está asignada a una
columna de la base de datos.

[Link]<Department>().Ignore(t => [Link]);

Asignar una propiedad CLR a una columna específica en la base de datos


En el ejemplo siguiente se asigna el nombre de la propiedad CLR a la columna de base de datos
DepartmentName.

[Link]<Department>()
.Property(t => [Link])
.HasColumnName("DepartmentName");

Cambiar el nombre de una clave externa que no está definida en el modelo


Si decide no definir una clave externa en un tipo CLR, pero desea especificar el nombre que debe tener en la
base de datos, haga lo siguiente:

[Link]<Course>()
.HasRequired(c => [Link])
.WithMany(t => [Link])
.Map(m => [Link]("ChangedDepartmentID"));

Configurar si una propiedad de cadena admite contenido Unicode


De forma predeterminada, las cadenas son Unicode (nvarchar en SQL Server). Puede usar el método IsUnicode
para especificar que una cadena debe ser de tipo VARCHAR.

[Link]<Department>()
.Property(t => [Link])
.IsUnicode(false);

Configurar el tipo de datos de una columna de base de datos


El método HasColumnType permite la asignación a distintas representaciones del mismo tipo básico. El uso de
este método no permite realizar ninguna conversión de los datos en tiempo de ejecución. Tenga en cuenta que
IsUnicode es la forma preferida de establecer columnas en VARCHAR, ya que es independiente de la base de
datos.

[Link]<Department>()
.Property(p => [Link])
.HasColumnType("varchar");

Configurar propiedades en un tipo complejo


Hay dos maneras de configurar las propiedades escalares en un tipo complejo.
Puede llamar a la propiedad en ComplexTypeConfiguration.

[Link]<Details>()
.Property(t => [Link])
.HasMaxLength(20);

También puede usar la notación de puntos para tener acceso a una propiedad de un tipo complejo.

[Link]<OnsiteCourse>()
.Property(t => [Link])
.HasMaxLength(20);

Configuración de una propiedad que se va a usar como un token de simultaneidad optimista


Para especificar que una propiedad de una entidad representa un token de simultaneidad, puede usar el atributo
ConcurrencyCheck o el método IsConcurrencyToken.

[Link]<OfficeAssignment>()
.Property(t => [Link])
.IsConcurrencyToken();

También puede usar el método IsRowVersion para configurar la propiedad de modo que sea una versión de fila
en la base de datos. Al establecer la propiedad como una versión de fila, se configura automáticamente para que
sea un token de simultaneidad optimista.

[Link]<OfficeAssignment>()
.Property(t => [Link])
.IsRowVersion();

Asignación de tipos
Especificar que una clase es un tipo complejo
Por Convención, un tipo que no tiene ninguna clave principal especificada se trata como un tipo complejo. Hay
algunos escenarios en los que Code First no detectarán un tipo complejo (por ejemplo, si tiene una propiedad
denominada ID, pero no significa que sea una clave principal). En tales casos, usaría la API fluida para especificar
explícitamente que un tipo es un tipo complejo.

[Link]<Details>();

Especificar no asignar un tipo de entidad CLR a una tabla de la base de datos


En el ejemplo siguiente se muestra cómo excluir un tipo CLR para que no se asigne a una tabla de la base de
datos.

[Link]<OnlineCourse>();

Asignar un tipo de entidad a una tabla específica de la base de datos


Todas las propiedades de Department se asignarán a las columnas de una tabla denominada t_ Department.

[Link]<Department>()
.ToTable("t_Department");

También puede especificar el nombre del esquema de la siguiente manera:

[Link]<Department>()
.ToTable("t_Department", "school");

Asignar la herencia de tabla por jerarquía (TPH )


En el escenario de asignación TPH, todos los tipos de una jerarquía de herencia se asignan a una sola tabla. Una
columna de discriminador se utiliza para identificar el tipo de cada fila. Al crear el modelo con Code First, TPH es
la estrategia predeterminada para los tipos que participan en la jerarquía de herencia. De forma
predeterminada, la columna discriminadora se agrega a la tabla con el nombre "Discriminator" y el nombre de
tipo de CLR de cada tipo de la jerarquía se usa para los valores de discriminador. Puede modificar el
comportamiento predeterminado mediante la API fluida.

[Link]<Course>()
.Map<Course>(m => [Link]("Type").HasValue("Course"))
.Map<OnsiteCourse>(m => [Link]("Type").HasValue("OnsiteCourse"));

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


En el escenario de asignación de TPT, todos los tipos se asignan a tablas individuales. Las propiedades que
pertenecen solamente a un tipo base o a un tipo derivado se almacenan en una tabla que se asigna a ese tipo.
Las tablas que se asignan a tipos derivados también almacenan una clave externa que une la tabla derivada con
la tabla base.

[Link]<Course>().ToTable("Course");
[Link]<OnsiteCourse>().ToTable("OnsiteCourse");

Asignación de la herencia de la tabla por clase concreta (TPC )


En el escenario de asignación de TPC, todos los tipos no abstractos de la jerarquía se asignan a tablas
individuales. Las tablas que se asignan a las clases derivadas no tienen ninguna relación con la tabla que se
asigna a la clase base en la base de datos. Todas las propiedades de una clase, incluidas las propiedades
heredadas, se asignan a las columnas de la tabla correspondiente.
Llame al método MapInheritedProperties para configurar cada tipo derivado. MapInheritedProperties reasigna
todas las propiedades heredadas de la clase base a nuevas columnas de la tabla para la clase derivada.

NOTE
Tenga en cuenta que, dado que las tablas que participan en la jerarquía de herencia de TPC no comparten una clave
principal, habrá claves de entidad duplicadas al insertar en las tablas que se asignan a las subclases si tiene valores
generados por la base de datos con el mismo valor de inicialización de identidad. Para solucionar este problema, puede
especificar un valor de inicialización inicial diferente para cada tabla o desactivar la identidad en la propiedad de clave
principal. Identity es el valor predeterminado para las propiedades de clave de entero cuando se trabaja con Code First.

[Link]<Course>()
.Property(c => [Link])
.HasDatabaseGeneratedOption([Link]);

[Link]<OnsiteCourse>().Map(m =>
{
[Link]();
[Link]("OnsiteCourse");
});

[Link]<OnlineCourse>().Map(m =>
{
[Link]();
[Link]("OnlineCourse");
});

Asignar propiedades de un tipo de entidad a varias tablas en la base de datos (División de entidades)
La división de entidades permite que las propiedades de un tipo de entidad se repartan entre varias tablas. En el
ejemplo siguiente, la entidad Department se divide en dos tablas: Department y DepartmentDetails. La división
de entidades utiliza varias llamadas al método Map para asignar un subconjunto de propiedades a una tabla
específica.

[Link]<Department>()
.Map(m =>
{
[Link](t => new { [Link], [Link] });
[Link]("Department");
})
.Map(m =>
{
[Link](t => new { [Link], [Link], [Link], [Link] });
[Link]("DepartmentDetails");
});

Asignar varios tipos de entidad a una tabla de la base de datos (División de tablas)
En el ejemplo siguiente se asignan dos tipos de entidad que comparten una clave principal a una tabla.
[Link]<OfficeAssignment>()
.HasKey(t => [Link]);

[Link]<Instructor>()
.HasRequired(t => [Link])
.WithRequiredPrincipal(t => [Link]);

[Link]<Instructor>().ToTable("Instructor");

[Link]<OfficeAssignment>().ToTable("Instructor");

Asignar un tipo de entidad para insertar/actualizar/eliminar procedimientos almacenados (EF6 en adelante )


A partir de EF6, puede asignar una entidad para usar procedimientos almacenados para INSERT UPDATE y
DELETE. Para obtener más información, vea code First INSERT/UPDATE/DELETE Stored Procedures.

Modelo usado en los ejemplos


El siguiente modelo de Code First se usa para los ejemplos de esta página.

using [Link];
using [Link];
// add a reference to [Link] DLL
using [Link];
using [Link];
using System;

public class SchoolEntities : DbContext


{
public DbSet<Course> Courses { get; set; }
public DbSet<Department> Departments { get; set; }
public DbSet<Instructor> Instructors { get; set; }
public DbSet<OfficeAssignment> OfficeAssignments { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
// Configure Code First to ignore PluralizingTableName convention
// If you keep this convention then the generated tables will have pluralized names.
[Link]<PluralizingTableNameConvention>();
}
}

public class Department


{
public Department()
{
[Link] = new HashSet<Course>();
}
// Primary key
public int DepartmentID { get; set; }
public string Name { get; set; }
public decimal Budget { get; set; }
public [Link] StartDate { get; set; }
public int? Administrator { get; set; }

// Navigation property
public virtual ICollection<Course> Courses { get; private set; }
}

public class Course


{
public Course()
{
[Link] = new HashSet<Instructor>();
}
// Primary key
public int CourseID { get; set; }

public string Title { get; set; }


public int Credits { get; set; }

// Foreign key
public int DepartmentID { get; set; }

// Navigation properties
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; private set; }
}

public partial class OnlineCourse : Course


{
public string URL { get; set; }
}

public partial class OnsiteCourse : Course


{
public OnsiteCourse()
{
Details = new Details();
}

public Details Details { get; set; }


}

public class Details


{
public [Link] Time { get; set; }
public string Location { get; set; }
public string Days { get; set; }
}

public class Instructor


{
public Instructor()
{
[Link] = new List<Course>();
}

// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public [Link] HireDate { get; set; }

// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}

public class OfficeAssignment


{
// Specifying InstructorID as a primary
[Key()]
public Int32 InstructorID { get; set; }

public string Location { get; set; }

// When Entity Framework sees Timestamp attribute


// it configures ConcurrencyCheck and DatabaseGeneratedPattern=Computed.
[Timestamp]
public Byte[] Timestamp { get; set; }

// Navigation property
public virtual Instructor Instructor { get; set; }
}
}
API fluida con [Link]
12/03/2021 • 11 minutes to read

Code First permite definir el modelo mediante # las clases C o [Link]. Opcionalmente, se puede realizar una
configuración adicional mediante atributos en las clases y propiedades o mediante una API fluida. En este
tutorial se muestra cómo realizar una configuración de API fluida mediante [Link].
En esta página se supone que tiene un conocimiento básico de Code First. Consulte los siguientes tutoriales para
obtener más información sobre Code First:
Code First en una nueva base de datos
Code First a una base de datos existente

Requisitos previos
Debe tener instalado al menos Visual Studio 2010 o Visual Studio 2012 para completar este tutorial.
Si usa Visual Studio 2010, también debe tener instalado NuGet .

Crear la aplicación
Para simplificar las cosas, vamos a crear una aplicación de consola básica que use Code First para realizar el
acceso a los datos.
Apertura de Visual Studio
Archivo- > nuevo- > proyecto...
Seleccionar ventanas en el menú izquierdo y en la aplicación de consola
Escriba CodeFirstVBSample como nombre
Seleccione Aceptar .

Definir el modelo
En este paso definirá tipos de entidad de [Link] POCO que representan el modelo conceptual. No es necesario
que las clases se deriven de ninguna clase base ni se implementen interfaces.
Agregue una nueva clase al proyecto, escriba SchoolModel como nombre de clase.
Reemplace el contenido de la nueva clase por el código siguiente.

Public Class Department


Public Sub New()
[Link] = New List(Of Course)()
End Sub

' Primary key


Public Property DepartmentID() As Integer
Public Property Name() As String
Public Property Budget() As Decimal
Public Property StartDate() As Date
Public Property Administrator() As Integer?
Public Overridable Property Courses() As ICollection(Of Course)
End Class

Public Class Course


Public Sub New()
[Link] = New HashSet(Of Instructor)()
[Link] = New HashSet(Of Instructor)()
End Sub

' Primary key


Public Property CourseID() As Integer
Public Property Title() As String
Public Property Credits() As Integer

' Foreign key that does not follow the Code First convention.
' The fluent API will be used to configure DepartmentID_FK to be the foreign key for this entity.
Public Property DepartmentID_FK() As Integer

' Navigation properties


Public Overridable Property Department() As Department
Public Overridable Property Instructors() As ICollection(Of Instructor)
End Class

Public Class OnlineCourse


Inherits Course

Public Property URL() As String


End Class

Partial Public Class OnsiteCourse


Inherits Course

Public Sub New()


Details = New OnsiteCourseDetails()
End Sub

Public Property Details() As OnsiteCourseDetails


End Class

' Complex type


Public Class OnsiteCourseDetails
Public Property Time() As Date
Public Property Location() As String
Public Property Days() As String
End Class

Public Class Person


' Primary key
Public Property PersonID() As Integer
Public Property LastName() As String
Public Property FirstName() As String
End Class

Public Class Instructor


Inherits Person

Public Sub New()


[Link] = New List(Of Course)()
End Sub

Public Property HireDate() As Date

' Navigation properties


Private privateCourses As ICollection(Of Course)
Public Overridable Property Courses() As ICollection(Of Course)
Public Overridable Property OfficeAssignment() As OfficeAssignment
End Class

Public Class OfficeAssignment


' Primary key that does not follow the Code First convention.
' The HasKey method is used later to configure the primary key for the entity.
Public Property InstructorID() As Integer

Public Property Location() As String


Public Property Timestamp() As Byte()
' Navigation property
Public Overridable Property Instructor() As Instructor
End Class

Definir un contexto derivado


Estamos a punto de empezar a usar tipos del Entity Framework por lo que necesitamos agregar el paquete
NuGet EntityFramework.
* * Proyecto: > administrar paquetes NuGet...

NOTE
Si no tiene la Administración de paquetes de NuGet.. . opción debe instalar la versión más reciente de NuGet

Seleccione la pestaña en línea


Seleccione el paquete EntityFramework
Haz clic en Instalar
Ahora es el momento de definir un contexto derivado, que representa una sesión con la base de datos, lo que
nos permite consultar y guardar datos. Definimos un contexto que se deriva de System. Data. Entity. DbContext y
expone una carpa DbSet con tipo < > para cada clase de nuestro modelo.
Agregue una nueva clase al proyecto, escriba SchoolContext para el nombre de clase.
Reemplace el contenido de la nueva clase por el código siguiente.

Imports [Link]
Imports [Link]
Imports [Link]
Imports [Link]
Imports [Link]

Public Class SchoolContext


Inherits DbContext

Public Property OfficeAssignments() As DbSet(Of OfficeAssignment)


Public Property Instructors() As DbSet(Of Instructor)
Public Property Courses() As DbSet(Of Course)
Public Property Departments() As DbSet(Of Department)

Protected Overrides Sub OnModelCreating(ByVal modelBuilder As DbModelBuilder)


End Sub
End Class

Configuración con la API fluida


En esta sección se muestra cómo usar las API fluidas para configurar los tipos para la asignación de tablas, las
propiedades para la asignación de columnas y las relaciones entre \ el tipo de tablas en el modelo. La API fluida
se expone a través del tipo DbModelBuilder y se suele acceder a ella mediante la invalidación del método
OnModelCreating en DbContext .
Copie el código siguiente y agréguelo al método OnModelCreating definido en la clase SchoolContext .
los comentarios explican lo que hace cada asignación

' Configure Code First to ignore PluralizingTableName convention


' If you keep this convention then the generated tables
' will have pluralized names.
[Link](Of PluralizingTableNameConvention)()

' Specifying that a Class is a Complex Type

' The model defined in this topic defines a type OnsiteCourseDetails.


' By convention, a type that has no primary key specified
' is treated as a complex type.
' There are some scenarios where Code First will not
' detect a complex type (for example, if you do have a property
' called ID, but you do not mean for it to be a primary key).
' In such cases, you would use the fluent API to
' explicitly specify that a type is a complex type.
[Link](Of OnsiteCourseDetails)()

' Mapping a CLR Entity Type to a Specific Table in the Database.

' All properties of OfficeAssignment will be mapped


' to columns in a table called t_OfficeAssignment.
[Link](Of OfficeAssignment)().ToTable("t_OfficeAssignment")

' Mapping the Table-Per-Hierarchy (TPH) Inheritance

' In the TPH mapping scenario, all types in an inheritance hierarchy


' are mapped to a single table.
' A discriminator column is used to identify the type of each row.
' When creating your model with Code First,
' TPH is the default strategy for the types that
' participate in the inheritance hierarchy.
' By default, the discriminator column is added
' to the table with the name “Discriminator”
' and the CLR type name of each type in the hierarchy
' is used for the discriminator values.
' You can modify the default behavior by using the fluent API.
[Link](Of Person)().
Map(Of Person)(Function(t) [Link]("Type").
HasValue("Person")).
Map(Of Instructor)(Function(t) [Link]("Type").
HasValue("Instructor"))

' Mapping the Table-Per-Type (TPT) Inheritance

' In the TPT mapping scenario, all types are mapped to individual tables.
' Properties that belong solely to a base type or derived type are stored
' in a table that maps to that type. Tables that map to derived types
' also store a foreign key that joins the derived table with the base table.
[Link](Of Course)().ToTable("Course")
[Link](Of OnsiteCourse)().ToTable("OnsiteCourse")
[Link](Of OnlineCourse)().ToTable("OnlineCourse")

' Configuring a Primary Key

' If your class defines a property whose name is “ID” or “Id”,


' or a class name followed by “ID” or “Id”,
' the Entity Framework treats this property as a primary key by convention.
' If your property name does not follow this pattern, use the HasKey method
' to configure the primary key for the entity.
[Link](Of OfficeAssignment)().
HasKey(Function(t) [Link])

' Specifying the Maximum Length on a Property

' In the following example, the Name property


' In the following example, the Name property
' should be no longer than 50 characters.
' If you make the value longer than 50 characters,
' you will get a DbEntityValidationException exception.
[Link](Of Department)().Property(Function(t) [Link]).
HasMaxLength(60)

' Configuring the Property to be Required

' In the following example, the Name property is required.


' If you do not specify the Name,
' you will get a DbEntityValidationException exception.
' The database column used to store this property will be non-nullable.
[Link](Of Department)().Property(Function(t) [Link]).
IsRequired()

' Switching off Identity for Numeric Primary Keys

' The following example sets the DepartmentID property to


' [Link] to indicate that
' the value will not be generated by the database.
[Link](Of Course)().Property(Function(t) [Link]).
HasDatabaseGeneratedOption([Link])

'Specifying NOT to Map a CLR Property to a Column in the Database


[Link](Of Department)().
Ignore(Function(t) [Link])

'Mapping a CLR Property to a Specific Column in the Database


[Link](Of Department)().Property(Function(t) [Link]).
HasColumnName("DepartmentBudget")

'Configuring the Data Type of a Database Column


[Link](Of Department)().Property(Function(t) [Link]).
HasColumnType("varchar")

'Configuring Properties on a Complex Type


[Link](Of OnsiteCourse)().Property(Function(t) [Link]).
HasColumnName("Days")
[Link](Of OnsiteCourse)().Property(Function(t) [Link]).
HasColumnName("Location")
[Link](Of OnsiteCourse)().Property(Function(t) [Link]).
HasColumnName("Time")

' Map one-to-zero or one relationship

' The OfficeAssignment has the InstructorID


' property that is a primary key and a foreign key.
[Link](Of OfficeAssignment)().
HasRequired(Function(t) [Link]).
WithOptional(Function(t) [Link])

' Configuring a Many-to-Many Relationship

' The following code configures a many-to-many relationship


' between the Course and Instructor types.
' In the following example, the default Code First conventions
' are used to create a join table.
' As a result the CourseInstructor table is created with
' Course_CourseID and Instructor_InstructorID columns.
[Link](Of Course)().
HasMany(Function(t) [Link]).
WithMany(Function(t) [Link])

' Configuring a Many-to-Many Relationship and specifying the names


' Configuring a Many-to-Many Relationship and specifying the names
' of the columns in the join table

' If you want to specify the join table name


' and the names of the columns in the table
' you need to do additional configuration by using the Map method.
' The following code generates the CourseInstructor
' table with CourseID and InstructorID columns.
[Link](Of Course)().
HasMany(Function(t) [Link]).
WithMany(Function(t) [Link]).
Map(Sub(m)
[Link]("CourseInstructor")
[Link]("CourseID")
[Link]("InstructorID")
End Sub)

' Configuring a foreign key name that does not follow the Code First convention

' The foreign key property on the Course class is called DepartmentID_FK
' since that does not follow Code First conventions you need to explicitly specify
' that you want DepartmentID_FK to be the foreign key.
[Link](Of Course)().
HasRequired(Function(t) [Link]).
WithMany(Function(t) [Link]).
HasForeignKey(Function(t) t.DepartmentID_FK)

' Enabling Cascade Delete

' By default, if a foreign key on the dependent entity is not nullable,


' then Code First sets cascade delete on the relationship.
' If a foreign key on the dependent entity is nullable,
' Code First does not set cascade delete on the relationship,
' and when the principal is deleted the foreign key will be set to null.
' The following code configures cascade delete on the relationship.

' You can also remove the cascade delete conventions by using:
' [Link]<OneToManyCascadeDeleteConvention>()
' and [Link]<ManyToManyCascadeDeleteConvention>().
[Link](Of Course)().
HasRequired(Function(t) [Link]).
WithMany(Function(t) [Link]).
HasForeignKey(Function(d) d.DepartmentID_FK).
WillCascadeOnDelete(False)

Uso del modelo


Vamos a realizar algunos accesos a datos con SchoolContext para ver el modelo en acción.
Abra el archivo Module1. VB en el que se define la función main.
Copie y pegue la siguiente definición de Module1
Imports [Link]

Module Module1

Sub Main()

Using context As New SchoolContext()

' Create and save a new Department.


[Link]("Enter a name for a new Department: ")
Dim name = [Link]()

Dim department = New Department With { .Name = name, .StartDate = [Link] }


[Link](department)
[Link]()

' Display all Departments from the database ordered by name


Dim departments =
From d In [Link]
Order By [Link]
Select d

[Link]("All Departments in the database:")


For Each department In departments
[Link]([Link])
Next

End Using

[Link]("Press any key to exit...")


[Link]()

End Sub

End Module

Ahora puede ejecutar la aplicación y probarla.

Enter a name for a new Department: Computing


All Departments in the database:
Computing
Press any key to exit...
Code First procedimientos almacenados de
inserción, actualización y eliminación
12/03/2021 • 14 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

De forma predeterminada, Code First configurará todas las entidades para que realicen los comandos de
inserción, actualización y eliminación mediante el acceso directo a tablas. A partir de EF6, puede configurar el
modelo de Code First para que use procedimientos almacenados para algunas o todas las entidades del modelo.

Asignación de entidad básica


Puede optar por usar procedimientos almacenados para INSERT, Update y DELETE mediante la API fluida.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures();

Esto hará que Code First utilice algunas convenciones para generar la forma esperada de los procedimientos
almacenados en la base de datos.
Tres procedimientos almacenados denominados ** <type_name> _Insert**, ** <type_name> _Update** y **
<type_name> _Delete** (por ejemplo, Blog_Insert, Blog_Update y Blog_Delete).
Los nombres de parámetro se corresponden con los nombres de propiedad.

NOTE
Si usa HasColumnName () o el atributo de columna para cambiar el nombre de la columna de una propiedad
determinada, este nombre se utiliza para los parámetros en lugar del nombre de la propiedad.

El procedimiento almacenado Inser t tendrá un parámetro para cada propiedad, a excepción de los
marcados como almacenamiento generado (identidad o calculado). El procedimiento almacenado debe
devolver un conjunto de resultados con una columna para cada propiedad generada por el almacén.
El procedimiento almacenado de actualización tendrá un parámetro para cada propiedad, excepto
aquellos marcados con un modelo generado por un almacén de ' Calculated '. Algunos tokens de
simultaneidad requieren un parámetro para el valor original; vea la sección tokens de simultaneidad a
continuación para obtener más información. El procedimiento almacenado debe devolver un conjunto de
resultados con una columna para cada propiedad calculada.
El procedimiento almacenado Delete debe tener un parámetro para el valor de clave de la entidad (o
varios parámetros si la entidad tiene una clave compuesta). Además, el procedimiento de eliminación
también debe tener parámetros para cualquier clave externa de asociación independiente en la tabla de
destino (relaciones que no tienen las propiedades de clave externa correspondientes declaradas en la
entidad). Algunos tokens de simultaneidad requieren un parámetro para el valor original; vea la sección
tokens de simultaneidad a continuación para obtener más información.
Use la siguiente clase como ejemplo:

public class Blog


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

Los procedimientos almacenados predeterminados serían:

CREATE PROCEDURE [dbo].[Blog_Insert]


@Name nvarchar(max),
@Url nvarchar(max)
AS
BEGIN
INSERT INTO [dbo].[Blogs] ([Name], [Url])
VALUES (@Name, @Url)

SELECT SCOPE_IDENTITY() AS BlogId


END
CREATE PROCEDURE [dbo].[Blog_Update]
@BlogId int,
@Name nvarchar(max),
@Url nvarchar(max)
AS
UPDATE [dbo].[Blogs]
SET [Name] = @Name, [Url] = @Url
WHERE BlogId = @BlogId;
CREATE PROCEDURE [dbo].[Blog_Delete]
@BlogId int
AS
DELETE FROM [dbo].[Blogs]
WHERE BlogId = @BlogId

Reemplazar los valores predeterminados


Puede invalidar parte o todo lo que se configuró de forma predeterminada.
Puede cambiar el nombre de uno o más procedimientos almacenados. En este ejemplo se cambia el nombre del
procedimiento almacenado de actualización únicamente.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
[Link](u => [Link]("modify_blog")));

En este ejemplo se cambia el nombre de los tres procedimientos almacenados.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
[Link](u => [Link]("modify_blog"))
.Delete(d => [Link]("delete_blog"))
.Insert(i => [Link]("insert_blog")));

En estos ejemplos, las llamadas se encadenan entre sí, pero también puede usar la sintaxis de bloque lambda.
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
{
[Link](u => [Link]("modify_blog"));
[Link](d => [Link]("delete_blog"));
[Link](i => [Link]("insert_blog"));
});

En este ejemplo se cambia el nombre del parámetro de la propiedad BlogId en el procedimiento almacenado de
actualización.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
[Link](u => [Link](b => [Link], "blog_id")));

Estas llamadas son todas encadenables y que admiten composición. Este es un ejemplo en el que se cambia el
nombre de los tres procedimientos almacenados y sus parámetros.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
[Link](u => [Link]("modify_blog")
.Parameter(b => [Link], "blog_id")
.Parameter(b => [Link], "blog_name")
.Parameter(b => [Link], "blog_url"))
.Delete(d => [Link]("delete_blog")
.Parameter(b => [Link], "blog_id"))
.Insert(i => [Link]("insert_blog")
.Parameter(b => [Link], "blog_name")
.Parameter(b => [Link], "blog_url")));

También puede cambiar el nombre de las columnas del conjunto de resultados que contiene los valores
generados por la base de datos.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
[Link](i => [Link](b => [Link], "generated_blog_identity")));

CREATE PROCEDURE [dbo].[Blog_Insert]


@Name nvarchar(max),
@Url nvarchar(max)
AS
BEGIN
INSERT INTO [dbo].[Blogs] ([Name], [Url])
VALUES (@Name, @Url)

SELECT SCOPE_IDENTITY() AS generated_blog_id


END

Relaciones sin una clave externa en la clase (asociaciones


independientes)
Cuando se incluye una propiedad de clave externa en la definición de clase, se puede cambiar el nombre del
parámetro correspondiente de la misma manera que cualquier otra propiedad. Cuando existe una relación sin
una propiedad de clave externa en la clase, el nombre del parámetro predeterminado es **
<navigation_property_name> _ <primary_key_name> **.
Por ejemplo, las siguientes definiciones de clase darían lugar a la espera de un parámetro Blog_BlogId en los
procedimientos almacenados para insertar y actualizar las entradas.

public class Blog


{
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 Blog Blog { get; set; }


}

Reemplazar los valores predeterminados


Puede cambiar los parámetros de las claves externas que no están incluidas en la clase proporcionando la ruta
de acceso a la propiedad de clave principal al método de parámetro.

modelBuilder
.Entity<Post>()
.MapToStoredProcedures(s =>
[Link](i => [Link](p => [Link], "blog_id")));

Si no tiene una propiedad de navegación en la entidad dependiente (es decir, no se puede usar el método
Association para identificar el otro extremo de la relación y, después, configurar los parámetros
correspondientes a cada una de las propiedades de clave.

modelBuilder
.Entity<Post>()
.MapToStoredProcedures(s =>
[Link](i => [Link]<Blog>(
b => [Link],
c => [Link](b => [Link], "blog_id"))));

Tokens de simultaneidad
Los procedimientos almacenados de actualización y eliminación también pueden necesitar tratar la
simultaneidad:
Si la entidad contiene tokens de simultaneidad, el procedimiento almacenado puede tener opcionalmente un
parámetro de salida que devuelve el número de filas actualizadas o eliminadas (filas afectadas). Este tipo de
parámetro se debe configurar mediante el método RowsAffectedParameter.
De forma predeterminada, EF usa el valor devuelto por ExecuteNonQuery para determinar el número de filas
afectadas. La especificación de un parámetro de salida de filas afectado es útil si se realiza cualquier lógica en
el procedimiento almacenado, lo que daría lugar a que el valor devuelto de ExecuteNonQuery fuera
incorrecto (desde la perspectiva del EF) al final de la ejecución.
Para cada token de simultaneidad habrá un parámetro denominado ** <property_name> _Original** (por
ejemplo, Timestamp_Original). Se pasará el valor original de esta propiedad, que es el valor cuando se
consulta desde la base de datos.
Los tokens de simultaneidad calculados por la base de datos, como las marcas de tiempo, solo
tendrán un parámetro de valor original.
Las propiedades no calculadas que se establecen como tokens de simultaneidad también tendrán un
parámetro para el nuevo valor en el procedimiento de actualización. Utiliza las convenciones de
nomenclatura ya descritas para los nuevos valores. Un ejemplo de este tipo de token sería usar la
dirección URL de un blog como un token de simultaneidad, el nuevo valor es necesario porque puede
actualizarse a un nuevo valor por el código (a diferencia de un token de marca de tiempo que solo se
actualiza en la base de datos).
Esta es una clase de ejemplo y un procedimiento almacenado de actualización con un token de simultaneidad de
marca de tiempo.

public class Blog


{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
}

CREATE PROCEDURE [dbo].[Blog_Update]


@BlogId int,
@Name nvarchar(max),
@Url nvarchar(max),
@Timestamp_Original rowversion
AS
UPDATE [dbo].[Blogs]
SET [Name] = @Name, [Url] = @Url
WHERE BlogId = @BlogId AND [Timestamp] = @Timestamp_Original

A continuación se muestra una clase de ejemplo y un procedimiento almacenado de actualización con un token
de simultaneidad no calculado.

public class Blog


{
public int BlogId { get; set; }
public string Name { get; set; }
[ConcurrencyCheck]
public string Url { get; set; }
}

CREATE PROCEDURE [dbo].[Blog_Update]


@BlogId int,
@Name nvarchar(max),
@Url nvarchar(max),
@Url_Original nvarchar(max),
AS
UPDATE [dbo].[Blogs]
SET [Name] = @Name, [Url] = @Url
WHERE BlogId = @BlogId AND [Url] = @Url_Original

Reemplazar los valores predeterminados


Opcionalmente, puede introducir un parámetro de filas afectadas.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
[Link](u => [Link]("rows_affected")));

En el caso de tokens de simultaneidad calculados de base de datos, donde solo se pasa el valor original, solo
puede usar el mecanismo de cambio de nombre de parámetros estándar para cambiar el nombre del parámetro
para el valor original.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
[Link](u => [Link](b => [Link], "blog_timestamp")));

En el caso de los tokens de simultaneidad no calculados, donde se pasan tanto el valor original como el nuevo,
puede utilizar una sobrecarga de parámetro que le permita proporcionar un nombre para cada parámetro.

modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s => [Link](u => [Link](b => [Link], "blog_url", "blog_original_url")));

Relaciones de varios a varios


Usaremos las clases siguientes como ejemplo en esta sección.

public class Post


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

public List<Tag> Tags { get; set; }


}

public class Tag


{
public int TagId { get; set; }
public string TagName { get; set; }

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


}

Las relaciones de varios a varios se pueden asignar a procedimientos almacenados con la siguiente sintaxis.

modelBuilder
.Entity<Post>()
.HasMany(p => [Link])
.WithMany(t => [Link])
.MapToStoredProcedures();

Si no se proporciona ninguna otra configuración, se utiliza de forma predeterminada la siguiente forma de


procedimiento almacenado.
Dos procedimientos almacenados denominados ** <type_one> <type_two> _Insert** y ** <type_one>
<type_two> _Delete** (por ejemplo, PostTag_Insert y PostTag_Delete).
Los parámetros serán los valores de clave de cada tipo. El nombre de cada parámetro es ** <type_name> _
<property_name> ** (por ejemplo, Post_PostId y Tag_TagId).
Estos son los procedimientos almacenados de inserción y actualización de ejemplo.

CREATE PROCEDURE [dbo].[PostTag_Insert]


@Post_PostId int,
@Tag_TagId int
AS
INSERT INTO [dbo].[Post_Tags] (Post_PostId, Tag_TagId)
VALUES (@Post_PostId, @Tag_TagId)
CREATE PROCEDURE [dbo].[PostTag_Delete]
@Post_PostId int,
@Tag_TagId int
AS
DELETE FROM [dbo].[Post_Tags]
WHERE Post_PostId = @Post_PostId AND Tag_TagId = @Tag_TagId

Reemplazar los valores predeterminados


Los nombres de parámetros y procedimientos se pueden configurar de forma similar a los procedimientos
almacenados de entidad.

modelBuilder
.Entity<Post>()
.HasMany(p => [Link])
.WithMany(t => [Link])
.MapToStoredProcedures(s =>
[Link](i => [Link]("add_post_tag")
.LeftKeyParameter(p => [Link], "post_id")
.RightKeyParameter(t => [Link], "tag_id"))
.Delete(d => [Link]("remove_post_tag")
.LeftKeyParameter(p => [Link], "post_id")
.RightKeyParameter(t => [Link], "tag_id")));
Migraciones de Code First
12/03/2021 • 20 minutes to read

Migraciones de Code First es la manera recomendada de desarrollar el esquema de base de datos de la


aplicación si usa el flujo de trabajo de Code First. Migraciones proporciona un conjunto de herramientas que
permiten:
1. Crear una base de datos inicial que funciona con el modelo de EF
2. Generar migraciones para realizar un seguimiento de los cambios realizados en el modelo de EF
3. Mantener actualizada la base de datos con esos cambios
En el siguiente tutorial se proporciona información general sobre Migraciones de Code First en Entity
Framework. Puede realizar el tutorial completo o ir al tema que le interesa. Se tratan los siguientes temas:

Creación de un modelo inicial y una base de datos


Antes de empezar a usar Migraciones, se necesitan un proyecto y un modelo de Code First con los que trabajar.
En este tutorial se va a usar el modelo canónico Blog y Post .
Cree una nueva aplicación de consola MigrationsDemo
Agregue la versión más reciente del paquete NuGet de EntityFramework al proyecto
Herramientas –> Administrador de paquetes de biblioteca–> Consola del Administrador
de paquetes
Ejecute el comando Install-Package EntityFramework
Agregue un archivo [Link] con el código que se muestra a continuación. Este código define una sola clase
Blog que conforma el modelo de dominio y una clase BlogContext que es el contexto de EF Code First

using [Link];
using [Link];
using [Link];
using [Link];

namespace MigrationsDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}

public class Blog


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

Ahora que tenemos un modelo, es hora de usarlo para el acceso a los datos. Actualice el archivo [Link]
con el código que se muestra a continuación.
using System;
using [Link];
using [Link];
using [Link];

namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
[Link](new Blog { Name = "Another Blog " });
[Link]();

foreach (var blog in [Link])


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

[Link]("Press any key to exit...");


[Link]();
}
}
}

Ejecute la aplicación y verá que se ha creado automáticamente una base de datos


[Link] .

Habilitación de migraciones
Es hora de realizar más cambios en el modelo.
Vamos a introducir una propiedad Url en la clase Blog.

public string Url { get; set; }

Si volviera a ejecutar la aplicación de nuevo, aparecería una excepción InvalidOperationException con el texto El
modelo que respalda el contexto 'BlogContext' ha cambiado desde que se creó la base de datos. Considere la
posibilidad de usar Migraciones de Code First para actualizar la base de datos ( [Link]
LinkId=238269 ).
Como sugiere la excepción, es hora de empezar a usar Migraciones de Code First. El primer paso es habilitar las
migraciones para el contexto.
Ejecute el comando Enable-Migrations en la consola del Administrador de paquetes
Este comando ha agregado una carpeta Migraciones al proyecto. Esta nueva carpeta contiene dos
archivos:
La clase Configuration. Esta clase permite configurar el comportamiento de Migraciones en el
contexto. En este tutorial se usa la configuración predeterminada. Puesto que hay un solo contexto de
Code First en el proyecto, Enable-Migrations ha rellenado automáticamente el tipo de contexto al que se
aplica esta configuración.
Una migración InitialCreate . Esta migración se ha generado porque Code First ya había creado
automáticamente una base de datos antes de que se habilitaran las migraciones. El código de esta
migración con scaffolding representa los objetos que ya se han creado en la base de datos. En nuestro
caso, es la tabla Blog con las columnas BlogId y Name . El nombre de archivo incluye una marca de
tiempo para ayudar con el orden. Si la base de datos aún no se hubiera creado, esta migración
InitialCreate no se hubiera agregado al proyecto. Por el contrario, la primera vez que se llamara a Add-
Migration, al código para crear estas tablas se le aplicaría scaffolding para una nueva migración.
Varios modelos con la misma base de datos como destino
Al usar versiones anteriores a EF6, solo se puede usar un modelo de Code First para generar o administrar el
esquema de una base de datos. Este es el resultado de una sola tabla __MigrationsHistor y por base de datos
sin forma de identificar qué entradas pertenecen a qué modelo.
A partir de EF6, la clase Configuration incluye una propiedad ContextKey . Esta actúa como identificador único
para cada modelo de Code First. Una columna correspondiente de la tabla __MigrationsHistor y permite que
entradas de varios modelos compartan la tabla. De forma predeterminada, esta propiedad se establece en el
nombre completo del contexto.

Generación y ejecución de migraciones


Migraciones de Code First tiene dos comandos principales con los que se va a familiarizar.
Add-Migration aplica la técnica scaffolding a la siguiente migración en función de los cambios realizados
en el modelo desde la creación de la última migración
Update-Database aplica las migraciones pendientes a la base de datos
Es necesario aplicar scaffolding a una migración para encargarse de la nueva propiedad Url que se ha agregado.
El comando Add-Migration permite poner un nombre a estas migraciones; a la nuestra la llamaremos
AddBlogUrl .
Ejecute el comando Add-Migration AddBlogUrl en la consola del Administrador de paquetes
En la carpeta Migraciones ahora tenemos una nueva migración AddBlogUrl . El nombre de archivo de la
migración lleva una marca de tiempo para ayudar con el orden
namespace [Link]
{
using System;
using [Link];

public partial class AddBlogUrl : DbMigration


{
public override void Up()
{
AddColumn("[Link]", "Url", c => [Link]());
}

public override void Down()


{
DropColumn("[Link]", "Url");
}
}
}

Ahora se podría editar esta migración o agregarle elementos, pero todo tiene bastante buen aspecto. Vamos a
usar Update-Database para aplicar esta migración a la base de datos.
Ejecute el comando Update-Database en la consola del Administrador de paquetes
Migraciones de Code First compara las migraciones de la carpeta Migraciones con las que se han aplicado
a la base de datos. Verá que es necesario aplicar la migración AddBlogUrl y ejecutarla.
La base de datos [Link] se ha actualizado para incluir la columna Url en la tabla
Blogs .

Personalización de migraciones
Hasta ahora hemos generado y ejecutado una migración sin realizar ningún cambio. Ahora vamos a editar el
código que se genera de forma predeterminada.
Es hora de realizar algunos cambios más en el modelo: vamos a agregar una nueva propiedad Rating a la
clase Blog

public int Rating { get; set; }

También vamos a agregar una nueva clase Post

public class Post


{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}

Además agregaremos una colección Posts a la clase Blog para formar el otro extremo de la relación entre
Blog y Post

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


Vamos a usar el comando Add-Migration para permitir que Migraciones de Code First aplique scaffolding de
su mejor estimación en la migración. Vamos a llamar a esta migración AddPostClass .
Ejecute el comando Add-Migration AddPostClass en la consola del Administrador de paquetes.
Migraciones de Code First hizo un muy buen trabajo de scaffolding de estos cambios, pero hay algunas cosas
que es posible que queramos cambiar:
1. En primer lugar, vamos a agregar un índice único a la columna [Link] (adición en la línea 22 y 29 del
código siguiente).
2. También vamos a agregar una columna [Link] que no admite valores null. Si hay datos existentes en
la tabla, se les asigna el valor predeterminado CLR del tipo de datos para la nueva columna (Rating es entero,
por lo que sería 0 ). Pero queremos especificar un valor predeterminado de 3 , para que las filas existentes en
la tabla Blogs comiencen con una clasificación decente. (Puede ver el valor predeterminado especificado en
la línea 24 del código siguiente)

namespace [Link]
{
using System;
using [Link];

public partial class AddPostClass : DbMigration


{
public override void Up()
{
CreateTable(
"[Link]",
c => new
{
PostId = [Link](nullable: false, identity: true),
Title = [Link](maxLength: 200),
Content = [Link](),
BlogId = [Link](nullable: false),
})
.PrimaryKey(t => [Link])
.ForeignKey("[Link]", t => [Link], cascadeDelete: true)
.Index(t => [Link])
.Index(p => [Link], unique: true);

AddColumn("[Link]", "Rating", c => [Link](nullable: false, defaultValue: 3));


}

public override void Down()


{
DropIndex("[Link]", new[] { "Title" });
DropIndex("[Link]", new[] { "BlogId" });
DropForeignKey("[Link]", "BlogId", "[Link]");
DropColumn("[Link]", "Rating");
DropTable("[Link]");
}
}
}

La migración editada ya está lista, así que vamos a usar Update-Database para actualizar la base de datos. Esta
vez vamos a especificar la marca -Verbose para que pueda ver el SQL que ejecuta Migraciones de Code First.
Ejecute el comando Update-Database –Verbose en la consola del Administrador de paquetes.

Movimiento de datos o SQL personalizado


Hasta ahora hemos examinado las operaciones de migración que no cambian ni mueven datos, y ahora vamos a
ver algo que necesita mover algunos datos. Todavía no hay compatibilidad nativa con el movimiento de datos,
pero podemos ejecutar algunos comandos SQL arbitrarios en cualquier punto del script.
Vamos a agregar una propiedad [Link] al modelo. Luego, vamos a rellenar previamente el elemento
Abstract de publicaciones existentes con algún texto del inicio de la columna Content .

public string Abstract { get; set; }

Vamos a usar el comando Add-Migration para permitir que Migraciones de Code First aplique scaffolding de
su mejor estimación en la migración.
Ejecute el comando Add-Migration AddPostAbstract en la consola del Administrador de paquetes.
La migración generada se encarga de los cambios de esquema, pero además queremos rellenar previamente
la columna Abstract con los 100 primeros caracteres del contenido de cada publicación. Lo podemos hacer
si recurrimos a SQL y ejecutamos una instrucción UPDATE después de agregar la columna. (Adición en la
línea 12 del código siguiente)

namespace [Link]
{
using System;
using [Link];

public partial class AddPostAbstract : DbMigration


{
public override void Up()
{
AddColumn("[Link]", "Abstract", c => [Link]());

Sql("UPDATE [Link] SET Abstract = LEFT(Content, 100) WHERE Abstract IS NULL");


}

public override void Down()


{
DropColumn("[Link]", "Abstract");
}
}
}

La migración editada tiene buen aspecto, así que vamos a usar Update-Database para actualizar la base de
datos. Especificamos la marca –Verbose para poder ver el SQL que se ejecuta en la base de datos.
Ejecute el comando Update-Database –Verbose en la consola del Administrador de paquetes.

Migrar a una versión determinada (incluido un cambio a una versión


anterior)
Hasta ahora siempre hemos actualizado a la migración más reciente, pero puede haber ocasiones en que quiera
cambiar a una migración anterior o posterior.
Supongamos que queremos migrar la base de datos al estado en que estaba después de ejecutar la migración
AddBlogUrl . Podemos usar el modificador –TargetMigration para cambiar a esta migración anterior.
Ejecute el comando Update-Database –TargetMigration: AddBlogUrl en la consola del Administrador
de paquetes.
Este comando ejecuta el script Down de las migraciones AddBlogAbstract y AddPostClass .
Si quiere revertir a una base de datos vacía, puede usar el comando Update-Database –TargetMigration:
$InitialDatabase .
Obtención de un script SQL
Si otro desarrollador quiere estos cambios en su equipo, puede sincronizar una vez que se protejan los cambios
en el control de código fuente. Cuando tenga las nuevas migraciones, puede ejecutar el comando Update-
Database para que los cambios se apliquen localmente. Pero si queremos enviar estos cambios a un servidor de
prueba y, finalmente, a producción, probablemente querremos un script SQL que podamos pasar al DBA.
Ejecute el comando Update-Database , pero esta vez especifique la marca –Script para que los cambios se
escriban en un script en lugar de aplicarse. También se especifican una migración de origen y de destino para
las que generar el script. Queremos un script que abarque desde una base de datos vacía
($InitialDatabase ) a la versión más reciente (migración AddPostAbstract ). Si no se especifica una
migración de destino, Migraciones usa la última migración como destino. Si no se especifica una migración
de origen, Migraciones usa el estado actual de la base de datos.
Ejecute el comando Update-Database -Script -SourceMigration: $InitialDatabase -TargetMigration:
AddPostAbstract en la consola del Administrador de paquetes
Migraciones de Code First ejecuta la canalización de migración, pero en lugar de aplicar los cambios, los escribe
en un archivo .sql. Una vez que se ha generado el script, se abre automáticamente en Visual Studio, listo para
verse o guardarse.
Generación de scripts idempotentes
A partir de EF6, si especifica –SourceMigration $InitialDatabase , el script generado será "idempotente". Los
scripts idempotentes pueden actualizar una base de datos de cualquier versión a la versión más reciente (o la
versión especificada si se usa –TargetMigration ). El script generado incluye lógica para comprobar la tabla
__MigrationsHistor y y aplicar solo los cambios que no se han aplicado anteriormente.

Actualización automática al iniciar la aplicación (inicializador


MigrateDatabaseToLatestVersion)
Si va a implementar la aplicación, es posible que quiera que actualice la base de datos automáticamente (al
aplicar las migraciones pendientes) cuando se inicie. Puede hacerlo si registra el inicializador de base de datos
MigrateDatabaseToLatestVersion . Un inicializador de base de datos simplemente contiene alguna lógica que
se usa para asegurarse de que la base de datos se ha configurado correctamente. Esta lógica se ejecuta la
primera vez que se usa el contexto dentro del proceso de aplicación (AppDomain ).
Se puede actualizar el archivo [Link] , como se muestra a continuación, para establecer el inicializador
MigrateDatabaseToLatestVersion para BlogContext antes de usar el contexto (línea 14). Tenga en cuenta que
también debe agregar una instrucción using para el espacio de nombres [Link] (línea 5).
Cuando se crea una instancia de este inicializador, se debe especificar el tipo de contexto (BlogContext ) y la
configuración de las migraciones (Configuration ): la configuración de las migraciones es la clase que se ha
agregado a la carpeta Migraciones al habilitar Migraciones.
using System;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
[Link](new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());

using (var db = new BlogContext())


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

foreach (var blog in [Link])


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

[Link]("Press any key to exit...");


[Link]();
}
}
}

Ahora, siempre que se ejecute la aplicación, en primer lugar comprobará si la base de datos de destino está
actualizada y, si no lo está, aplicará las migraciones pendientes.
Migraciones de Code First automática
12/03/2021 • 11 minutes to read

Las migraciones automáticas permiten usar Migraciones de Code First sin tener un archivo de código en el
proyecto para cada cambio que realice. No todos los cambios se pueden aplicar automáticamente; por ejemplo,
el cambio de nombre de columna requiere el uso de una migración basada en código.

NOTE
En este artículo se supone que sabe cómo usar Migraciones de Code First en escenarios básicos. Si no lo hace, tendrá que
leer migraciones de Code First antes de continuar.

Recomendación para entornos de equipo


Puede intercalar migraciones automáticas y basadas en código, pero esto no se recomienda en escenarios de
desarrollo en equipo. Si forma parte de un equipo de desarrolladores que usan el control de código fuente, debe
usar migraciones automáticas o exclusivamente basadas en código. Dadas las limitaciones de las migraciones
automáticas, se recomienda usar migraciones basadas en código en entornos de equipo.

Creación de un modelo inicial y una base de datos


Antes de empezar a usar Migraciones, se necesitan un proyecto y un modelo de Code First con los que trabajar.
En este tutorial se va a usar el modelo canónico Blog y Post .
Creación de una nueva aplicación de consola de MigrationsAutomaticDemo
Agregue la versión más reciente del paquete NuGet de EntityFramework al proyecto
Herramientas –> Administrador de paquetes de biblioteca–> Consola del Administrador
de paquetes
Ejecute el comando Install-Package EntityFramework
Agregue un archivo [Link] con el código que se muestra a continuación. Este código define una sola clase
Blog que conforma el modelo de dominio y una clase BlogContext que es el contexto de EF Code First

using [Link];
using [Link];
using [Link];
using [Link];

namespace MigrationsAutomaticDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}

public class Blog


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

Ahora que tenemos un modelo, es hora de usarlo para el acceso a los datos. Actualice el archivo [Link]
con el código que se muestra a continuación.

using System;
using [Link];
using [Link];
using [Link];

namespace MigrationsAutomaticDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
[Link](new Blog { Name = "Another Blog " });
[Link]();

foreach (var blog in [Link])


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

[Link]("Press any key to exit...");


[Link]();
}
}
}

Ejecute la aplicación y verá que se crea una base de datos MigrationsAutomaticCodeDemo.


BlogContext .

Habilitación de migraciones
Es hora de realizar más cambios en el modelo.
Vamos a introducir una propiedad Url en la clase Blog.

public string Url { get; set; }

Si volviera a ejecutar la aplicación de nuevo, aparecería una excepción InvalidOperationException con el texto El
modelo que respalda el contexto 'BlogContext' ha cambiado desde que se creó la base de datos. Considere la
posibilidad de usar Migraciones de Code First para actualizar la base de datos ( [Link]
LinkId=238269 ).
Como sugiere la excepción, es hora de empezar a usar Migraciones de Code First. Dado que queremos usar
migraciones automáticas, vamos a especificar el modificador – EnableAutomaticMigrations .
Ejecute el comando enable-Migrations – EnableAutomaticMigrations en la consola del
administrador de paquetes. este comando ha agregado una carpeta Migrations a nuestro proyecto. Esta
nueva carpeta contiene un archivo:
La clase Configuration. Esta clase permite configurar el comportamiento de Migraciones en el
contexto. En este tutorial se usa la configuración predeterminada. Puesto que hay un solo contexto de
Code First en el proyecto, Enable-Migrations ha rellenado automáticamente el tipo de contexto al que se
aplica esta configuración.

La primera migración automática


Migraciones de Code First tiene dos comandos principales con los que se va a familiarizar.
Add-Migration aplica la técnica scaffolding a la siguiente migración en función de los cambios realizados
en el modelo desde la creación de la última migración
Update-Database aplica las migraciones pendientes a la base de datos
Vamos a evitar el uso de Add-Migration (a menos que realmente sea necesario) y nos centraremos en permitir
que Migraciones de Code First calcule y aplique los cambios automáticamente. Vamos a usar Update-
Database para obtener migraciones de Code First para enviar los cambios a nuestro modelo (la nueva
propiedad blog. ur l) a la base de datos.
Ejecute el comando Update-Database en la consola del administrador de paquetes.
La base de datos MigrationsAutomaticDemo. BlogContext se ha actualizado para incluir la columna URL en
la tabla blogs .

La segunda migración automática


Vamos a realizar otro cambio y dejar que Migraciones de Code First inserten automáticamente los cambios en
la base de datos.
También vamos a agregar una nueva clase Post

public class Post


{
public int PostId { get; set; }
[MaxLength(200)]
public string Title { get; set; }
public string Content { get; set; }

public int BlogId { get; set; }


public Blog Blog { get; set; }
}

Además agregaremos una colección Posts a la clase Blog para formar el otro extremo de la relación entre
Blog y Post

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

Ahora use Update-Database para actualizar la base de datos. Esta vez vamos a especificar la marca -
Verbose para que pueda ver el SQL que ejecuta Migraciones de Code First.
Ejecute el comando Update-Database –Verbose en la consola del Administrador de paquetes.

Agregar una migración basada en código


Ahora veamos algo que es posible que quieramos usar una migración basada en código para.
Vamos a agregar una propiedad rating a la clase blog

public int Rating { get; set; }

Podríamos simplemente ejecutar Update-Database para realizar estos cambios en la base de datos. Sin
embargo, vamos a agregar una columna blogs. Rating que no aceptan valores NULL, si hay datos existentes
en la tabla, se asignará el valor predeterminado de CLR del tipo de datos de la nueva columna (la clasificación es
Integer, por lo que sería 0 ). Pero queremos especificar un valor predeterminado de 3 , para que las filas
existentes en la tabla Blogs comiencen con una clasificación decente. Vamos a usar el comando Add-Migration
para escribir este cambio en una migración basada en código para poder editarlo. El comando Add-Migration
nos permite dar un nombre a estas migraciones, simplemente vamos a llamar a nuestra AddBlogRating .
Ejecute el comando Add-Migration AddBlogRating en la consola del administrador de paquetes.
En la carpeta migraciones ahora tenemos una nueva migración de AddBlogRating . El nombre de archivo
de la migración se ha fijado previamente con una marca de tiempo para ayudar con la ordenación. Vamos a
editar el código generado para especificar un valor predeterminado de 3 para blog. Rating (línea 10 en el
código siguiente)
La migración también tiene un archivo de código subyacente que captura algunos metadatos. Estos metadatos
permitirán que Migraciones de Code First repliquen las migraciones automáticas realizadas antes de esta
migración basada en código. Esto es importante si otro desarrollador desea ejecutar nuestras migraciones o
Cuándo es el momento de implementar la aplicación.

namespace [Link]
{
using System;
using [Link];

public partial class AddBlogRating : DbMigration


{
public override void Up()
{
AddColumn("Blogs", "Rating", c => [Link](nullable: false, defaultValue: 3));
}

public override void Down()


{
DropColumn("Blogs", "Rating");
}
}
}

La migración editada tiene buen aspecto, así que vamos a usar Update-Database para actualizar la base de
datos.
Ejecute el comando Update-Database en la consola del administrador de paquetes.

Volver a migraciones automáticas


Ahora podemos volver a pasar a las migraciones automáticas para nuestros cambios más sencillos. Migraciones
de Code First se encargará de realizar las migraciones automáticas y basadas en código en el orden correcto en
función de los metadatos que se almacenan en el archivo de código subyacente para cada migración basada en
código.
Vamos a agregar una propiedad post. Abstract a nuestro modelo

public string Abstract { get; set; }

Ahora podemos usar Update-Database para obtener migraciones de Code First para realizar este cambio en la
base de datos mediante una migración automática.
Ejecute el comando Update-Database en la consola del administrador de paquetes.

Resumen
En este tutorial, ha visto cómo usar las migraciones automáticas para realizar cambios en el modelo en la base
de datos. También ha visto cómo aplicar scaffolding y ejecutar migraciones basadas en código entre
migraciones automáticas cuando necesite más control.
Migraciones de Code First con una base de datos
existente
12/03/2021 • 16 minutes to read

NOTE
EF 4.3 y versiones posteriores: las características, las API, etc. que se describen en esta página se introdujeron en Entity
Framework 4,1. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

En este artículo se describe el uso de Migraciones de Code First con una base de datos existente, una que no se
ha creado con Entity Framework.

NOTE
En este artículo se supone que sabe cómo usar Migraciones de Code First en escenarios básicos. Si no lo hace, tendrá que
leer migraciones de Code First antes de continuar.

Presentaciones en pantalla
Si prefiere ver una screencast que lea este artículo, los dos vídeos siguientes cubren el mismo contenido que
este artículo.
Vídeo uno: "migraciones-bajo el capó"
En esta presentación en pantalla se explica cómo realiza la migración y usa información sobre el modelo para
detectar los cambios de modelo.
Vídeo dos: "migraciones: bases de datos existentes"
Basándose en los conceptos del vídeo anterior, esta presentación en pantalla explica cómo habilitar y usar las
migraciones con una base de datos existente.

Paso 1: creación de un modelo


El primer paso será crear un modelo de Code First que tenga como destino la base de datos existente. En el
tema code First a una base de datos existente se proporcionan instrucciones detalladas sobre cómo hacerlo.

NOTE
Es importante seguir el resto de los pasos de este tema antes de realizar cambios en el modelo que requieran cambios en
el esquema de la base de datos. Los pasos siguientes requieren que el modelo esté sincronizado con el esquema de la
base de datos.

Paso 2: habilitar las migraciones


El siguiente paso consiste en habilitar las migraciones. Para ello, ejecute el comando enable-Migrations en la
consola del administrador de paquetes.
Este comando creará una carpeta en la solución denominada migraciones y colocará una sola clase en ella
llamada configuración. La clase de configuración es donde se configuran las migraciones de la aplicación, puede
encontrar más información en el tema migraciones de Code First .

Paso 3: agregar una migración inicial


Una vez que se han creado y aplicado las migraciones en la base de datos local, es posible que también desee
aplicar estos cambios a otras bases de datos. Por ejemplo, la base de datos local puede ser una base de datos de
prueba y, en última instancia, querrá aplicar también los cambios a una base de datos de producción o a otros
desarrolladores de prueba. Hay dos opciones para este paso y la que debe elegir depende de si el esquema de
otras bases de datos está vacío o no coincide actualmente con el esquema de la base de datos local.
Opción uno: usar el esquema existente como punto de par tida. Debe usar este enfoque si las demás
bases de datos que se van a aplicar a las migraciones en el futuro tendrán el mismo esquema que la base de
datos local que tiene actualmente. Por ejemplo, podría utilizarlo si la base de datos de prueba local coincide
con la versión 1 de la base de datos de producción y, posteriormente, aplicará estas migraciones para
actualizar la base de datos de producción a V2.
Opción dos: usar una base de datos vacía como punto de par tida. Debe usar este enfoque cuando
las demás bases de datos que se van a aplicar a las migraciones en el futuro estén vacías (o aún no existan).
Por ejemplo, podría utilizarlo si comenzó a desarrollar la aplicación con una base de datos de prueba pero sin
usar migraciones y posteriormente deseará crear una base de datos de producción desde cero.
Opción uno: usar el esquema existente como punto de partida
Migraciones de Code First usa una instantánea del modelo almacenada en la migración más reciente para
detectar los cambios en el modelo (puede encontrar información detallada sobre esto en migraciones de Code
First en entornos de equipo). Como vamos a suponer que las bases de datos ya tienen el esquema del modelo
actual, generaremos una migración vacía (no operativa) que tenga el modelo actual como una instantánea.
1. Ejecute el comando Add-Migration InitialCreate – IgnoreChanges en la consola del administrador de
paquetes. Esto crea una migración vacía con el modelo actual como una instantánea.
2. Ejecute el comando Update-Database en la consola del administrador de paquetes. Esto aplicará la
migración de InitialCreate a la base de datos. Dado que la migración real no contiene ningún cambio,
simplemente agregará una fila a la _ _ tabla MigrationsHistory que indica que ya se ha aplicado esta
migración.
Opción dos: usar una base de datos vacía como punto de partida
En este escenario, se necesitan migraciones para poder crear la base de datos completa desde cero, incluidas las
tablas que ya están presentes en nuestra base de datos local. Vamos a generar una migración de InitialCreate
que incluye la lógica para crear el esquema existente. A continuación, haremos que nuestra base de datos
existente tenga el mismo aspecto que ya se ha aplicado esta migración.
1. Ejecute el comando Add-Migration InitialCreate en la consola del administrador de paquetes. Esto crea
una migración para crear el esquema existente.
2. Comente todo el código del método up de la migración recién creada. Esto nos permitirá "aplicar" la
migración a la base de datos local sin intentar volver a crear todas las tablas, etc., que ya existen.
3. Ejecute el comando Update-Database en la consola del administrador de paquetes. Esto aplicará la
migración de InitialCreate a la base de datos. Dado que la migración real no contiene ningún cambio (dado
que hemos comentado temporalmente), simplemente agregará una fila a la _ _ tabla MigrationsHistory que
indica que ya se ha aplicado esta migración.
4. Quite la marca de comentario del código en el método up. Esto significa que, cuando esta migración se aplica
a bases de datos futuras, el esquema que ya existía en la base de datos local lo crearán las migraciones.

Aspectos que debe tener en cuenta


Hay algunas cosas que debe tener en cuenta al usar migraciones en una base de datos existente.
Los nombres predeterminados o calculados pueden no coincidir con el esquema existente
Las migraciones especifican explícitamente los nombres de las columnas y las tablas cuando se scaffolding una
migración. Sin embargo, hay otros objetos de base de datos que las migraciones calculan un nombre
predeterminado para cuando se aplican las migraciones. Esto incluye los índices y las restricciones Foreign Key.
Cuando el destino es un esquema existente, es posible que estos nombres calculados no coincidan con lo que
existe realmente en la base de datos.
Estos son algunos ejemplos de Cuándo es necesario tener en cuenta lo siguiente:
Si usó ' opción uno: usar el esquema existente como punto de par tida ' del paso 3:
Si los cambios futuros en el modelo requieren el cambio o la eliminación de uno de los objetos de base de
datos que se denominan de forma diferente, tendrá que modificar la migración con scaffolding para
especificar el nombre correcto. Las API de migraciones tienen un parámetro de nombre opcional que le
permite hacerlo. Por ejemplo, el esquema existente puede tener una tabla post con una columna de clave
externa BlogId que tenga un índice denominado IndexFk _ BlogId. Sin embargo, de forma predeterminada,
las migraciones esperan que este índice se denomine IX _ BlogId. Si realiza un cambio en el modelo que tiene
como resultado la eliminación de este índice, tendrá que modificar la llamada a scaffolding DropIndex para
especificar el nombre de la BlogId de IndexFk _ .
Si ha usado la opción dos: usar una base de datos vacía como punto de par tida en el paso 3:
Puede producirse un error al intentar ejecutar el método Down de la migración inicial (es decir, revertir a una
base de datos vacía) en la base de datos local, ya que las migraciones intentarán quitar índices y restricciones
de clave externa usando los nombres incorrectos. Esto solo afectará a la base de datos local, ya que las
demás bases de datos se crearán desde cero con el método up de la migración inicial. Si desea cambiar la
base de datos local existente a un estado vacío, es más fácil hacerlo manualmente, ya sea quitando la base de
datos o quitando todas las tablas. Después de esta degradación inicial, todos los objetos de base de datos se
volverán a crear con los nombres predeterminados, por lo que este problema no se volverá a presentar.
Si los cambios futuros en el modelo requieren el cambio o la eliminación de uno de los objetos de base de
datos con el nombre diferente, no funcionará en la base de datos local existente, ya que los nombres no
coinciden con los valores predeterminados. Sin embargo, funcionará en las bases de datos que se crearon
desde cero, ya que se usarán los nombres predeterminados elegidos por las migraciones. Puede realizar
estos cambios de forma manual en la base de datos existente o considerar la posibilidad de hacer que las
migraciones vuelvan a crear la base de datos desde cero, como se hará en otras máquinas.
Las bases de datos creadas con el método up de la migración inicial pueden diferir ligeramente de la base de
datos local, ya que se usarán los nombres predeterminados calculados para los índices y las restricciones
Foreign Key. También puede acabar con índices adicionales ya que las migraciones crearán índices en
columnas de clave externa de forma predeterminada; esto puede no haber sido el caso en la base de datos
local original.
No todos los objetos de base de datos se representan en el modelo
Los objetos de base de datos que no forman parte de su modelo no serán controlados por las migraciones. Esto
puede incluir vistas, procedimientos almacenados, permisos, tablas que no forman parte del modelo, índices
adicionales, etc.
Estos son algunos ejemplos de Cuándo es necesario tener en cuenta lo siguiente:
Independientemente de la opción elegida en el ' paso 3 ', si los cambios futuros en el modelo requieren el
cambio o la eliminación de estas migraciones de objetos adicionales, no sabrá realizar estos cambios. Por
ejemplo, si quita una columna que tiene un índice adicional en ella, las migraciones no sabrán quitar el
índice. Tendrá que agregarlo manualmente a la migración con scaffolding.
Si ha usado la opción dos: usar una base de datos vacía como punto de partida, estos objetos adicionales no
se crearán con el método up de la migración inicial. Puede modificar los métodos arriba y abajo para que se
ocupen de estos objetos adicionales si lo desea. En el caso de los objetos que no se admiten de forma nativa
en la API de migraciones, como las vistas, puede usar el método SQL para ejecutar SQL sin formato para
crearlos y quitarlos.
Personalización de la tabla de historial de
migraciones
12/03/2021 • 6 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

NOTE
En este artículo se supone que sabe cómo usar Migraciones de Code First en escenarios básicos. Si no lo hace, tendrá que
leer migraciones de Code First antes de continuar.

¿Qué es la tabla de historial de migraciones?


La tabla de historial de migraciones es una tabla que usa Migraciones de Code First para almacenar detalles
sobre las migraciones que se aplican a la base de datos. De forma predeterminada, el nombre de la tabla de la
base de datos es _ _ MigrationHistory y se crea al aplicar la primera migración a la base de datos. En Entity
Framework 5, esta tabla era una tabla del sistema si la aplicación usaba la base de datos de Microsoft SQL
Server. No obstante, esto ha cambiado en Entity Framework 6 y la tabla de historial de migraciones ya no está
marcada como una tabla del sistema.

¿Por qué personalizar la tabla de historial de migraciones?


La tabla de historial de migraciones se supone que se usará únicamente en Migraciones de Code First y
cambiarla manualmente puede interrumpir las migraciones. Sin embargo, a veces la configuración
predeterminada no es adecuada y la tabla debe personalizarse, por ejemplo:
Debe cambiar los nombres y/o las caras de las columnas para habilitar un proveedor de migracionesde terceras
partes.

Desea cambiar el nombre de la tabla


Debe usar un esquema no predeterminado para la _ _ tabla MigrationHistory
Necesita almacenar datos adicionales para una versión determinada del contexto y, por tanto, necesita
agregar una columna adicional a la tabla.

Palabras de precaución
Cambiar la tabla de historial de migración es eficaz, pero debe tener cuidado de no abusar. En la actualidad, el
tiempo de ejecución de EF no comprueba si la tabla de historial de migraciones personalizada es compatible con
el tiempo de ejecución. Si no es así, es posible que la aplicación se interrumpa en tiempo de ejecución o se
comporte de maneras imprevisibles. Esto es aún más importante si usa varios contextos por base de datos, en
cuyo caso varios contextos pueden usar la misma tabla de historial de migración para almacenar información
acerca de las migraciones.

¿Cómo se personaliza la tabla de historial de migraciones?


Antes de empezar, debe saber que solo puede personalizar la tabla de historial de migraciones antes de aplicar
la primera migración. Ahora, en el código.
En primer lugar, tendrá que crear una clase derivada de la clase System. Data. Entity. Migrations. History.
HistoryContext. La clase HistoryContext se deriva de la clase DbContext, por lo que la configuración de la tabla
de historial de migraciones es muy similar a la configuración de modelos EF con la API fluida. Solo tiene que
invalidar el método OnModelCreating y usar la API fluida para configurar la tabla.

NOTE
Normalmente, cuando se configuran modelos EF no es necesario llamar a base. OnModelCreating () del método
OnModelCreating invalidado, ya que DbContext. OnModelCreating () tiene un cuerpo vacío. Este no es el caso cuando se
configura la tabla de historial de migraciones. En este caso, lo primero que hay que hacer en la invalidación de
OnModelCreating () es, en realidad, llamar a base. OnModelCreating (). De esta forma, se configurará la tabla de historial
de migraciones de la manera predeterminada que se va a ajustar en el método de reemplazo.

Supongamos que desea cambiar el nombre de la tabla de historial de migraciones y colocarla en un esquema
personalizado denominado "admin". Además, el DBA le gustaría cambiar el nombre de la columna MigrationId a
_ ID. de migración. Para ello, cree la clase siguiente derivada de HistoryContext:

using [Link];
using [Link];
using [Link];

namespace CustomizableMigrationsHistoryTableSample
{
public class MyHistoryContext : HistoryContext
{
public MyHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema)
{
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link](modelBuilder);
[Link]<HistoryRow>().ToTable(tableName: "MigrationHistory", schemaName:
"admin");
[Link]<HistoryRow>().Property(p =>
[Link]).HasColumnName("Migration_ID");
}
}
}

Una vez que el HistoryContext personalizado esté listo, debe hacer que EF lo tenga al registrarlo mediante la
configuración basada en código:
using [Link];

namespace CustomizableMigrationsHistoryTableSample
{
public class ModelConfiguration : DbConfiguration
{
public ModelConfiguration()
{
[Link]("[Link]",
(connection, defaultSchema) => new MyHistoryContext(connection, defaultSchema));
}
}
}

Eso es prácticamente todo. Ahora puede ir a la consola del administrador de paquetes, enable-Migrations, Add-
Migration y, por último, Update-Database. Esto debería hacer que se agregue a la base de datos una tabla de
historial de migraciones configurada según los detalles especificados en la clase derivada HistoryContext.
Usar [Link]
12/03/2021 • 9 minutes to read

Migraciones de Code First se puede utilizar para actualizar una base de datos desde Visual Studio, pero también
se puede ejecutar mediante la herramienta de línea de comandos [Link]. Esta página proporcionará
información general rápida sobre cómo usar [Link] para ejecutar migraciones en una base de datos de.

NOTE
En este artículo se supone que sabe cómo usar Migraciones de Code First en escenarios básicos. Si no lo hace, tendrá que
leer migraciones de Code First antes de continuar.

Copiar [Link]
Al instalar Entity Framework mediante NuGet [Link] estará dentro de la carpeta Tools del paquete
descargado. En la < carpeta del proyecto > \ paquetes \ EntityFramework. < herramientas de versión > \
Una vez que tenga [Link], debe copiarlo en la ubicación del ensamblado que contiene las migraciones.
Si su aplicación tiene como destino .NET 4, y no 4,5, tendrá que copiar el [Link] también en la
ubicación y cambiarle el nombre [Link] . De este modo, [Link] obtiene las redirecciones de
enlace correctas para poder encontrar el ensamblado de Entity Framework.

. N ET 4. 5 . N ET 4, 0

NOTE
[Link] no es compatible con ensamblados x64.

Una vez que haya cambiado [Link] a la carpeta correcta, debería poder usarlo para ejecutar migraciones
en la base de datos. Todas las utilidades están diseñadas para ejecutar migraciones. No puede generar
migraciones ni crear un script SQL.

Vea opciones
[Link] /?

Lo anterior mostrará la página de ayuda asociada a esta utilidad. tenga en cuenta que tendrá que tener el
[Link] en la misma ubicación en la que se ejecuta [Link] para que esto funcione.

Migrar a la migración más reciente


[Link] [Link] /startupConfigurationFile="..\\[Link]"

Al ejecutar [Link] el único parámetro obligatorio es el ensamblado, que es el ensamblado que contiene las
migraciones que está intentando ejecutar, pero utilizará todos los valores basados en convenciones si no
especifica el archivo de configuración.

Migrar a una migración específica


[Link] [Link] /startupConfigurationFile="[Link]" /targetMigration="AddTitle"

Si desea ejecutar migraciones hasta una migración específica, puede especificar el nombre de la migración. Se
ejecutarán todas las migraciones anteriores según sea necesario hasta llegar a la migración especificada.

Especificar el directorio de trabajo


[Link] [Link] /startupConfigurationFile="[Link]" /startupDirectory="c:\\MyApp"

Si el ensamblado tiene dependencias o lee archivos en relación con el directorio de trabajo, tendrá que
establecer startupDirectory.

Especificar la configuración de migración que se va a usar


[Link] MyAssembly CustomConfig /startupConfigurationFile="..\\[Link]"

Si tiene varias clases de configuración de migración, las clases que heredan de DbMigrationConfiguration, debe
especificar qué se va a usar para esta ejecución. Esto se especifica proporcionando el segundo parámetro
opcional sin un modificador como el anterior.

Proporcionar una cadena de conexión


[Link] [Link] /connectionString="Data Source=localhost;Initial Catalog=BlogDemo;Integrated
Security=SSPI" /connectionProviderName="[Link]"

Si desea especificar una cadena de conexión en la línea de comandos, también debe proporcionar el nombre del
proveedor. Si no se especifica el nombre del proveedor, se producirá una excepción.

Problemas habituales
M EN SA JE DE ERRO R SO L UC IÓ N

Excepción no controlada: System. IO. FileLoadException: no Esto suele significar que se está ejecutando una aplicación de
se pudo cargar el archivo o ensamblado ' EntityFramework, .NET 4 sin el archivo de [Link]. Debe copiar el
version = [Link], Culture = neutral, PublicKeyToken = [Link] en la misma ubicación que [Link] y
b77a5c561934e089 ' o una de sus dependencias. La cambiarle el nombre a [Link].
definición del manifiesto del ensamblado encontrado no
coincide con la referencia de ensamblado. (Excepción de
HRESULT: 0x80131040)

Excepción no controlada: System. IO. FileLoadException: no Esta excepción significa que está ejecutando una aplicación
se pudo cargar el archivo o ensamblado ' EntityFramework, .NET 4,5 con la [Link] copiada en la ubicación
version = [Link], Culture = neutral, PublicKeyToken = [Link]. Si la aplicación es .NET 4,5, no es necesario tener
b77a5c561934e089 ' o una de sus dependencias. La el archivo de configuración con las redirecciones dentro de.
definición del manifiesto del ensamblado encontrado no Elimine el archivo de [Link].
coincide con la referencia de ensamblado. (Excepción de
HRESULT: 0x80131040)

ERROR: no se puede actualizar la base de datos para que Este error se produce si se ejecuta Migrate cuando no se ha
coincida con el modelo actual porque hay cambios creado una migración para hacer frente a los cambios
pendientes y la migración automática está deshabilitada. realizados en el modelo y la base de datos no coincide con el
Escriba los cambios de modelo pendientes en una migración modelo. La adición de una propiedad a una clase de modelo
basada en código o habilite la migración automática. y, a continuación, la ejecución de [Link] sin crear una
Establezca DbMigrationsConfiguration. migración para actualizar la base de datos es un ejemplo de
AutomaticMigrationsEnabled en true para habilitar la esto.
migración automática.

ERROR: el tipo no se ha resuelto para el miembro ' System. Este error puede deberse al especificar un directorio de inicio
Data. Entity. Migrations. Design. ToolingFacade + incorrecto. Debe ser la ubicación de [Link]
UpdateRunner, EntityFramework, version = [Link], Culture
= neutral, PublicKeyToken = b77a5c561934e089 '.

Excepción no controlada: System. NullReferenceException: Esto puede deberse a que no se especifica un parámetro
referencia de objeto no establecida en una instancia de un necesario para un escenario que se está usando. Por
objeto. ejemplo, si se especifica una cadena de conexión sin
en System. Data. Entity. Migrations. Console. Program. especificar el nombre del proveedor.
Main (String [] args)

ERROR: se encontró más de un tipo de configuración de Como indica el error, hay más de una clase de configuración
migraciones en el ensamblado ' ClassLibrary1 '. Especifique el en el ensamblado especificado. Debe usar el
nombre del que se va a usar. modificador/configurationType para especificar cuál utilizar.

ERROR: no se pudo cargar el archivo o ensamblado ' < Esto puede deberse al hecho de especificar un nombre de
AssemblyName > ' o una de sus dependencias. El nombre de ensamblado incorrectamente o no tener
ensamblado o el código base especificados no son válidos.
(Excepción de HRESULT: 0x80131047)

ERROR: no se pudo cargar el archivo o ensamblado ' < Esto sucede si está intentando ejecutar [Link] en una
AssemblyName > ' o una de sus dependencias. Se ha aplicación x64. EF 5,0 y versiones anteriores solo funcionarán
intentado cargar un programa con un formato incorrecto. en x86.
Code First Migrations in Team Environments
(Migraciones de Code First en entornos de equipo)
12/03/2021 • 30 minutes to read

NOTE
En este artículo se supone que sabe cómo usar Migraciones de Code First en escenarios básicos. Si no lo hace, tendrá que
leer migraciones de Code First antes de continuar.

Tome un café, debe leer todo el artículo


Los problemas en los entornos de equipo están principalmente en torno a la combinación de migraciones
cuando dos desarrolladores han generado migraciones en su base de código local. Aunque los pasos para
solucionar estos problemas son bastante sencillos, es necesario tener un conocimiento sólido de cómo
funcionan las migraciones. No avance hasta el final; Tómese el tiempo necesario para leer todo el artículo y
asegurarse de que se ha realizado correctamente.

Algunas directrices generales


Antes de profundizar en cómo administrar las migraciones de combinación generadas por varios
desarrolladores, estas son algunas directrices generales para configurar el éxito.
Cada miembro del equipo debe tener una base de datos de desarrollo local
Las migraciones usan la tabla ** _ _ MigrationsHistory** para almacenar las migraciones que se han aplicado a
la base de datos. Si tiene varios desarrolladores que generan diferentes migraciones al intentar tener como
destino la misma base de datos (y, por tanto, compartir una tabla ** _ _ MigrationsHistory** ), las migraciones se
van a confundir.
Por supuesto, si tiene miembros del equipo que no están generando migraciones, no hay problemas para que
compartan una base de datos de desarrollo central.
Evitar migraciones automáticas
La línea inferior es que las migraciones automáticas tienen un aspecto correcto en los entornos de equipo, pero
en realidad no funcionan. Si desea saber por qué, siga leyendo; si no es así, puede ir directamente a la sección
siguiente.
Migraciones automáticas permite actualizar el esquema de la base de datos para que coincida con el modelo
actual sin necesidad de generar archivos de código (migraciones basadas en código). Las migraciones
automáticas funcionarían bien en un entorno de equipo si solo se usaron y nunca se generaron migraciones
basadas en código. El problema es que las migraciones automáticas están limitadas y no controlan varias
operaciones: cambio de nombre de propiedades o columnas, movimiento de datos a otra tabla, etc. Para
controlar estos escenarios, acaba de generar migraciones basadas en código (y editar el código con scaffolding)
que se mezclan entre los cambios administrados por migraciones automáticas. Esto hace que resulte casi
imposible combinar los cambios cuando dos desarrolladores protegen las migraciones.

Presentaciones en pantalla
Si prefiere ver una screencast que lea este artículo, los dos vídeos siguientes cubren el mismo contenido que
este artículo.
Vídeo uno: "migraciones-bajo el capó"
En esta presentación en pantalla se explica cómo realiza la migración y usa información sobre el modelo para
detectar los cambios de modelo.
Vídeo dos: "migraciones: entornos de equipo"
Basándose en los conceptos del vídeo anterior, esta presentación en pantalla trata los problemas que surgen en
un entorno de equipo y cómo resolverlos.

Descripción de cómo funcionan las migraciones


La clave para usar correctamente las migraciones en un entorno de equipo es una descripción básica de cómo
realiza la migración y usa información sobre el modelo para detectar los cambios de modelo.
La primera migración
Al agregar la primera migración al proyecto, ejecute algo como Add-Migration First en la consola del
administrador de paquetes. Los pasos de alto nivel que realiza este comando se muestran a continuación.

El modelo actual se calcula a partir del código (1). Los objetos de base de datos necesarios se calculan a
continuación por el modelo difiere (2): dado que se trata de la primera migración, el modelo solo se usa un
modelo vacío para la comparación. Los cambios necesarios se pasan al generador de código para compilar el
código de migración necesario (3) que después se agrega a la solución de Visual Studio (4).
Además del código de migración real que se almacena en el archivo de código principal, las migraciones
también generan algunos archivos de código subyacente adicionales. Estos archivos son metadatos que se usan
en las migraciones y no son algo que deba editar. Uno de estos archivos es un archivo de recursos (. resx) que
contiene una instantánea del modelo en el momento en que se generó la migración. Verá cómo se usa en el
paso siguiente.
En este momento, probablemente ejecute Update-Database para aplicar los cambios a la base de datos y, a
continuación, vaya a la implementación de otras áreas de la aplicación.
Migraciones posteriores
Más adelante, volverá a realizar algunos cambios en el modelo. en nuestro ejemplo, agregaremos una
propiedad de dirección URL al blog . A continuación, debe emitir un comando como Add-Migration AddUrl
para aplicar scaffolding a una migración a fin de aplicar los cambios de base de datos correspondientes. Los
pasos de alto nivel que realiza este comando se muestran a continuación.
Al igual que la última vez, el modelo actual se calcula a partir del código (1). Sin embargo, esta vez hay
migraciones existentes, por lo que el modelo anterior se recupera de la última migración (2). Estos dos modelos
se diferencian para encontrar los cambios de base de datos necesarios (3) y, a continuación, el proceso se
completa como antes.
Este mismo proceso se usa para las migraciones posteriores que agregue al proyecto.
¿Por qué molestarse con la instantánea del modelo?
Es posible que se pregunte por qué EF ambos con la instantánea del modelo, por qué no solo mirar en la base
de datos. En caso afirmativo, siga leyendo. Si no está interesado, puede omitir esta sección.
Hay varias razones por las que EF mantiene la instantánea del modelo:
Permite que la base de datos se desvíe del modelo EF. Estos cambios se pueden realizar directamente en la
base de datos o puede cambiar el código con scaffolding en las migraciones para realizar los cambios. Estos
son algunos ejemplos de esto en la práctica:
Desea agregar una columna Inserted y Updated to a una o varias tablas, pero no desea incluir estas
columnas en el modelo EF. Si las migraciones examinaban la base de datos, intentaría quitar
continuamente estas columnas cada vez que se aplicara una migración scaffolding. Con la instantánea
del modelo, EF solo detectará los cambios legítimos en el modelo.
Desea cambiar el cuerpo de un procedimiento almacenado usado para que las actualizaciones
incluyan algún registro. Si las migraciones examinaban este procedimiento almacenado desde la base
de datos, intentaría volver a restablecerla continuamente a la definición que EF espera. Mediante el
uso de la instantánea del modelo, EF solo creará código scaffolding para modificar el procedimiento
almacenado cuando cambie la forma del procedimiento en el modelo EF.
Estos mismos principios se aplican a la adición de índices adicionales, incluidas tablas adicionales en la
base de datos, la asignación de EF a una vista de base de datos que se encuentra sobre una tabla, etc.
El modelo EF contiene algo más que la forma de la base de datos. Tener el modelo completo permite a las
migraciones ver información sobre las propiedades y las clases del modelo y cómo se asignan a las
columnas y tablas. Esta información permite que las migraciones sean más inteligentes en el código que
scaffolding. Por ejemplo, si cambia el nombre de la columna que una propiedad se asigna a las migraciones
puede detectar el cambio de nombre observando que es la misma propiedad, algo que no se puede hacer si
solo tiene el esquema de la base de datos.
Qué provoca problemas en los entornos de equipo
El flujo de trabajo que se trata en la sección anterior funciona bien cuando es un desarrollador único que trabaja
en una aplicación. También funciona bien en un entorno de equipo si es la única persona que realiza cambios en
el modelo. En este escenario puede realizar cambios en el modelo, generar migraciones y enviarlos al control de
código fuente. Otros desarrolladores pueden sincronizar los cambios y ejecutar Update-Database para aplicar
los cambios de esquema.
Los problemas empiezan a surgir cuando tiene varios desarrolladores que realizan cambios en el modelo de EF
y envían al control de código fuente al mismo tiempo. Lo que es EF falta es una primera forma de combinar las
migraciones locales con las migraciones que otro desarrollador ha enviado al control de código fuente desde
que se sincronizó por última vez.

Ejemplo de un conflicto de combinación


En primer lugar, echemos un vistazo a un ejemplo concreto de este tipo de conflicto de combinación.
Continuaremos con el ejemplo que hemos examinado anteriormente. Como punto de partida, supongamos que
el desarrollador original protegió los cambios de la sección anterior. Haremos un seguimiento de dos
desarrolladores a medida que realizan cambios en el código base.
Seguiremos el modelo EF y las migraciones a través de una serie de cambios. Para un punto de partida, ambos
desarrolladores se han sincronizado con el repositorio de control de código fuente, tal y como se muestra en el
gráfico siguiente.
Developer # 1 y Developer # 2 ahora realizan algunos cambios en el modelo de EF en su base de código local.
Developer # 1 agrega una propiedad rating al blog y genera una migración AddRating para aplicar los
cambios a la base de datos. Developer # 2 agrega una propiedad Readers al blog y genera la migración de
AddReaders correspondiente. Ambos desarrolladores ejecutan Update-Database para aplicar los cambios a
sus bases de datos locales y, a continuación, seguir desarrollando la aplicación.

NOTE
Las migraciones llevan el prefijo de una marca de tiempo, por lo que nuestro gráfico representa que la migración de
AddReaders de Developer # 2 viene después de la migración de AddRating de Developer # 1. Si # el desarrollador 1 o # 2
generó la migración en primer lugar no realiza ninguna diferencia con los problemas de trabajo en un equipo o el proceso
para combinarlos en la sección siguiente.

Es el día de la suerte para desarrolladores # 1 a medida que se producen para enviar sus cambios en primer
lugar. Dado que no se ha protegido ningún otro usuario desde que se sincronizó su repositorio, solo pueden
enviar sus cambios sin realizar ninguna combinación.
Ahora es el momento de enviar el desarrollador # 2. No son tan afortunados. Dado que otra persona ha enviado
cambios desde que se sincronizaron, deberán desplegar los cambios y mezclar. Es posible que el sistema de
control de código fuente pueda fusionar mediante combinación automáticamente los cambios en el nivel de
código, ya que son muy sencillos. El estado del # repositorio local del desarrollador 2 después de la
sincronización se muestra en el gráfico siguiente.
En esta fase # , Developer 2 puede ejecutar Update-Database , que detectará la nueva migración de
AddRating (que no se ha aplicado a la # base de datos del desarrollador 2) y la aplicará. Ahora se agrega la
columna clasificación a la tabla blogs y la base de datos está sincronizada con el modelo.
Sin embargo, hay un par de problemas:
1. Aunque Update-Database aplicará la migración de AddRating también generará una advertencia: no se
puede actualizar la base de datos para que coincida con el modelo actual porque hay cambios pendientes y
la migración automática está deshabilitada... El problema es que la instantánea del modelo almacenada en la
última migración (AddReader ) no tiene la propiedad rating en el blog (puesto que no formaba parte del
modelo cuando se generó la migración). Code First detecta que el modelo de la última migración no coincide
con el modelo actual y genera la advertencia.
2. Ejecutar la aplicación daría como resultado una excepción InvalidOperationException que indica que "el
modelo de respaldo del contexto ' BloggingContext ' ha cambiado desde que se creó la base de datos.
Considere la posibilidad de usar Migraciones de Code First para actualizar la base de datos... " De nuevo, el
problema es que la instantánea del modelo almacenada en la última migración no coincide con el modelo
actual.
3. Por último, esperamos que la ejecución de Add-Migration genere una migración vacía (ya que no hay
ningún cambio que se aplique a la base de datos). Sin embargo, dado que las migraciones comparan el
modelo actual con el de la última migración (que no tiene la propiedad rating ), en realidad scaffolding otra
llamada AddColumn para agregarla a la columna de clasificación . Por supuesto, esta migración
produciría un error durante la actualización de la base de datos porque la columna de clasificación ya
existe.
Resolver el conflicto de fusión mediante combinación
La buena noticia es que no es demasiado difícil tratar la combinación manualmente, siempre que tenga un
conocimiento de cómo funcionan las migraciones. Por lo tanto, si ha omitido el paso anterior a esta sección... Lo
sentimos, debe volver y leer el resto del artículo en primer lugar.
Hay dos opciones, lo más fácil es generar una migración en blanco que tenga el modelo actual correcto como
una instantánea. La segunda opción es actualizar la instantánea de la última migración para que tenga la
instantánea del modelo correcta. La segunda opción es un poco más difícil y no se puede usar en todos los
escenarios, pero también es más limpia porque no implica agregar una migración adicional.
Opción 1: agregar una migración de ' Merge ' en blanco
En esta opción, se genera una migración en blanco únicamente con el fin de asegurarse de que la migración
más reciente tenga almacenada la instantánea del modelo correcta.
Esta opción se puede usar independientemente de quién haya generado la última migración. En el ejemplo en el
que hemos estado # el desarrollador 2 se está encargando de la combinación y que se han producido para
generar la última migración. Pero se pueden usar los mismos pasos si # el desarrollador 1 generó la última
migración. Los pasos también se aplican si hay varias migraciones implicadas: hemos visto dos con el fin de
simplificar el proceso.
El siguiente proceso se puede usar para este enfoque, a partir del momento en que se dé cuenta de que tiene
cambios que deben sincronizarse desde el control de código fuente.
1. Asegúrese de que los cambios de modelos pendientes en la base de código local se han escrito en una
migración. Este paso garantiza que no se pierda ningún cambio legítimo cuando llegue el momento de
generar la migración en blanco.
2. Sincronizar con control de código fuente.
3. Ejecute Update-Database para aplicar las nuevas migraciones que hayan protegido otros desarrolladores.
Nota: si no recibe ninguna advertencia del comando Update-Database, no habrá nuevas migraciones de
otros desarrolladores y no será necesario realizar ninguna combinación adicional.
4. Ejecute Add-Migration < picking _ _ name > – IgnoreChanges (por ejemplo, Add-Migration Merge
– IgnoreChanges ). Esto genera una migración con todos los metadatos (incluida una instantánea del
modelo actual), pero omitirá todos los cambios que detecte al comparar el modelo actual con la instantánea
en las últimas Migraciones (lo que significa que obtiene un método en blanco y ver tical ).
5. Ejecute Update-Database para volver a aplicar la migración más reciente con los metadatos actualizados.
6. Continúe desarrollando o envíe el control de código fuente (después de ejecutar las pruebas unitarias del
curso).
Este es el estado de la # base de código local del desarrollador 2 después de usar este enfoque.
Opción 2: actualizar la instantánea del modelo en la última migración
Esta opción es muy similar a la opción 1, pero quita la migración en blanco adicional, porque nos encargamos
de ello, que quiere archivos de código adicionales en su solución.
Este enfoque solo es factible si la migración más reciente solo existe en la base de código local y
aún no se ha enviado al control de código fuente (por ejemplo, si la última migración fue
generada por el usuario que realizó la mezcla) . La edición de los metadatos de las migraciones que otros
desarrolladores pueden haber aplicado ya a su base de datos de desarrollo, o incluso peor si se aplican a una
base de datos de producción, puede producir efectos secundarios inesperados. Durante el proceso, vamos a
revertir la última migración en nuestra base de datos local y volver a aplicarla con los metadatos actualizados.
Aunque la última migración debe estar solo en la base de código local, no hay ninguna restricción sobre el
número o el orden de las migraciones que lo continúan. Puede haber varias migraciones de varios
desarrolladores diferentes y se aplican los mismos pasos: simplemente hemos visto dos con el fin de
mantenerlos sencillos.
El siguiente proceso se puede usar para este enfoque, a partir del momento en que se dé cuenta de que tiene
cambios que deben sincronizarse desde el control de código fuente.
1. Asegúrese de que los cambios de modelos pendientes en la base de código local se han escrito en una
migración. Este paso garantiza que no se pierda ningún cambio legítimo cuando llegue el momento de
generar la migración en blanco.
2. Sincronizar con el control de código fuente.
3. Ejecute Update-Database para aplicar las nuevas migraciones que hayan protegido otros desarrolladores.
Nota: si no recibe ninguna advertencia del comando Update-Database, no habrá nuevas migraciones de
otros desarrolladores y no será necesario realizar ninguna combinación adicional.
4. Ejecute **Update-Database – TargetMigration < Second _ Last _ Migration > ** (en el ejemplo que hemos
estado siguiendo esto sería Update-Database – TargetMigration AddRating ). De este modo, la base de
datos vuelve al estado de la segunda migración, con lo que se "anula la aplicación" de la última migración de
la base de datos. Nota: este paso es necesario para que sea seguro editar los metadatos de la migración, ya
que los metadatos también se almacenan en el _ _ MigrationsHistoryTable de la base de datos. Esta es la
razón por la que solo debe usar esta opción si la última migración solo está en la base de código local. Si se
ha aplicado la última migración a otras bases de datos, también tendrá que revertirla y volver a aplicar la
última migración para actualizar los metadatos.
5. Ejecute el ** < nombre completo de Add-Migration _ _ , incluida la _ marca de tiempo de la _ _ última _
migración** > (en el ejemplo que hemos estado siguiendo esto sería algo como Add-Migration
201311062215252 _ AddReaders ). Nota: debe incluir la marca de tiempo para que las migraciones
sepan que desea editar la migración existente en lugar de aplicar la técnica scaffolding a una nueva. De esta
forma, se actualizarán los metadatos de la última migración para que coincida con el modelo actual.
Obtendrá la siguiente advertencia cuando se complete el comando, pero eso es exactamente lo que desea.
"Solo se volvió a scaffolding el código del diseñador para la migración ' 201311062215252 _ AddReaders '.
Para volver a aplicar el scaffolding a toda la migración, use el parámetro-Force ".
6. Ejecute Update-Database para volver a aplicar la migración más reciente con los metadatos actualizados.
7. Continúe desarrollando o envíe el control de código fuente (después de ejecutar las pruebas unitarias del
curso).
Este es el estado de la # base de código local del desarrollador 2 después de usar este enfoque.
Resumen
El uso de Migraciones de Code First en un entorno de equipo conlleva algunos desafíos. Sin embargo, una
descripción básica de cómo funcionan las migraciones y algunos enfoques sencillos para resolver los conflictos
de combinación facilitan la superación de estos desafíos.
El problema fundamental son los metadatos incorrectos almacenados en la última migración. Esto hace que
Code First detecte incorrectamente que el modelo actual y el esquema de la base de datos no coinciden y que se
debe aplicar scaffolding al código incorrecto en la siguiente migración. Esta situación se puede solucionar
generando una migración en blanco con el modelo correcto o actualizando los metadatos en la última
migración.
Model First
12/03/2021 • 16 minutes to read

Este vídeo y el tutorial paso a paso proporcionan una introducción al desarrollo de Model First mediante Entity
Framework. Model First permite crear un nuevo modelo mediante el Entity Framework Designer y, a
continuación, generar un esquema de base de datos a partir del modelo. El modelo se almacena en un archivo
EDMX (extensión. EDMX) y se puede ver y editar en el Entity Framework Designer. Las clases con las que
interactúa en la aplicación se generan automáticamente a partir del archivo EDMX.

Visualización del vídeo


Este vídeo y el tutorial paso a paso proporcionan una introducción al desarrollo de Model First mediante Entity
Framework. Model First permite crear un nuevo modelo mediante el Entity Framework Designer y, a
continuación, generar un esquema de base de datos a partir del modelo. El modelo se almacena en un archivo
EDMX (extensión. EDMX) y se puede ver y editar en el Entity Framework Designer. Las clases con las que
interactúa en la aplicación se generan automáticamente a partir del archivo EDMX.
Presentado por : Rowan Miller
Vídeo : WMV | MP4 | WMV (zip)

Requisitos previos
Para completar este tutorial, deberá tener instalados Visual Studio 2010 o Visual Studio 2012.
Si usa Visual Studio 2010, también debe tener instalado NuGet .

1. crear la aplicación
Para simplificar las cosas, vamos a crear una aplicación de consola básica que use el Model First para realizar el
acceso a los datos:
Apertura de Visual Studio
Archivo- > nuevo- > proyecto...
Seleccionar ventanas en el menú izquierdo y en la aplicación de consola
Escriba ModelFirstSample como nombre
Seleccione Aceptar .

2. crear modelo
Vamos a hacer uso de Entity Framework Designer, que se incluye como parte de Visual Studio, para crear
nuestro modelo.
Proyecto- > Agregar nuevo elemento...
Seleccione datos en el menú de la izquierda y, a continuación, [Link] Entity Data Model
Escriba BloggingModel como nombre y haga clic en Aceptar . Esto iniciará el Asistente para Entity Data
Model
Seleccione modelo vacío y haga clic en Finalizar .
El Entity Framework Designer se abre con un modelo en blanco. Ahora podemos empezar a agregar entidades,
propiedades y asociaciones al modelo.
Haga clic con el botón derecho en la superficie de diseño y seleccione propiedades .
En el ventana Propiedades cambiar el nombre del contenedor de entidades a BloggingContext
este es el nombre del contexto derivado que se generará automáticamente, el contexto representa una
sesión con la base de datos, lo que nos permite consultar y guardar datos .
Haga clic con el botón derecho en la superficie de diseño y seleccione Agregar nueva > entidad.. .
Escriba blog como el nombre de la entidad y BlogId como nombre de la clave y haga clic en Aceptar .
Haga clic con el botón derecho en la nueva entidad en la superficie de diseño y seleccione Agregar
nueva > propiedad-escalar , escriba el nombre de la propiedad.
Repita este proceso para agregar una propiedad de dirección URL .
Haga clic con el botón secundario en la propiedad dirección URL en la superficie de diseño y seleccione
propiedades , en el ventana Propiedades cambie la configuración que acepta valores NULL a true
esto nos permite guardar un blog en la base de datos sin asignarle una dirección URL .
Con las técnicas que acaba de aprender, agregue una entidad post con una propiedad de clave PostId
Agregar propiedades escalares de título y de contenido a la entidad post
Ahora que tenemos un par de entidades, es el momento de agregar una asociación (o relación) entre ellas.
Haga clic con el botón derecho en la superficie de diseño y seleccione Agregar nueva- > asociación.. .
Haga que un extremo del punto de relación se envíe a un blog con una multiplicidad de uno y el otro
extremo para publicar con una multiplicidad de muchos significa que un blog tiene muchas
publicaciones y una publicación pertenece a un blog .
Asegúrese de que el cuadro de la entidad agregar propiedades de clave externa al "post" está
activado y haga clic en Aceptar .
Ahora tenemos un modelo simple en el que se puede generar una base de datos y se usa para leer y escribir
datos.

Pasos adicionales en Visual Studio 2010


Si está trabajando en Visual Studio 2010, hay algunos pasos adicionales que debe seguir para actualizar a la
versión más reciente de Entity Framework. La actualización es importante porque le proporciona acceso a una
superficie de API mejorada, que es mucho más fácil de usar, así como las correcciones de errores más recientes.
En primer lugar, necesitamos obtener la versión más reciente de Entity Framework de NuGet.
Proyecto: > Administrar paquetes de NuGet... Si no tiene la opción administrar paquetes Nuget... ,
debe instalar la versión más reciente de Nuget .
Seleccione la pestaña en línea
Seleccione el paquete EntityFramework
Haz clic en Instalar
A continuación, necesitamos cambiar nuestro modelo para generar código que haga uso de la API de
DbContext, que se presentó en versiones posteriores de Entity Framework.
Haga clic con el botón derecho en una zona vacía del modelo en el diseñador de EF y seleccione Agregar
elemento de generación de código.. .
Seleccione plantillas en línea en el menú de la izquierda y busque DbContext
Seleccione el generador de DbContext de EF 5. x # para C , escriba BloggingModel como nombre y
haga clic en Agregar .

3. generar la base de datos


Dado nuestro modelo, Entity Framework puede calcular un esquema de base de datos que nos permitirá
almacenar y recuperar datos mediante el modelo.
El servidor de base de datos que se instala con Visual Studio es diferente en función de la versión de Visual
Studio que haya instalado:
Si usa Visual Studio 2010, va a crear una base de datos de SQL Express.
Si usa Visual Studio 2012, va a crear una base de datos de LocalDB .
Vamos a generar la base de datos.
Haga clic con el botón secundario en la superficie de diseño y seleccione generar base de datos del
modelo.. .
Haga clic en nueva conexión.. . y especifique LocalDB o SQL Express, en función de la versión de Visual
Studio que use, escriba ModelFirst. blog como nombre de la base de datos.
Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .
Seleccione siguiente y el Entity Framework Designer calculará un script para crear el esquema de la base
de datos.
Una vez que se muestre el script, haga clic en Finalizar y el script se agregará al proyecto y se abrirá.
Haga clic con el botón derecho en el script y seleccione Ejecutar ; se le pedirá que especifique la base de
datos a la que se va a conectar, especifique LocalDB o SQL Server Express, en función de la versión de
Visual Studio que use.

4. leer & escribir datos


Ahora que tenemos un modelo, es el momento de usarlo para tener acceso a algunos datos. Las clases que se
van a usar para obtener acceso a los datos se generan automáticamente basándose en el archivo EDMX.
Esta captura de pantalla es de Visual Studio 2012, si usa Visual Studio 2010, los archivos [Link] y
[Link] estarán directamente en el proyecto en lugar de anidados en el archivo EDMX.
Implemente el método Main en [Link] como se muestra a continuación. Este código crea una nueva
instancia de nuestro contexto y, a continuación, la usa para insertar un nuevo blog. A continuación, usa una
consulta LINQ para recuperar todos los blogs de la base de datos ordenados alfabéticamente por título.

class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
[Link]("Enter a name for a new Blog: ");
var name = [Link]();

var blog = new Blog { Name = name };


[Link](blog);
[Link]();

// Display all Blogs from the database


var query = from b in [Link]
orderby [Link]
select b;

[Link]("All blogs in the database:");


foreach (var item in query)
{
[Link]([Link]);
}

[Link]("Press any key to exit...");


[Link]();
}
}
}

Ahora puede ejecutar la aplicación y probarla.

Enter a name for a new Blog: [Link] Blog


All blogs in the database:
[Link] Blog
Press any key to exit...
5. tratar con cambios en el modelo
Ahora es el momento de realizar algunos cambios en nuestro modelo, cuando realizamos estos cambios
también necesitamos actualizar el esquema de la base de datos.
Comenzaremos agregando una nueva entidad de usuario a nuestro modelo.
Agregue un nuevo nombre de entidad de usuario con el nombre de usuario como el nombre de clave y
la cadena como el tipo de propiedad de la clave.

Haga clic con el botón derecho en la propiedad username en la superficie de diseño y seleccione
propiedades , en el ventana Propiedades cambie la configuración de MaxLength a 50 esto restringe los
datos que se pueden almacenar en el nombre de usuario a 50 caracteres .
Agregar una propiedad escalar displayName a la entidad User
Ahora tenemos un modelo actualizado y estamos preparados para actualizar la base de datos para dar cabida a
nuestro nuevo tipo de entidad de usuario.
Haga clic con el botón secundario en la superficie de diseño y seleccione generar base de datos a par tir
del modelo..., Entity Framework calculará un script para volver a crear un esquema basado en el modelo
actualizado.
Haga clic en Finish (Finalizar).
Puede recibir advertencias sobre cómo sobrescribir el script DDL existente y las partes de asignación y
almacenamiento del modelo; haga clic en sí para ambas advertencias.
El script SQL actualizado para crear la base de datos se abre automáticamente.
El script que se genera quitará todas las tablas existentes y, a continuación, volverá a crear el esquema desde
el principio. Esto puede funcionar para el desarrollo local, pero no es viable para insertar cambios en una
base de datos que ya se ha implementado. Si necesita publicar cambios en una base de datos que ya se ha
implementado, deberá editar el script o usar una herramienta de comparación de esquemas para calcular un
script de migración.
Haga clic con el botón derecho en el script y seleccione Ejecutar ; se le pedirá que especifique la base de
datos a la que se va a conectar, especifique LocalDB o SQL Server Express, en función de la versión de Visual
Studio que use.

Resumen
En este tutorial, hemos visto Model First desarrollo, que nos permitió crear un modelo en EF Designer y, a
continuación, generar una base de datos a partir de ese modelo. Después usamos el modelo para leer y escribir
algunos datos de la base de datos. Por último, hemos actualizado el modelo y, a continuación, hemos recreado el
esquema de la base de datos para que coincida con el modelo.
Database First
12/03/2021 • 13 minutes to read

Este vídeo y el tutorial paso a paso proporcionan una introducción al desarrollo de Database First mediante
Entity Framework. Database First permite aplicar ingeniería inversa a un modelo a partir de una base de datos
existente. El modelo se almacena en un archivo EDMX (extensión. EDMX) y se puede ver y editar en el Entity
Framework Designer. Las clases con las que interactúa en la aplicación se generan automáticamente a partir del
archivo EDMX.

Visualización del vídeo


Este vídeo proporciona una introducción al desarrollo de Database First mediante Entity Framework. Database
First permite aplicar ingeniería inversa a un modelo a partir de una base de datos existente. El modelo se
almacena en un archivo EDMX (extensión. EDMX) y se puede ver y editar en el Entity Framework Designer. Las
clases con las que interactúa en la aplicación se generan automáticamente a partir del archivo EDMX.
Presentado por : Rowan Miller
Vídeo : WMV | MP4 | WMV (zip)

Requisitos previos
Debe tener instalado al menos Visual Studio 2010 o Visual Studio 2012 para completar este tutorial.
Si usa Visual Studio 2010, también debe tener instalado NuGet .

1. crear una base de datos existente


Normalmente, cuando el destino es una base de datos existente, ya se creará, pero para este tutorial es
necesario crear una base de datos para tener acceso a.
El servidor de base de datos que se instala con Visual Studio es diferente en función de la versión de Visual
Studio que haya instalado:
Si usa Visual Studio 2010, va a crear una base de datos de SQL Express.
Si usa Visual Studio 2012, va a crear una base de datos de LocalDB .

Vamos a generar la base de datos.


Apertura de Visual Studio
Vista > Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos: > Agregar conexión...
Si no se ha conectado a una base de datos desde Explorador de servidores antes de que tenga que
seleccionar Microsoft SQL Server como origen de datos
Conéctese a LocalDB o a SQL Express, en función de la que haya instalado y escriba DatabaseFirst. blog
como nombre de la base de datos.
Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .

La nueva base de datos aparecerá ahora en Explorador de servidores, haga clic con el botón derecho en
ella y seleccione nueva consulta .
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
CREATE TABLE [dbo].[Blogs] (
[BlogId] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (200) NULL,
[Url] NVARCHAR (200) NULL,
CONSTRAINT [PK_dbo.Blogs] PRIMARY KEY CLUSTERED ([BlogId] ASC)
);

CREATE TABLE [dbo].[Posts] (


[PostId] INT IDENTITY (1, 1) NOT NULL,
[Title] NVARCHAR (200) NULL,
[Content] NTEXT NULL,
[BlogId] INT NOT NULL,
CONSTRAINT [PK_dbo.Posts] PRIMARY KEY CLUSTERED ([PostId] ASC),
CONSTRAINT [FK_dbo.Posts_dbo.Blogs_BlogId] FOREIGN KEY ([BlogId]) REFERENCES [dbo].[Blogs] ([BlogId]) ON
DELETE CASCADE
);

2. crear la aplicación
Para simplificar las cosas, vamos a crear una aplicación de consola básica que use el Database First para realizar
el acceso a los datos:
Apertura de Visual Studio
Archivo- > nuevo- > proyecto...
Seleccionar ventanas en el menú izquierdo y en la aplicación de consola
Escriba DatabaseFirstSample como nombre
Seleccione Aceptar .

3. modelo de ingeniería inversa


Vamos a hacer uso de Entity Framework Designer, que se incluye como parte de Visual Studio, para crear
nuestro modelo.
Proyecto- > Agregar nuevo elemento...
Seleccione datos en el menú de la izquierda y, a continuación, [Link] Entity Data Model
Escriba BloggingModel como nombre y haga clic en Aceptar .
Se iniciará el Asistente para Entity Data Model
Seleccione generar desde la base de datos y haga clic en siguiente .
Seleccione la conexión a la base de datos creada en la primera sección, escriba BloggingContext como
nombre de la cadena de conexión y haga clic en siguiente .

Haga clic en la casilla situada junto a "tablas" para importar todas las tablas y haga clic en "finalizar".
Una vez que se completa el proceso de ingeniería inversa, el nuevo modelo se agrega al proyecto y se abre para
que pueda verlo en el Entity Framework Designer. También se ha agregado un archivo [Link] al proyecto
con los detalles de conexión de la base de datos.

Pasos adicionales en Visual Studio 2010


Si está trabajando en Visual Studio 2010, hay algunos pasos adicionales que debe seguir para actualizar a la
versión más reciente de Entity Framework. La actualización es importante porque le proporciona acceso a una
superficie de API mejorada, que es mucho más fácil de usar, así como las correcciones de errores más recientes.
En primer lugar, necesitamos obtener la versión más reciente de Entity Framework de NuGet.
Proyecto: > Administrar paquetes de NuGet... Si no tiene la opción administrar paquetes Nuget... ,
debe instalar la versión más reciente de Nuget .
Seleccione la pestaña en línea
Seleccione el paquete EntityFramework
Haz clic en Instalar
A continuación, necesitamos cambiar nuestro modelo para generar código que haga uso de la API de
DbContext, que se presentó en versiones posteriores de Entity Framework.
Haga clic con el botón derecho en una zona vacía del modelo en el diseñador de EF y seleccione Agregar
elemento de generación de código.. .
Seleccione plantillas en línea en el menú de la izquierda y busque DbContext
Seleccione el generador de DbContext de EF 5. x # para C , escriba BloggingModel como nombre y
haga clic en Agregar .

4. leer & escribir datos


Ahora que tenemos un modelo, es el momento de usarlo para tener acceso a algunos datos. Las clases que se
van a usar para obtener acceso a los datos se generan automáticamente basándose en el archivo EDMX.
Esta captura de pantalla es de Visual Studio 2012, si usa Visual Studio 2010, los archivos [Link] y
[Link] estarán directamente en el proyecto en lugar de anidados en el archivo EDMX.
Implemente el método Main en [Link] como se muestra a continuación. Este código crea una nueva
instancia de nuestro contexto y, a continuación, la usa para insertar un nuevo blog. A continuación, usa una
consulta LINQ para recuperar todos los blogs de la base de datos ordenados alfabéticamente por título.

class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
[Link]("Enter a name for a new Blog: ");
var name = [Link]();

var blog = new Blog { Name = name };


[Link](blog);
[Link]();

// Display all Blogs from the database


var query = from b in [Link]
orderby [Link]
select b;

[Link]("All blogs in the database:");


foreach (var item in query)
{
[Link]([Link]);
}

[Link]("Press any key to exit...");


[Link]();
}
}
}

Ahora puede ejecutar la aplicación y probarla.

Enter a name for a new Blog: [Link] Blog


All blogs in the database:
[Link] Blog
Press any key to exit...

5. trabajar con cambios en la base de datos


Ahora es el momento de realizar algunos cambios en el esquema de la base de datos, cuando realizamos estos
cambios también necesitamos actualizar nuestro modelo para reflejar los cambios.
El primer paso consiste en realizar algunos cambios en el esquema de la base de datos. Vamos a agregar una
tabla de usuarios al esquema.
Haga clic con el botón derecho en la base de datos DatabaseFirst. blogs en explorador de servidores y
seleccione nueva consulta .
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
CREATE TABLE [dbo].[Users]
(
[Username] NVARCHAR(50) NOT NULL PRIMARY KEY,
[DisplayName] NVARCHAR(MAX) NULL
)

Ahora que se ha actualizado el esquema, es el momento de actualizar el modelo con esos cambios.
Haga clic con el botón derecho en una zona vacía del modelo en el diseñador de EF y seleccione
"actualizar modelo desde base de datos". se iniciará el Asistente para actualización.
En la pestaña agregar del Asistente para actualización, active la casilla situada junto a tablas, lo que indica
que queremos agregar nuevas tablas del esquema. La pestaña actualizar muestra las tablas existentes en
el modelo en las que se comprobarán los cambios durante la actualización. Las pestañas eliminar
muestran las tablas que se han quitado del esquema y que también se quitarán del modelo como parte
de la actualización. La información de estas dos pestañas se detecta automáticamente y se proporciona
solo con fines informativos, no se puede cambiar la configuración.

Haga clic en finalizar en el Asistente para actualización

El modelo se ha actualizado para incluir una nueva entidad de usuario que se asigna a la tabla de usuarios que
se agregó a la base de datos.
Resumen
En este tutorial, hemos visto Database First desarrollo, que nos permitió crear un modelo en EF Designer basado
en una base de datos existente. Después usamos ese modelo para leer y escribir algunos datos de la base de
datos. Por último, se actualizó el modelo para reflejar los cambios realizados en el esquema de la base de datos.
Tipos complejos: EF Designer
12/03/2021 • 14 minutes to read

En este tema se muestra cómo asignar tipos complejos con el Entity Framework Designer (EF Designer) y cómo
consultar entidades que contienen propiedades de tipo complejo.
En la imagen siguiente se muestran las ventanas principales que se usan al trabajar con el diseñador de EF.

NOTE
Al crear el modelo conceptual, es posible que aparezcan advertencias sobre entidades y asociaciones no asignadas en la
Lista de errores. Puede omitir estas advertencias porque, después de elegir generar la base de datos a partir del modelo,
los errores desaparecerán.

Qué es un tipo complejo


Los tipos complejos son propiedades no escalares de tipos de entidad que permiten organizar las propiedades
escalares dentro de las entidades. Al igual que las entidades, los tipos complejos están compuestos de
propiedades escalares u otras propiedades de tipo complejo.
Al trabajar con objetos que representan tipos complejos, tenga en cuenta lo siguiente:
Los tipos complejos no tienen claves y, por lo tanto, no pueden existir de forma independiente. Los tipos
complejos solo pueden existir como propiedades de tipos de entidad u otros tipos complejos.
Los tipos complejos no pueden participar en asociaciones y no pueden contener propiedades de navegación.
Las propiedades de tipo complejo no pueden ser null . Se produce una **excepción
InvalidOperationException **cuando se llama a DbContext. SaveChanges y se encuentra un objeto
complejo null. Las propiedades escalares de objetos complejos pueden ser null .
Los tipos complejos no pueden heredar de otros tipos complejos.
Debe definir el tipo complejo como una clase .
EF detecta los cambios en los miembros de un objeto de tipo complejo cuando se llama a DbContext.
DetectChanges . Entity Framework llama automáticamente a DetectChanges cuando se llama a los
siguientes miembros: DbSet. Find , DbSet. local , DbSet. Remove , DbSet. Add , DbSet. Attach ,
dbcontext. SaveChanges , dbcontext. GetValidationErrors , dbcontext. entr y , DbChangeTracker.
Entries .

Refactorizar las propiedades de una entidad en un nuevo tipo


complejo
Si ya tiene una entidad en el modelo conceptual, puede que desee refactorizar algunas de las propiedades en
una propiedad de tipo complejo.
En la superficie del diseñador, seleccione una o varias propiedades (excepto las propiedades de navegación) de
una entidad, haga clic con el botón derecho y seleccione refactorizar- > desplazar al nuevo tipo complejo .

Un nuevo tipo complejo con las propiedades seleccionadas se agrega al Explorador de modelos . Se asigna un
nombre predeterminado al tipo complejo.
Una propiedad compleja del tipo que se acaba de crear reemplaza las propiedades seleccionadas. Se conservan
todas las asignaciones de propiedades.

Crear un nuevo tipo complejo


También puede crear un nuevo tipo complejo que no contenga las propiedades de una entidad existente.
Haga clic con el botón secundario en la carpeta tipos complejos en el explorador de modelos,
seleccione AgregarNuevo tipo complejo.... Como alternativa, puede seleccionar la carpeta tipos complejos
y presionar la tecla Inser tar del teclado.
Un nuevo tipo complejo se agrega a la carpeta con un nombre predeterminado. Ahora puede Agregar
propiedades al tipo.

Agregar propiedades a un tipo complejo


Las propiedades de un tipo complejo pueden ser tipos escalares o los tipos complejos existentes. Sin embargo,
las propiedades de tipo complejo no pueden tener las referencias circulares. Por ejemplo, un tipo
complejo OnsiteCourseDetails no puede tener una propiedad de tipo complejo OnsiteCourseDetails .
Puede agregar una propiedad a un tipo complejo de cualquiera de las formas enumeradas a continuación.
Haga clic con el botón secundario en un tipo complejo en el explorador de modelos, seleccione Agregar ,
seleccione propiedad escalar o propiedad compleja y, a continuación, seleccione el tipo de
propiedad que desee. Como alternativa, puede seleccionar un tipo complejo y, a continuación, presionar
la tecla Inser tar del teclado.

Se agrega una nueva propiedad al tipo complejo con un nombre predeterminado.


O BIEN
Haga clic con el botón derecho en una propiedad de entidad en la superficie del Diseñador de EF y
seleccione copiar , a continuación, haga clic con el botón secundario en el tipo complejo en el
Explorador de modelos y seleccione pegar .

Cambiar el nombre de un tipo complejo


Al cambiar el nombre de un tipo complejo, todas las referencias al tipo se actualizan en el proyecto.
Haga doble clic lentamente en un tipo complejo en el Explorador de modelos . El nombre se
seleccionará y se abrirá en modo de edición.
O BIEN
Haga clic con el botón secundario en un tipo complejo en el Explorador de modelos y
seleccione cambiar nombre .
O BIEN
Seleccione un tipo complejo en el Explorador de modelos y presione la tecla F2.
O BIEN
Haga clic con el botón secundario en un tipo complejo en el Explorador de modelos y
seleccione propiedades . Edite el nombre en la ventana propiedades .

Agregar un tipo complejo existente a una entidad y asignar sus


propiedades a las columnas de la tabla
1. Haga clic con el botón secundario en una entidad, seleccione Agregar nuevo y seleccione propiedad
compleja . Se agrega a la entidad una propiedad de tipo complejo con un nombre predeterminado. Se
asigna un tipo predeterminado (que se elige entre los tipos complejos existentes) a la propiedad.
2. Asigne el tipo deseado a la propiedad en la ventana propiedades . Después de agregar una propiedad
de tipo complejo a una entidad, debe asignar sus propiedades a las columnas de una tabla.
3. Haga clic con el botón secundario en un tipo de entidad en la superficie de diseño o en el Explorador de
modelos y seleccione asignaciones de tablas . Las asignaciones de tabla se muestran en la ventana
detalles de la asignación .
4. Expanda el nodo asigna a < nombre > de tabla . Aparece un nodo asignaciones de columnas .
5. Expanda el nodo asignaciones de columnas . Aparece una lista de todas las columnas de la tabla. Las
propiedades predeterminadas (si existen) a las que se asignan las columnas aparecen bajo el
encabezado valor/propiedad .
6. Seleccione la columna que desea asignar y, a continuación, haga clic con el botón secundario en el campo
de valor o propiedad correspondiente . Se muestra una lista desplegable de todas las propiedades
escalares.
7. Seleccione la propiedad apropiada.

8. Repita los pasos 6 y 7 para cada columna de tabla.


NOTE
Para eliminar una asignación de columna, seleccione la columna que desea asignar y, a continuación, haga clic en el
campo valor/propiedad . A continuación, seleccione eliminar en la lista desplegable.

Asignación de una importación de función a un tipo complejo


Las importaciones de función se basan en procedimientos almacenados. Para asignar una importación de
función a un tipo complejo, las columnas devueltas por el procedimiento almacenado correspondiente deben
coincidir con el número de propiedades del tipo complejo y deben tener tipos de almacenamiento compatibles
con los tipos de propiedad.
Haga doble clic en una función importada que desee asignar a un tipo complejo.

Rellene los valores para la nueva importación de función de la siguiente forma:


Especifique el procedimiento almacenado para el que va a crear una importación de función en el
campo nombre del procedimiento almacenado . El campo es una lista desplegable que
muestra todos los procedimientos almacenados del modelo de almacenamiento.
Especifique el nombre de la importación de función en el campo Nombre de la impor tación de
función .
Seleccione complejo como el tipo de valor devuelto y, a continuación, especifique el tipo de
valor devuelto complejo específico eligiendo el tipo adecuado en la lista desplegable.
Haga clic en Aceptar . Se crea la entrada de importación de función en el modelo conceptual.
Personalizar la asignación de columnas para la importación de función
Haga clic con el botón derecho en la importación de función en el explorador de modelos y
seleccione función impor tar asignación . Aparece la ventana detalles de la asignación y muestra la
asignación predeterminada para la importación de función. Las flechas indican las asignaciones entre los
valores de columna y de propiedad. De forma predeterminada, se presupone que los nombres de columna
son los mismos que los nombres de propiedad del tipo complejo. Los nombres de columna predeterminados
aparecen en texto gris.
Si es necesario, cambie los nombres de columna para que coincidan con los nombres de columna devueltos
por el procedimiento almacenado que corresponden a la importación de función.

Eliminar un tipo complejo


Al eliminar un tipo complejo, se elimina el tipo del modelo conceptual, así como las asignaciones para todas las
instancias del tipo. Sin embargo, no se actualizan las referencias al tipo. Por ejemplo, si una entidad tiene una
propiedad de tipo complejo de tipo ComplexType1 y ComplexType1 se elimina en el Explorador de modelos ,
no se actualiza la propiedad de entidad correspondiente. El modelo no se validará porque contiene una entidad
que hace referencia a un tipo complejo eliminado. Puede actualizar o eliminar referencias a los tipos complejos
eliminados utilizando Entity Designer.
Haga clic con el botón secundario en un tipo complejo en el explorador de modelos y
seleccione eliminar .
O BIEN
Seleccione un tipo complejo en el Explorador de modelos y presione la tecla Suprimir del teclado.
Consultar las entidades que contienen propiedades de tipo complejo
En el código siguiente se muestra cómo ejecutar una consulta que devuelve una colección de objetos de tipo
entidad que contienen una propiedad de tipo complejo.

using (SchoolEntities context = new SchoolEntities())


{
var courses =
from c in [Link]
order by [Link]
select c;

foreach (var c in courses)


{
[Link]("Time: " + [Link]);
[Link]("Days: " + [Link]);
[Link]("Location: " + [Link]);
}
}
Compatibilidad con la enumeración: EF Designer
12/03/2021 • 11 minutes to read

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

Este tutorial de vídeo y paso a paso muestra cómo utilizar tipos de enumeración con el Entity Framework
Designer. También se muestra cómo usar las enumeraciones en una consulta LINQ.
En este tutorial se utilizará Model First para crear una nueva base de datos, pero el diseñador de EF también
puede usarse con el flujo de trabajo de Database First para asignar a una base de datos existente.
La compatibilidad con la enumeración se presentó en Entity Framework 5. Para usar las nuevas características,
como las enumeraciones, los tipos de datos espaciales y las funciones con valores de tabla, debe tener como
destino .NET Framework 4,5. Visual Studio 2012 tiene como destino .NET 4,5 de forma predeterminada.
En Entity Framework, una enumeración puede tener los siguientes tipos subyacentes: byte , Int16 , Int32 , Int64
o SByte .

Ver el vídeo
En este vídeo se muestra cómo utilizar tipos de enumeración con el Entity Framework Designer. También se
muestra cómo usar las enumeraciones en una consulta LINQ.
Presentada por : Julia Kornich
Vídeo : WMV | MP4 | WMV (zip)

Requisitos previos
Deberá tener instalado Visual Studio 2012, Ultimate, Premium, Professional o Web Express Edition para
completar este tutorial.

Configurar el proyecto
1. Abra Visual Studio 2012
2. En el menú archivo , seleccione nuevo y, a continuación, haga clic en proyecto .
3. En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
4. Escriba EnumEFDesigner como nombre del proyecto y haga clic en Aceptar .

Crear un nuevo modelo con EF Designer


1. Haga clic con el botón secundario en el nombre del proyecto en Explorador de soluciones, seleccione
Agregar y, a continuación, haga clic en nuevo elemento .
2. Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model en
el panel Plantillas.
3. Escriba EnumTestModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
4. En la página del Asistente para Entity Data Model, seleccione modelo vacío en el cuadro de diálogo elegir
contenido del modelo.
5. Haga clic en Finish (Finalizar).
Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo.
El asistente realiza las siguientes acciones:
Genera el archivo EnumTestModel. edmx que define el modelo conceptual, el modelo de almacenamiento y la
asignación entre ellos. Establece la propiedad de procesamiento de artefactos de metadatos del archivo.
edmx que se va a incrustar en el ensamblado de salida para incrustar los archivos de metadatos generados
en el ensamblado.
Agrega una referencia a los siguientes ensamblados: EntityFramework, System. ComponentModel.
DataAnnotations y System. Data. Entity.
Crea archivos [Link] y [Link] y los agrega en el archivo. edmx. Estos archivos
de plantilla T4 generan el código que define el tipo derivado de DbContext y los tipos POCO que se asignan a
las entidades en el modelo. edmx.

Agregar un nuevo tipo de entidad


1. Haga clic con el botón secundario en un área vacía de la superficie de diseño, seleccione agregar- >
entidad , aparecerá el cuadro de diálogo nueva entidad.
2. Especifique Depar tment como nombre de tipo y especifique depar tmentId como nombre de la propiedad
de clave y deje el tipo como Int32 .
3. Haga clic en Aceptar
4. Haga clic con el botón derecho en la entidad y seleccione Agregar nueva > propiedad-escalar .
5. Cambiar el nombre de la nueva propiedad a nombre
6. Cambie el tipo de la nueva propiedad a Int32 (de forma predeterminada, la nueva propiedad es de tipo
cadena) para cambiar el tipo, abra el ventana Propiedades y cambie la propiedad Type a Int32 .
7. Agregue otra propiedad escalar y cambie su nombre a presupuesto , cambie el tipo a decimal .

Agregar un tipo de enumeración


1. En el Entity Framework Designer, haga clic con el botón secundario en la propiedad nombre, seleccione
conver tir en enumeración .
2. En el cuadro de diálogo Agregar enumeración , escriba Depar tmentNames para el nombre del tipo
de enumeración, cambie el tipo subyacente a Int32 y, a continuación, agregue los siguientes miembros al
tipo: Inglés, matemáticas y economía.

3. Presione Aceptar
4. Guardar el modelo y compilar el proyecto
NOTE
Al compilar, las advertencias sobre entidades y asociaciones no asignadas pueden aparecer en el Lista de errores.
Puede omitir estas advertencias porque, después de elegir generar la base de datos a partir del modelo, los
errores desaparecerán.

Si observa el ventana Propiedades, observará que el tipo de la propiedad Name se ha cambiado a


Depar tmentNames y que el tipo de enumeración recién agregado se ha agregado a la lista de tipos.
Si cambia a la ventana Explorador de modelos, verá que el tipo también se agregó al nodo tipos de
enumeración.

NOTE
También puede agregar nuevos tipos de enumeración desde esta ventana haciendo clic con el botón secundario del
mouse y seleccionando Agregar tipo de enumeración . Una vez creado el tipo, aparecerá en la lista de tipos y podrá
asociarlo a una propiedad.

Generar base de datos a partir del modelo


Ahora se puede generar una base de datos basada en el modelo.
1. Haga clic con el botón secundario en un espacio vacío en la superficie de Entity Designer y seleccione
generar base de datos a par tir del modelo .
2. Aparece el cuadro de diálogo elegir la conexión de datos del Asistente para generar base de datos, haga clic
en el botón nueva conexión especifique (LocalDB) \ mssqllocaldb para el nombre del servidor y
EnumTest para la base de datos y haga clic en Aceptar .
3. Aparecerá un cuadro de diálogo en el que se le pregunta si desea crear una nueva base de datos, haga clic en
sí .
4. Haga clic en siguiente y el Asistente para crear bases de datos genera el lenguaje de definición de datos
(DDL) para crear una base de datos. la DDL generada se muestra en el cuadro de diálogo Resumen y
configuración. tenga en cuenta que el DDL no contiene una definición para una tabla que se asigna al tipo de
enumeración
5. Haga clic en Finalizar al hacer clic en finalizar no se ejecuta el script DDL.
6. El Asistente para crear bases de datos hace lo siguiente: abre EnumTest. edmx. SQL en el editor de T-SQL
genera el esquema de almacenamiento y las secciones de asignación del archivo edmx agrega información
de la cadena de conexión al archivo [Link]
7. Haga clic con el botón secundario del mouse en el editor de T-SQL y seleccione Ejecutar el cuadro de
diálogo conectar con el servidor, escriba la información de conexión del paso 2 y haga clic en conectar .
8. Para ver el esquema generado, haga clic con el botón derecho en el nombre de la base de datos en
Explorador de objetos de SQL Server y seleccione Actualizar .

Conservar y recuperar datos


Abra el archivo [Link] en el que se define el método Main. Agregue el código siguiente a la función main. El
código agrega un nuevo objeto Department al contexto. Después guarda los datos. El código también ejecuta
una consulta LINQ que devuelve un departamento en el que el nombre es DepartmentNames. English.

using (var context = new EnumTestModelContainer())


{
[Link](new Department{ Name = [Link] });

[Link]();

var department = (from d in [Link]


where [Link] == [Link]
select d).FirstOrDefault();

[Link](
"DepartmentID: {0} and Name: {1}",
[Link],
[Link]);
}

Compile y ejecute la aplicación. El programa produce el siguiente resultado:

DepartmentID: 1 Name: English

Para ver los datos de la base de datos, haga clic con el botón derecho en el nombre de la base de datos en
Explorador de objetos de SQL Server y seleccione Actualizar . A continuación, haga clic con el botón secundario
del mouse en la tabla y seleccione ver datos .

Resumen
En este tutorial, hemos visto cómo asignar tipos de enumeración mediante el Entity Framework Designer y
cómo usar las enumeraciones en el código.
Diseñador espacial-EF
12/03/2021 • 11 minutes to read

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

En el tutorial de vídeo y paso a paso se muestra cómo asignar tipos espaciales con el Entity Framework
Designer. También se muestra cómo usar una consulta LINQ para buscar una distancia entre dos ubicaciones.
En este tutorial se utilizará Model First para crear una nueva base de datos, pero el diseñador de EF también
puede usarse con el flujo de trabajo de Database First para asignar a una base de datos existente.
La compatibilidad con tipos espaciales se presentó en Entity Framework 5. Tenga en cuenta que para usar las
nuevas características, como el tipo espacial, las enumeraciones y las funciones con valores de tabla, debe tener
como destino .NET Framework 4,5. Visual Studio 2012 tiene como destino .NET 4,5 de forma predeterminada.
Para usar los tipos de datos espaciales, también debe usar un proveedor de Entity Framework que tenga
compatibilidad espacial. Consulte compatibilidad con proveedores para tipos espaciales para obtener más
información.
Hay dos tipos de datos espaciales principales: Geography y Geometry. El tipo de datos Geography almacena los
datos de datos elipsoidales (por ejemplo, las coordenadas de latitud y longitud de GPS). El tipo de datos
Geometry representa el sistema de coordenadas euclidiana (plano).

Visualización del vídeo


Este vídeo muestra cómo asignar tipos espaciales con el Entity Framework Designer. También se muestra cómo
usar una consulta LINQ para buscar una distancia entre dos ubicaciones.
Presentada por : Julia Kornich
Vídeo : WMV | MP4 | WMV (zip)

Requisitos previos
Deberá tener instalado Visual Studio 2012, Ultimate, Premium, Professional o Web Express Edition para
completar este tutorial.

Configurar el proyecto
1. Abra Visual Studio 2012
2. En el menú archivo , seleccione nuevo y, a continuación, haga clic en proyecto .
3. En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
4. Escriba SpatialEFDesigner como nombre del proyecto y haga clic en Aceptar .

Crear un nuevo modelo con EF Designer


1. Haga clic con el botón secundario en el nombre del proyecto en Explorador de soluciones, seleccione
Agregar y, a continuación, haga clic en nuevo elemento .
2. Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model en
el panel Plantillas.
3. Escriba UniversityModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
4. En la página del Asistente para Entity Data Model, seleccione modelo vacío en el cuadro de diálogo elegir
contenido del modelo.
5. Haga clic en Finish (Finalizar).
Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo.
El asistente realiza las siguientes acciones:
Genera el archivo EnumTestModel. edmx que define el modelo conceptual, el modelo de almacenamiento y la
asignación entre ellos. Establece la propiedad de procesamiento de artefactos de metadatos del archivo.
edmx que se va a incrustar en el ensamblado de salida para incrustar los archivos de metadatos generados
en el ensamblado.
Agrega una referencia a los siguientes ensamblados: EntityFramework, System. ComponentModel.
DataAnnotations y System. Data. Entity.
Crea archivos [Link] y [Link] y los agrega en el archivo. edmx. Estos
archivos de plantilla T4 generan el código que define el tipo derivado de DbContext y los tipos POCO que se
asignan a las entidades en el modelo. edmx.

Agregar un nuevo tipo de entidad


1. Haga clic con el botón secundario en un área vacía de la superficie de diseño, seleccione agregar- >
entidad , aparecerá el cuadro de diálogo nueva entidad.
2. Especifique Universidad como nombre de tipo y especifique UniversityID para el nombre de la propiedad
de clave y deje el tipo como Int32 .
3. Haga clic en Aceptar
4. Haga clic con el botón derecho en la entidad y seleccione Agregar nueva > propiedad-escalar .
5. Cambiar el nombre de la nueva propiedad a nombre
6. Agregue otra propiedad escalar y cámbiela a Ubicación . abra el ventana Propiedades y cambie el tipo de la
nueva propiedad a geografía .
7. Guardar el modelo y compilar el proyecto

NOTE
Al compilar, las advertencias sobre entidades y asociaciones no asignadas pueden aparecer en el Lista de errores.
Puede omitir estas advertencias porque, después de elegir generar la base de datos a partir del modelo, los
errores desaparecerán.

Generar base de datos a partir del modelo


Ahora se puede generar una base de datos basada en el modelo.
1. Haga clic con el botón secundario en un espacio vacío en la superficie de Entity Designer y seleccione
generar base de datos a par tir del modelo .
2. Se muestra el cuadro de diálogo elegir la conexión de datos del Asistente para generar base de datos, haga
clic en el botón nueva conexión especifique (LocalDB) \ mssqllocaldb para el nombre del servidor y la
Universidad para la base de datos y haga clic en Aceptar .
3. Aparecerá un cuadro de diálogo en el que se le pregunta si desea crear una nueva base de datos, haga clic en
sí .
4. Haga clic en siguiente y el Asistente para crear bases de datos genera el lenguaje de definición de datos
(DDL) para crear una base de datos. la DDL generada se muestra en el cuadro de diálogo Resumen y
configuración. tenga en cuenta que el DDL no contiene una definición para una tabla que se asigna al tipo de
enumeración
5. Haga clic en Finalizar al hacer clic en finalizar no se ejecuta el script DDL.
6. El Asistente para crear bases de datos hace lo siguiente: abre UniversityModel. edmx. SQL en el editor de
T-SQL genera el esquema de almacenamiento y las secciones de asignación del archivo edmx agrega
información de la cadena de conexión al archivo [Link]
7. Haga clic con el botón secundario del mouse en el editor de T-SQL y seleccione Ejecutar el cuadro de
diálogo conectar con el servidor, escriba la información de conexión del paso 2 y haga clic en conectar .
8. Para ver el esquema generado, haga clic con el botón derecho en el nombre de la base de datos en
Explorador de objetos de SQL Server y seleccione Actualizar .

Conservar y recuperar datos


Abra el archivo [Link] en el que se define el método Main. Agregue el código siguiente a la función main.
El código agrega dos nuevos objetos universitarios al contexto. Las propiedades espaciales se inicializan
mediante el método DbGeography. FromText. El punto de geografía representado como WellKnownText se pasa
al método. Después, el código guarda los datos. A continuación, se crea y se ejecuta la consulta LINQ que
devuelve un objeto University donde su ubicación es más cercana a la ubicación especificada.

using (var context = new UniversityModelContainer())


{
[Link](new University()
{
Name = "Graphic Design Institute",
Location = [Link]("POINT(-122.336106 47.605049)"),
});

[Link](new University()
{
Name = "School of Fine Art",
Location = [Link]("POINT(-122.335197 47.646711)"),
});

[Link]();

var myLocation = [Link]("POINT(-122.296623 47.640405)");

var university = (from u in [Link]


orderby [Link](myLocation)
select u).FirstOrDefault();

[Link](
"The closest University to you is: {0}.",
[Link]);
}

Compile y ejecute la aplicación. El programa produce el siguiente resultado:

The closest University to you is: School of Fine Art.

Para ver los datos de la base de datos, haga clic con el botón derecho en el nombre de la base de datos en
Explorador de objetos de SQL Server y seleccione Actualizar . A continuación, haga clic con el botón secundario
del mouse en la tabla y seleccione ver datos .

Resumen
En este tutorial, hemos visto cómo asignar tipos espaciales mediante el Entity Framework Designer y cómo usar
tipos espaciales en el código.
División de entidades del diseñador
12/03/2021 • 8 minutes to read

En este tutorial se muestra cómo asignar un tipo de entidad a dos tablas modificando un modelo con el Entity
Framework Designer (EF Designer). Puede asignar una entidad a varias tablas cuando estas comparten una
clave común. Los conceptos que se aplican en la asignación de un tipo de entidad a dos tablas se extienden con
facilidad a la asignación de un tipo de entidad a más de dos tablas.
En la imagen siguiente se muestran las ventanas principales que se usan al trabajar con el diseñador de EF.

Requisitos previos
Visual Studio 2012 o Visual Studio 2010, Ultimate, Premium, Professional o Web Express.

Crear la base de datos


El servidor de base de datos que se instala con Visual Studio es diferente en función de la versión de Visual
Studio que haya instalado:
Si usa Visual Studio 2012, va a crear una base de datos de LocalDB.
Si usa Visual Studio 2010, va a crear una base de datos de SQL Express.
En primer lugar, vamos a crear una base de datos con dos tablas que se van a combinar en una sola entidad.
Apertura de Visual Studio
Vista > Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos: > Agregar conexión...
Si no se ha conectado a una base de datos desde Explorador de servidores antes de que tenga que
seleccionar Microsoft SQL Ser ver como origen de datos
Conéctese a LocalDB o a SQL Express, en función de la que haya instalado.
Escriba EntitySplitting como nombre de la base de datos
Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .
La nueva base de datos aparecerá ahora en Explorador de servidores
Si usa Visual Studio 2012
Haga clic con el botón derecho en la base de datos en el Explorador de servidores y seleccione Nueva
consulta
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
Si usa Visual Studio 2010
Seleccionar datos: > Editor de Transact SQL: > nueva conexión de consulta...
Escriba . \ SQLEXPRESS como nombre del servidor y haga clic en Aceptar
Seleccione la base de datos EntitySplitting en la lista desplegable de la parte superior del editor de
consultas.
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione ejecutar SQL .

CREATE TABLE [dbo].[Person] (


[PersonId] INT IDENTITY (1, 1) NOT NULL,
[FirstName] NVARCHAR (200) NULL,
[LastName] NVARCHAR (200) NULL,
CONSTRAINT [PK_Person] PRIMARY KEY CLUSTERED ([PersonId] ASC)
);

CREATE TABLE [dbo].[PersonInfo] (


[PersonId] INT NOT NULL,
[Email] NVARCHAR (200) NULL,
[Phone] NVARCHAR (50) NULL,
CONSTRAINT [PK_PersonInfo] PRIMARY KEY CLUSTERED ([PersonId] ASC),
CONSTRAINT [FK_Person_PersonInfo] FOREIGN KEY ([PersonId]) REFERENCES [dbo].[Person] ([PersonId]) ON DELETE
CASCADE
);

Crear el proyecto
En el menú Archivo , seleccione Nuevo y haga clic en Proyecto .
En el panel izquierdo, haga clic en Visual # C y, a continuación, seleccione la plantilla aplicación de
consola .
Escriba MapEntityToTablesSample como nombre del proyecto y haga clic en Aceptar .
Haga clic en no si se le pide que guarde la consulta SQL creada en la primera sección.

Crear un modelo basado en la base de datos


Haga clic con el botón secundario en el nombre del proyecto en Explorador de soluciones, seleccione
Agregar y, a continuación, haga clic en nuevo elemento .
Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model en
el panel Plantillas.
Escriba MapEntityToTablesModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
En el cuadro de diálogo elegir contenido del modelo, seleccione generar desde la base de datos y, a
continuación, haga clic en siguiente.
Seleccione la conexión EntitySplitting en el menú desplegable y haga clic en siguiente .
En el cuadro de diálogo elija los objetos de base de datos, active la casilla situada junto al nodo tablas . Se
agregarán todas las tablas de la base de datos EntitySplitting al modelo.
Haga clic en Finalizar .
Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo.
Asignación de una entidad a dos tablas
En este paso, actualizaremos el tipo de entidad Person para combinar datos de las tablas Person y PersonInfo
.
Seleccione las propiedades de correo electrónico y teléfono de la entidad **PersonInfo **y presione
las teclas Ctrl + X .
Seleccione la entidad **Person **y presione las teclas Ctrl + V .
En la superficie de diseño, seleccione la entidad PersonInfo y presione el botón eliminar en el teclado.
Haga clic en no cuando se le pregunte si desea quitar la tabla PersonInfo del modelo, vamos a asignarla
a la entidad Person .

Los pasos siguientes requieren la ventana detalles de la asignación . Si no puede ver esta ventana, haga clic
con el botón secundario en la superficie de diseño y seleccione detalles de la asignación .
Seleccione el Person tipo de entidad person y haga clic en ** < Agregar una > tabla o vista** en la
ventana detalles de la asignación .
Seleccione **PersonInfo ** en la lista desplegable. La ventana detalles de la asignación se actualiza con
las asignaciones de columnas predeterminadas, que son correctas para nuestro escenario.
El Person tipo de entidad Person ahora se asigna a las tablas Person y PersonInfo .

Usar el modelo
Pegue el código siguiente en el método Main.

using (var context = new EntitySplittingEntities())


{
var person = new Person
{
FirstName = "John",
LastName = "Doe",
Email = "john@[Link]",
Phone = "555-555-5555"
};

[Link](person);
[Link]();

foreach (var item in [Link])


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

Compile y ejecute la aplicación.


Las siguientes instrucciones T-SQL se ejecutaron en la base de datos como resultado de la ejecución de esta
aplicación.
Las dos instrucciones Inser t siguientes se ejecutaron como resultado de la ejecución del contexto.
SaveChanges (). Toman los datos de la entidad Person y los dividen entre las tablas Person y
PersonInfo .

La siguiente selección se ejecutó como resultado de enumerar las personas en la base de datos.
Combina los datos de la tabla Person y PersonInfo .
División de tablas del diseñador
12/03/2021 • 8 minutes to read

En este tutorial se muestra cómo asignar varios tipos de entidad a una sola tabla modificando un modelo con el
Entity Framework Designer (EF Designer).
Uno de los motivos por los que puede querer usar la división de tablas es retrasar la carga de algunas
propiedades al usar la carga diferida para cargar los [Link] separar las propiedades que pueden
contener una gran cantidad de datos en una entidad independiente y solo cargarlos cuando sea necesario.
En la imagen siguiente se muestran las ventanas principales que se usan al trabajar con el diseñador de EF.

Requisitos previos
Para completar este tutorial, necesitará:
Una versión reciente de Visual Studio.
La base de datos de ejemplo School.

Configurar el proyecto
En este tutorial se usa Visual Studio 2012.
Abra Visual Studio 2012.
En el menú Archivo , seleccione Nuevo y haga clic en Proyecto .
En el panel izquierdo, haga clic en Visual C # y, a continuación, seleccione la plantilla aplicación de consola.
Escriba TableSplittingSample como nombre del proyecto y haga clic en Aceptar .

Creación de un modelo basado en la base de datos School


Haga clic con el botón secundario en el nombre del proyecto en Explorador de soluciones, seleccione
Agregar y, a continuación, haga clic en nuevo elemento .
Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model en
el panel Plantillas.
Escriba TableSplittingModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
En el cuadro de diálogo elegir contenido del modelo, seleccione generar desde la base de datos y, a
continuación, haga clic en siguiente.
Haga clic en nueva conexión. En el cuadro de diálogo Propiedades de conexión, escriba el nombre del
servidor (por ejemplo, (LocalDB) \ mssqllocaldb ), seleccione el método de autenticación, escriba School
como nombre de la base de datos y, a continuación, haga clic en Aceptar . El cuadro de diálogo elegir la
conexión de datos se actualiza con la configuración de conexión de la base de datos.
En el cuadro de diálogo elija los objetos de base de Tables datos, desactive el nodo tablas y Compruebe la
tabla Person . Esto agregará la tabla especificada al modelo School .
Haga clic en Finalizar .
Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo. Todos
los objetos que seleccionó en el cuadro de diálogo Elija los objetos de base de datos se agregan al
modelo.

Asignación de dos entidades a una sola tabla


En esta sección dividirá la entidad Person en dos entidades y, a continuación, las asignará a una sola tabla.

NOTE
La entidad Person no contiene propiedades que puedan contener grandes cantidades de datos. simplemente se usa
como ejemplo.

Haga clic con el botón secundario en un área vacía de la superficie de diseño, seleccione Agregar nuevo y
haga clic en entidad . Aparecerá el cuadro de diálogo nueva entidad .
Escriba HireInfo para el nombre de entidad y PersonID para el nombre de la propiedad de clave .
Haga clic en Aceptar .
Se crea y se muestra un nuevo tipo de entidad en la superficie de diseño.
Seleccione la HireDate propiedad HireDate del tipo de entidad Person y presione las teclas Ctrl + X .
Seleccione la entidad HireInfo y presione las teclas Ctrl + V .
Cree una asociación entre Person y HireInfo . Para ello, haga clic con el botón secundario en un área vacía
de la superficie de diseño, seleccione Agregar nuevo y haga clic en Asociación .
Aparece el cuadro de diálogo Agregar Asociación . El nombre de PersonHireInfo se proporciona de
forma predeterminada.
Especifique Multiplicity 1 (uno) en ambos extremos de la relación.
Haga clic en Aceptar .
El paso siguiente requiere la ventana detalles de la asignación . Si no puede ver esta ventana, haga clic con el
botón secundario en la superficie de diseño y seleccione detalles de la asignación .
Seleccione el HireInfo tipo de entidad HireInfo y haga clic en ** < Agregar una > tabla o vista** en la
ventana detalles de la asignación .
Seleccione persona en la lista desplegable ** < Agregar una > tabla o vista** . La lista contiene tablas o
vistas a las que se puede asignar la entidad seleccionada. Las propiedades adecuadas deben estar
asignadas de forma predeterminada.
Seleccione la Asociación PersonHireInfo en la superficie de diseño.
Haga clic con el botón secundario en la asociación en la superficie de diseño y seleccione propiedades .
En la ventana propiedades , seleccione la propiedad restricciones referenciales y haga clic en el
botón de puntos suspensivos.
Seleccione persona en la lista desplegable principal .
Haga clic en Aceptar .

Usar el modelo
Pegue el código siguiente en el método Main.

using (var context = new SchoolEntities())


{
Person person = new Person()
{
FirstName = "Kimberly",
LastName = "Morgan",
Discriminator = "Instructor",
};

[Link] = new HireInfo()


{
HireDate = [Link]
};

// Add the new person to the context.


[Link](person);

// Insert a row into the Person table.


[Link]();

// Execute a query against the Person table.


// The query returns columns that map to the Person entity.
var existingPerson = [Link]();

// Execute a query against the Person table.


// The query returns columns that map to the Instructor entity.
var hireInfo = [Link];

[Link]("{0} was hired on {1}",


[Link], [Link]);
}

Compile y ejecute la aplicación.


Las siguientes instrucciones T-SQL se ejecutaron en la base de datos School como resultado de la ejecución de
esta aplicación.
La siguiente inserción se ejecutó como resultado de la ejecución del contexto. SaveChanges () y combina
datos de las entidades Person y HireInfo

La siguiente selección se ejecutó como resultado de la ejecución del contexto. People. FirstOrDefault () y
selecciona solo las columnas asignadas a Person

La siguiente selección se ejecutó como resultado del acceso a la propiedad de navegación


ExistingPerson. instructor y selecciona solo las columnas asignadas a HireInfo
Herencia TPH del diseñador
12/03/2021 • 11 minutes to read

En este tutorial paso a paso se muestra cómo implementar la herencia de tabla por jerarquía (TPH) en el modelo
conceptual con el Entity Framework Designer (EF Designer). La herencia TPH utiliza una tabla de base de datos
para mantener los datos de todos los tipos de entidad en una jerarquía de herencia.
En este tutorial asignaremos la tabla Person a tres tipos de entidad: person (el tipo base), Student (derive de
person) y instructor (derive de person). Vamos a crear un modelo conceptual a partir de la base de datos
(Database First) y, a continuación, modificar el modelo para implementar la herencia TPH mediante el diseñador
de EF.
Es posible asignar una herencia TPH mediante Model First pero tendría que escribir su propio flujo de trabajo de
generación de base de datos, que es complejo. A continuación, asignaría este flujo de trabajo a la propiedad
flujo de trabajo de generación de base de datos en el diseñador de EF. Una alternativa más sencilla es
usar Code First.

Otras opciones de herencia


Tabla por tipo (TPT) es otro tipo de herencia en la que las tablas independientes de la base de datos se asignan a
las entidades que participan en la herencia. Para obtener información sobre cómo asignar la herencia de tabla
por tipo con el diseñador de EF, consulte herencia del diseñador de EF.
El tiempo de ejecución de Entity Framework admite la herencia de tipo de tabla por hormigón (TPC) y los
modelos de herencia mixto, pero no son compatibles con el diseñador de EF. Si desea utilizar la herencia de TPC
o mixta, tiene dos opciones: usar Code First o editar manualmente el archivo EDMX. Si decide trabajar con el
archivo EDMX, la ventana detalles de la asignación se colocará en "modo seguro" y no podrá usar el diseñador
para cambiar las asignaciones.

Requisitos previos
Para completar este tutorial, necesitará:
Una versión reciente de Visual Studio.
La base de datos de ejemplo School.

Configurar el proyecto
Abra Visual Studio 2012.
Seleccionar archivo- > nuevo- > proyecto
En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
Escriba TPHDBFirstSample como nombre.
Seleccione Aceptar .

Creación de un modelo
Haga clic con el botón derecho en el nombre del proyecto en Explorador de soluciones y seleccione agregar
> nuevo elemento .
Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model en
el panel Plantillas.
Escriba TPHModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
En el cuadro de diálogo elegir contenido del modelo, seleccione generar desde la base de datos y, a
continuación, haga clic en siguiente .
Haga clic en nueva conexión . En el cuadro de diálogo Propiedades de conexión, escriba el nombre del
servidor (por ejemplo, (LocalDB) \ mssqllocaldb ), seleccione el método de autenticación, escriba School
como nombre de la base de datos y, a continuación, haga clic en Aceptar . El cuadro de diálogo elegir la
conexión de datos se actualiza con la configuración de conexión de la base de datos.
En el cuadro de diálogo elija los objetos de base de datos, en el nodo tablas, seleccione la tabla Person .
Haga clic en Finalizar .
Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo. Todos
los objetos que seleccionó en el cuadro de diálogo elija los objetos de base de datos se agregan al modelo.
Este es el aspecto de la tabla Person en la base de datos.

Implementar la herencia de tabla por jerarquía


La tabla Person tiene la columna Discriminator , que puede tener uno de dos valores: "Student" y "instructor".
En función del valor, la tabla Person se asignará a la entidad Student o a la entidad instructor . La
tabla Person también tiene dos columnas, HireDate y EnrollmentDate , que deben admitir valores NULL
porque una persona no puede ser estudiante y un instructor al mismo tiempo (al menos no en este tutorial).
Agregar nuevas entidades
Agregue una nueva entidad. Para ello, haga clic con el botón secundario en un espacio vacío de la superficie
de diseño del Entity Framework Designer y seleccione Add- > Entity .
Escriba instructor como nombre de la entidad y seleccione persona en la lista desplegable para el tipo
base .
Haga clic en Aceptar .
Agregue otra entidad nueva. Escriba Student como nombre de la entidad y seleccione persona en la
lista desplegable para el tipo base .
Se han agregado dos nuevos tipos de entidad a la superficie de diseño. Una flecha señala desde los nuevos tipos
de entidad hasta el tipo de entidad Person ; Esto indica que Person es el tipo base para los nuevos tipos de
entidad.
Haga clic con el botón secundario en la propiedad HireDate de la entidad Person . Seleccione cor tar (o
use la tecla Ctrl-X).
Haga clic con el botón secundario en la entidad instructor y seleccione pegar (o use la tecla Ctrl-V).
Haga clic con el botón secundario en la propiedad HireDate y seleccione propiedades .
En la ventana propiedades , establezca la propiedad Nullable en false .
Haga clic con el botón secundario en la propiedad EnrollmentDate de la entidad Person .
Seleccione cor tar (o use la tecla Ctrl-X).
Haga clic con el botón secundario en la entidad Student y seleccione pegar (o use la tecla Ctrl-V).
Seleccione la propiedad EnrollmentDate y establezca la propiedad Nullable en false .
Seleccione el Person tipo de entidad person. En la ventana propiedades , establezca su
propiedad abstract en true .
Elimine la propiedad Discriminator de Person . La razón por la que se debe eliminar se explica en la sección
siguiente.
Asignación de las entidades
Haga clic con el botón secundario en el instructor y seleccione asignación de tabla. La entidad
instructor está seleccionada en la ventana detalles de la asignación.
Haga clic en ** < Agregar una tabla > o una vista** en la ventana detalles de la asignación . El
campo ** < Agregar una tabla o > vista** se convierte en una lista desplegable de tablas o vistas a las
que se puede asignar la entidad seleccionada.
Seleccione persona en la lista desplegable.
La ventana detalles de la asignación se actualiza con las asignaciones de columnas predeterminadas y
una opción para agregar una condición.
Haga clic en ** < Agregar una > condición**. El campo ** < Agregar una > condición** se convierte en
una lista desplegable de columnas para las que se pueden establecer condiciones.
Seleccione discriminador en la lista desplegable.
En la Operator columna operador de la ventana detalles de la asignación , seleccione = en la lista
desplegable.
En la columna valor/propiedad , escriba instructor . El resultado final debería ser similar al siguiente:

Repita estos pasos para el Student tipo de entidad Student, pero haga que la condición sea igual que el
valor Student .
La razón por la que queríamos quitar la propiedad Discriminator , es que no se puede asignar más de
una vez una columna de tabla. Esta columna se utilizará para la asignación condicional, por lo que no se
puede usar también para la asignación de propiedades. La única manera en que se puede usar para
ambos, si una condición usa una comparación **is null* o is not null .*
Ahora se implementa la herencia de tabla por jerarquía.
Usar el modelo
Abra el archivo [Link] en el que se define el método Main . Pegue el código siguiente en la función Main
. El código ejecuta tres consultas. La primera consulta devuelve todos los objetos Person . La segunda consulta
utiliza el método Intype para devolver los objetos instructor . La tercera consulta utiliza el método Intype para
devolver objetos Student .

using (var context = new SchoolEntities())


{
[Link]("All people:");
foreach (var person in [Link])
{
[Link](" {0} {1}", [Link], [Link]);
}

[Link]("Instructors only: ");


foreach (var person in [Link]<Instructor>())
{
[Link](" {0} {1}", [Link], [Link]);
}

[Link]("Students only: ");


foreach (var person in [Link]<Student>())
{
[Link](" {0} {1}", [Link], [Link]);
}
}
Herencia de diseñador TPT
12/03/2021 • 7 minutes to read

En este tutorial paso a paso se muestra cómo implementar la herencia de tabla por tipo (TPT) en el modelo
mediante el Entity Framework Designer (EF Designer). La herencia de tabla por tipo utiliza una tabla
independiente de la base de datos para mantener los datos de las propiedades no heredadas y de las
propiedades de clave para cada tipo de la jerarquía de herencia.
En este tutorial, asignaremos las entidades Course (tipo base), OnlineCourse (se deriva de Course)
y OnsiteCourse (deriva de Course ) a tablas con los mismos nombres. Vamos a crear un modelo a partir de la
base de datos y, a continuación, modificaremos el modelo para implementar la herencia de TPT.
También puede empezar con el Model First y, a continuación, generar la base de datos a partir del modelo. El
diseñador de EF usa la estrategia TPT de forma predeterminada, por lo que cualquier herencia del modelo se
asignará a tablas independientes.

Otras opciones de herencia


Tabla por jerarquía (TPH) es otro tipo de herencia en la que se utiliza una tabla de base de datos para mantener
los datos de todos los tipos de entidad en una jerarquía de [Link] obtener información sobre cómo
asignar la herencia de tabla por jerarquía con Entity Designer, vea la herencia TPH del diseñador de EF.
Tenga en cuenta que, la herencia de tipos por hormigón (TPC) y los modelos de herencia mixtos son compatibles
con el tiempo de ejecución de Entity Framework pero no son compatibles con el diseñador de EF. Si desea
utilizar la herencia de TPC o mixta, tiene dos opciones: usar Code First o editar manualmente el archivo EDMX. Si
decide trabajar con el archivo EDMX, la ventana detalles de la asignación se colocará en "modo seguro" y no
podrá usar el diseñador para cambiar las asignaciones.

Requisitos previos
Para completar este tutorial, necesitará:
Una versión reciente de Visual Studio.
La base de datos de ejemplo School.

Configurar el proyecto
Abra Visual Studio 2012.
Seleccionar archivo- > nuevo- > proyecto
En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
Escriba TPTDBFirstSample como nombre.
Seleccione Aceptar .

Creación de un modelo
Haga clic con el botón derecho en el proyecto en Explorador de soluciones y seleccione agregar > nuevo
elemento .
Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model en
el panel Plantillas.
Escriba TPTModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
En el cuadro de diálogo elegir contenido del modelo, seleccione** generar desde la base de datosy, a
continuación, haga clic en siguiente**.
Haga clic en nueva conexión . En el cuadro de diálogo Propiedades de conexión, escriba el nombre del
servidor (por ejemplo, (LocalDB) \ mssqllocaldb ), seleccione el método de autenticación, escriba School
como nombre de la base de datos y, a continuación, haga clic en Aceptar . El cuadro de diálogo elegir la
conexión de datos se actualiza con la configuración de conexión de la base de datos.
En el cuadro de diálogo elija los objetos de base de datos, en el nodo tablas, seleccione las
tablas Depar tment , Course, OnlineCourse y OnsiteCourse .
Haga clic en Finalizar .
Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo. Todos
los objetos que seleccionó en el cuadro de diálogo elija los objetos de base de datos se agregan al modelo.

Implementar la herencia de tabla por tipo


En la superficie de diseño, haga clic con el botón secundario en el tipo de entidad OnlineCourse y
seleccione propiedades .
En la ventana propiedades , establezca la propiedad tipo base en Course .
Haga clic con el botón secundario en el tipo de entidad OnsiteCourse y seleccione propiedades .
En la ventana propiedades , establezca la propiedad tipo base en Course .
Haga clic con el botón secundario en la Asociación (la línea) entre los tipos de entidad OnlineCourse y
Course . Seleccione eliminar del modelo .
Haga clic con el botón secundario en la asociación entre los tipos de entidad OnsiteCourse y Course .
Seleccione eliminar del modelo .
Ahora eliminaremos la propiedad CourseID de OnlineCourse y OnsiteCourse , ya que estas clases heredan
courseid del tipo base Course .
Haga clic con el botón secundario en la propiedad CourseID del tipo de entidad OnlineCourse y, a
continuación, seleccione eliminar del modelo .
Haga clic con el botón secundario en la propiedad CourseID del tipo de entidad OnsiteCourse y, a
continuación, seleccione eliminar del modelo .
Ya está implementada la herencia de tabla por tipo.

Usar el modelo
Abra el archivo [Link] en el que se define el método Main . Pegue el código siguiente en la función Main
. El código ejecuta tres consultas. La primera consulta devuelve todos los cursos relacionados con el
Departamento especificado. La segunda consulta utiliza el método Intype para devolver OnlineCourses
relacionado con el Departamento especificado. La tercera consulta devuelve OnsiteCourses .
using (var context = new SchoolEntities())
{
foreach (var department in [Link])
{
[Link]("The {0} department has the following courses:",
[Link]);

[Link](" All courses");


foreach (var course in [Link] )
{
[Link](" {0}", [Link]);
}

foreach (var course in [Link].


OfType<OnlineCourse>())
{
[Link](" Online - {0}", [Link]);
}

foreach (var course in [Link].


OfType<OnsiteCourse>())
{
[Link](" Onsite - {0}", [Link]);
}
}
}
Procedimientos almacenados de consulta del
diseñador
12/03/2021 • 6 minutes to read

En este tutorial paso a paso se muestra cómo usar el Entity Framework Designer (EF Designer) para importar
procedimientos almacenados en un modelo y, a continuación, llamar a los procedimientos almacenados
importados para recuperar los resultados.
Tenga en cuenta que Code First no admite la asignación a funciones o procedimientos almacenados. Sin
embargo, puede llamar a procedimientos almacenados o funciones mediante el método System. Data. Entity.
DbSet. SqlQuery. Por ejemplo:

var query = [Link]("EXECUTE [dbo].[GetAllProducts]")`;

Requisitos previos
Para completar este tutorial, necesitará:
Una versión reciente de Visual Studio.
La base de datos de ejemplo School.

Configurar el proyecto
Abra Visual Studio 2012.
Seleccionar archivo- > nuevo- > proyecto
En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
Escriba EFwithSProcsSample como nombre.
Seleccione Aceptar .

Creación de un modelo
Haga clic con el botón derecho en el proyecto en Explorador de soluciones y seleccione agregar >
nuevo elemento .
Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model
en el panel Plantillas.
Escriba EFwithSProcsModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
En el cuadro de diálogo elegir contenido del modelo, seleccione generar desde la base de datos y, a
continuación, haga clic en siguiente .
Haga clic en nueva conexión .
En el cuadro de diálogo Propiedades de conexión, escriba el nombre del servidor (por ejemplo,
(LocalDB) \ mssqllocaldb ), seleccione el método de autenticación, escriba School como nombre de
la base de datos y, a continuación, haga clic en Aceptar .
El cuadro de diálogo elegir la conexión de datos se actualiza con la configuración de conexión de la base
de datos.
En el cuadro de diálogo elija los objetos de base de datos, active la casilla tablas para seleccionar todas
las tablas.
Además, seleccione los siguientes procedimientos almacenados en el nodo procedimientos
almacenados y funciones : GetStudentGrades y GetDepar tmentName .

A partir de Visual Studio 2012, el diseñador de EF admite la importación masiva de procedimientos


almacenados. La impor tación de funciones y procedimientos almacenados seleccionados en el
modelo de entidad está activada de forma predeterminada.
Haga clic en Finalizar .
De forma predeterminada, la forma de resultado de cada procedimiento almacenado importado o función que
devuelve más de una columna se convierte automáticamente en un nuevo tipo complejo. En este ejemplo,
queremos asignar los resultados de la función GetStudentGrades a la entidad StudentGrade y los resultados
de GetDepar tmentName a None (ninguno es el valor predeterminado).
Para que una importación de función devuelva un tipo de entidad, las columnas devueltas por el procedimiento
almacenado correspondiente deben coincidir exactamente con las propiedades escalares del tipo de entidad
devuelto. Una importación de función también puede devolver colecciones de tipos simples, tipos complejos o
ningún valor.
Haga clic con el botón secundario en la superficie de diseño y seleccione Explorador de modelos .
En el Explorador de modelos , seleccione impor taciones de función y, a continuación, haga doble clic en
la función GetStudentGrades .
En el cuadro de diálogo Editar importación de función, seleccione entidades y elija StudentGrade .
La casilla impor tación de función con composición en la parte superior del cuadro de diálogo de
impor taciones de funciones le permitirá asignar funciones que admiten composición. Si activa esta
casilla, solo aparecerán las funciones que admiten composición (funciones con valores de tabla) en la lista
desplegable procedimiento almacenado o nombre de función . Si no activa esta casilla, solo se
mostrarán en la lista las funciones que no admiten composición.

Usar el modelo
Abra el archivo [Link] en el que se define el método Main . Agregue el código siguiente a la función
main.
El código llama a dos procedimientos almacenados: GetStudentGrades (devuelve StudentGrades para el
StudentIdespecificado) y GetDepar tmentName (devuelve el nombre del Departamento en el parámetro de
salida).

using (SchoolEntities context = new SchoolEntities())


{
// Specify the Student ID.
int studentId = 2;

// Call GetStudentGrades and iterate through the returned collection.


foreach (StudentGrade grade in [Link](studentId))
{
[Link]("StudentID: {0}\tSubject={1}", studentId, [Link]);
[Link]("Student grade: " + [Link]);
}

// Call GetDepartmentName.
// Declare the name variable that will contain the value returned by the output parameter.
ObjectParameter name = new ObjectParameter("Name", typeof(String));
[Link](1, name);
[Link]("The department name is {0}", [Link]);

Compile y ejecute la aplicación. El programa produce el siguiente resultado:

StudentID: 2
Student grade: 4.00
StudentID: 2
Student grade: 3.50
The department name is Engineering

Parámetros de salida
Si se utilizan parámetros de salida, sus valores no estarán disponibles hasta que los resultados se hayan leído
por completo. Esto se debe al comportamiento subyacente de DbDataReader, consulte recuperación de datos
mediante un DataReader para obtener más detalles.
Procedimientos almacenados CUD del diseñador
12/03/2021 • 11 minutes to read

En este tutorial paso a paso se muestra cómo asignar las \ operaciones de creación de inserción, actualización y
eliminación (CUD) de un tipo de entidad a procedimientos almacenados mediante el Entity Framework Designer
(EF Designer). De forma predeterminada, el Entity Framework genera automáticamente las instrucciones SQL
para las operaciones CUD, pero también puede asignar procedimientos almacenados a estas operaciones.
Tenga en cuenta que Code First no admite la asignación a funciones o procedimientos almacenados. Sin
embargo, puede llamar a procedimientos almacenados o funciones mediante el método System. Data. Entity.
DbSet. SqlQuery. Por ejemplo:

var query = [Link]("EXECUTE [dbo].[GetAllProducts]");

Consideraciones al asignar las operaciones CUD a procedimientos


almacenados
Al asignar las operaciones CUD a los procedimientos almacenados, se aplican las consideraciones siguientes:
Si va a asignar una de las operaciones de CUD a un procedimiento almacenado, asígnela todas. Si no asigna
los tres, se producirá un error en las operaciones no asignadas si se ejecuta y se producirá
una UpdateException .
Debe asignar todos los parámetros del procedimiento almacenado a las propiedades de la entidad.
Si el servidor genera el valor de clave principal para la fila insertada, debe volver a asignar este valor a la
propiedad clave de la entidad. En el ejemplo siguiente, el Inser tPerson procedimiento almacenado
InsertPerson devuelve la clave principal recién creada como parte del conjunto de resultados del
procedimiento almacenado. La clave principal se asigna a la clave de entidad (PersonID ) mediante la
característica ** < Agregar enlaces > de resultados** del diseñador de EF.
Las llamadas a procedimientos almacenados se asignan 1:1 con las entidades del modelo conceptual. Por
ejemplo, si implementa una jerarquía de herencia en el modelo conceptual y, a continuación, asigna los
procedimientos almacenados CUD para las entidades primaria (base) y secundaria (derivada), al guardar
los cambios secundarios solo se llamará a los procedimientos almacenados del elemento secundario , no
se desencadenarán las llamadas a procedimientos almacenados del elemento primario .

Requisitos previos
Para completar este tutorial, necesitará:
Una versión reciente de Visual Studio.
La base de datos de ejemplo School.

Configurar el proyecto
Abra Visual Studio 2012.
Seleccionar archivo- > nuevo- > proyecto
En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
Escriba CUDSProcsSample como nombre.
Seleccione Aceptar .
Creación de un modelo
Haga clic con el botón derecho en el nombre del proyecto en Explorador de soluciones y seleccione
agregar > nuevo elemento .
Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model
en el panel Plantillas.
Escriba CUDSProcs. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
En el cuadro de diálogo elegir contenido del modelo, seleccione generar desde la base de datos y, a
continuación, haga clic en siguiente .
Haga clic en nueva conexión . En el cuadro de diálogo Propiedades de conexión, escriba el nombre del
servidor (por ejemplo, (LocalDB) \ mssqllocaldb ), seleccione el método de autenticación,
escriba School como nombre de la base de datos y, a continuación, haga clic en Aceptar . El cuadro de
diálogo elegir la conexión de datos se actualiza con la configuración de conexión de la base de datos.
En el cuadro de diálogo elija los objetos de base de datos, en el nodo tablas , seleccione la tabla Person
.
Además, seleccione los siguientes procedimientos almacenados en el nodo procedimientos
almacenados y funciones : DeletePerson , Inser tPerson y UpdatePerson .
A partir de Visual Studio 2012, el diseñador de EF admite la importación masiva de procedimientos
almacenados. La impor tación de funciones y procedimientos almacenados seleccionados en el
modelo de entidad está activada de forma predeterminada. Como en este ejemplo tenemos
procedimientos almacenados que insertan, actualizan y eliminan tipos de entidad, no queremos
importarlos y desactivarán esta casilla.

Haga clic en Finalizar . Se muestra el diseñador de EF, que proporciona una superficie de diseño para
editar el modelo.

Asignar la entidad Person a procedimientos almacenados


Haga clic con el botón secundario en el tipo de entidad Person y seleccione asignación de
procedimiento almacenado .
Las asignaciones de procedimientos almacenados aparecen en la ventana detalles de la asignación .
Haga clic en ** < seleccionar > Insertar función**. El campo se convierte en una lista desplegable de los
procedimientos almacenados del modelo de almacenamiento que se puede asignar a tipos de entidad del
modelo conceptual. Seleccione Inser tPerson en la lista desplegable.
Aparecen las asignaciones predeterminadas entre los parámetros de procedimiento almacenado y las
propiedades de entidad. Observe que las flechas indican la dirección de la asignación: se proporcionan
valores de propiedad como parámetros de procedimiento almacenado.
Haga clic en ** < agregar > enlace de resultados**.
Escriba NewPersonID , el nombre del parámetro devuelto por el Inser tPerson procedimiento
almacenado InsertPerson. Asegúrese de no escribir espacios iniciales ni finales.
Presione entrar .
De forma predeterminada, NewPersonID se asigna a la clave de entidad PersonID . Observe que una
flecha indica la dirección de la asignación: el valor de la columna de resultado se proporciona para la
propiedad.

Haga clic en ** < seleccionar > función de actualización** y seleccione UpdatePerson en la lista
desplegable resultante.
Aparecen las asignaciones predeterminadas entre los parámetros de procedimiento almacenado y las
propiedades de entidad.
Haga clic en ** < seleccionar > eliminar función** y seleccione DeletePerson en la lista desplegable
resultante.
Aparecen las asignaciones predeterminadas entre los parámetros de procedimiento almacenado y las
propiedades de entidad.
Las operaciones de inserción, actualización y eliminación del tipo de entidad Person ahora están asignadas a
procedimientos almacenados.
Si desea habilitar la comprobación de simultaneidad al actualizar o eliminar una entidad con procedimientos
almacenados, use una de las siguientes opciones:
Use un parámetro de salida para devolver el número de filas afectadas desde el procedimiento almacenado
y active la casilla del parámetro filas afectadas junto al nombre del parámetro. Si el valor devuelto es
cero cuando se llama a la operación, se producirá una OptimisticConcurrencyException .
Active la casilla usar valor original junto a una propiedad que quiera usar para la comprobación de
simultaneidad. Cuando se intenta realizar una actualización, se usará el valor de la propiedad que se leyó
originalmente de la base de datos al escribir datos de nuevo en la base de datos. Si el valor no coincide con el
valor de la base de datos, se producirá una OptimisticConcurrencyException .
Usar el modelo
Abra el archivo [Link] en el que se define el método Main . Agregue el código siguiente a la función
main.
El código crea un nuevo objeto Person y, a continuación, actualiza el objeto y, por último, elimina el objeto.

using (var context = new SchoolEntities())


{
var newInstructor = new Person
{
FirstName = "Robyn",
LastName = "Martin",
HireDate = [Link],
Discriminator = "Instructor"
}

// Add the new object to the context.


[Link](newInstructor);

[Link]("Added {0} {1} to the context.",


[Link], [Link]);

[Link]("Before SaveChanges, the PersonID is: {0}",


[Link]);

// SaveChanges will call the InsertPerson sproc.


// The PersonID property will be assigned the value
// returned by the sproc.
[Link]();

[Link]("After SaveChanges, the PersonID is: {0}",


[Link]);

// Modify the object and call SaveChanges.


// This time, the UpdatePerson will be called.
[Link] = "Rachel";
[Link]();

// Remove the object from the context and call SaveChanges.


// The DeletePerson sproc will be called.
[Link](newInstructor);
[Link]();

Person deletedInstructor = [Link].


Where(p => [Link] == [Link]).
FirstOrDefault();

if (deletedInstructor == null)
[Link]("A person with PersonID {0} was deleted.",
[Link]);
}

Compile y ejecute la aplicación. El programa genera el siguiente resultado *

NOTE
El servidor genera automáticamente PersonID, por lo que probablemente verá un número diferente *
Added Robyn Martin to the context.
Before SaveChanges, the PersonID is: 0
After SaveChanges, the PersonID is: 51
A person with PersonID 51 was deleted.

Si está trabajando con la versión Ultimate de Visual Studio, puede usar IntelliTrace con el depurador para ver las
instrucciones SQL que se ejecutan.
Relaciones: EF Designer
12/03/2021 • 11 minutes to read

NOTE
En esta página se proporciona información sobre cómo configurar las relaciones en el modelo mediante el diseñador de
EF. Para obtener información general sobre las relaciones en EF y cómo obtener acceso a los datos y manipularlos
mediante relaciones, vea relaciones & propiedades de navegación.

Las asociaciones definen las relaciones entre los tipos de entidad de un modelo. En este tema se muestra cómo
asignar asociaciones con el Entity Framework Designer (EF Designer). En la imagen siguiente se muestran las
ventanas principales que se usan al trabajar con el diseñador de EF.

NOTE
Al crear el modelo conceptual, es posible que aparezcan advertencias sobre entidades y asociaciones no asignadas en la
Lista de errores. Puede omitir estas advertencias porque, después de elegir generar la base de datos a partir del modelo,
los errores desaparecerán.

Información general sobre asociaciones


Al diseñar el modelo mediante el diseñador de EF, un archivo. edmx representa el modelo. En el archivo. edmx,
un elemento Association define una relación entre dos tipos de entidad. Una asociación debe especificar los
tipos de entidad que están implicados en la relación y el posible número de tipos de entidad en cada extremo de
la relación, que se conoce como multiplicidad. La multiplicidad de un extremo de asociación puede tener un
valor de uno (1), cero o uno (0.. 1) o varios ( * ). Esta información se especifica en dos elementos End
secundarios.
En tiempo de ejecución, se puede tener acceso a las instancias de tipo de entidad en un extremo de una
asociación a través de las propiedades de navegación o las claves externas (si elige exponer las claves externas
en las entidades). Con las claves externas expuestas, la relación entre las entidades se administra con un
elemento ReferentialConstraint (un elemento secundario del elemento Association ). Se recomienda
exponer siempre las claves externas para las relaciones de las entidades.

NOTE
En varios a varios ( * : * ) no se pueden agregar claves externas a las entidades. En una * relación de: * , la información de
asociación se administra con un objeto independiente.

Para obtener información sobre los elementos CSDL (ReferentialConstraint , Association , etc.), vea la
especificación de CSDL.

Crear y eliminar asociaciones


La creación de una asociación con el diseñador de EF actualiza el contenido del modelo del archivo. edmx.
Después de crear una asociación, debe crear las asignaciones para la Asociación (que se describen más adelante
en este tema).

NOTE
En esta sección se supone que ya ha agregado las entidades con las que desea crear una asociación entre el modelo.

Para crear una asociación


1. Haga clic con el botón secundario en un área vacía de la superficie de diseño, seleccione Agregar
nuevo y seleccione asociación....
2. Rellene la configuración de la asociación en el cuadro de diálogo Agregar Asociación .
NOTE
Puede optar por no agregar propiedades de navegación ni propiedades de clave externa a las entidades en los
extremos de la asociación si desactiva las casillas **propiedad de navegación **y **Agregar propiedades de clave
externa a la < > entidad nombre de tipo de entidad **. Si agrega solo una propiedad de navegación, la asociación
se podrá recorrer en una única dirección. Si no agrega ninguna propiedad de navegación, deberá agregar
propiedades de clave externa para poder tener acceso a las entidades situadas en los extremos de la asociación.

3. Haga clic en Aceptar .


Para eliminar una asociación
Para eliminar una asociación, realice una de las acciones siguientes:
Haga clic con el botón derecho en la asociación en la superficie del diseñador EF y seleccione eliminar .
O BIEN
Seleccione una o más asociaciones y presione la tecla Supr.

Incluir propiedades de clave externa en las entidades (restricciones


referenciales)
Se recomienda exponer siempre las claves externas para las relaciones de las entidades. Entity Framework usa
una restricción referencial para identificar que una propiedad actúa como clave externa de una relación.
Si ha activado la casilla de verificación ***Agregar propiedades de clave externa al < nombre de tipo de entidad
> *** al crear una relación, se le ha agregado esta restricción referencial.
Cuando se usa EF Designer para agregar o editar una restricción referencial, el diseñador EF agrega o modifica
un elemento ReferentialConstraint en el contenido CSDL del archivo. edmx.
Haga doble clic en la asociación que desee editar. Aparecerá el cuadro de diálogo restricción
referencial .
En la Principal lista desplegable principal, seleccione la entidad de entidad de seguridad en la
restricción referencial. Las propiedades de clave de la entidad se agregan a la lista de claves principales
en el cuadro de diálogo.
En la Dependent lista desplegable dependiente, seleccione la entidad dependiente en la restricción
referencial.
Para cada clave principal que tenga una clave dependiente, seleccione una clave dependiente
correspondiente en las listas desplegables de la columna de clave dependiente .
Haga clic en Aceptar .

Crear y editar asignaciones de asociación


Puede especificar cómo se asigna una asociación a la base de datos en la ventana detalles de la asignación del
diseñador de EF.

NOTE
Solo puede asignar los detalles de las asociaciones que no tienen una restricción referencial especificada. Si se especifica
una restricción referencial, se incluye una propiedad de clave externa en la entidad y se pueden usar los detalles de
asignación de la entidad para controlar a qué columna se asigna la clave externa.

Crear una asignación de asociación


Haga clic con el botón secundario en una asociación en la superficie de diseño y seleccione asignación
de tabla . Esto muestra la asignación de asociación en la ventana detalles de la asignación .
Haga clic en Agregar una tabla o vista . Aparece una lista desplegable que incluye todas las tablas del
modelo de almacenamiento.
Seleccione la tabla a la que se asignará la asociación. La ventana detalles de la asignación muestra los
extremos de la asociación y las propiedades clave del tipo de entidad en cada extremo .
Para cada propiedad de clave, haga clic en el campo columna y seleccione la columna a la que se
asignará la propiedad.

Editar una asignación de asociación


Haga clic con el botón secundario en una asociación en la superficie de diseño y seleccione asignación de
tabla . Esto muestra la asignación de asociación en la ventana detalles de la asignación .
Haga clic en **maps to < TABLE name > **. Aparece una lista desplegable que incluye todas las tablas del
modelo de almacenamiento.
Seleccione la tabla a la que se asignará la asociación. La ventana detalles de la asignación muestra los
extremos de la asociación y las propiedades clave del tipo de entidad en cada extremo.
Para cada propiedad de clave, haga clic en el campo columna y seleccione la columna a la que se asignará
la propiedad.

Editar y eliminar propiedades de navegación


Las propiedades de navegación son propiedades de acceso directo que se usan para buscar las entidades en los
extremos de una asociación en un modelo. Las propiedades de navegación se pueden crear al definir una
asociación entre dos tipos de entidad.
Para editar las propiedades de navegación
Seleccione una propiedad de navegación en la superficie del diseñador de EF. En la ventana propiedades de
Visual Studio se muestra información sobre la propiedad de navegación .
Cambie la configuración de las propiedades en la ventana propiedades .
Para eliminar propiedades de navegación
Si las claves externas no se exponen en los tipos de entidad del modelo conceptual, la eliminación de una
propiedad de navegación puede provocar que la asociación correspondiente solo se pueda recorrer en una
dirección o incluso en ninguna.
Haga clic con el botón derecho en una propiedad de navegación en la superficie del diseñador EF y
seleccione eliminar .
Varios diagramas por modelo
12/03/2021 • 8 minutes to read

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

En este vídeo y en la página se muestra cómo dividir un modelo en varios diagramas mediante el Entity
Framework Designer (EF Designer). Es posible que desee usar esta característica cuando el modelo sea
demasiado grande para verlo o editarlo.
En versiones anteriores del diseñador de EF solo podía tener un diagrama por el archivo EDMX. A partir de
Visual Studio 2012, puede usar el diseñador de EF para dividir el archivo EDMX en varios diagramas.

Visualización del vídeo


En este vídeo se muestra cómo dividir un modelo en varios diagramas mediante el Entity Framework Designer
(EF Designer). Es posible que desee usar esta característica cuando el modelo sea demasiado grande para verlo
o editarlo.
Presentada por : Julia Kornich
Vídeo : WMV | MP4 | WMV (zip)

Información general del diseñador EF


Al crear un modelo mediante el Asistente para Entity Data Model del diseñador de EF, se crea un archivo. edmx y
se agrega a la solución. Este archivo define la forma de las entidades y cómo se asignan a la base de datos.
El diseñador de EF consta de los siguientes componentes:
Superficie de diseño visual para editar el modelo. Puede crear, modificar o eliminar entidades y asociaciones.
Model Browser Ventana del explorador de modelos que proporciona vistas de árbol del [Link]
entidades y sus asociaciones se encuentran en la carpeta * [ modelname ] * . Las tablas y restricciones de
base de datos se encuentran en la * [ modelname ] *. Carpeta de almacenamiento.
Una ventana de detalles de asignación para ver y editar asignaciones. Puede asignar tipos de entidad o
asociaciones a tablas, columnas y procedimientos almacenados de base de datos.
La ventana superficie de diseño visual se abre automáticamente cuando finaliza el Asistente para Entity Data
Model. Si el explorador de modelos no está visible, haga clic con el botón secundario en la superficie de diseño
principal y seleccione Explorador de modelos .
En la captura de pantalla siguiente se muestra un archivo. edmx abierto en el diseñador de EF. En la captura de
pantalla se muestra la superficie de diseño visual (a la izquierda) y la ventana del Explorador de modelos (a
la derecha).
Para deshacer una operación realizada en EF Designer, haga clic en Ctrl + Z.

Trabajar con diagramas


De forma predeterminada, el diseñador de EF crea un diagrama denominado Diagram1. Si tiene un diagrama
con un gran número de entidades y asociaciones, lo más conveniente es dividirlos lógicamente. A partir de
Visual Studio 2012, puede ver el modelo conceptual en varios diagramas.
A medida que agrega nuevos diagramas, aparecen en la carpeta diagramas de la ventana Explorador de
modelos. Para cambiar el nombre de un diagrama: seleccione el diagrama en la ventana Explorador de modelos,
haga clic en una vez en el nombre y escriba el nuevo nombre. También puede hacer clic con el botón secundario
en el nombre del diagrama y seleccionar cambiar nombre .
El nombre del diagrama se muestra junto al nombre del archivo. edmx en el editor de Visual Studio. Por
ejemplo, Model1. edmx [ Diagram1 ] .

El contenido de los diagramas (forma y color de las entidades y asociaciones) se almacena en el archivo. edmx.
diagram. Para ver este archivo, seleccione Explorador de soluciones y desdoblar el archivo. edmx.
No debe modificar manualmente el archivo. edmx. Diagram, es posible que el contenido de este archivo se
sobrescriba con el diseñador de EF.

Dividir entidades y asociaciones en un nuevo diagrama


Puede seleccionar entidades en el diagrama existente (mantenga presionada la tecla Mayús para seleccionar
varias entidades). Haga clic con el botón secundario del mouse y seleccione ir al nuevo diagrama . Se crea el
nuevo diagrama y las entidades seleccionadas y sus asociaciones se mueven al diagrama.
Como alternativa, puede hacer clic con el botón secundario en la carpeta diagramas del explorador de modelos
y seleccionar Agregar nuevo diagrama. Después, puede arrastrar y colocar entidades desde la carpeta tipos
de entidad del explorador de modelos hasta la superficie de diseño.
También puede cortar o copiar entidades (con las teclas Ctrl-X o Ctrl + C) de un diagrama y pegar (mediante la
tecla Ctrl-V) en la otra. Si el diagrama en el que pega una entidad ya contiene una entidad con el mismo
nombre, se creará una nueva entidad y se agregará al [Link] ejemplo: Diagram2 contiene la entidad
Department. A continuación, pegue otro departamento en Diagram2. La entidad Department1 se crea y se
agrega al modelo conceptual.
Para incluir entidades relacionadas en un diagrama, haga clic en la entidad y seleccione incluir relacionados .
Esto hará que se realice una copia de las entidades y asociaciones relacionadas en el diagrama especificado.

Cambiar el color de las entidades


Además de dividir un modelo en varios diagramas, también puede cambiar los colores de las entidades.
Para cambiar el color, seleccione una entidad (o varias entidades) en la superficie de diseño. A continuación,
haga clic con el botón secundario del mouse y seleccione propiedades . En el ventana Propiedades, seleccione
la propiedad color de relleno . Especifique el color mediante un nombre de color válido (por ejemplo, rojo) o
un RGB válido (por ejemplo, 255, 128, 128).

Resumen
En este tema, hemos examinado cómo dividir un modelo en varios diagramas y cómo especificar un color
diferente para una entidad mediante el Entity Framework Designer.
Selección de Entity Framework versión en tiempo
de ejecución para los modelos EF Designer
12/03/2021 • 3 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

A partir de EF6, se ha agregado la siguiente pantalla a EF Designer para que pueda seleccionar la versión del
tiempo de ejecución al que desea dirigirse al crear un modelo. La pantalla aparecerá cuando la versión más
reciente de Entity Framework no esté ya instalada en el proyecto. Si ya está instalada la versión más reciente, se
usará de forma predeterminada.

Destino de EF6. x
Puede elegir EF6 en la pantalla "elegir su versión" para agregar el tiempo de ejecución de EF6 al proyecto. Una
vez que haya agregado EF6, dejará de ver esta pantalla en el proyecto actual.
EF6 se deshabilitará si ya tiene instalada una versión anterior de EF (ya que no puede tener como destino varias
versiones del tiempo de ejecución del mismo proyecto). Si la opción EF6 no está habilitada aquí, siga estos
pasos para actualizar el proyecto a EF6:
1. Haga clic con el botón derecho en el proyecto en Explorador de soluciones y seleccione administrar
paquetes NuGet...
2. Seleccionar actualizaciones
3. Seleccione EntityFramework (Asegúrese de que se va a actualizar a la versión que quiera)
4. Haga clic en Update (Actualizar).

Destino de EF5. x
Puede elegir EF5 en la pantalla "elegir su versión" para agregar el tiempo de ejecución de EF5 al proyecto. Una
vez que haya agregado EF5, seguirá viendo la pantalla con la opción EF6 deshabilitada.
Si ya tiene instalada una versión de EF4. x del Runtime, verá que la versión de EF aparece en la pantalla en lugar
de EF5. En esta situación, puede actualizar a EF5 siguiendo estos pasos:
1. Seleccione herramientas- > Administrador de paquetes de la biblioteca- > consola del
administrador de paquetes
2. Ejecute Install-Package EntityFramework-version 5.0.0

Destino de EF4. x
Puede instalar el tiempo de ejecución de EF4. x en el proyecto mediante los pasos siguientes:
1. Seleccione herramientas- > Administrador de paquetes de la biblioteca- > consola del
administrador de paquetes
2. Ejecute Install-Package EntityFramework-version 4.3.0
Plantillas de generación de código del diseñador
12/03/2021 • 14 minutes to read

Cuando se crea un modelo con Entity Framework Designer, las clases y el contexto derivado se generan
automáticamente. Además de la generación de código predeterminada, también se proporcionan una serie de
plantillas que pueden usarse para personalizar el código que se genera. Estas plantillas se proporcionan como
plantillas de texto T4, lo que permite personalizarlas en caso necesario.
El código que se genera de forma predeterminada depende de la versión de Visual Studio en que se cree el
modelo:
Los modelos creados en Visual Studio 2012 y 2013 generan clases de entidad POCO simples y un contexto
que se deriva del elemento DbContext simplificado.
Los modelos creados en Visual Studio 2010 generan clases de entidad que se derivan de EntityObject y un
contexto que se deriva de ObjectContext.

NOTE
Se recomienda cambiar a la plantilla Generador de DbContext una vez agregado el modelo.

En esta página se habla de las plantillas disponibles y luego se proporcionan instrucciones para agregar una
plantilla al modelo.

Plantillas disponibles
El equipo de Entity Framework proporciona las siguientes plantillas:
Generador de DbContext
Esta plantilla genera clases de entidad POCO simples y un contexto que se deriva de DbContext mediante EF6.
Es la plantilla recomendada, a menos que tenga motivos para usar una de las otras plantillas que se indican a
continuación. También es la plantilla de generación de código que se obtiene de forma predeterminada cuando
se usan versiones recientes de Visual Studio (Visual Studio 2013 y versiones posteriores): cuando se crea un
nuevo modelo, se usa esta plantilla de forma predeterminada y los archivos T4 (.tt) se anidan en el archivo
.edmx.
Versiones anteriores de Visual Studio
Visual Studio 2012: para obtener las plantillas EF 6.x DbContextGenerator , tiene que instalar la versión
más reciente de Entity Framework Tools para Visual Studio : vea la página Get Entity Framework
(Obtener Entity Framework) para obtener más información.
Visual Studio 2010: las plantillas EF 6.x DbContextGenerator no están disponibles para Visual Studio
2010.
Generador de DbContext para EF 5.x
Si usa una versión anterior del paquete NuGet de EntityFramework (uno con una versión principal de 5), tiene
que usar la plantilla Generador de DbContext para EF 5.x .
Si usa Visual Studio 2013 o 2012, esta plantilla ya está instalada.
Si usa Visual Studio 2010, tiene que seleccionar la pestaña Online al agregar la plantilla para descargarla desde
la Galería de Visual Studio. También puede instalar la plantilla directamente desde la Galería de Visual Studio
previamente. Dado que las plantillas están incluidas en las versiones posteriores de Visual Studio, las versiones
de la galería solo se pueden instalar en Visual Studio 2010.
Generador de DbContext para EF 5.x para C#
Generador de DbContext para EF 5.x para sitios web de C#
Generador de DbContext para EF 5.x para [Link]
Generador de DbContext para EF 5.x para sitios web de [Link]
Generador de DbContext para EF 4.x
Si usa una versión anterior del paquete NuGet de EntityFramework (uno con una versión principal de 4), tiene
que usar la plantilla Generador de DbContext para EF 4.x . La encontrará en la pestaña Online al agregar la
plantilla o puede instalarla directamente desde la Galería de Visual Studio previamente.
Generador de DbContext para EF 4.x para C#
Generador de DbContext para EF 4.x para sitios web de C#
Generador de DbContext para EF 4.x para [Link]
Generador de DbContext para EF 4.x para sitios web de [Link]
Generador de EntityObject
Esta plantilla genera clases de entidad que se derivan de EntityObject y un contexto que se deriva de
ObjectContext.

NOTE
Considere la posibilidad de usar Generador de DbContext

Generador de DbContext es ahora la plantilla recomendada para las nuevas aplicaciones. Generador de
DbContext aprovecha la API de DbContext, que es más sencilla. Generador de EntityObject sigue estando
disponible para la compatibilidad con las aplicaciones existentes.
Visual Studio 2010, 2012 & 2013
Tiene que seleccionar la pestaña Online al agregar la plantilla para descargarla desde la Galería de Visual
Studio. También puede instalar la plantilla directamente desde la Galería de Visual Studio previamente.
Generador de EntityObject para EF 6.x para C#
Generador de EntityObject para EF 6.x para sitios web de C#
Generador de EntityObject para EF 6.x para [Link]
Generador de EntityObject para EF 6.x para sitios web de [Link]
Generador de EntityObject para EF 5.x
Si usa Visual Studio 2012 o 2013, tiene que seleccionar la pestaña Online al agregar la plantilla para
descargarla desde la Galería de Visual Studio. También puede instalar la plantilla directamente desde la Galería
de Visual Studio previamente. Dado que las plantillas están incluidas en Visual Studio 2010, las versiones de la
galería solo se pueden instalar en Visual Studio 2012 y 2013.
Generador de EntityObject para EF 5.x para C#
Generador de EntityObject para EF 5.x para sitios web de C#
Generador de EntityObject para EF 5.x para [Link]
Generador de EntityObject para EF 5.x para sitios web de [Link]
Si solo quiere la generación de código de ObjectContext y no necesita editar la plantilla, puede revertir a la
generación de código de EntityObject.
Si usa Visual Studio 2010, esta plantilla ya está instalada. Si crea un nuevo modelo en Visual Studio 2010, esta
plantilla se usa de forma predeterminada, pero los archivos .tt no se incluyen en el proyecto. Si quiere
personalizar la plantilla, tiene que agregarla al proyecto.
Generador de entidades de autoseguimiento (STE)
Esta plantilla genera clases de entidad de autoseguimiento y un contexto que se deriva de ObjectContext. En una
aplicación de EF, un contexto es el responsable de realizar el seguimiento de los cambios en las entidades. Pero
en los escenarios de n niveles, es posible que el contexto no esté disponible en el nivel que modifica las
entidades. Las entidades de autoseguimiento ayudan a realizar el seguimiento de los cambios en cualquier nivel.
Para obtener más información, vea Entidades de autoseguimiento.

NOTE
La plantilla STE no se recomienda

Ya no se recomienda usar la plantilla STE en las aplicaciones nuevas, aunque sigue estando disponible para la
compatibilidad con las aplicaciones existentes. Visite el artículo sobre entidades desconectadas para ver otras
opciones recomendadas para escenarios de n niveles.

NOTE
No hay ninguna versión para EF 6.x de la plantilla STE.

NOTE
No hay ninguna versión para Visual Studio 2013 de la plantilla STE.

Visual Studio 2012


Si usa Visual Studio 2012, tiene que seleccionar la pestaña Online al agregar la plantilla para descargarla desde
la Galería de Visual Studio. También puede instalar la plantilla directamente desde la Galería de Visual Studio
previamente. Dado que las plantillas están incluidas en Visual Studio 2010, las versiones de la galería solo se
pueden instalar en Visual Studio 2012.
Generador de STE para EF 5.x para C#
Generador de STE para EF 5.x para sitios web de C#
Generador de STE para EF 5.x para [Link]
Generador de STE para EF 5.x para sitios web de [Link]
Visual Studio 2010**
Si usa Visual Studio 2010, esta plantilla ya está instalada.
Generador de entidades POCO
Esta plantilla genera clases de entidad POCO y un contexto que se deriva de ObjectContext

NOTE
Considere la posibilidad de usar Generador de DbContext

Generador de DbContext es ahora la plantilla recomendada para generar clases POCO en nuevas aplicaciones.
Generador de DbContext aprovecha la nueva API de DbContext y puede generar clases POCO más sencillas.
Generador de entidades POCO sigue estando disponible para la compatibilidad con las aplicaciones existentes.
NOTE
No hay ninguna versión para EF 5.x o EF 6.x de la plantilla STE.

NOTE
No hay ninguna versión para Visual Studio 2013 de la plantilla POCO.

Visual Studio 2012 y Visual Studio 2010


Tiene que seleccionar la pestaña Online al agregar la plantilla para descargarla desde la Galería de Visual
Studio. También puede instalar la plantilla directamente desde la Galería de Visual Studio previamente.
Generador de POCO para EF 4.x para C#
Generador de POCO para EF 4.x para sitios web de C#
Generador de POCO para EF 4.x para [Link]
Generador de POCO para EF 4.x para sitios web de [Link]
Qué son las plantillas "Sitios web"
Las plantillas "Sitios web" (es decir, Generador de DbContext para EF 5.x para sitios web de C# ) se usan
en proyectos de sitio web creados con Archivo -> Nuevo -> Sitio web... . Son distintas a las aplicaciones
web, creadas con Archivo -> Nuevo -> Proyecto... , que usan las plantillas estándar. Se proporcionan
plantillas independientes porque así lo exige el sistema de plantilla de elemento de Visual Studio.

Uso de una plantilla


Para empezar a usar una plantilla de generación de código, haga clic con el botón derecho en un punto vacío de
la superficie de diseño de EF Designer y seleccione Agregar elemento de generación de código...

Si ya ha instalado la plantilla que quiere usar (o estaba incluida en Visual Studio), estará disponible en la sección
Código o Datos del menú izquierdo.
Si aún no tiene instalada la plantilla, seleccione Online en el menú izquierdo y busque la plantilla que quiere.

Si usa Visual Studio 2012, los nuevos archivos .tt aparecen anidados en el archivo .edmx.*

NOTE
En el caso de los modelos creados en Visual Studio 2012, tiene que eliminar las plantillas usadas para la generación de
código predeterminada; si no lo hace, se generarán clases y contexto duplicados. Los archivos predeterminados son
<nombreDelModelo>.tt y <nombreDelModelo>.[Link] .
Si usa Visual Studio 2010, los archivos tt se agregan directamente al proyecto.
Revertir a ObjectContext en Entity Framework
Designer
12/03/2021 • 2 minutes to read

Con la versión anterior de Entity Framework un modelo creado con EF Designer generaría un contexto derivado
de ObjectContext y clases de entidad derivadas de EntityObject.
A partir de EF 4.1, se recomienda cambiar a una plantilla de generación de código que genera un contexto que
se deriva de las clases de entidad DbContext y POCO.
En Visual Studio 2012, se obtiene el código DbContext generado de forma predeterminada para todos los
nuevos modelos creados con el diseñador de EF. Los modelos existentes seguirán generando código basado en
ObjectContext a menos que decida cambiar al generador de código basado en DbContext.

Revertir a la generación de código de ObjectContext


1. deshabilitar la generación de código DbContext
La generación de las clases DbContext y POCO derivadas se controla mediante dos archivos. TT en el proyecto.
Si expande el archivo. edmx en el explorador de soluciones, verá estos archivos. Elimine ambos archivos del
proyecto.

Si usa [Link], debe seleccionar el botón Mostrar todos los archivos para ver los archivos anidados.

2. Re -Enable la generación de código de ObjectContext


Abra el modelo en el diseñador de EF, haga clic con el botón derecho en una sección en blanco de la superficie
de diseño y seleccione propiedades .
En el ventana Propiedades cambie la estrategia de generación de código de ninguno a predeterminado .
Especificación CSDL
12/03/2021 • 121 minutes to read

El lenguaje de definición de esquemas conceptuales (CSDL) es un lenguaje basado en XML que describe las
entidades, las relaciones y las funciones que conforman un modelo conceptual de una aplicación controlada por
datos. Este modelo conceptual puede ser utilizado por el Entity Framework o WCF Data Services. Los metadatos
que se describen con CSDL los utiliza el Entity Framework para asignar entidades y relaciones que se definen en
un modelo conceptual a un origen de datos. Para obtener más información, vea especificación de SSDL y
especificación de MSL.
CSDL es la implementación del Entity Framework del Entity Data Model.
En una aplicación Entity Framework, los metadatos del modelo conceptual se cargan desde un archivo. CSDL
(escrito en CSDL) en una instancia de System. Data. Metadata. Edm. EdmItemCollection y son accesibles
mediante métodos en la clase System. Data. Metadata. Edm. MetadataWorkspace. Entity Framework utiliza los
metadatos del modelo conceptual para traducir las consultas del modelo conceptual a comandos específicos del
origen de datos.
El diseñador de EF almacena la información del modelo conceptual en un archivo. edmx en tiempo de diseño. En
el momento de la compilación, el diseñador de EF usa información en un archivo. edmx para crear el archivo.
CSDL que necesita Entity Framework en tiempo de ejecución.
Las versiones de CSDL se diferencian por los espacios de nombres XML.

VERSIÓ N DE C SDL ESPA C IO DE N O M B RES XM L

CSDL v1 [Link]

CSDL V2 [Link]

CSDL V3 [Link]

Association (Elemento) (CSDL)


Un elemento Association define una relación entre dos tipos de entidad. Una asociación debe especificar los
tipos de entidad que están implicados en la relación y el posible número de tipos de entidad en cada extremo de
la relación, que se conoce como multiplicidad. La multiplicidad de un extremo de asociación puede tener un
valor de uno (1), cero o uno (0.. 1) o varios ( * ). Esta información se especifica en dos elementos End
secundarios.
Es posible obtener acceso a las instancias de tipo de entidad situadas en un extremo de la asociación a través de
las propiedades de navegación o las claves externas, si estas se exponen en un tipo de entidad.
En una aplicación, una instancia de una asociación representa una asociación concreta entre las instancias de
tipos de entidad. Las instancias de asociación se agrupan de manera lógica en un conjunto de asociaciones.
Un elemento Association puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
End (exactamente 2 elementos)
ReferentialConstraint (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Association .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre de la asociación.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Association .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que define la Asociación CustomerOrders
cuando las claves externas no se han expuesto en los tipos de entidad Customer y Order . Los valores de
multiplicidad de cada extremo de la Asociación indican que se pueden asociar muchos pedidos a un cliente ,
pero solo un cliente puede asociarse a un pedido . Además, el elemento aleliminar indica que todos los
pedidos relacionados con un cliente determinado y que se han cargado en el ObjectContext se eliminarán si se
elimina el cliente .

<Association Name="CustomerOrders">
<End Type="[Link]" Role="Customer" Multiplicity="1" >
<OnDelete Action="Cascade" />
</End>
<End Type="[Link]" Role="Order" Multiplicity="*" />
</Association>

En el ejemplo siguiente se muestra un elemento Association que define la Asociación CustomerOrders


cuando las claves externas se han expuesto en los tipos de entidad Customer y Order . Con las claves externas
expuestas, la relación entre las entidades se administra con un elemento ReferentialConstraint . Un elemento
AssociationSetMapping correspondiente no es necesario para asignar esta asociación al origen de datos.

<Association Name="CustomerOrders">
<End Type="[Link]" Role="Customer" Multiplicity="1" >
<OnDelete Action="Cascade" />
</End>
<End Type="[Link]" Role="Order" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customer">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Order">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
AssociationSet (Elemento) (CSDL)
El elemento AssociationSet en el lenguaje de definición de esquemas conceptuales (CSDL) es un contenedor
lógico para las instancias de asociación del mismo tipo. Un conjunto de asociaciones proporciona una definición
para agrupar las instancias de la asociación con objeto de que se puedan asignar a un origen de datos.
El elemento AssociationSet puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
End (se requieren exactamente dos elementos)
Elementos Annotation (cero o más elementos)
El atributo Association especifica el tipo de asociación que contiene un conjunto de asociaciones. Los conjuntos
de entidades que componen los extremos de un conjunto de asociaciones se especifican con exactamente dos
elementos finales secundarios.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento AssociationSet .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre del conjunto de entidades. El


valor del atributo Name no puede ser
el mismo que el valor del atributo
Association .

Asociación Sí Nombre completo de la asociación


cuyas instancias contiene el conjunto
de asociaciones. La asociación debe
estar en el mismo espacio de nombres
que el conjunto de asociaciones.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento AssociationSet
. Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer con dos elementos AssociationSet :
<EntityContainer Name="BooksContainer" >
<EntitySet Name="Books" EntityType="[Link]" />
<EntitySet Name="Publishers" EntityType="[Link]" />
<EntitySet Name="Authors" EntityType="[Link]" />
<AssociationSet Name="PublishedBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

CollectionType (Elemento de CSDL)


El elemento CollectionType del lenguaje de definición de esquemas conceptuales (CSDL) especifica que un
parámetro de función o un tipo de valor devuelto de función es una colección. El elemento CollectionType
puede ser un elemento secundario del elemento Parameter o del elemento ReturnType (function). El tipo de
colección se puede especificar mediante el atributo Type o uno de los siguientes elementos secundarios:
CollectionType
ReferenceType
RowType
TypeRef

NOTE
Un modelo no se validará si el tipo de una colección se especifica con el atributo Type y un elemento secundario.

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento CollectionType . Tenga en
cuenta que los atributos DefaultValue , MaxLength , FixedLength , Precision , Scale , Unicode y collation
solo se aplican a las colecciones de EDMSimpleTypes .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Tipo No Tipo de la colección.

Admisión de valores NULL No True (el valor predeterminado) o


False , según si la propiedad puede
tener un valor nulo.
[!NOTE]

> en CSDL v1, una propiedad de tipo


complejo debe tener
Nullable="False" .

DefaultValue No Valor predeterminado de la propiedad.


N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

MaxLength No La longitud máxima del valor de la


propiedad.

FixedLength No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena de longitud fija.

Precisión No Precisión del valor de propiedad.

Escala No Escala del valor de propiedad.

SRID No Identificador de referencia del sistema


espacial. Solo es válido para las
propiedades de los tipos
[Link] obtener más
información, consulte SRID y SRID
(SQL Server) .

Unicode No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena Unicode.

Intercalación No Cadena que especifica la secuencia de


intercalación que se usará en el origen
de datos.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento CollectionType
. Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra una función definida por el modelo que utiliza un elemento CollectionType
para especificar que la función devuelve una colección de tipos de entidad Person (tal y como se especifica con
el atributo elementType ).

<Function Name="LastNamesAfter">
<Parameter Name="someString" Type="[Link]"/>
<ReturnType>
<CollectionType ElementType="[Link]"/>
</ReturnType>
<DefiningExpression>
SELECT VALUE p
FROM [Link] AS p
WHERE [Link] >= someString
</DefiningExpression>
</Function>
En el ejemplo siguiente se muestra una función definida por el modelo que utiliza un elemento CollectionType
para especificar que la función devuelve una colección de filas (tal y como se especifica en el elemento
RowType ).

<Function Name="LastNamesAfter">
<Parameter Name="someString" Type="[Link]" />
<ReturnType>
<CollectionType>
<RowType>
<Property Name="FirstName" Type="[Link]" Nullable="false" />
<Property Name="LastName" Type="[Link]" Nullable="false" />
</RowType>
</CollectionType>
</ReturnType>
<DefiningExpression>
SELECT VALUE ROW([Link], [Link])
FROM [Link] AS p
WHERE [Link] &gt;= somestring
</DefiningExpression>
</Function>

En el ejemplo siguiente se muestra una función definida por el modelo que usa el elemento CollectionType
para especificar que la función acepta como parámetro una colección de tipos de entidad Depar tment .

<Function Name="GetAvgBudget">
<Parameter Name="Departments">
<CollectionType>
<TypeRef Type="[Link]"/>
</CollectionType>
</Parameter>
<ReturnType Type="Collection([Link])"/>
<DefiningExpression>
SELECT VALUE AVG([Link]) FROM Departments AS d
</DefiningExpression>
</Function>

ComplexType (Elemento) (CSDL)


Un elemento complexType define una estructura de datos formada por propiedades EdmSimpleType u otros
tipos [Link] tipo complejo puede ser una propiedad de un tipo de entidad o de otro tipo complejo. Un
tipo complejo se parece a un tipo de entidad en que también define datos. Sin embargo, existen algunas
diferencias clave entre los tipos complejos y los tipos de entidad:
Los tipos complejos no tienen identidades (o claves) y, por consiguiente, no pueden existir de forma
independiente. Los tipos complejos solo pueden existir como propiedades de tipos de entidad u otros tipos
complejos.
Los tipos complejos no pueden participar en asociaciones. Los extremos de una asociación no pueden ser
tipos complejos y, por consiguiente, no se pueden definir propiedades de navegación para tipos complejos.
Una propiedad de tipo complejo no puede tener un valor nulo, aunque las propiedades escalares de un tipo
complejo se pueden establecer cada una con el valor nulo.
Un elemento complexType puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Property (cero o más elementos)
Elementos Annotation (cero o más elementos)
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento complexType .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del tipo complejo. El


nombre de un tipo complejo no puede
ser igual que el nombre de otro tipo
complejo, tipo de entidad o asociación
que esté dentro del ámbito del
modelo.

BaseType No El nombre de otro tipo complejo que


es el tipo base del tipo complejo que
se define.
[!NOTE]

> este atributo no es aplicable en


CSDL v1. La herencia de los tipos
complejos no se admite en esa versión.

Descripción breve No True o false (valor predeterminado)


en función de si el tipo complejo es un
tipo abstracto.
[!NOTE]

> este atributo no es aplicable en


CSDL v1. Los tipos complejos de esa
versión no pueden ser tipos
abstractos.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento complexType .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un tipo complejo, Address , con las propiedades EdmSimpleType
StreetAddress , City , StateOrProvince , Countr y y PostalCode .

<ComplexType Name="Address" >


<Property Type="String" Name="StreetAddress" Nullable="false" />
<Property Type="String" Name="City" Nullable="false" />
<Property Type="String" Name="StateOrProvince" Nullable="false" />
<Property Type="String" Name="Country" Nullable="false" />
<Property Type="String" Name="PostalCode" Nullable="false" />
</ComplexType>
Para definir la Dirección de tipo complejo (anterior) como una propiedad de un tipo de entidad, debe declarar
el tipo de propiedad en la definición de tipo de entidad. En el ejemplo siguiente se muestra la propiedad
Address como un tipo complejo en un tipo de entidad (publicador ):

<EntityType Name="Publisher">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="[Link]" Name="Address" Nullable="false" />
<NavigationProperty Name="Books" Relationship="[Link]"
FromRole="Publisher" ToRole="Book" />
</EntityType>

DefiningExpression (Elemento) (CSDL)


El elemento DefiningExpression en el lenguaje de definición de esquemas conceptuales (CSDL) contiene una
expresión Entity SQL que define una función en el modelo conceptual.

NOTE
Para fines de validación, un elemento DefiningExpression puede contener contenido arbitrario. Sin embargo, Entity
Framework producirá una excepción en tiempo de ejecución si un elemento DefiningExpression no contiene Entity SQL
válidos.

Atributos aplicables
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
DefiningExpression . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún
espacio de nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener
nombres completos idénticos.
Ejemplo
En el ejemplo siguiente se usa un elemento DefiningExpression para definir una función que devuelve el
número de años transcurridos desde que se publicó un libro. El contenido del elemento DefiningExpression
se escribe en Entity SQL.

<Function Name="GetYearsInPrint" ReturnType="Edm.Int32" >


<Parameter Name="book" Type="[Link]" />
<DefiningExpression>
Year(CurrentDateTime()) - Year(cast([Link] as DateTime))
</DefiningExpression>
</Function>

Dependent (Elemento) (CSDL)


El elemento dependiente en el lenguaje de definición de esquemas conceptuales (CSDL) es un elemento
secundario del elemento ReferentialConstraint y define el extremo dependiente de una restricción referencial.
Un elemento ReferentialConstraint define una funcionalidad similar a una restricción de integridad
referencial en una base de datos relacional. Del mismo modo que una columna (o columnas) de una tabla de
base de datos puede hacer referencia a la clave principal de otra tabla, una propiedad (o propiedades) de un tipo
de entidad puede hacer referencia a la clave de entidad de otro tipo de entidad. El tipo de entidad al que se hace
referencia se denomina extremo principal de la restricción. El tipo de entidad que hace referencia al extremo
principal se denomina extremo dependiente de la restricción. Los elementos Proper tyRef se usan para
especificar qué claves hacen referencia al extremo principal.
El elemento dependiente puede tener los elementos secundarios siguientes (en el orden mostrado):
PropertyRef (uno o varios elementos)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento dependiente .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Rol Sí Nombre del tipo de entidad del


extremo dependiente de la asociación.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento dependiente .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento ReferentialConstraint que se usa como parte de la definición
de la Asociación PublishedBy . La propiedad PublisherId del tipo de entidad book constituye el extremo
dependiente de la restricción referencial.

<Association Name="PublishedBy">
<End Type="[Link]" Role="Book" Multiplicity="*" >
</End>
<End Type="[Link]" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>
Documentation (Elemento) (CSDL)
El elemento Documentation del lenguaje de definición de esquemas conceptuales (CSDL) se puede usar para
proporcionar información sobre un objeto que se define en un elemento primario. En un archivo. edmx, cuando
el elemento Documentation es un elemento secundario de un elemento que aparece como un objeto en la
superficie de diseño del diseñador EF (como una entidad, asociación o propiedad), el contenido del elemento
Documentation aparecerá en la ventana propiedades de Visual Studio para el objeto.
El elemento Documentation puede tener los elementos secundarios siguientes (en el orden mostrado):
Summar y : breve descripción del elemento primario. (cero o un elemento).
LongDescription : una descripción amplia del elemento primario. (cero o un elemento).
Elementos de anotación. (cero o más elementos).
Atributos aplicables
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
Documentation . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio
de nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres
completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra el elemento Documentation como un elemento secundario de un elemento
EntityType. Si el fragmento de código siguiente estaba en el contenido CSDL de un archivo. edmx, el contenido
de los elementos Summar y y longDescription aparecerá en la ventana propiedades de Visual Studio al
hacer clic en el Customer tipo de entidad.

<EntityType Name="Customer">
<Documentation>
<Summary>Summary here.</Summary>
<LongDescription>Long description here.</LongDescription>
</Documentation>
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Type="Int32" Name="CustomerId" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
</EntityType>

End (Elemento) (CSDL)


El elemento End en el lenguaje de definición de esquemas conceptuales (CSDL) puede ser un elemento
secundario del elemento Association o del elemento AssociationSet. En cada caso, el rol del elemento final es
diferente y los atributos correspondientes son diferentes.
Elemento End como elemento secundario del elemento Association
Un elemento End (como elemento secundario del elemento Association ) identifica el tipo de entidad en un
extremo de una asociación y el número de instancias de tipo de entidad que pueden existir en ese extremo de
una asociación. Los extremos de asociación se definen como parte de una asociación, y esta debe tener
exactamente dos extremos. Es posible obtener acceso a las instancias de tipo de entidad situadas en un extremo
de la asociación a través de las propiedades de navegación o las claves externas si estas se exponen en un tipo
de entidad.
Un elemento End puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
OnDelete (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento End cuando es el elemento
secundario de un elemento Association .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Tipo Sí Nombre del tipo de entidad de un


extremo de la asociación.

Rol No Nombre para el extremo de la


asociación. Si no se proporciona
ningún nombre, se usará el nombre
del tipo de entidad del extremo de la
asociación.

Multiplicidad Sí 1 , 0.. 1 o, * dependiendo del número


de instancias de tipo de entidad que
pueden estar al final de la asociación.
1 indica que existe exactamente una
instancia de tipo de entidad en el
extremo de la asociación.
0.. 1 indica que hay cero o una
instancia de tipo de entidad en el
extremo de la asociación.
* indica que hay cero, una o más
instancias de tipo de entidad en el
extremo de la asociación.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento final . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que define la Asociación CustomerOrders . Los
valores de multiplicidad de cada extremo de la Asociación indican que se pueden asociar muchos pedidos a
un cliente , pero solo un cliente puede asociarse a un pedido . Además, el elemento aleliminar indica que
todos los pedidos relacionados con un cliente determinado y que se han cargado en el ObjectContext se
eliminarán si se elimina el cliente .

<Association Name="CustomerOrders">
<End Type="[Link]" Role="Customer" Multiplicity="1" />
<End Type="[Link]" Role="Order" Multiplicity="*">
<OnDelete Action="Cascade" />
</End>
</Association>
Elemento End como elemento secundario del elemento AssociationSet
El elemento End especifica un extremo de un conjunto de asociaciones. El elemento AssociationSet debe
contener dos elementos End . La información contenida en un elemento End se utiliza para asignar un conjunto
de asociaciones a un origen de datos.
Un elemento End puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Elementos Annotation (cero o más elementos)

NOTE
Los elementos de anotación deben aparecer después de todos los demás elementos secundarios. Los elementos
Annotation solo se permiten en CSDL V2 y versiones posteriores.

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento End cuando es el elemento
secundario de un elemento AssociationSet .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

EntitySet Sí Nombre del elemento EntitySet que


define un extremo del elemento
primario AssociationSet . El elemento
EntitySet debe definirse en el mismo
contenedor de entidades que el
elemento primario AssociationSet .

Rol No Nombre del extremo del conjunto de


asociaciones. Si no se utiliza el atributo
role , el nombre del extremo del
conjunto de asociaciones será el
nombre del conjunto de entidades.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento final . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer con dos elementos AssociationSet , cada
uno con dos elementos End :
<EntityContainer Name="BooksContainer" >
<EntitySet Name="Books" EntityType="[Link]" />
<EntitySet Name="Publishers" EntityType="[Link]" />
<EntitySet Name="Authors" EntityType="[Link]" />
<AssociationSet Name="PublishedBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

EntityContainer (Elemento) (CSDL)


El elemento EntityContainer en el lenguaje de definición de esquemas conceptuales (CSDL) es un contenedor
lógico para conjuntos de entidades, conjuntos de asociaciones e importaciones de funciones. Un contenedor de
entidades de modelo conceptual se asigna a un contenedor de entidades de modelo de almacenamiento a
través del elemento EntityContainerMapping. Un contenedor de entidades de modelo de almacenamiento
describe la estructura de la base de datos: los conjuntos de entidades describen las tablas, los conjuntos de
asociaciones describen las restricciones de clave externa y las importaciones de función describen los
procedimientos almacenados en una base de datos.
Un elemento EntityContainer puede tener cero o un elemento de documentación. Si hay un elemento de
documentación , debe preceder a todos los elementos EntitySet , AssociationSet y FunctionImpor t .
Un elemento EntityContainer puede tener cero o más de los elementos secundarios siguientes (en el orden
mostrado):
EntitySet
AssociationSet
FunctionImport
Elementos Annotation
Puede extender un elemento EntityContainer para incluir el contenido de otro EntityContainer que esté en el
mismo espacio de nombres. Para incluir el contenido de otro EntityContainer , en el elemento
EntityContainer de referencia, establezca el valor del atributo extends en el nombre del elemento
EntityContainer que desea incluir. Todos los elementos secundarios del elemento EntityContainer incluido se
tratarán como elementos secundarios del elemento EntityContainer que hace la referencia.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento using .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre del contenedor de entidades.

Llegar No Nombre de otro contenedor de


entidades dentro del mismo espacio de
nombres.
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
EntityContainer . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de
nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos
idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer que define tres conjuntos de entidades y dos
conjuntos de asociaciones.

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="[Link]" />
<EntitySet Name="Publishers" EntityType="[Link]" />
<EntitySet Name="Authors" EntityType="[Link]" />
<AssociationSet Name="PublishedBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

EntitySet (Elemento) (CSDL)


El elemento EntitySet del lenguaje de definición de esquemas conceptuales es un contenedor lógico para las
instancias de un tipo de entidad y las instancias de cualquier tipo que se deriva de ese tipo de entidad. La
relación entre un tipo de entidad y un conjunto de entidades es análoga a la relación entre una fila y una tabla
en una base de datos relacional. Igual que una fila, un tipo de entidad define un conjunto de datos relacionados
y, lo mismo que una tabla, un conjunto de entidades contiene instancias de esa definición. Un conjunto de
entidades proporciona una construcción para agrupar las instancias del tipo de entidad para que se pueden
asignar a las estructuras de datos relacionadas en un origen de datos.
Se pueden definir varios conjuntos de entidades para un tipo de entidad determinado.

NOTE
El diseñador de EF no es compatible con los modelos conceptuales que contienen varios conjuntos de entidades por tipo.

El elemento EntitySet puede tener los elementos secundarios siguientes (en el orden mostrado):
Elemento Documentation (cero o más elementos)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntitySet .
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre del conjunto de entidades.

EntityType Sí El nombre completo del tipo de


entidad para el que el conjunto de
entidades contiene las instancias.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento EntitySet . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer con tres elementos EntitySet :

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="[Link]" />
<EntitySet Name="Publishers" EntityType="[Link]" />
<EntitySet Name="Authors" EntityType="[Link]" />
<AssociationSet Name="PublishedBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>

Es posible definir varios conjuntos de entidades por tipo (MEST). En el ejemplo siguiente se define un
contenedor de entidades con dos conjuntos de entidades para el tipo de entidad book :

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Books" EntityType="[Link]" />
<EntitySet Name="FictionBooks" EntityType="[Link]" />
<EntitySet Name="Publishers" EntityType="[Link]" />
<EntitySet Name="Authors" EntityType="[Link]" />
<AssociationSet Name="PublishedBy" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="BookAuthor" Association="[Link]">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>
EntityType (Elemento) (CSDL)
El elemento EntityType representa la estructura de un concepto de nivel superior, como un cliente o un pedido,
en un modelo conceptual. Un tipo de entidad es una plantilla para las instancias de los tipos de entidad de una
aplicación. Cada plantilla contiene la información siguiente:
Un nombre único. (Requerido)
Una clave de entidad definida por una o varias propiedades. (Requerido)
Propiedades para el almacenamiento de datos. (Opcional).
Propiedades de navegación que permiten desplazarse de un extremo de la asociación al otro. (Opcional).
En una aplicación, una instancia de un tipo de entidad representa un objeto específico (como un cliente o un
pedido concreto). Cada una de las instancias de un tipo de entidad debe tener una clave de entidad única dentro
de un conjunto de entidades.
Dos instancias de tipo de entidad se consideran iguales solo si son del mismo tipo y los valores de sus claves de
entidad son idénticos.
Un elemento EntityType puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Key (cero o un elemento)
Property (cero o más elementos)
NavigationProperty (cero o más elementos)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntityType .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del tipo de entidad.

BaseType No El nombre de otro tipo de entidad que


sea el tipo base del tipo de entidad
que se define.

Descripción No True o false , dependiendo de si el tipo


de entidad es un tipo abstracto.

OpenType No True o false , dependiendo de si el


tipo de entidad es un tipo de entidad
abierto.
[!NOTE]

> el atributo OpenType solo es


aplicable a los tipos de entidad que se
definen en los modelos conceptuales
que se usan con el Data Services
[Link].
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento EntityType . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType con tres elementos Proper ty y dos elementos
NavigationProper ty :

<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="[Link]"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="[Link]"
FromRole="Book" ToRole="Author" />
</EntityType>

Elemento EnumType (CSDL)


El elemento enumType representa un tipo enumerado.
Un elemento enumType puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Miembro (cero o más elementos)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento enumType .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del tipo de entidad.

IsFlags No True o false , dependiendo de si el tipo


de enumeración se puede usar como
un conjunto de marcas. El valor
predeterminado es false.

UnderlyingType No EDM. Byte , EDM. Int16 , EDM.


Int32 , EDM. Int64 o EDM. SByte
que define el intervalo de valores del
tipo. El tipo subyacente
predeterminado de los elementos de
enumeración es EDM. Int32. .
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento enumType . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento enumType con tres elementos member :

<EnumType Name="Color" IsFlags=”false” UnderlyingTyp=”[Link]”>


<Member Name="Red" />
<Member Name="Green" />
<Member Name="Blue" />
</EntityType>

Function (Elemento) (CSDL)


El elemento function en el lenguaje de definición de esquemas conceptuales (CSDL) se utiliza para definir o
declarar funciones en el modelo conceptual. Una función se define utilizando un elemento DefiningExpression.
Un elemento function puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Parameter (cero o más elementos)
DefiningExpression (cero o un elemento)
ReturnType (función) (cero o un elemento)
Elementos Annotation (cero o más elementos)
Un tipo de valor devuelto para una función debe especificarse con el elemento ReturnType (function) o el
atributo ReturnType (consulte a continuación), pero no ambos. Los tipos de valores devueltos posibles son
EdmSimpleType, tipo de entidad, tipo complejo, tipo de fila o tipo ref (o una colección de uno de estos tipos).
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento de función .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre de la función.

ReturnType No El tipo devuelto por la función.


NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento de función . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se usa un elemento function para definir una función que devuelve el número de años
transcurridos desde que se contrató a un instructor.

<Function Name="YearsSince" ReturnType="Edm.Int32">


<Parameter Name="date" Type="[Link]" />
<DefiningExpression>
Year(CurrentDateTime()) - Year(date)
</DefiningExpression>
</Function>

FunctionImport (Elemento) (CSDL)


El elemento FunctionImpor t del lenguaje de definición de esquemas conceptuales (CSDL) representa una
función que se define en el origen de datos, pero que está disponible para los objetos a través del modelo
conceptual. Por ejemplo, un elemento Function del modelo de almacenamiento se puede usar para representar
un procedimiento almacenado en una base de datos. Un elemento FunctionImpor t del modelo conceptual
representa la función correspondiente en una aplicación Entity Framework y se asigna a la función de modelo
de almacenamiento mediante el elemento FunctionImportMapping. Cuando se llama a la función en la
aplicación, el procedimiento almacenado correspondiente se ejecuta en la base de datos.
El elemento FunctionImpor t puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Parameter (cero o más elementos)
Elementos Annotation (cero o más elementos)
ReturnType (FunctionImport) (cero o más elementos permitidos)
Debe definirse un elemento de parámetro para cada parámetro aceptado por la función.
Un tipo de valor devuelto para una función debe especificarse con el elemento ReturnType (FunctionImport) o
el atributo ReturnType (consulte a continuación), pero no ambos. El valor de tipo devuelto debe ser una
colección de EdmSimpleType, EntityType o ComplexType.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento FunctionImpor t .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre de la función importada.


N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

ReturnType No Tipo devuelto por la función. No utilice


este atributo si la función no devuelve
un valor. De lo contrario, el valor debe
ser una colección de ComplexType,
EntityType o EDMSimpleType.

EntitySet No Si la función devuelve una colección de


tipos de entidad, el valor de EntitySet
debe ser el conjunto de entidades al
que pertenece la colección. De lo
contrario, el atributo EntitySet no
debe usarse.

IsComposable No Si el valor se establece en true, la


función es ajustable (función con
valores de tabla) y se puede usar en
una consulta [Link] valor
predeterminado es false .

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
FunctionImpor t . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de
nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos
idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento FunctionImpor t que acepta un parámetro y devuelve una
colección de tipos de entidad:

<FunctionImport Name="GetStudentGrades"
EntitySet="StudentGrade"
ReturnType="Collection([Link])">
<Parameter Name="StudentID" Mode="In" Type="Int32" />
</FunctionImport>

Key (Elemento) (CSDL)


El elemento key es un elemento secundario del elemento EntityType y define una clave de entidad (una
propiedad o un conjunto de propiedades de un tipo de entidad que determinan la identidad). Las propiedades
que constituyen una entidad se eligen en tiempo de diseño. Los valores de las propiedades de clave de entidad
deben identificar de forma inequívoca en tiempo de ejecución una instancia de tipo de entidad dentro de un
conjunto de entidades. Las propiedades que constituyen una clave de entidad se deben elegir de tal forma que
garanticen la unicidad de las instancias de un conjunto de entidades. El elemento key define una clave de
entidad haciendo referencia a una o más de las propiedades de un tipo de entidad.
El elemento key puede tener los siguientes elementos secundarios:
PropertyRef (uno o varios elementos)
Elementos Annotation (cero o más elementos)
Atributos aplicables
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento key .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML
reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.
Ejemplo
En el ejemplo siguiente se define un tipo de entidad denominado book . La clave de entidad se define haciendo
referencia a la propiedad ISBN del tipo de entidad.

<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="[Link]"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="[Link]"
FromRole="Book" ToRole="Author" />
</EntityType>

La propiedad ISBN es una buena elección para la clave de entidad porque un número de libro estándar
internacional (ISBN) identifica de forma única un libro.
En el ejemplo siguiente se muestra un tipo de entidad (autor ) que tiene una clave de entidad que consta de dos
propiedades, nombre y Dirección .

<EntityType Name="Author">
<Key>
<PropertyRef Name="Name" />
<PropertyRef Name="Address" />
</Key>
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="String" Name="Address" Nullable="false" />
<NavigationProperty Name="Books" Relationship="[Link]"
FromRole="Author" ToRole="Book" />
</EntityType>

Usar el nombre y la Dirección de la clave de entidad es una opción razonable, ya que es poco probable que
dos autores del mismo nombre se encuentren en la misma dirección. Sin embargo, esta opción no garantiza por
completo la existencia de claves de entidad únicas en un conjunto de entidades. En este caso, se recomienda
agregar una propiedad, como AuthorId , que pueda usarse para identificar de forma única un autor.

Elemento Member (CSDL)


El elemento member es un elemento secundario del elemento enumType y define un miembro del tipo
enumerado.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento FunctionImpor t .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre del miembro.

Valor No Valor del miembro. De forma


predeterminada, el primer miembro
tiene el valor 0 y el valor de cada
enumerador sucesivo se incrementa en
1. Pueden existir varios miembros con
los mismos valores.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
FunctionImpor t . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de
nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos
idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento enumType con tres elementos member :

<EnumType Name="Color">
<Member Name="Red" Value=”1”/>
<Member Name="Green" Value=”3” />
<Member Name="Blue" Value=”5”/>
</EntityType>

NavigationProperty (Elemento) (CSDL)


Un elemento NavigationProper ty define una propiedad de navegación, que proporciona una referencia al
otro extremo de una asociación. A diferencia de las propiedades definidas con el elemento Property, las
propiedades de navegación no definen la forma ni las características de los datos. Proporcionan una manera de
desplazarse por una asociación entre dos tipos de entidad.
Las propiedades de navegación son opcionales en los dos tipos de entidad de los extremos de una asociación. Si
define una propiedad de navegación en un tipo de entidad del extremo de una asociación, no tiene que definir
una propiedad de navegación en el tipo de entidad del otro extremo de la asociación.
El tipo de datos devuelto por una propiedad de navegación viene determinado por la multiplicidad de su
extremo remoto de la asociación. Por ejemplo, supongamos que existe una propiedad de navegación,
OrdersNavProp , en un tipo de entidad Customer y navega por una asociación uno a varios entre Customer
y Order . Dado que el extremo remoto de la Asociación para la propiedad de navegación tiene una multiplicidad
de varios ( * ), su tipo de datos es una colección (de orden ). Del mismo modo, si una propiedad de navegación,
CustomerNavProp , existe en el tipo de entidad Order , su tipo de datos sería Customer , ya que la
multiplicidad del extremo remoto es uno (1).
Un elemento NavigationProper ty puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento NavigationProper ty .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre de la propiedad de


navegación.

Relación Sí Nombre de una asociación que se


encuentra dentro del ámbito del
modelo.

ToRole Sí Extremo de la asociación en el que


finaliza la navegación. El valor del
atributo ToRole debe ser el mismo que
el valor de uno de los atributos de rol
definidos en uno de los extremos de la
Asociación (definidos en el elemento
AssociationEnd).

FromRole Sí Extremo de la asociación desde el que


comienza la navegación. El valor del
atributo FromRole debe ser el mismo
que el valor de uno de los atributos de
rol definidos en uno de los extremos
de la Asociación (definidos en el
elemento AssociationEnd).

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
NavigationProper ty . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de
nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos
idénticos.

Ejemplo
En el ejemplo siguiente se define un tipo de entidad (book ) con dos propiedades de navegación (PublishedBy
y WrittenBy ):
<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="[Link]"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="[Link]"
FromRole="Book" ToRole="Author" />
</EntityType>

OnDelete (Elemento) (CSDL)


El elemento aldelete en el lenguaje de definición de esquemas conceptuales (CSDL) define el comportamiento
que está conectado a una asociación. Si el atributo Action se establece en Cascade en un extremo de una
asociación, los tipos de entidad relacionados en el otro extremo de la asociación se eliminan cuando se elimina
el tipo de entidad del primer extremo. Si la asociación entre dos tipos de entidad es una relación entre clave
principal y clave principal, se elimina un objeto dependiente cargado cuando se elimina el objeto de entidad de
seguridad del otro extremo de la asociación, independientemente de la especificación de la eliminación .

NOTE
El elemento aldelete solo afecta al comportamiento de tiempo de ejecución de una aplicación; no afecta al
comportamiento en el origen de datos. El comportamiento definido en el origen de datos debe ser igual que el definido
en la aplicación.

Un elemento aldelete puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento aleliminar .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Acción Sí Cascade o None . Si en cascada , los


tipos de entidad dependientes se
eliminarán cuando se elimine el tipo de
entidad de entidad de seguridad. Si no
hay ninguno , los tipos de entidad
dependientes no se eliminarán cuando
se elimine el tipo de entidad de
entidad de seguridad.
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Association .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que define la Asociación CustomerOrders . El
elemento aleliminar indica que todos los pedidos relacionados con un cliente determinado y que se han
cargado en el ObjectContext se eliminarán cuando se elimine el cliente .

<Association Name="CustomerOrders">
<End Type="[Link]" Role="Customer" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Type="[Link]" Role="Order" Multiplicity="*" />
</Association>

Parameter (Elemento) (CSDL)


El elemento Parameter en el lenguaje de definición de esquemas conceptuales (CSDL) puede ser un elemento
secundario del elemento FunctionImport o del elemento function.
Aplicación para el elemento FunctionImport
Un elemento Parameter (como elemento secundario del elemento FunctionImpor t ) se usa para definir los
parámetros de entrada y salida para las importaciones de funciones que se declaran en CSDL.
El elemento Parameter puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Parameter .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del parámetro.

Tipo Sí El tipo de parámetro. El valor tiene que


ser un tipo EDMSimpleType o un
tipo complejo que se encuentre dentro
del ámbito del modelo.

Modo No In , out o INOUT dependiendo de si el


parámetro es un parámetro de
entrada, de salida o de entrada/salida.

MaxLength No La longitud máxima permitida del


parámetro.
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Precisión No La precisión del parámetro.

Escala No La escala del parámetro.

SRID No Identificador de referencia del sistema


espacial. Válido solo para los
parámetros de los tipos espaciales.
Para obtener más información, vea
SRID y SRID (SQL Server).

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Parameter . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento FunctionImpor t con un elemento secundario Parameter . La
función acepta un parámetro de entrada y devuelve una colección de tipos de entidad.

<FunctionImport Name="GetStudentGrades"
EntitySet="StudentGrade"
ReturnType="Collection([Link])">
<Parameter Name="StudentID" Mode="In" Type="Int32" />
</FunctionImport>

Aplicación para el elemento Function


Un elemento Parameter (como elemento secundario del elemento function ) define los parámetros de las
funciones que se definen o declaran en un modelo conceptual.
El elemento Parameter puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
CollectionType (cero o un elemento)
ReferenceType (cero o un elemento)
RowType (cero o un elemento)

NOTE
Solo uno de los elementos CollectionType , referenceType o RowType puede ser un elemento secundario de un
elemento Proper ty .

Elementos Annotation (cero o más elementos)


NOTE
Los elementos de anotación deben aparecer después de todos los demás elementos secundarios. Los elementos
Annotation solo se permiten en CSDL V2 y versiones posteriores.

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Parameter .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del parámetro.

Tipo No El tipo de parámetro. Un parámetro


puede ser de cualquiera de los
siguientes tipos (o colecciones de estos
tipos):
EdmSimpleType
tipo de entidad
tipo complejo
tipo de fila
tipo de referencia

Admisión de valores NULL No True (valor predeterminado) o false ,


dependiendo de si la propiedad puede
tener un valor null .

DefaultValue No Valor predeterminado de la propiedad.

MaxLength No La longitud máxima del valor de la


propiedad.

FixedLength No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena de longitud fija.

Precisión No Precisión del valor de propiedad.

Escala No Escala del valor de propiedad.

SRID No Identificador de referencia del sistema


espacial. Solo es válido para las
propiedades de los tipos espaciales.
Para obtener más información, vea
SRID y SRID (SQL Server).

Unicode No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena Unicode.

Intercalación No Cadena que especifica la secuencia de


intercalación que se usará en el origen
de datos.
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Parameter . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento de función que usa un elemento secundario Parameter para
definir un parámetro de función.

<Function Name="GetYearsEmployed" ReturnType="Edm.Int32">


<Parameter Name="Instructor" Type="[Link]" />
<DefiningExpression>
Year(CurrentDateTime()) - Year(cast([Link] as DateTime))
</DefiningExpression>
</Function>

Principal (Elemento) (CSDL)


El elemento principal del lenguaje de definición de esquemas conceptuales (CSDL) es un elemento secundario
del elemento ReferentialConstraint que define el extremo principal de una restricción referencial. Un elemento
ReferentialConstraint define una funcionalidad similar a una restricción de integridad referencial en una base
de datos relacional. Del mismo modo que una columna (o columnas) de una tabla de base de datos puede hacer
referencia a la clave principal de otra tabla, una propiedad (o propiedades) de un tipo de entidad puede hacer
referencia a la clave de entidad de otro tipo de entidad. El tipo de entidad al que se hace referencia se denomina
extremo principal de la restricción. El tipo de entidad que hace referencia al extremo principal se denomina
extremo dependiente de la restricción. Los elementos Proper tyRef se utilizan para especificar las claves a las
que hace referencia el extremo dependiente.
El elemento principal puede tener los elementos secundarios siguientes (en el orden mostrado):
PropertyRef (uno o varios elementos)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento principal .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Rol Sí Nombre del tipo de entidad del


extremo principal de la asociación.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento principal . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra un elemento ReferentialConstraint que forma parte de la definición de la
Asociación PublishedBy . La propiedad ID del tipo de entidad Publisher constituye el extremo principal de la
restricción referencial.

<Association Name="PublishedBy">
<End Type="[Link]" Role="Book" Multiplicity="*" >
</End>
<End Type="[Link]" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>

Property (Elemento) (CSDL)


El elemento Proper ty del lenguaje de definición de esquemas conceptuales (CSDL) puede ser un elemento
secundario del elemento EntityType, el elemento complexType o el elemento RowType.
Aplicaciones de elemento EntityType y ComplexType
Los elementos de propiedad (como elementos secundarios de los elementos EntityType o complexType )
definen la forma y las características de los datos que contendrá una instancia de tipo de entidad o una instancia
de tipo complejo. Las propiedades en un modelo conceptual son análogas a las propiedades que se definen en
una clase. Del mismo modo que las propiedades en una clase definen la forma de la clase y proporcionan
información sobre los objetos, las propiedades en un modelo conceptual definen la forma de un tipo de entidad
y proporcionan información sobre las instancias del tipo de entidad.
El elemento Proper ty puede tener los elementos secundarios siguientes (en el orden mostrado):
Elemento Documentation (cero o más elementos)
Elementos Annotation (cero o más elementos)
Las siguientes caras se pueden aplicar a un elemento Proper ty : Nullable , DefaultValue , MaxLength ,
FixedLength , Precision , Scale , Unicode , collation , ConcurrencyMode . Las facetas son atributos XML que
proporcionan información sobre cómo los valores de propiedad se almacenan en el almacén de datos.

NOTE
Las caras solo se pueden aplicar a propiedades de tipo EDMSimpleType .

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Proper ty .
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la propiedad.

Tipo Sí El tipo de valor de la propiedad. El tipo


de valor de la propiedad debe ser un
tipo EDMSimpleType o un tipo
complejo (indicado mediante un
nombre completo) que se encuentre
dentro del ámbito del modelo.

Admisión de valores NULL No True (el valor predeterminado) o


False , según si la propiedad puede
tener un valor nulo.
[!NOTE]

> en el CSDL v1, una propiedad de


tipo complejo debe tener
Nullable="False" .

DefaultValue No Valor predeterminado de la propiedad.

MaxLength No La longitud máxima del valor de la


propiedad.

FixedLength No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena de longitud fija.

Precisión No Precisión del valor de propiedad.

Escala No Escala del valor de propiedad.

SRID No Identificador de referencia del sistema


espacial. Solo es válido para las
propiedades de los tipos espaciales.
Para obtener más información, vea
SRID y SRID (SQL Server).

Unicode No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena Unicode.

Intercalación No Cadena que especifica la secuencia de


intercalación que se usará en el origen
de datos.

ConcurrencyMode No None (el valor predeterminado) o


Fixed . Si el valor se establece en
Fixed , el valor de la propiedad se
usará en las comprobaciones de
simultaneidad optimista.
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento de propiedad .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType con tres elementos Proper ty :

<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="[Link]"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="[Link]"
FromRole="Book" ToRole="Author" />
</EntityType>

En el ejemplo siguiente se muestra un elemento complexType con cinco elementos Proper ty :

<ComplexType Name="Address" >


<Property Type="String" Name="StreetAddress" Nullable="false" />
<Property Type="String" Name="City" Nullable="false" />
<Property Type="String" Name="StateOrProvince" Nullable="false" />
<Property Type="String" Name="Country" Nullable="false" />
<Property Type="String" Name="PostalCode" Nullable="false" />
</ComplexType>

Aplicación de elemento RowType


Los elementos de propiedad (como elementos secundarios de un elemento RowType ) definen la forma y las
características de los datos que se pueden pasar o devolver desde una función definida por el modelo.
El elemento Proper ty puede tener exactamente uno de los siguientes elementos secundarios:
CollectionType
ReferenceType
RowType
El elemento Proper ty puede tener cualquier número de elementos Annotation secundarios.

NOTE
Los elementos Annotation solo se permiten en CSDL V2 y versiones posteriores.

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Proper ty .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la propiedad.

Tipo Sí El tipo de valor de la propiedad.

Admisión de valores NULL No True (el valor predeterminado) o


False , según si la propiedad puede
tener un valor nulo.
[!NOTE]

> en CSDL v1, una propiedad de tipo


complejo debe tener
Nullable="False" .

DefaultValue No Valor predeterminado de la propiedad.

MaxLength No La longitud máxima del valor de la


propiedad.

FixedLength No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena de longitud fija.

Precisión No Precisión del valor de propiedad.

Escala No Escala del valor de propiedad.

SRID No Identificador de referencia del sistema


espacial. Solo es válido para las
propiedades de los tipos espaciales.
Para obtener más información, vea
SRID y SRID (SQL Server).

Unicode No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena Unicode.

Intercalación No Cadena que especifica la secuencia de


intercalación que se usará en el origen
de datos.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento de propiedad .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestran los elementos de propiedad que se utilizan para definir la forma del tipo
de valor devuelto de una función definida por el modelo.

<Function Name="LastNamesAfter">
<Parameter Name="someString" Type="[Link]" />
<ReturnType>
<CollectionType>
<RowType>
<Property Name="FirstName" Type="[Link]" Nullable="false" />
<Property Name="LastName" Type="[Link]" Nullable="false" />
</RowType>
</CollectionType>
</ReturnType>
<DefiningExpression>
SELECT VALUE ROW([Link], [Link])
FROM [Link] AS p
WHERE [Link] &gt;= somestring
</DefiningExpression>
</Function>

PropertyRef (Elemento) (CSDL)


El elemento Proper tyRef en el lenguaje de definición de esquemas conceptuales (CSDL) hace referencia a una
propiedad de un tipo de entidad para indicar que la propiedad realizará uno de los roles siguientes:
Parte de la clave de la entidad (una propiedad o un conjunto de propiedades de un tipo de entidad que
determinan la identidad). Se pueden usar uno o varios elementos Proper tyRef para definir una clave de
entidad.
El extremo dependiente o principal de una restricción referencial.
El elemento Proper tyRef solo puede tener elementos Annotation (cero o más) como elementos secundarios.

NOTE
Los elementos Annotation solo se permiten en CSDL V2 y versiones posteriores.

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Proper tyRef .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre de la propiedad a la que se


hace referencia.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Proper tyRef .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.
Ejemplo
En el ejemplo siguiente se define un tipo de entidad (book ). La clave de entidad se define haciendo referencia a
la propiedad ISBN del tipo de entidad.

<EntityType Name="Book">
<Key>
<PropertyRef Name="ISBN" />
</Key>
<Property Type="String" Name="ISBN" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" />
<Property Type="Decimal" Name="Revision" Nullable="false" Precision="29" Scale="29" />
<NavigationProperty Name="Publisher" Relationship="[Link]"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="[Link]"
FromRole="Book" ToRole="Author" />
</EntityType>

En el ejemplo siguiente, se usan dos elementos Proper tyRef para indicar que dos propiedades (ID y
PublisherId ) son los extremos principal y dependiente de una restricción referencial.

<Association Name="PublishedBy">
<End Type="[Link]" Role="Book" Multiplicity="*" >
</End>
<End Type="[Link]" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>

ReferenceType (Elemento) (CSDL)


El elemento referenceType en el lenguaje de definición de esquemas conceptuales (CSDL) especifica una
referencia a un tipo de entidad. El elemento referenceType puede ser un elemento secundario de los siguientes
elementos:
ReturnType (función)
Parámetro
CollectionType
El elemento referenceType se utiliza al definir un parámetro o un tipo de valor devuelto para una función.
Un elemento referenceType puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento referenceType .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Tipo Sí Nombre del tipo de entidad al que se


hace referencia.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento referenceType
. Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra el elemento referenceType que se usa como elemento secundario de un
elemento Parameter en una función definida por el modelo que acepta una referencia a un tipo de entidad
Person :

<Function Name="GetYearsEmployed" ReturnType="Edm.Int32">


<Parameter Name="instructor">
<ReferenceType Type="[Link]" />
</Parameter>
<DefiningExpression>
Year(CurrentDateTime()) - Year(cast([Link] as DateTime))
</DefiningExpression>
</Function>

En el ejemplo siguiente se muestra el elemento referenceType usado como elemento secundario de un


elemento ReturnType (function) en una función definida por el modelo que devuelve una referencia a un tipo
de entidad Person :

<Function Name="GetPersonReference">
<Parameter Name="p" Type="[Link]" />
<ReturnType>
<ReferenceType Type="[Link]" />
</ReturnType>
<DefiningExpression>
REF(p)
</DefiningExpression>
</Function>

ReferentialConstraint (Elemento) (CSDL)


Un elemento ReferentialConstraint en el lenguaje de definición de esquemas conceptuales (CSDL) define una
funcionalidad similar a una restricción de integridad referencial en una base de datos relacional. Del mismo
modo que una columna (o columnas) de una tabla de base de datos puede hacer referencia a la clave principal
de otra tabla, una propiedad (o propiedades) de un tipo de entidad puede hacer referencia a la clave de entidad
de otro tipo de entidad. El tipo de entidad al que se hace referencia se denomina extremo principal de la
restricción. El tipo de entidad que hace referencia al extremo principal se denomina extremo dependiente de la
restricción.
Si una clave externa que se expone en un tipo de entidad hace referencia a una propiedad en otro tipo de
entidad, el elemento ReferentialConstraint define una asociación entre los dos tipos de entidad. Dado que el
elemento ReferentialConstraint proporciona información sobre cómo se relacionan dos tipos de entidad, no
es necesario ningún elemento AssociationSetMapping correspondiente en el lenguaje de especificación de
asignaciones (MSL). Una asociación entre dos tipos de entidad que no tienen claves externas expuestas debe
tener un elemento AssociationSetMapping correspondiente para asignar información de asociación al origen
de datos.
Si una clave externa no se expone en un tipo de entidad, el elemento ReferentialConstraint solo puede definir
una restricción PRIMARY KEY-PRIMARY KEY entre el tipo de entidad y otro tipo de entidad.
Un elemento ReferentialConstraint puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Principal (exactamente un elemento)
Dependent (exactamente un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
El elemento ReferentialConstraint puede tener cualquier número de atributos de anotación (atributos XML
personalizados). Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de
nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres
completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra un elemento ReferentialConstraint que se usa como parte de la definición
de la Asociación PublishedBy .

<Association Name="PublishedBy">
<End Type="[Link]" Role="Book" Multiplicity="*" >
</End>
<End Type="[Link]" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>

ReturnType (function) (elemento) (CSDL)


El elemento ReturnType (function) en el lenguaje de definición de esquemas conceptuales (CSDL) especifica el
tipo de valor devuelto para una función que se define en un elemento de función. Un tipo de valor devuelto de
función también se puede especificar con un atributo ReturnType .
Los tipos de valor devuelto pueden ser cualquier EdmSimpleType , tipo de entidad, tipo complejo, tipo de fila,
tipo de referencia o una colección de uno de estos tipos.
El tipo de valor devuelto de una función se puede especificar con el atributo Type del elemento ReturnType
(function) o con uno de los siguientes elementos secundarios:
CollectionType
ReferenceType
RowType

NOTE
Un modelo no se validará si especifica un tipo de valor devuelto de función con el atributo Type del elemento
ReturnType (function) y uno de los elementos secundarios.

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento ReturnType (function).

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

ReturnType No El tipo devuelto por la función.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento ReturnType
(function). Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML
reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se usa un elemento function para definir una función que devuelve el número de años
que un libro se ha impreso. Tenga en cuenta que el tipo de valor devuelto se especifica mediante el atributo
Type de un elemento ReturnType (function).

<Function Name="GetYearsInPrint">
<ReturnType Type=="Edm.Int32">
<Parameter Name="book" Type="[Link]" />
<DefiningExpression>
Year(CurrentDateTime()) - Year(cast([Link] as DateTime))
</DefiningExpression>
</Function>

ReturnType (FunctionImport) (elemento) (CSDL)


El elemento ReturnType (FunctionImport) en el lenguaje de definición de esquemas conceptuales (CSDL)
especifica el tipo de valor devuelto para una función que se define en un elemento FunctionImport. Un tipo de
valor devuelto de función también se puede especificar con un atributo ReturnType .
Los tipos devueltos pueden ser cualquier colección de tipo de entidad, tipo complejo o EdmSimpleType .
El tipo de valor devuelto de una función se especifica con el atributo Type del elemento ReturnType
(FunctionImport).
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento ReturnType (FunctionImport).

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Tipo No Tipo devuelto por la función. El valor


debe ser una colección de
ComplexType, EntityType o
EDMSimpleType.

EntitySet No Si la función devuelve una colección de


tipos de entidad, el valor de EntitySet
debe ser el conjunto de entidades al
que pertenece la colección. De lo
contrario, el atributo EntitySet no
debe usarse.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento ReturnType
(FunctionImport). Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres
XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se usa una FunctionImpor t que devuelve libros y publicadores. Tenga en cuenta que la
función devuelve dos conjuntos de resultados y, por tanto, se especifican dos elementos ReturnType
(FunctionImport).

<FunctionImport Name="GetBooksAndPublishers">
<ReturnType Type=="Collection([Link] )" EntitySet=”Books”>
<ReturnType Type=="Collection([Link])" EntitySet=”Publishers”>
</FunctionImport>

RowType (Elemento) (CSDL)


Un elemento RowType en el lenguaje de definición de esquemas conceptuales (CSDL) define una estructura sin
nombre como un parámetro o tipo de valor devuelto para una función definida en el modelo conceptual.
Un elemento RowType puede ser el elemento secundario de los siguientes elementos:
CollectionType
Parámetro
ReturnType (función)
Un elemento RowType puede tener los elementos secundarios siguientes (en el orden mostrado):
Property (uno o varios)
Elementos Annotation (cero o más)
Atributos aplicables
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
RowType . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de
nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres
completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra una función definida por el modelo que utiliza un elemento CollectionType
para especificar que la función devuelve una colección de filas (tal y como se especifica en el elemento
RowType ).

<Function Name="LastNamesAfter">
<Parameter Name="someString" Type="[Link]" />
<ReturnType>
<CollectionType>
<RowType>
<Property Name="FirstName" Type="[Link]" Nullable="false" />
<Property Name="LastName" Type="[Link]" Nullable="false" />
</RowType>
</CollectionType>
</ReturnType>
<DefiningExpression>
SELECT VALUE ROW([Link], [Link])
FROM [Link] AS p
WHERE [Link] &gt;= somestring
</DefiningExpression>
</Function>

Schema (Elemento) (CSDL)


El elemento Schema es el elemento raíz de una definición de modelo conceptual. Contiene las definiciones para
los objetos, las funciones y los contenedores que conforman un modelo conceptual.
El elemento Schema puede contener cero o más de los siguientes elementos secundarios:
Uso
EntityContainer
EntityType
EnumType
Asociación
ComplexType
Función
Un elemento Schema puede contener cero o un elemento Annotation.

NOTE
El elemento de función y los elementos de anotación solo se permiten en CSDL V2 y versiones posteriores.
El elemento Schema usa el atributo namespace para definir el espacio de nombres para el tipo de entidad, el
tipo complejo y los objetos de Asociación de un modelo conceptual. Dentro de un espacio de nombres, no
puede haber dos objetos con el mismo nombre. Los espacios de nombres pueden abarcar varios elementos de
esquema y varios archivos. CSDL.
Un espacio de nombres del modelo conceptual es diferente del espacio de nombres XML del elemento Schema
. Un espacio de nombres del modelo conceptual (tal y como se define en el atributo de espacio de nombres )
es un contenedor lógico para tipos de entidad, tipos complejos y tipos de asociación. El espacio de nombres
XML (indicado por el atributo xmlns ) de un elemento Schema es el espacio de nombres predeterminado para
los elementos secundarios y los atributos del elemento Schema . Los espacios de nombres XML del formulario
[Link] (donde YYYY y mm representan un año y un mes
respectivamente) se reservan para CSDL. No puede haber elementos y atributos personalizados en espacios de
nombres que tengan este formato.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Schema .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Espacio de nombres Sí El espacio de nombres del modelo


conceptual. El valor del atributo
namespace se usa para formar el
nombre completo de un tipo. Por
ejemplo, si un EntityType
denominado Customer está en el
espacio de nombres simple. example.
Model, el nombre completo del
EntityType es SimpleExampleModel.
Customer.
Las siguientes cadenas no se pueden
usar como el valor del atributo
namespace : System , Transient o
EDM . El valor del atributo
namespace no puede ser el mismo
que el valor del atributo namespace
del elemento Schema de SSDL.

Alias No Un identificador usado en lugar del


nombre del espacio de nombres. Por
ejemplo, si un EntityType
denominado Customer está en el
espacio de nombres simple. example.
Model y el valor del atributo alias es
Model, puede usar Model. Customer
como nombre completo del
EntityType.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Schema . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra un elemento Schema que contiene un elemento EntityContainer , dos
elementos EntityType y un elemento Association .

<Schema xmlns="[Link]
xmlns:cg="[Link]
xmlns:store="[Link]
Namespace="ExampleModel" Alias="Self">
<EntityContainer Name="ExampleModelContainer">
<EntitySet Name="Customers"
EntityType="[Link]" />
<EntitySet Name="Orders" EntityType="[Link]" />
<AssociationSet
Name="CustomerOrder"
Association="[Link]">
<End Role="Customer" EntitySet="Customers" />
<End Role="Order" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>
<EntityType Name="Customer">
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Type="Int32" Name="CustomerId" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
<NavigationProperty
Name="Orders"
Relationship="[Link]"
FromRole="Customer" ToRole="Order" />
</EntityType>
<EntityType Name="Order">
<Key>
<PropertyRef Name="OrderId" />
</Key>
<Property Type="Int32" Name="OrderId" Nullable="false" />
<Property Type="Int32" Name="ProductId" Nullable="false" />
<Property Type="Int32" Name="Quantity" Nullable="false" />
<NavigationProperty
Name="Customer"
Relationship="[Link]"
FromRole="Order" ToRole="Customer" />
<Property Type="Int32" Name="CustomerId" Nullable="false" />
</EntityType>
<Association Name="CustomerOrders">
<End Type="[Link]"
Role="Customer" Multiplicity="1" />
<End Type="[Link]"
Role="Order" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customer">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Order">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
</Schema>

TypeRef (Elemento) (CSDL)


El elemento TypeRef en el lenguaje de definición de esquemas conceptuales (CSDL) proporciona una referencia
a un tipo con nombre existente. El elemento TypeRef puede ser un elemento secundario del elemento
CollectionType, que se usa para especificar que una función tiene una colección como parámetro o tipo de valor
devuelto.
Un elemento TypeRef puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento TypeRef . Tenga en cuenta que
los atributos DefaultValue , MaxLength , FixedLength , Precision , Scale , Unicode y collation solo se aplican
a EDMSimpleTypes .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Tipo No El nombre del tipo al que se hace


referencia.

Admisión de valores NULL No True (el valor predeterminado) o


False , según si la propiedad puede
tener un valor nulo.
[!NOTE]

> en CSDL v1, una propiedad de tipo


complejo debe tener
Nullable="False" .

DefaultValue No Valor predeterminado de la propiedad.

MaxLength No La longitud máxima del valor de la


propiedad.

FixedLength No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena de longitud fija.

Precisión No Precisión del valor de propiedad.

Escala No Escala del valor de propiedad.

SRID No Identificador de referencia del sistema


espacial. Solo es válido para las
propiedades de los tipos espaciales.
Para obtener más información, vea
SRID y SRID (SQL Server).

Unicode No True o false , dependiendo de si el


valor de la propiedad se almacenará
como una cadena Unicode.

Intercalación No Cadena que especifica la secuencia de


intercalación que se usará en el origen
de datos.
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento CollectionType
. Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra una función definida por el modelo que usa el elemento TypeRef (como
elemento secundario de un elemento CollectionType ) para especificar que la función acepta una colección de
tipos de entidad Depar tment .

<Function Name="GetAvgBudget">
<Parameter Name="Departments">
<CollectionType>
<TypeRef Type="[Link]"/>
</CollectionType>
</Parameter>
<ReturnType Type="Collection([Link])"/>
<DefiningExpression>
SELECT VALUE AVG([Link]) FROM Departments AS d
</DefiningExpression>
</Function>

Using (Elemento) (CSDL)


El elemento using del lenguaje de definición de esquemas conceptuales (CSDL) importa el contenido de un
modelo conceptual que existe en un espacio de nombres diferente. Al establecer el valor del atributo de espacio
de nombres , puede hacer referencia a los tipos de entidad, tipos complejos y tipos de asociación que se
definen en otro modelo conceptual. Más de un elemento using puede ser un elemento secundario de un
elemento Schema .

NOTE
El elemento using en CSDL no funciona exactamente igual que una instrucción using en un lenguaje de programación.
Al importar un espacio de nombres con una instrucción using en un lenguaje de programación, no afecta a los objetos
del espacio de nombres original. En CSDL, un espacio de nombres importado puede contener un tipo de entidad derivado
de un tipo de entidad del espacio de nombres original. Esto puede afectar a los conjuntos de entidades declarados en el
espacio de nombres original.

El elemento using puede tener los siguientes elementos secundarios:


Documentation (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento using .
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Espacio de nombres Sí Nombre del espacio de nombres


importado.

Alias Sí Un identificador usado en lugar del


nombre del espacio de nombres.
Aunque este atributo es obligatorio,
no es necesario usarlo en lugar del
nombre del espacio de nombres para
calificar los nombres de los objetos.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento using . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra el elemento using que se usa para importar un espacio de nombres que se
define en otro lugar. Tenga en cuenta que el espacio de nombres para el elemento de esquema mostrado es
BooksModel . La Address propiedad del Publisher EntityType es un tipo complejo que se define en el
ExtendedBooksModel espacio de nombres (importado con el elemento using ).

<Schema xmlns="[Link]
xmlns:cg="[Link]
xmlns:store="[Link]
Namespace="BooksModel" Alias="Self">

<Using Namespace="[Link]" Alias="BMExt" />

<EntityContainer Name="BooksContainer" >


<EntitySet Name="Publishers" EntityType="[Link]" />
</EntityContainer>

<EntityType Name="Publisher">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="[Link]" Name="Address" Nullable="false" />
</EntityType>

</Schema>

Atributos de anotación (CSDL)


En el lenguaje de definición de esquemas conceptuales (CSDL), los atributos de anotación son atributos XML
personalizados del modelo conceptual. Además de tener una estructura XML válida, los atributos de anotación
deben cumplir las condiciones siguientes:
Los atributos de anotación no deben estar en ningún espacio de nombres XML reservado para CSDL.
Se pueden aplicar varios atributos de anotación a un elemento CSDL determinado.
Dos atributos de anotación cualesquiera no pueden tener el mismo nombre completo.
Los atributos de anotación se pueden usar para proporcionar metadatos adicionales sobre los elementos en un
modelo conceptual. Se puede tener acceso a los metadatos contenidos en los elementos Annotation en tiempo
de ejecución mediante el uso de clases en el espacio de nombres System. Data. Metadata. Edm.
Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType con un atributo Annotation (CustomAttribute ). El
ejemplo también muestra un elemento Annotation aplicado al elemento de tipo de entidad.

<Schema Namespace="SchoolModel" Alias="Self"


xmlns:annotation="[Link]
xmlns="[Link]
<EntityContainer Name="SchoolEntities" annotation:LazyLoadingEnabled="true">
<EntitySet Name="People" EntityType="[Link]" />
</EntityContainer>
<EntityType Name="Person" xmlns:p="[Link]
p:CustomAttribute="Data here.">
<Key>
<PropertyRef Name="PersonID" />
</Key>
<Property Name="PersonID" Type="Int32" Nullable="false"
annotation:StoreGeneratedPattern="Identity" />
<Property Name="LastName" Type="String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="FirstName" Type="String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="HireDate" Type="DateTime" />
<Property Name="EnrollmentDate" Type="DateTime" />
<p:CustomElement>
Custom metadata.
</p:CustomElement>
</EntityType>
</Schema>

El siguiente código recupera los metadatos del atributo Annotation y los escribe en la consola:

EdmItemCollection collection = new EdmItemCollection("[Link]");


MetadataWorkspace workspace = new MetadataWorkspace();
[Link](collection);
EdmType contentType;
[Link]("Person", "SchoolModel", [Link], out contentType);
if ([Link]("[Link]
{
MetadataProperty annotationProperty =
[Link]["[Link]
object annotationValue = [Link];
[Link]([Link]());
}

El código anterior supone que el archivo [Link] está en el directorio de resultados del proyecto y que se
han agregado las siguientes instrucciones Imports y Using al proyecto:
using [Link];

Elementos Annotation (CSDL)


Los elementos Annotation en el lenguaje de definición de esquemas conceptuales (CSDL) son los elementos
XML personalizados del modelo conceptual. Además de tener una estructura XML válida, lo siguiente debe ser
verdadero para los elementos Annotation:
Los elementos Annotation no deben estar en un espacio de nombres XML que esté reservado para CSDL.
Más de un elemento Annotation puede ser un elemento secundario de un elemento CSDL determinado.
Los nombres completos de dos elementos Annotation cualesquiera no deben ser los mismos.
Los elementos Annotation deben aparecer después de todos los demás elementos secundarios de un
elemento CSDL determinado.
Los elementos Annotation pueden utilizarse para proporcionar metadatos adicionales sobre los elementos en
un modelo conceptual. A partir de la .NET Framework versión 4, se puede tener acceso a los metadatos
contenidos en los elementos Annotation en tiempo de ejecución mediante el uso de clases en el espacio de
nombres System. Data. Metadata. Edm.
Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType con un elemento Annotation (CustomElement ). El
ejemplo también muestra un atributo de anotación aplicado al elemento de tipo de entidad.

<Schema Namespace="SchoolModel" Alias="Self"


xmlns:annotation="[Link]
xmlns="[Link]
<EntityContainer Name="SchoolEntities" annotation:LazyLoadingEnabled="true">
<EntitySet Name="People" EntityType="[Link]" />
</EntityContainer>
<EntityType Name="Person" xmlns:p="[Link]
p:CustomAttribute="Data here.">
<Key>
<PropertyRef Name="PersonID" />
</Key>
<Property Name="PersonID" Type="Int32" Nullable="false"
annotation:StoreGeneratedPattern="Identity" />
<Property Name="LastName" Type="String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="FirstName" Type="String" Nullable="false"
MaxLength="50" Unicode="true" FixedLength="false" />
<Property Name="HireDate" Type="DateTime" />
<Property Name="EnrollmentDate" Type="DateTime" />
<p:CustomElement>
Custom metadata.
</p:CustomElement>
</EntityType>
</Schema>

El siguiente código recupera los metadatos del elemento de anotación y los escribe en la consola:
EdmItemCollection collection = new EdmItemCollection("[Link]");
MetadataWorkspace workspace = new MetadataWorkspace();
[Link](collection);
EdmType contentType;
[Link]("Person", "SchoolModel", [Link], out contentType);
if ([Link]("[Link]
{
MetadataProperty annotationProperty =
[Link]["[Link]
object annotationValue = [Link];
[Link]([Link]());
}

El código anterior supone que el archivo [Link] está en el directorio de resultados del proyecto y que se
han agregado las siguientes instrucciones Imports y Using al proyecto:

using [Link];

Tipos de modelos conceptuales (CSDL)


El lenguaje de definición de esquemas conceptuales (CSDL) admite un conjunto de tipos de datos primitivos
abstractos, denominados EDMSimpleTypes , que definen propiedades en un modelo conceptual. Los
EDMSimpleTypes son proxies para los tipos de datos primitivos que se admiten en el entorno de
almacenamiento o de hospedaje.
En la tabla siguiente se enumeran los tipos de datos primitivos admitidos por CSDL. En la tabla también se
enumeran las caras que se pueden aplicar a cada EDMSimpleType .

EDM SIM P L ET Y P E DESC RIP C IÓ N FA C ETA S A P L IC A B L ES

[Link] y Contiene datos binarios. MaxLength, FixedLength, Nullable,


Default

[Link] Contiene el valor true o false . Nullable, Default

[Link] Contiene un valor entero de 8 bits sin Precision, Nullable, Default


signo.

[Link] Representa una fecha y hora. Precision, Nullable, Default

[Link] Contiene una fecha y hora como un Precision, Nullable, Default


desplazamiento en minutos con
respecto a GMT.

[Link] Contiene un valor numérico con una Precision, Nullable, Default


precisión y escala fijas.

[Link] Contiene un número de punto flotante Precision, Nullable, Default


con una precisión de 15 dígitos
EDM SIM P L ET Y P E DESC RIP C IÓ N FA C ETA S A P L IC A B L ES

EDM. Float Contiene un número de punto flotante Precision, Nullable, Default


con una precisión de 7 dígitos.

[Link] Contiene un identificador único de 16 Precision, Nullable, Default


bytes.

Edm.Int16 Contiene un valor entero de 16 bits Precision, Nullable, Default


con signo.

Edm.Int32 Contiene un valor entero de 32 bits Precision, Nullable, Default


con signo.

Edm.Int64 Contiene un valor entero de 64 bits Precision, Nullable, Default


con signo.

[Link] Contiene un valor entero de 8 bits con Precision, Nullable, Default


signo.

[Link] Contiene datos de caracteres. Unicode, FixedLength, MaxLength,


Collation, Precision, Nullable, Default

[Link] Contiene una hora del día. Precision, Nullable, Default

EDM. Geography Nullable, default, SRID

[Link] Nullable, default, SRID

EDM. GeographyLineString Nullable, default, SRID

EDM. GeographyPolygon Nullable, default, SRID

EDM. GeographyMultiPoint Nullable, default, SRID

EDM. GeographyMultiLineString Nullable, default, SRID

EDM. GeographyMultiPolygon Nullable, default, SRID

EDM. GeographyCollection Nullable, default, SRID

EDM. Geometr y Nullable, default, SRID

EDM. Geometr yPoint Nullable, default, SRID

EDM. Geometr yLineString Nullable, default, SRID

EDM. Geometr yPolygon Nullable, default, SRID

EDM. Geometr yMultiPoint Nullable, default, SRID

EDM. Geometr yMultiLineString Nullable, default, SRID


EDM SIM P L ET Y P E DESC RIP C IÓ N FA C ETA S A P L IC A B L ES

EDM. Geometr yMultiPolygon Nullable, default, SRID

EDM. Geometr yCollection Nullable, default, SRID

Facets (CSDL)
En el lenguaje de definición de esquemas conceptuales (CSDL), las facetas representan restricciones en las
propiedades de tipos de entidad y tipos complejos. Las facetas aparecen como atributos XML en los elementos
CSDL siguientes:
Propiedad
TypeRef
Parámetro
En la tabla siguiente se describen las facetas que se admiten en CSDL. Todas las facetas son opcionales. El Entity
Framework usa algunas de las caras que se indican a continuación al generar una base de datos a partir de un
modelo conceptual.

NOTE
Para obtener información sobre los tipos de datos de un modelo conceptual, vea tipos de modelos conceptuales (CSDL).

SE UT IL IZ A PA RA L A
GEN ERA C IÓ N DE L A
FA C ETA DESC RIP C IÓ N SE A P L IC A A B A SE DE DATO S L A USA EL RUN T IM E.

Intercalación Especifica la [Link] Sí No


secuencia de
intercalación (o
secuencia de orden)
que se va a usar
cuando se realicen las
operaciones de
comparación y
ordenación sobre los
valores de la
propiedad.

ConcurrencyMode Indica que el valor de Todas las No Sí


propiedad se debería propiedades de
utilizar para las EDMSimpleType
comprobaciones de
la simultaneidad
optimista.

Valor Especifica el valor Todas las Sí Sí


predeterminado predeterminado de la propiedades de
propiedad si no se EDMSimpleType
proporciona ningún
valor al crear las
instancias.
SE UT IL IZ A PA RA L A
GEN ERA C IÓ N DE L A
FA C ETA DESC RIP C IÓ N SE A P L IC A A B A SE DE DATO S L A USA EL RUN T IM E.

FixedLength Especifica si la EDM. Binar y , EDM. Sí No


longitud del valor de String
propiedad puede
variar.

MaxLength Especifica la longitud EDM. Binar y , EDM. Sí No


máxima del valor de String
propiedad.

Admisión de Especifica si la Todas las Sí Sí


valores NULL propiedad puede propiedades de
tener un valor null . EDMSimpleType

Precisión Para las propiedades EDM. DateTime , Sí No


de tipo decimal, EDM.
especifica el número DateTimeOffset ,
de dígitos que puede EDM. decimal,
tener un valor de EDM. Time
propiedad. En el caso
de las propiedades
de tipo Time ,
DateTime y
DateTimeOffset ,
especifica el número
de dígitos para la
parte fraccionaria de
los segundos del
valor de propiedad.

Escala Especifica el número [Link] Sí No


de dígitos que puede
haber a la derecha
del separador
decimal para el valor
de propiedad.
SE UT IL IZ A PA RA L A
GEN ERA C IÓ N DE L A
FA C ETA DESC RIP C IÓ N SE A P L IC A A B A SE DE DATO S L A USA EL RUN T IM E.

SRID Especifica el EDM. Geography, No Sí


identificador del EDM.
sistema espacial de GeographyPoint,
referencia del EDM.
sistema. Para obtener GeographyLineStri
más información, vea ng, EDM.
SRID y SRID (SQL GeographyPolygo
Server). n, EDM.
GeographyMultiPo
int, EDM.
GeographyMultiLi
neString, EDM.
GeographyMultiPo
lygon, EDM.
GeographyCollecti
on, EDM.
Geometr y, EDM.
Geometr yPoint,
EDM.
Geometr yLineStrin
g, EDM.
Geometr yPolygon,
EDM.
Geometr yMultiPoi
nt, EDM.
Geometr yMultiLin
eString, EDM.
Geometr yMultiPol
ygon, EDM.
Geometr yCollectio
n

Unicode Indica si el valor de [Link] Sí Sí


propiedad está
almacenado como
Unicode.

NOTE
Al generar una base de datos a partir de un modelo conceptual, el Asistente para generar base de datos reconocerá el
valor del atributo StoreGeneratedPattern en un elemento Proper ty si está en el siguiente espacio de nombres:
[Link] . Los valores admitidos para el atributo son Identity y
Computed . Un valor de Identity producirá una columna de base de datos con un valor de identidad que se genera en
la base de datos. Un valor de calculado producirá una columna con un valor que se calcula en la base de datos.

Ejemplo
En el ejemplo siguiente se muestran facetas aplicadas a las propiedades de un tipo de entidad:
<EntityType Name="Product">
<Key>
<PropertyRef Name="ProductId" />
</Key>
<Property Type="Int32"
Name="ProductId" Nullable="false"
a:StoreGeneratedPattern="Identity"
xmlns:a="[Link] />
<Property Type="String"
Name="ProductName"
Nullable="false"
MaxLength="50" />
<Property Type="String"
Name="Location"
Nullable="true"
MaxLength="25" />
</EntityType>
Especificación MSL
12/03/2021 • 76 minutes to read

El lenguaje de especificación de asignaciones (MSL) es un lenguaje basado en XML que describe la asignación
entre el modelo conceptual y el modelo de almacenamiento de una aplicación Entity Framework.
En una aplicación Entity Framework, la asignación de metadatos se carga desde un archivo. MSL (escrito en
MSL) en tiempo de compilación. Entity Framework usa la asignación de metadatos en tiempo de ejecución para
traducir las consultas en el modelo conceptual para almacenar comandos específicos de.
El Entity Framework Designer (EF Designer) almacena la información de asignación en un archivo. edmx en
tiempo de diseño. En tiempo de compilación, Entity Designer usa información en un archivo. edmx para crear el
archivo. MSL que Entity Framework necesita en tiempo de ejecución.
Los nombres de todos los tipos de modelos conceptuales o de almacenamiento a los que se hace referencia en
MSL deben estar calificados con sus respectivos nombres de espacios de nombres. Para obtener información
sobre el nombre del espacio de nombres del modelo conceptual, vea especificación de CSDL. Para obtener
información sobre el nombre del espacio de nombres del modelo de almacenamiento, vea SSDL Specification.
Las versiones de MSL se diferencian en los espacios de nombres XML.

VERSIÓ N DE M SL ESPA C IO DE N O M B RES XM L

MSL v1 urn: schemas-microsoft-com: Windows: Storage: asignación:


CS

MSL V2 [Link]

MSL V3 [Link]

Alias (Elemento) (MSL)


El elemento alias del lenguaje de especificación de asignaciones (MSL) es un elemento secundario del elemento
mapping que se utiliza para definir los alias para los espacios de nombres de modelos conceptuales y de
almacenamiento. Los nombres de todos los tipos de modelos conceptuales o de almacenamiento a los que se
hace referencia en MSL deben estar calificados con sus respectivos nombres de espacios de nombres. Para
obtener información sobre el nombre del espacio de nombres del modelo conceptual, vea elemento Schema
(CSDL). Para obtener información sobre el nombre del espacio de nombres del modelo de almacenamiento, vea
schema (elemento) (SSDL).
El elemento alias no puede tener elementos secundarios.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento alias .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA LO R

Clave Sí El alias para el espacio de nombres


especificado por el atributo de valor .
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA LO R

Valor Sí Espacio de nombres para el que el


valor del elemento key es un alias.

Ejemplo
En el ejemplo siguiente se muestra un elemento alias que define un alias, c , para los tipos que se definen en
el modelo conceptual.

<Mapping Space="C-S"
xmlns="[Link]
<Alias Key="c" Value="SchoolModel"/>
<EntityContainerMapping StorageEntityContainer="SchoolModelStoreContainer"
CdmEntityContainer="SchoolModelEntities">
<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="Departments">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Budget" ColumnName="Budget" />
<ScalarProperty Name="StartDate" ColumnName="StartDate" />
<ScalarProperty Name="Administrator" ColumnName="Administrator" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
</Mapping>

AssociationEnd (Elemento) (MSL)


El elemento AssociationEnd del lenguaje de especificación de asignaciones (MSL) se utiliza cuando las
funciones de modificación de un tipo de entidad en el modelo conceptual se asignan a los procedimientos
almacenados en la base de datos subyacente. Si un procedimiento almacenado de modificación toma un
parámetro cuyo valor se mantiene en una propiedad de asociación, el elemento AssociationEnd asigna el valor
de la propiedad al parámetro. Para obtener más información, vea el ejemplo siguiente.
Para obtener más información sobre la asignación de funciones de modificación de tipos de entidad a
procedimientos almacenados, vea ModificationFunctionMapping (elemento) (MSL) y Walkthrough: asignar una
entidad a procedimientos almacenados.
El elemento AssociationEnd puede tener los siguientes elementos secundarios:
ScalarProperty
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento AssociationEnd .
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

AssociationSet Sí El nombre de la asociación que se está


asignando.

From Sí Valor del atributo FromRole de la


propiedad de navegación que
corresponde a la asociación que se
está asignando. Para obtener más
información, vea elemento
NavigationProperty (CSDL).

To Sí Valor del atributo ToRole de la


propiedad de navegación que
corresponde a la asociación que se
está asignando. Para obtener más
información, vea elemento
NavigationProperty (CSDL).

Ejemplo
Considere el siguiente tipo de entidad del modelo conceptual:

<EntityType Name="Course">
<Key>
<PropertyRef Name="CourseID" />
</Key>
<Property Type="Int32" Name="CourseID" Nullable="false" />
<Property Type="String" Name="Title" Nullable="false" MaxLength="100"
FixedLength="false" Unicode="true" />
<Property Type="Int32" Name="Credits" Nullable="false" />
<NavigationProperty Name="Department"
Relationship="SchoolModel.FK_Course_Department"
FromRole="Course" ToRole="Department" />
</EntityType>

Considere también el siguiente procedimiento almacenado:

CREATE PROCEDURE [dbo].[UpdateCourse]


@CourseID int,
@Title nvarchar(50),
@Credits int,
@DepartmentID int
AS
UPDATE Course SET Title=@Title,
Credits=@Credits,
DepartmentID=@DepartmentID
WHERE CourseID=@CourseID;

Para asignar la función de actualización de la Course entidad a este procedimiento almacenado, debe
proporcionar un valor al parámetro depar tmentId . El valor para DepartmentID no corresponde a una
propiedad del tipo de entidad; está contenido en una asociación independiente cuya asignación se muestra aquí:
<AssociationSetMapping Name="FK_Course_Department"
TypeName="SchoolModel.FK_Course_Department"
StoreEntitySet="Course">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<EndProperty Name="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</EndProperty>
</AssociationSetMapping>

En el código siguiente se muestra el elemento AssociationEnd que se usa para asignar la propiedad
depar tmentId de la Asociación de **Departamento de FK _ Course _ ** al procedimiento almacenado
UpdateCourse (al que se asigna la función de actualización del tipo de entidad Course ):

<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<UpdateFunction FunctionName="[Link]">
<AssociationEnd AssociationSet="FK_Course_Department"
From="Course" To="Department">
<ScalarProperty Name="DepartmentID"
ParameterName="DepartmentID"
Version="Current" />
</AssociationEnd>
<ScalarProperty Name="Credits" ParameterName="Credits"
Version="Current" />
<ScalarProperty Name="Title" ParameterName="Title"
Version="Current" />
<ScalarProperty Name="CourseID" ParameterName="CourseID"
Version="Current" />
</UpdateFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
</EntitySetMapping>

AssociationSetMapping (Elemento) (MSL)


El elemento AssociationSetMapping del lenguaje de especificación de asignaciones (MSL) define la
asignación entre una asociación en el modelo conceptual y las columnas de tabla en la base de datos
subyacente.
Las asociaciones del modelo conceptual son tipos cuyas propiedades representan columnas de clave primaria y
clave externa de la base de datos subyacente. El elemento AssociationSetMapping utiliza dos elementos
EndProperty para definir las asignaciones entre las propiedades de tipo de asociación y las columnas de la base
de datos. Puede definir condiciones en estas asignaciones con el elemento Condition. Asigne las funciones de
inserción, actualización y eliminación para las asociaciones a procedimientos almacenados de la base de datos
mediante el elemento ModificationFunctionMapping. Defina las asignaciones de solo lectura entre las
asociaciones y las columnas de la tabla mediante una Entity SQL cadena en un elemento QueryView.
NOTE
Si se define una restricción referencial para una asociación en el modelo conceptual, no es necesario asignar la asociación
con un elemento AssociationSetMapping . Si hay un elemento AssociationSetMapping para una asociación que
tiene una restricción referencial, se omitirán las asignaciones definidas en el elemento AssociationSetMapping . Para
obtener más información, vea ReferentialConstraint (elemento) (CSDL).

El elemento AssociationSetMapping puede tener los siguientes elementos secundarios


QueryView (cero o uno)
EndProperty (cero o dos)
Condition (cero o más)
ModificationFunctionMapping (cero o uno)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento AssociationSetMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del conjunto de


asociaciones del modelo conceptual
que se está asignando.

TypeName No El nombre completo, calificado con el


espacio de nombres, del tipo de
asociación del modelo conceptual que
se está asignando.

StoreEntitySet No El nombre de la tabla que se está


asignando.

Ejemplo
En el ejemplo siguiente se muestra un elemento AssociationSetMapping en el que el conjunto de
asociaciones del Depar tamento del _ curso _ de FK en el modelo conceptual se asigna a la tabla Course en
la base de datos. Las asignaciones entre las propiedades de tipo de asociación y las columnas de tabla se
especifican en los elementos secundarios EndProper ty .

<AssociationSetMapping Name="FK_Course_Department"
TypeName="SchoolModel.FK_Course_Department"
StoreEntitySet="Course">
<EndProperty Name="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
</AssociationSetMapping>

ComplexProperty (Elemento) (MSL)


Un elemento ComplexProper ty en el lenguaje de especificación de asignaciones (MSL) define la asignación
entre una propiedad de tipo complejo en un tipo de entidad del modelo conceptual y las columnas de la tabla en
la base de datos subyacente. Las asignaciones entre columnas y propiedades se especifican en elementos
secundarios ScalarProperty.
El elemento de propiedad complexType puede tener los siguientes elementos secundarios:
ScalarProperty (cero o más)
ComplexProper ty (cero o más)
ComplextTypeMapping (cero o más)
Condition (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos aplicables al elemento ComplexProper ty :

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la propiedad compleja de


un tipo de entidad del modelo
conceptual que se está asignando.

TypeName No El nombre completo, calificado con el


espacio de nombres, del tipo de
propiedad del modelo conceptual.

Ejemplo
El siguiente ejemplo se basa en el modelo School. El siguiente tipo complejo se ha agregado al modelo
conceptual:

<ComplexType Name="FullName">
<Property Type="String" Name="LastName"
Nullable="false" MaxLength="50"
FixedLength="false" Unicode="true" />
<Property Type="String" Name="FirstName"
Nullable="false" MaxLength="50"
FixedLength="false" Unicode="true" />
</ComplexType>

Las propiedades LastName y FirstName del tipo de entidad Person se han reemplazado por una propiedad
compleja, Name :

<EntityType Name="Person">
<Key>
<PropertyRef Name="PersonID" />
</Key>
<Property Name="PersonID" Type="Int32" Nullable="false"
annotation:StoreGeneratedPattern="Identity" />
<Property Name="HireDate" Type="DateTime" />
<Property Name="EnrollmentDate" Type="DateTime" />
<Property Name="Name" Type="[Link]" Nullable="false" />
</EntityType>

En el siguiente MSL se muestra el elemento ComplexProper ty que se usa para asignar la propiedad Name a
las columnas de la base de datos subyacente:
<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<ScalarProperty Name="EnrollmentDate" ColumnName="EnrollmentDate" />
<ComplexProperty Name="Name" TypeName="[Link]">
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
</ComplexProperty>
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>

ComplexTypeMapping (Elemento) (MSL)


El elemento ComplexTypeMapping del lenguaje de especificación de asignaciones (MSL) es un elemento
secundario del elemento ResultMapping y define la asignación entre una importación de función en el modelo
conceptual y un procedimiento almacenado en la base de datos subyacente cuando se cumplen las condiciones
siguientes:
La importación de función devuelve un tipo complejo conceptual.
Los nombres de las columnas devueltas por el procedimiento almacenado no coinciden exactamente con los
nombres de las propiedades en el tipo complejo.
De forma predeterminada, la asignación entre las columnas devueltas por un procedimiento almacenado y un
tipo complejo se basa en los nombres de las propiedades y de las columnas. Si los nombres de columna no
coinciden exactamente con los nombres de propiedad, debe utilizar el elemento ComplexTypeMapping para
definir la asignación. Para obtener un ejemplo de la asignación predeterminada, vea FunctionImportMapping
(elemento) (MSL).
El elemento ComplexTypeMapping puede tener los siguientes elementos secundarios:
ScalarProperty (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos aplicables al elemento ComplexTypeMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

TypeName Sí El nombre completo, incluido el


espacio de nombres, del tipo complejo
que se está asignando.

Ejemplo
Observe el siguiente procedimiento almacenado:

CREATE PROCEDURE [dbo].[GetGrades]


@student_Id int
AS
SELECT EnrollmentID as enroll_id,
Grade as grade,
CourseID as course_id,
StudentID as student_id
FROM [Link]
WHERE StudentID = @student_Id
Considere también el siguiente tipo complejo del modelo conceptual:

<ComplexType Name="GradeInfo">
<Property Type="Int32" Name="EnrollmentID" Nullable="false" />
<Property Type="Decimal" Name="Grade" Nullable="true"
Precision="3" Scale="2" />
<Property Type="Int32" Name="CourseID" Nullable="false" />
<Property Type="Int32" Name="StudentID" Nullable="false" />
</ComplexType>

Para crear una importación de función que devuelva instancias del tipo complejo anterior, la asignación entre las
columnas devueltas por el procedimiento almacenado y el tipo de entidad debe definirse en un elemento
ComplexTypeMapping :

<FunctionImportMapping FunctionImportName="GetGrades"
FunctionName="[Link]" >
<ResultMapping>
<ComplexTypeMapping TypeName="[Link]">
<ScalarProperty Name="EnrollmentID" ColumnName="enroll_id"/>
<ScalarProperty Name="CourseID" ColumnName="course_id"/>
<ScalarProperty Name="StudentID" ColumnName="student_id"/>
<ScalarProperty Name="Grade" ColumnName="grade"/>
</ComplexTypeMapping>
</ResultMapping>
</FunctionImportMapping>

Condition (Elemento) (MSL)


El elemento Condition del lenguaje de especificación de asignaciones (MSL) coloca condiciones en las
asignaciones entre el modelo conceptual y la base de datos subyacente. La asignación que se define dentro de
un nodo XML es válida si se cumplen todas las condiciones, como se especifica en los elementos de condición
secundarios. De lo contrario, la asignación no es válida. Por ejemplo, si un elemento MappingFragment contiene
uno o varios elementos secundarios Condition , la asignación definida dentro del nodo MappingFragment
solo será válida si se cumplen todas las condiciones de los elementos de condición secundarios.
Cada condición se puede aplicar a un nombre (el nombre de una propiedad de entidad del modelo conceptual,
especificado por el atributo de nombre ) o un columnName (el nombre de una columna de la base de datos,
especificado por el atributo columnName ). Cuando se establece el atributo Name , la condición se comprueba
con respecto a un valor de propiedad de entidad. Cuando se establece el atributo columnName , la condición
se comprueba con respecto a un valor de columna. Solo se puede especificar uno de los atributos Name o
columnName en un elemento Condition .

NOTE
Cuando el elemento Condition se usa dentro de un elemento FunctionImportMapping, solo el atributo Name no es
aplicable.

El elemento Condition puede ser un elemento secundario de los siguientes elementos:


AssociationSetMapping
ComplexProperty
EntitySetMapping
MappingFragment
EntityTypeMapping
El elemento Condition no puede tener elementos secundarios.
Atributos aplicables
En la tabla siguiente se describen los atributos aplicables al elemento Condition :

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

ColumnName No El nombre de la columna de la tabla


cuyo valor se utiliza para evaluar la
condición.

IsNull No True o False . Si el valor es true y el


valor de la columna es null, o si el
valor es false y el valor de la columna
no es null, la condición es true. De lo
contrario, la condición es falsa.
Los atributos IsNull y Value no se
pueden usar al mismo tiempo.

Valor No El valor con el que se compara el valor


de la columna. Si los valores son los
mismos, la condición es verdadera. De
lo contrario, la condición es falsa.
Los atributos IsNull y Value no se
pueden usar al mismo tiempo.

Nombre No El nombre de la propiedad de entidad


del modelo conceptual cuyo valor se
utiliza para evaluar la condición.
Este atributo no es aplicable si el
elemento Condition se usa dentro de
un elemento FunctionImportMapping.

Ejemplo
En el ejemplo siguiente se muestran los elementos Condition como elementos secundarios de los elementos
MappingFragment . Cuando HireDate no es NULL y EnrollmentDate es null, los datos se asignan entre el
tipo SchoolModel. instructor y las columnas PersonID e HireDate de la tabla Person . Cuando
EnrollmentDate no es NULL y HireDate es null, los datos se asignan entre el tipo SchoolModel. Student y
las columnas PersonID e Enrollment de la tabla Person .
<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<Condition ColumnName="HireDate" IsNull="false" />
<Condition ColumnName="EnrollmentDate" IsNull="true" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="EnrollmentDate"
ColumnName="EnrollmentDate" />
<Condition ColumnName="EnrollmentDate" IsNull="false" />
<Condition ColumnName="HireDate" IsNull="true" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>

DeleteFunction (Elemento) (MSL)


El elemento Inser tFunction del lenguaje de especificación de asignaciones (MSL) asigna la función de
eliminación de un tipo de entidad o asociación del modelo conceptual a un procedimiento almacenado en la
base de datos subyacente. Los procedimientos almacenados a los que están asignados las funciones de
modificación se deben declarar en el modelo de almacenamiento. Para obtener más información, vea elemento
function (SSDL).

NOTE
Si no asigna las tres operaciones de inserción, actualización o eliminación de un tipo de entidad a los procedimientos
almacenados, se producirá un error en las operaciones no asignadas si se ejecutan en tiempo de ejecución y se genera un
UpdateException.

DeleteFunction aplicado a EntityTypeMapping


Cuando se aplica al elemento EntityTypeMapping, el elemento DeleteFunction asigna la función de eliminación
de un tipo de entidad del modelo conceptual a un procedimiento almacenado.
El elemento DeleteFunction puede tener los elementos secundarios siguientes cuando se aplica a un elemento
EntityTypeMapping :
AssociationEnd (cero o más)
ComplexProperty (cero o más)
ScalarProperty (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento DeleteFunction cuando se
aplica a un elemento EntityTypeMapping .
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombrefunción Sí El nombre completo, calificado con el


espacio de nombres, del procedimiento
almacenado al que la función de
eliminación está asignada. El
procedimiento almacenado se debe
declarar en el modelo de
almacenamiento.

RowsAffectedParameter No El nombre del parámetro de salida que


devuelve el número de filas afectadas.

Ejemplo
El ejemplo siguiente se basa en el modelo School y muestra el elemento DeleteFunction que asigna la función
Delete del tipo de entidad Person al procedimiento almacenado DeletePerson . El procedimiento almacenado
DeletePerson se declara en el modelo de almacenamiento.

<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<ScalarProperty Name="EnrollmentDate"
ColumnName="EnrollmentDate" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate" />
<ScalarProperty Name="HireDate" ParameterName="HireDate" />
<ScalarProperty Name="FirstName" ParameterName="FirstName" />
<ScalarProperty Name="LastName" ParameterName="LastName" />
<ResultBinding Name="PersonID" ColumnName="NewPersonID" />
</InsertFunction>
<UpdateFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate"
Version="Current" />
<ScalarProperty Name="HireDate" ParameterName="HireDate"
Version="Current" />
<ScalarProperty Name="FirstName" ParameterName="FirstName"
Version="Current" />
<ScalarProperty Name="LastName" ParameterName="LastName"
Version="Current" />
<ScalarProperty Name="PersonID" ParameterName="PersonID"
Version="Current" />
</UpdateFunction>
<DeleteFunction FunctionName="[Link]">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
</EntitySetMapping>

DeleteFunction aplicado a AssociationSetMapping


Cuando se aplica al elemento AssociationSetMapping, el elemento DeleteFunction asigna la función de
eliminación de una asociación del modelo conceptual a un procedimiento almacenado.
El elemento DeleteFunction puede tener los elementos secundarios siguientes cuando se aplica al elemento
AssociationSetMapping :
EndProperty
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento DeleteFunction cuando se
aplica al elemento AssociationSetMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombrefunción Sí El nombre completo, calificado con el


espacio de nombres, del procedimiento
almacenado al que la función de
eliminación está asignada. El
procedimiento almacenado se debe
declarar en el modelo de
almacenamiento.

RowsAffectedParameter No El nombre del parámetro de salida que


devuelve el número de filas afectadas.

Ejemplo
El ejemplo siguiente se basa en el modelo School y muestra el elemento DeleteFunction que se usa para
asignar la función de eliminación de la Asociación CourseInstructor al procedimiento almacenado
DeleteCourseInstructor . El procedimiento almacenado DeleteCourseInstructor se declara en el modelo de
almacenamiento.

<AssociationSetMapping Name="CourseInstructor"
TypeName="[Link]"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="[Link]">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>

EndProperty (Elemento) (MSL)


El elemento EndProper ty del lenguaje de especificación de asignaciones (MSL) define la asignación entre un
extremo o una función de modificación de una asociación del modelo conceptual y la base de datos subyacente.
La asignación entre columnas y propiedades se especifica en un elemento secundario ScalarProperty.
Cuando se utiliza un elemento EndProper ty para definir la asignación para el final de una asociación del
modelo conceptual, es un elemento secundario de un elemento AssociationSetMapping. Cuando el elemento
EndProper ty se utiliza para definir la asignación para una función de modificación de una asociación del
modelo conceptual, es un elemento secundario de un elemento InsertFunction o un elemento InsertFunction.
El elemento EndProper ty puede tener los siguientes elementos secundarios:
ScalarProperty (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos aplicables al elemento EndProper ty :

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del extremo de la asociación


que se está asignando.

Ejemplo
En el ejemplo siguiente se muestra un elemento AssociationSetMapping en el que la Asociación de
Depar tamento de FK _ _ Course en el modelo conceptual se asigna a la tabla Course de la base de datos.
Las asignaciones entre las propiedades de tipo de asociación y las columnas de tabla se especifican en los
elementos secundarios EndProper ty .

<AssociationSetMapping Name="FK_Course_Department"
TypeName="SchoolModel.FK_Course_Department"
StoreEntitySet="Course">
<EndProperty Name="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
</AssociationSetMapping>

Ejemplo
En el ejemplo siguiente se muestra el elemento EndProper ty que asigna las funciones de inserción y
eliminación de una asociación (CourseInstructor ) a los procedimientos almacenados en la base de datos
subyacente. Las funciones asignadas se declaran en el modelo de almacenamiento.
<AssociationSetMapping Name="CourseInstructor"
TypeName="[Link]"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="[Link]">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>

EntityContainerMapping (Elemento) (MSL)


El elemento EntityContainerMapping del lenguaje de especificación de asignaciones (MSL) asigna el
contenedor de entidades del modelo conceptual al contenedor de entidades en el modelo de almacenamiento.
El elemento EntityContainerMapping es un elemento secundario del elemento Mapping.
El elemento EntityContainerMapping puede tener los elementos secundarios siguientes (en el orden
mostrado):
EntitySetMapping (cero o más)
AssociationSetMapping (cero o más)
FunctionImportMapping (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntityContainerMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

StorageModelContainer Sí El nombre del contenedor de


entidades del modelo de
almacenamiento que se está
asignando.

CdmEntityContainer Sí El nombre del contenedor de


entidades del modelo conceptual que
se está asignando.
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

GenerateUpdateViews No True o False . Si es false , no se genera


ninguna vista de actualización. Este
atributo debe establecerse en false si
tiene una asignación de solo lectura
que no sería válida porque los datos
no pueden realizar una operación de
ida y vuelta correctamente.
El valor predeterminado es True .

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainerMapping que asigna el contenedor
SchoolModelEntities (el contenedor de entidades del modelo conceptual) al contenedor
SchoolModelStoreContainer (el contenedor de entidades del modelo de almacenamiento):

<EntityContainerMapping StorageEntityContainer="SchoolModelStoreContainer"
CdmEntityContainer="SchoolModelEntities">
<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="Departments">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Budget" ColumnName="Budget" />
<ScalarProperty Name="StartDate" ColumnName="StartDate" />
<ScalarProperty Name="Administrator" ColumnName="Administrator" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>

EntitySetMapping (Elemento) (MSL)


El elemento EntitySetMapping del lenguaje de especificación de asignaciones (MSL) asigna todos los tipos de
un conjunto de entidades del modelo conceptual a los conjuntos de entidades del modelo de almacenamiento.
Un conjunto de entidades del modelo conceptual es un contenedor lógico para instancias de entidades del
mismo tipo (y tipos derivados). Un conjunto de entidades del modelo de almacenamiento representa una tabla
o vista de la base de datos subyacente. El conjunto de entidades del modelo conceptual se especifica mediante el
valor del atributo Name del elemento EntitySetMapping . La tabla o vista asignada a se especifica mediante el
atributo StoreEntitySet en cada elemento MappingFragment secundario o en el propio elemento
EntitySetMapping .
El elemento EntitySetMapping puede tener los siguientes elementos secundarios:
EntityTypeMapping (cero o más)
QueryView (cero o uno)
MappingFragment (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntitySetMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del conjunto de entidades


del modelo conceptual que se está
asignando.

TypeName 1 No El nombre del tipo de entidad del


modelo conceptual que se está
asignando.

StoreEntitySet 1 No El nombre del conjunto de entidades


del modelo de almacenamiento al que
se está asignando.

MakeColumnsDistinct No True o false , dependiendo de si solo


se devuelven filas distintas.
Si este atributo se establece en true , el
atributo GenerateUpdateViews del
elemento EntityContainerMapping
debe establecerse en false .

1 los atributos TypeName y StoreEntitySet se pueden usar en lugar de los elementos secundarios
EntityTypeMapping y MappingFragment para asignar un tipo de entidad único a una sola tabla.
Ejemplo
En el ejemplo siguiente se muestra un elemento EntitySetMapping que asigna tres tipos (un tipo base y dos
tipos derivados) en el conjunto de entidades Courses del modelo conceptual a tres tablas diferentes en la base
de datos subyacente. Las tablas se especifican mediante el atributo StoreEntitySet en cada elemento
MappingFragment .

<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="Title" ColumnName="Title" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="OnlineCourse">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="URL" ColumnName="URL" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="OnsiteCourse">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="Time" ColumnName="Time" />
<ScalarProperty Name="Days" ColumnName="Days" />
<ScalarProperty Name="Location" ColumnName="Location" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
EntityTypeMapping (Elemento) (MSL)
El elemento EntityTypeMapping del lenguaje de especificación de asignaciones (MSL) define la asignación
entre un tipo de entidad en el modelo conceptual y las tablas o vistas de la base de datos subyacente. Para
obtener información sobre los tipos de entidad del modelo conceptual y las tablas o vistas de la base de datos
subyacente, vea EntityType (Elemento) (CSDL) y EntitySet (Elemento) (SSDL). El tipo de entidad del modelo
conceptual que se está asignando se especifica mediante el atributo TypeName del elemento
EntityTypeMapping . La tabla o vista que se está asignando se especifica mediante el atributo StoreEntitySet
del elemento MappingFragment secundario.
El elemento secundario ModificationFunctionMapping se puede utilizar para asignar las funciones de inserción,
actualización o eliminación de tipos de entidad a procedimientos almacenados de la base de datos.
El elemento EntityTypeMapping puede tener los siguientes elementos secundarios:
MappingFragment (cero o más)
ModificationFunctionMapping (cero o uno)
ScalarProperty
Condición

NOTE
Los elementos MappingFragment y ModificationFunctionMapping no pueden ser elementos secundarios del
elemento EntityTypeMapping al mismo tiempo.

NOTE
Los elementos ScalarProper ty y Condition solo pueden ser elementos secundarios del elemento EntityTypeMapping
cuando se usa dentro de un elemento FunctionImportMapping.

Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntityTypeMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

TypeName Sí El nombre completo, calificado con el


espacio de nombres, del tipo de
entidad del modelo conceptual que se
está asignando.
Si el tipo es abstracto o un tipo
derivado, el valor debe ser
IsOfType(Namespace-
qualified_type_name)
.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntitySetMapping con dos elementos EntityTypeMapping
secundarios. En el primer elemento EntityTypeMapping , el tipo de entidad SchoolModel. person se asigna
a la tabla Person . En el segundo elemento EntityTypeMapping , la funcionalidad de actualización del tipo
SchoolModel. person se asigna a un procedimiento almacenado, UpdatePerson , en la base de datos.
<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<ScalarProperty Name="EnrollmentDate" ColumnName="EnrollmentDate" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<UpdateFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate" ParameterName="EnrollmentDate"
Version="Current" />
<ScalarProperty Name="HireDate" ParameterName="HireDate"
Version="Current" />
<ScalarProperty Name="FirstName" ParameterName="FirstName"
Version="Current" />
<ScalarProperty Name="LastName" ParameterName="LastName"
Version="Current" />
<ScalarProperty Name="PersonID" ParameterName="PersonID"
Version="Current" />
</UpdateFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
</EntitySetMapping>

Ejemplo
En el ejemplo siguiente se muestra la asignación de una jerarquía de tipos en la que el tipo raíz es abstracto.
Tenga en cuenta el uso de la IsOfType sintaxis para los atributos TypeName .

<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<Condition ColumnName="HireDate" IsNull="false" />
<Condition ColumnName="EnrollmentDate" IsNull="true" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="EnrollmentDate"
ColumnName="EnrollmentDate" />
<Condition ColumnName="EnrollmentDate" IsNull="false" />
<Condition ColumnName="HireDate" IsNull="true" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>

FunctionImportMapping (Elemento) (MSL)


El elemento FunctionImpor tMapping del lenguaje de especificación de asignaciones (MSL) define la
asignación entre una importación de función en el modelo conceptual y un procedimiento almacenado o una
función en la base de datos subyacente. Las importaciones de función se deben declarar en el modelo
conceptual, mientras que los procedimientos almacenados se deben declarar en el modelo de almacenamiento.
Para obtener más información, vea elemento FunctionImport (CSDL) y elemento function (SSDL).

NOTE
De forma predeterminada, si una importación de función devuelve un tipo de entidad del modelo conceptual o un tipo
complejo, entonces los nombres de las columnas devueltas por el procedimiento almacenado subyacente deben coincidir
exactamente con los nombres de las propiedades del tipo del modelo conceptual. Si los nombres de columna no coinciden
exactamente con los nombres de propiedad, la asignación se debe definir en un elemento ResultMapping.

El elemento FunctionImpor tMapping puede tener los siguientes elementos secundarios:


ResultMapping (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos aplicables al elemento FunctionImpor tMapping :

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

FunctionImpor tName Sí El nombre de la importación de


función del modelo conceptual que se
está asignando.

Nombrefunción Sí El nombre, calificado con el espacio de


nombres, de la función del modelo de
almacenamiento que se está
asignando.

Ejemplo
El siguiente ejemplo se basa en el modelo School. Considere la siguiente función en el modelo de
almacenamiento:

<Function Name="GetStudentGrades" Aggregate="false"


BuiltIn="false" NiladicFunction="false"
IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="StudentID" Type="int" Mode="In" />
</Function>

También considere esta importación de función en el modelo conceptual:

<FunctionImport Name="GetStudentGrades" EntitySet="StudentGrades"


ReturnType="Collection([Link])">
<Parameter Name="StudentID" Mode="In" Type="Int32" />
</FunctionImport>

En el ejemplo siguiente se muestra un elemento FunctionImpor tMapping que se usa para asignar la función
y la importación de funciones anteriores entre sí:

<FunctionImportMapping FunctionImportName="GetStudentGrades"
FunctionName="[Link]" />
InsertFunction (Elemento) (MSL)
El elemento Inser tFunction del lenguaje de especificación de asignaciones (MSL) asigna la función de
inserción de un tipo de entidad o asociación del modelo conceptual a un procedimiento almacenado en la base
de datos subyacente. Los procedimientos almacenados a los que están asignados las funciones de modificación
se deben declarar en el modelo de almacenamiento. Para obtener más información, vea elemento function
(SSDL).

NOTE
Si no asigna las tres operaciones de inserción, actualización o eliminación de un tipo de entidad a los procedimientos
almacenados, se producirá un error en las operaciones no asignadas si se ejecutan en tiempo de ejecución y se genera un
UpdateException.

El elemento Inser tFunction puede ser un elemento secundario del elemento ModificationFunctionMapping y
aplicarse al elemento EntityTypeMapping o al elemento AssociationSetMapping.
InsertFunction aplicado a EntityTypeMapping
Cuando se aplica al elemento EntityTypeMapping, el elemento Inser tFunction asigna la función de inserción de
un tipo de entidad del modelo conceptual a un procedimiento almacenado.
El elemento Inser tFunction puede tener los elementos secundarios siguientes cuando se aplica a un elemento
EntityTypeMapping :
AssociationEnd (cero o más)
ComplexProperty (cero o más)
ResultBinding (cero o uno)
ScalarProperty (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Inser tFunction cuando se
aplican a un elemento EntityTypeMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombrefunción Sí El nombre completo, calificado con el


espacio de nombres, del procedimiento
almacenado al que la función de
inserción está asignada. El
procedimiento almacenado se debe
declarar en el modelo de
almacenamiento.

RowsAffectedParameter No El nombre del parámetro de salida que


devuelve el número de filas afectadas.

Ejemplo
El ejemplo siguiente se basa en el modelo School y muestra el elemento Inser tFunction que se usa para
asignar la función de inserción del tipo de entidad person al procedimiento almacenado Inser tPerson . El
procedimiento almacenado Inser tPerson se declara en el modelo de almacenamiento.
<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate" />
<ScalarProperty Name="HireDate" ParameterName="HireDate" />
<ScalarProperty Name="FirstName" ParameterName="FirstName" />
<ScalarProperty Name="LastName" ParameterName="LastName" />
<ResultBinding Name="PersonID" ColumnName="NewPersonID" />
</InsertFunction>
<UpdateFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate"
Version="Current" />
<ScalarProperty Name="HireDate" ParameterName="HireDate"
Version="Current" />
<ScalarProperty Name="FirstName" ParameterName="FirstName"
Version="Current" />
<ScalarProperty Name="LastName" ParameterName="LastName"
Version="Current" />
<ScalarProperty Name="PersonID" ParameterName="PersonID"
Version="Current" />
</UpdateFunction>
<DeleteFunction FunctionName="[Link]">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>

InsertFunction aplicado a AssociationSetMapping


Cuando se aplica al elemento AssociationSetMapping, el elemento Inser tFunction asigna la función de
inserción de una asociación del modelo conceptual a un procedimiento almacenado.
El elemento Inser tFunction puede tener los elementos secundarios siguientes cuando se aplica al elemento
AssociationSetMapping :
EndProperty
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Inser tFunction cuando se
aplica al elemento AssociationSetMapping .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombrefunción Sí El nombre completo, calificado con el


espacio de nombres, del procedimiento
almacenado al que la función de
inserción está asignada. El
procedimiento almacenado se debe
declarar en el modelo de
almacenamiento.

RowsAffectedParameter No El nombre del parámetro de salida que


devuelve el número de filas afectadas.

Ejemplo
El ejemplo siguiente se basa en el modelo School y muestra el elemento Inser tFunction que se usa para
asignar la función de inserción de la Asociación CourseInstructor al procedimiento almacenado
Inser tCourseInstructor . El procedimiento almacenado Inser tCourseInstructor se declara en el modelo de
almacenamiento.
<AssociationSetMapping Name="CourseInstructor"
TypeName="[Link]"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="[Link]">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>

Mapping (Elemento) (MSL)


El elemento mapping del lenguaje de especificación de asignaciones (MSL) contiene información para la
asignación de objetos que se definen en un modelo conceptual a una base de datos (tal y como se describe en
un modelo de almacenamiento). Para obtener más información, vea especificación de CSDL y especificación de
SSDL.
El elemento mapping es el elemento raíz de una especificación de asignación. El espacio de nombres XML para
las especificaciones de asignación es [Link] .
El elemento de asignación puede tener los elementos secundarios siguientes (en el orden mostrado):
Alias (cero o más)
EntityContainerMapping (exactamente uno)
Los nombres de los tipos de modelos conceptuales y de almacenamiento a los que se hace referencia en MSL
deben estar calificados con sus respectivos nombres de espacios de nombres. Para obtener información sobre el
nombre del espacio de nombres del modelo conceptual, vea elemento Schema (CSDL). Para obtener
información sobre el nombre del espacio de nombres del modelo de almacenamiento, vea schema (elemento)
(SSDL). Los alias para los espacios de nombres que se utilizan en MSL se pueden definir con el elemento Alias.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento de asignación .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Espacia Sí C-S. Este es un valor fijo y no se


puede cambiar.

Ejemplo
En el ejemplo siguiente se muestra un elemento de asignación basado en parte del modelo School. Para
obtener más información sobre el modelo School, consulte Inicio rápido (Entity Framework):

<Mapping Space="C-S"
xmlns="[Link]
<Alias Key="c" Value="SchoolModel"/>
<EntityContainerMapping StorageEntityContainer="SchoolModelStoreContainer"
CdmEntityContainer="SchoolModelEntities">
<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<EntitySetMapping Name="Departments">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Department">
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Budget" ColumnName="Budget" />
<ScalarProperty Name="StartDate" ColumnName="StartDate" />
<ScalarProperty Name="Administrator" ColumnName="Administrator" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
</EntityContainerMapping>
</Mapping>

MappingFragment (Elemento) (MSL)


El elemento MappingFragment del lenguaje de especificación de asignaciones (MSL) define la asignación
entre las propiedades de un tipo de entidad del modelo conceptual y una tabla o vista en la base de datos. Para
obtener información sobre los tipos de entidad del modelo conceptual y las tablas o vistas de la base de datos
subyacente, vea EntityType (Elemento) (CSDL) y EntitySet (Elemento) (SSDL). El MappingFragment puede ser
un elemento secundario del elemento EntityTypeMapping o el elemento EntitySetMapping.
El elemento MappingFragment puede tener los siguientes elementos secundarios:
ComplexType (cero o más)
ScalarProperty (cero o más)
Condition (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento MappingFragment .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

StoreEntitySet Sí El nombre de la tabla o vista que se


está asociando.
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

MakeColumnsDistinct No True o false , dependiendo de si solo


se devuelven filas distintas.
Si este atributo se establece en true , el
atributo GenerateUpdateViews del
elemento EntityContainerMapping
debe establecerse en false .

Ejemplo
En el ejemplo siguiente se muestra un elemento MappingFragment como elemento secundario de un
elemento EntityTypeMapping . En este ejemplo, las propiedades del tipo de curso en el modelo conceptual se
asignan a las columnas de la tabla Course en la base de datos.

<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>

Ejemplo
En el ejemplo siguiente se muestra un elemento MappingFragment como el elemento secundario de un
elemento EntitySetMapping . Como en el ejemplo anterior, las propiedades del tipo de curso en el modelo
conceptual se asignan a las columnas de la tabla Course en la base de datos.

<EntitySetMapping Name="Courses" TypeName="[Link]">


<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="DepartmentID" ColumnName="DepartmentID" />
</MappingFragment>
</EntitySetMapping>

ModificationFunctionMapping (Elemento) (MSL)


El elemento ModificationFunctionMapping del lenguaje de especificación de asignaciones (MSL) asigna las
funciones de inserción, actualización y eliminación de un tipo de entidad del modelo conceptual a los
procedimientos almacenados en la base de datos subyacente. El elemento ModificationFunctionMapping
también puede asignar las funciones de inserción y eliminación para las asociaciones de varios a varios del
modelo conceptual a los procedimientos almacenados en la base de datos subyacente. Los procedimientos
almacenados a los que están asignados las funciones de modificación se deben declarar en el modelo de
almacenamiento. Para obtener más información, vea elemento function (SSDL).

NOTE
Si no asigna las tres operaciones de inserción, actualización o eliminación de un tipo de entidad a los procedimientos
almacenados, se producirá un error en las operaciones no asignadas si se ejecutan en tiempo de ejecución y se genera un
UpdateException.
NOTE
Si las funciones de modificación para una entidad de una jerarquía de herencia están asignadas a procedimientos
almacenados, entonces las funciones de modificación para todos los tipos de la jerarquía deben estar asignadas a
procedimientos almacenados.

El elemento ModificationFunctionMapping puede ser un elemento secundario del elemento


EntityTypeMapping o el elemento AssociationSetMapping.
El elemento ModificationFunctionMapping puede tener los siguientes elementos secundarios:
DeleteFunction (cero o uno)
InsertFunction (cero o uno)
UpdateFunction (cero o uno)
No hay atributos aplicables al elemento ModificationFunctionMapping .
Ejemplo
En el ejemplo siguiente se muestra la asignación de conjunto de entidades para el conjunto de entidades
People del modelo School. Además de la asignación de columnas para el tipo de entidad Person , se muestra la
asignación de las funciones INSERT, Update y DELETE del tipo Person . Las funciones asignadas se declaran en
el modelo de almacenamiento.
<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<ScalarProperty Name="EnrollmentDate"
ColumnName="EnrollmentDate" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate" />
<ScalarProperty Name="HireDate" ParameterName="HireDate" />
<ScalarProperty Name="FirstName" ParameterName="FirstName" />
<ScalarProperty Name="LastName" ParameterName="LastName" />
<ResultBinding Name="PersonID" ColumnName="NewPersonID" />
</InsertFunction>
<UpdateFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate"
Version="Current" />
<ScalarProperty Name="HireDate" ParameterName="HireDate"
Version="Current" />
<ScalarProperty Name="FirstName" ParameterName="FirstName"
Version="Current" />
<ScalarProperty Name="LastName" ParameterName="LastName"
Version="Current" />
<ScalarProperty Name="PersonID" ParameterName="PersonID"
Version="Current" />
</UpdateFunction>
<DeleteFunction FunctionName="[Link]">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
</EntitySetMapping>

Ejemplo
En el ejemplo siguiente se muestra la asignación del conjunto de asociaciones para el conjunto de asociaciones
CourseInstructor en el modelo School. Además de la asignación de columnas para la Asociación
CourseInstructor , se muestra la asignación de las funciones de inserción y eliminación de la Asociación
CourseInstructor . Las funciones asignadas se declaran en el modelo de almacenamiento.
<AssociationSetMapping Name="CourseInstructor"
TypeName="[Link]"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="[Link]">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>

QueryView (Elemento) (MSL)


El elemento Quer yView del lenguaje de especificación de asignaciones (MSL) define una asignación de solo
lectura entre un tipo de entidad o una asociación en el modelo conceptual y una tabla en la base de datos
subyacente. La asignación se define con una consulta Entity SQL que se evalúa con el modelo de
almacenamiento y se expresa el conjunto de resultados en términos de una entidad o asociación en el modelo
conceptual. Dado que las vistas de consulta son de solo lectura, no puede utilizar los comandos de actualización
estándar para actualizar los tipos que se definen mediante vistas de consulta. Puede realizar las actualizaciones
de estos tipos utilizando funciones de modificación. Para obtener más información, vea cómo: asignar funciones
de modificación a procedimientos almacenados.

NOTE
En el elemento Quer yView , no se admiten Entity SQL expresiones que contengan GroupBy , agregados de grupo o
propiedades de navegación.

El elemento Quer yView puede ser un elemento secundario del elemento EntitySetMapping o del elemento
AssociationSetMapping. En el primer caso, la vista de consulta define una asignación de solo lectura para una
entidad del modelo conceptual. En el último caso, la vista de consulta define una asignación de solo lectura para
una asociación del modelo conceptual.
NOTE
Si el elemento AssociationSetMapping es para una asociación con una restricción referencial, se omite el elemento
AssociationSetMapping . Para obtener más información, vea ReferentialConstraint (elemento) (CSDL).

El elemento Quer yView no puede tener elementos secundarios.


Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Quer yView .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

TypeName No El nombre del tipo de modelo


conceptual que se está asignando
mediante la vista de consulta.

Ejemplo
En el ejemplo siguiente se muestra el elemento Quer yView como elemento secundario del elemento
EntitySetMapping y se define una asignación de vista de consulta para el tipo de entidad Depar tment en el
modelo School.

<EntitySetMapping Name="Departments" >


<QueryView>
SELECT VALUE [Link]([Link],
[Link],
[Link],
[Link])
FROM [Link] AS d
WHERE [Link] > 150000
</QueryView>
</EntitySetMapping>

Dado que la consulta solo devuelve un subconjunto de los miembros del tipo Depar tment en el modelo de
almacenamiento, el tipo Depar tment del modelo School se ha modificado según esta asignación de la manera
siguiente:

<EntityType Name="Department">
<Key>
<PropertyRef Name="DepartmentID" />
</Key>
<Property Type="Int32" Name="DepartmentID" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false"
MaxLength="50" FixedLength="false" Unicode="true" />
<Property Type="Decimal" Name="Budget" Nullable="false"
Precision="19" Scale="4" />
<Property Type="DateTime" Name="StartDate" Nullable="false" />
<NavigationProperty Name="Courses"
Relationship="SchoolModel.FK_Course_Department"
FromRole="Department" ToRole="Course" />
</EntityType>

Ejemplo
En el ejemplo siguiente se muestra el elemento Quer yView como elemento secundario de un elemento
AssociationSetMapping y se define una asignación de solo lectura para la FK_Course_Department asociación
en el modelo School.
<EntityContainerMapping StorageEntityContainer="SchoolModelStoreContainer"
CdmEntityContainer="SchoolEntities">
<EntitySetMapping Name="Courses" >
<QueryView>
SELECT VALUE [Link]([Link],
[Link],
[Link])
FROM [Link] AS c
</QueryView>
</EntitySetMapping>
<EntitySetMapping Name="Departments" >
<QueryView>
SELECT VALUE [Link]([Link],
[Link],
[Link],
[Link])
FROM [Link] AS d
WHERE [Link] > 150000
</QueryView>
</EntitySetMapping>
<AssociationSetMapping Name="FK_Course_Department" >
<QueryView>
SELECT VALUE SchoolModel.FK_Course_Department(
CREATEREF([Link], row([Link]), [Link]),
CREATEREF([Link], row([Link])) )
FROM [Link] AS c
</QueryView>
</AssociationSetMapping>
</EntityContainerMapping>

Comentarios
Puede definir vistas de consulta para habilitar los escenarios siguientes:
Defina una entidad en el modelo conceptual que no incluya todas las propiedades de la entidad en el modelo
de almacenamiento. Esto incluye propiedades que no tienen valores predeterminados y no admiten valores
null .
Asigne las columnas calculadas del modelo de almacenamiento a las propiedades de los tipos de entidad del
modelo conceptual.
Defina una asignación en la que las condiciones utilizadas para dividir las entidades del modelo conceptual
no se basen en la igualdad. Cuando se especifica una asignación condicional mediante el elemento
Condition , la condición proporcionada debe ser igual al valor especificado. Para obtener más información,
vea condition (elemento) (MSL).
Asigne la misma columna en el modelo de almacenamiento a varios tipos en el modelo conceptual.
Asigne varios tipos a la misma tabla.
Defina asociaciones en el modelo conceptual que no se basen en claves externas en el esquema relacional.
Utilice la lógica de negocios personalizada para establecer el valor de las propiedades del modelo conceptual.
Por ejemplo, podría asignar el valor de cadena "T" en el origen de datos a un valor true , un valor booleano,
en el modelo conceptual.
Defina filtros condicionales para los resultados de la consulta.
Aplique menos restricciones en los datos del modelo conceptual que en el modelo de almacenamiento. Por
ejemplo, podría hacer que una propiedad en el modelo conceptual acepte valores NULL incluso si la columna
a la que está asignada no admite valores null .
Las consideraciones siguientes se aplican al definir las vistas de consulta para las entidades:
Las vistas de consulta son de solo lectura. Solo puede realizar las actualizaciones a las entidades utilizando
las funciones de modificación.
Al definir un tipo de entidad mediante una vista de consulta, también debe definir todas las entidades
relacionadas mediante vistas de consulta.
Al asignar una asociación varios a varios a una entidad en el modelo de almacenamiento que representa una
tabla de vínculos en el esquema relacional, debe definir un elemento Quer yView en el elemento
AssociationSetMapping para esta tabla de vínculos.
Las vistas de consulta se deben definir para todos los tipos de una jerarquía de tipos. Puede hacer esto de las
siguientes maneras:
Con un único elemento Quer yView que especifica una única Entity SQL consulta que devuelve una
Unión de todos los tipos de entidad de la jerarquía.
Con un único elemento Quer yView que especifica una única Entity SQL consulta que usa el operador
Case para devolver un tipo de entidad específico en la jerarquía basándose en una condición
específica.
Con un elemento Quer yView adicional para un tipo específico en la jerarquía. En este caso, use el
atributo TypeName del elemento Quer yView para especificar el tipo de entidad para cada vista.
Cuando se define una vista de consulta, no se puede especificar el atributo StorageSetName en el elemento
EntitySetMapping .
Cuando se define una vista de consulta, el elemento EntitySetMapping no puede contener también
asignaciones de propiedades .

ResultBinding (Elemento) (MSL)


El elemento ResultBinding del lenguaje de especificación de asignaciones (MSL) asigna los valores de columna
devueltos por los procedimientos almacenados a las propiedades de entidad del modelo conceptual cuando las
funciones de modificación de tipo de entidad se asignan a los procedimientos almacenados en la base de datos
subyacente. Por ejemplo, cuando un procedimiento almacenado de inserción devuelve el valor de una columna
de identidad, el elemento ResultBinding asigna el valor devuelto a una propiedad de tipo de entidad en el
modelo conceptual.
El elemento ResultBinding puede ser secundario del elemento InsertFunction o del elemento UpdateFunction.
El elemento ResultBinding no puede tener elementos secundarios.
Atributos aplicables
En la tabla siguiente se describen los atributos aplicables al elemento ResultBinding :

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la propiedad de entidad


del modelo conceptual que se está
asignando.

ColumnName Sí El nombre de la columna que se está


asignando.

Ejemplo
El ejemplo siguiente se basa en el modelo School y muestra un elemento Inser tFunction que se usa para
asignar la función de inserción del tipo de entidad Person al procedimiento almacenado Inser tPerson . (El
procedimiento almacenado Inser tPerson se muestra a continuación y se declara en el modelo de
almacenamiento). Se usa un elemento ResultBinding para asignar un valor de columna devuelto por el
procedimiento almacenado (NewPersonID ) a una propiedad de tipo de entidad (PersonID ).
<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate" />
<ScalarProperty Name="HireDate" ParameterName="HireDate" />
<ScalarProperty Name="FirstName" ParameterName="FirstName" />
<ScalarProperty Name="LastName" ParameterName="LastName" />
<ResultBinding Name="PersonID" ColumnName="NewPersonID" />
</InsertFunction>
<UpdateFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate"
Version="Current" />
<ScalarProperty Name="HireDate" ParameterName="HireDate"
Version="Current" />
<ScalarProperty Name="FirstName" ParameterName="FirstName"
Version="Current" />
<ScalarProperty Name="LastName" ParameterName="LastName"
Version="Current" />
<ScalarProperty Name="PersonID" ParameterName="PersonID"
Version="Current" />
</UpdateFunction>
<DeleteFunction FunctionName="[Link]">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>

En el siguiente código de Transact-SQL se describe el procedimiento almacenado Inser tPerson :

CREATE PROCEDURE [dbo].[InsertPerson]


@LastName nvarchar(50),
@FirstName nvarchar(50),
@HireDate datetime,
@EnrollmentDate datetime
AS
INSERT INTO [Link] (LastName,
FirstName,
HireDate,
EnrollmentDate)
VALUES (@LastName,
@FirstName,
@HireDate,
@EnrollmentDate);
SELECT SCOPE_IDENTITY() as NewPersonID;

ResultMapping (Elemento) (MSL)


El elemento ResultMapping del lenguaje de especificación de asignaciones (MSL) define la asignación entre
una importación de función en el modelo conceptual y un procedimiento almacenado en la base de datos
subyacente cuando se cumplen las condiciones siguientes:
La importación de función devuelve un tipo de entidad del modelo conceptual o un tipo complejo.
Los nombres de las columnas devueltas por el procedimiento almacenado no coinciden exactamente con los
nombres de las propiedades del tipo de entidad o el tipo complejo.
De forma predeterminada, la asignación entre las columnas devueltas por un procedimiento almacenado y un
tipo de entidad o un tipo complejo se basa en los nombres de las propiedades y de las columnas. Si los nombres
de columna no coinciden exactamente con los nombres de propiedad, debe utilizar el elemento
ResultMapping para definir la asignación. Para obtener un ejemplo de la asignación predeterminada, vea
FunctionImportMapping (elemento) (MSL).
El elemento ResultMapping es un elemento secundario del elemento FunctionImportMapping.
El elemento ResultMapping puede tener los siguientes elementos secundarios:
EntityTypeMapping (cero o más)
ComplexTypeMapping
No hay atributos aplicables al elemento ResultMapping .
Ejemplo
Observe el siguiente procedimiento almacenado:

CREATE PROCEDURE [dbo].[GetGrades]


@student_Id int
AS
SELECT EnrollmentID as enroll_id,
Grade as grade,
CourseID as course_id,
StudentID as student_id
FROM [Link]
WHERE StudentID = @student_Id

Considere también el siguiente tipo de entidad del modelo conceptual:

<EntityType Name="StudentGrade">
<Key>
<PropertyRef Name="EnrollmentID" />
</Key>
<Property Name="EnrollmentID" Type="Int32" Nullable="false"
annotation:StoreGeneratedPattern="Identity" />
<Property Name="CourseID" Type="Int32" Nullable="false" />
<Property Name="StudentID" Type="Int32" Nullable="false" />
<Property Name="Grade" Type="Decimal" Precision="3" Scale="2" />
</EntityType>

Para crear una importación de función que devuelva instancias del tipo de entidad anterior, la asignación entre
las columnas devueltas por el procedimiento almacenado y el tipo de entidad debe definirse en un elemento
ResultMapping :

<FunctionImportMapping FunctionImportName="GetGrades"
FunctionName="[Link]" >
<ResultMapping>
<EntityTypeMapping TypeName="[Link]">
<ScalarProperty Name="EnrollmentID" ColumnName="enroll_id"/>
<ScalarProperty Name="CourseID" ColumnName="course_id"/>
<ScalarProperty Name="StudentID" ColumnName="student_id"/>
<ScalarProperty Name="Grade" ColumnName="grade"/>
</EntityTypeMapping>
</ResultMapping>
</FunctionImportMapping>

ScalarProperty (Elemento) (MSL)


El elemento ScalarProper ty del lenguaje de especificación de asignaciones (MSL) asigna una propiedad en un
tipo de entidad del modelo conceptual, un tipo complejo o una asociación a una columna de tabla o a un
parámetro de procedimiento almacenado en la base de datos subyacente.
NOTE
Los procedimientos almacenados a los que están asignados las funciones de modificación se deben declarar en el modelo
de almacenamiento. Para obtener más información, vea elemento function (SSDL).

El elemento ScalarProper ty puede ser un elemento secundario de los siguientes elementos:


MappingFragment
InsertFunction
UpdateFunction
DeleteFunction
EndProperty
ComplexProperty
ResultMapping
Como elemento secundario del elemento MappingFragment , ComplexProper ty o EndProper ty , el
elemento ScalarProper ty asigna una propiedad del modelo conceptual a una columna de la base de datos.
Como elemento secundario del elemento Inser tFunction , UpdateFunction o DeleteFunction , el elemento
ScalarProper ty asigna una propiedad del modelo conceptual a un parámetro de procedimiento almacenado.
El elemento ScalarProper ty no puede tener elementos secundarios.
Atributos aplicables
Los atributos que se aplican al elemento ScalarProper ty difieren en función del rol del elemento.
En la tabla siguiente se describen los atributos que se pueden aplicar cuando el elemento ScalarProper ty se
utiliza para asignar una propiedad del modelo conceptual a una columna de la base de datos:

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la propiedad del modelo


conceptual que se está asignando.

ColumnName Sí El nombre de la columna de tabla que


se está asociando.

En la tabla siguiente se describen los atributos aplicables al elemento ScalarProper ty cuando se utiliza para
asignar una propiedad del modelo conceptual a un parámetro de procedimiento almacenado:

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la propiedad del modelo


conceptual que se está asignando.

ParameterName Sí El nombre del parámetro que se está


asignando.

Versión No Actual o original , dependiendo de si


el valor actual o el valor original de la
propiedad se deben usar para las
comprobaciones de simultaneidad.

Ejemplo
En el ejemplo siguiente se muestra el elemento ScalarProper ty utilizado de dos maneras:
Para asignar las propiedades del tipo de entidad Person a las columnas de la tabla Person .
Para asignar las propiedades del tipo de entidad Person a los parámetros del procedimiento almacenado
UpdatePerson . Los procedimientos almacenados se declaran en el modelo de almacenamiento.

<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="[Link]">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<ScalarProperty Name="EnrollmentDate"
ColumnName="EnrollmentDate" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate" />
<ScalarProperty Name="HireDate" ParameterName="HireDate" />
<ScalarProperty Name="FirstName" ParameterName="FirstName" />
<ScalarProperty Name="LastName" ParameterName="LastName" />
<ResultBinding Name="PersonID" ColumnName="NewPersonID" />
</InsertFunction>
<UpdateFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate"
Version="Current" />
<ScalarProperty Name="HireDate" ParameterName="HireDate"
Version="Current" />
<ScalarProperty Name="FirstName" ParameterName="FirstName"
Version="Current" />
<ScalarProperty Name="LastName" ParameterName="LastName"
Version="Current" />
<ScalarProperty Name="PersonID" ParameterName="PersonID"
Version="Current" />
</UpdateFunction>
<DeleteFunction FunctionName="[Link]">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
</EntitySetMapping>

Ejemplo
En el ejemplo siguiente se muestra el elemento ScalarProper ty que se usa para asignar las funciones de
inserción y eliminación de una asociación del modelo conceptual a los procedimientos almacenados en la base
de datos. Los procedimientos almacenados se declaran en el modelo de almacenamiento.
<AssociationSetMapping Name="CourseInstructor"
TypeName="[Link]"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="[Link]">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>

UpdateFunction (Elemento) (MSL)


El elemento UpdateFunction del lenguaje de especificación de asignaciones (MSL) asigna la función de
actualización de un tipo de entidad del modelo conceptual a un procedimiento almacenado en la base de datos
subyacente. Los procedimientos almacenados a los que están asignados las funciones de modificación se deben
declarar en el modelo de almacenamiento. Para obtener más información, vea elemento function (SSDL).

NOTE
Si no asigna las tres operaciones de inserción, actualización o eliminación de un tipo de entidad a los procedimientos
almacenados, se producirá un error en las operaciones no asignadas si se ejecutan en tiempo de ejecución y se genera un
UpdateException.

El elemento UpdateFunction puede ser un elemento secundario del elemento ModificationFunctionMapping y


aplicarse al elemento EntityTypeMapping.
El elemento UpdateFunction puede tener los siguientes elementos secundarios:
AssociationEnd (cero o más)
ComplexProperty (cero o más)
ResultBinding (cero o uno)
ScalarProperty (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento UpdateFunction .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE


N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombrefunción Sí El nombre completo, calificado con el


espacio de nombres, del procedimiento
almacenado al que la función de
actualización está asignada. El
procedimiento almacenado se debe
declarar en el modelo de
almacenamiento.

RowsAffectedParameter No El nombre del parámetro de salida que


devuelve el número de filas afectadas.

Ejemplo
El ejemplo siguiente se basa en el modelo School y muestra el elemento UpdateFunction que se usa para
asignar la función de actualización del tipo de entidad Person al procedimiento almacenado UpdatePerson . El
procedimiento almacenado UpdatePerson se declara en el modelo de almacenamiento.

<EntityTypeMapping TypeName="[Link]">
<ModificationFunctionMapping>
<InsertFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate" />
<ScalarProperty Name="HireDate" ParameterName="HireDate" />
<ScalarProperty Name="FirstName" ParameterName="FirstName" />
<ScalarProperty Name="LastName" ParameterName="LastName" />
<ResultBinding Name="PersonID" ColumnName="NewPersonID" />
</InsertFunction>
<UpdateFunction FunctionName="[Link]">
<ScalarProperty Name="EnrollmentDate"
ParameterName="EnrollmentDate"
Version="Current" />
<ScalarProperty Name="HireDate" ParameterName="HireDate"
Version="Current" />
<ScalarProperty Name="FirstName" ParameterName="FirstName"
Version="Current" />
<ScalarProperty Name="LastName" ParameterName="LastName"
Version="Current" />
<ScalarProperty Name="PersonID" ParameterName="PersonID"
Version="Current" />
</UpdateFunction>
<DeleteFunction FunctionName="[Link]">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
Especificación SSDL
12/03/2021 • 63 minutes to read

El lenguaje de definición de esquemas de almacenamiento (SSDL) es un lenguaje basado en XML que describe
el modelo de almacenamiento de una aplicación Entity Framework.
En una aplicación Entity Framework, los metadatos del modelo de almacenamiento se cargan desde un archivo.
SSDL (escrito en SSDL) en una instancia de System. Data. Metadata. Edm. StoreItemCollection y es accesible
mediante métodos en la clase System. Data. Metadata. Edm. MetadataWorkspace. Entity Framework usa los
metadatos del modelo de almacenamiento para traducir las consultas en el modelo conceptual para almacenar
comandos específicos.
El Entity Framework Designer (EF Designer) almacena la información del modelo de almacenamiento en un
archivo. edmx en tiempo de diseño. En tiempo de compilación, Entity Designer usa información en un archivo.
edmx para crear el archivo. SSDL que Entity Framework necesita en tiempo de ejecución.
Las versiones de SSDL se diferencian por los espacios de nombres XML.

VERSIÓ N DE SSDL ESPA C IO DE N O M B RES XM L

SSDL v1 [Link]

SSDL V2 [Link]

SSDL V3 [Link]

Association (Elemento) (SSDL)


Un elemento Association en el lenguaje de definición de esquemas de almacenamiento (SSDL) especifica las
columnas de tabla que participan en una restricción FOREIGN KEY en la base de datos subyacente. Dos
elementos End secundarios necesarios especifican las tablas de los extremos de la asociación y la multiplicidad
en cada extremo. Un elemento ReferentialConstraint opcional especifica los extremos principal y dependiente de
la asociación así como las columnas participantes. Si no hay ningún elemento ReferentialConstraint , se debe
usar un elemento AssociationSetMapping para especificar las asignaciones de columnas para la asociación.
El elemento Association puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o uno)
End (exactamente dos)
ReferentialConstraint (cero o uno)
Elementos Annotation (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Association .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la restricción de clave


externa correspondiente de la base de
datos subyacente.
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Association .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que utiliza un elemento ReferentialConstraint
para especificar las columnas que participan en la restricción FOREIGN KEY externa de **FK _ ** :

<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="[Link]" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="[Link]" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>

AssociationSet (Elemento) (SSDL)


El elemento AssociationSet en el lenguaje de definición de esquemas de almacenamiento (SSDL) representa
una restricción de clave externa entre dos tablas en la base de datos subyacente. Las columnas de la tabla que
participan en la restricción de clave externa se especifican en un elemento Association. El elemento Association
que corresponde a un elemento AssociationSet determinado se especifica en el atributo Association del
elemento AssociationSet .
Los conjuntos de asociaciones SSDL están asignados a conjuntos de asociaciones CSDL mediante un elemento
AssociationSetMapping. Sin embargo, si la Asociación CSDL para un conjunto de asociaciones CSDL
determinado se define mediante un elemento ReferentialConstraint, no es necesario ningún elemento
AssociationSetMapping correspondiente. En este caso, si hay un elemento AssociationSetMapping , las
asignaciones que define se reemplazarán por el elemento ReferentialConstraint .
El elemento AssociationSet puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o uno)
End (cero o dos)
Elementos Annotation (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento AssociationSet .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la restricción de clave


externa representada por el conjunto
de asociaciones.
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Asociación Sí El nombre de la asociación que define


las columnas que participan en la
restricción de clave externa.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento AssociationSet
. Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento AssociationSet que representa la FK_CustomerOrders
restricción FOREIGN KEY en la base de datos subyacente:

<AssociationSet Name="FK_CustomerOrders"
Association="[Link].FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>

CollectionType (elemento) (SSDL)


El elemento CollectionType del lenguaje de definición de esquemas de almacenamiento (SSDL) especifica que
el tipo de valor devuelto de una función es una colección. El elemento CollectionType es un elemento
secundario del elemento ReturnType. El tipo de colección se especifica mediante el elemento secundario
RowType:

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento CollectionType
. Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra una función que usa un elemento CollectionType para especificar que la
función devuelve una colección de filas.

<Function Name="GetProducts" IsComposable="true" Schema="dbo">


<ReturnType>
<CollectionType>
<RowType>
<Property Name="ProductID" Type="int" Nullable="false" />
<Property Name="CategoryID" Type="bigint" Nullable="false" />
<Property Name="ProductName" Type="nvarchar" MaxLength="40" Nullable="false" />
<Property Name="UnitPrice" Type="money" />
<Property Name="Discontinued" Type="bit" />
</RowType>
</CollectionType>
</ReturnType>
</Function>
CommandText (Elemento) (SSDL)
El elemento CommandText del lenguaje de definición de esquemas de almacenamiento (SSDL) es un elemento
secundario del elemento function que permite definir una instrucción SQL que se ejecuta en la base de datos. El
elemento CommandText permite agregar funcionalidad similar a un procedimiento almacenado en la base de
datos, pero se define el elemento CommandText en el modelo de almacenamiento.
El elemento CommandText no puede tener elementos secundarios. El cuerpo del elemento CommandText
debe ser una instrucción SQL válida para la base de datos subyacente.
No hay atributos aplicables al elemento CommandText .
Ejemplo
En el ejemplo siguiente se muestra un elemento function con un elemento CommandText secundario.
Exponga la función UpdateProductInOrder como un método en el ObjectContext importándolos en el
modelo conceptual.

<Function Name="UpdateProductInOrder" IsComposable="false">


<CommandText>
UPDATE Orders
SET ProductId = @productId
WHERE OrderId = @orderId;
</CommandText>
<Parameter Name="productId"
Mode="In"
Type="int"/>
<Parameter Name="orderId"
Mode="In"
Type="int"/>
</Function>

DefiningQuery (Elemento) (SSDL)


El elemento DefiningQuer y del lenguaje de definición de esquemas de almacenamiento (SSDL) permite
ejecutar una instrucción SQL directamente en la base de datos subyacente. El elemento DefiningQuer y se
utiliza normalmente como una vista de base de datos, pero la vista se define en el modelo de almacenamiento
en lugar de en la base de datos. La vista definida en un elemento DefiningQuer y se puede asignar a un tipo de
entidad en el modelo conceptual a través de un elemento EntitySetMapping. Estas asignaciones son de solo
lectura.
La sintaxis de SSDL siguiente muestra la declaración de un EntitySet seguido del elemento DefiningQuer y
que contiene una consulta utilizada para recuperar la vista.

<Schema>
<EntitySet Name="Tables" EntityType="[Link]">
<DefiningQuery>
SELECT TABLE_CATALOG,
'test' as TABLE_SCHEMA,
TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
</DefiningQuery>
</EntitySet>
</Schema>

Puede usar procedimientos almacenados en el Entity Framework para habilitar escenarios de lectura y escritura
en las [Link] usar una vista del origen de datos o una vista de Entity SQL como tabla base para recuperar
datos y para el procesamiento de cambios por parte de los procedimientos almacenados.
Puede usar el elemento DefiningQuer y para tener como destino Microsoft SQL Server Compact 3,5. Aunque
SQL Server Compact 3,5 no admite procedimientos almacenados, puede implementar una funcionalidad similar
con el elemento DefiningQuer y . Otro caso donde puede ser útil es en la creación de procedimientos
almacenados para resolver una desigualdad entre los tipos de datos utilizados en el lenguaje de programación y
los del origen de datos. Podría escribir una DefiningQuer y que toma un determinado conjunto de parámetros
y, a continuación, llama a un procedimiento almacenado con un conjunto diferente de parámetros, por ejemplo,
un procedimiento almacenado que elimina datos.

Dependent (Elemento) (SSDL)


El elemento dependiente del lenguaje de definición de esquemas de almacenamiento (SSDL) es un elemento
secundario del elemento ReferentialConstraint que define el extremo dependiente de una restricción FOREIGN
KEY (también denominada restricción referencial). El elemento dependiente especifica la columna (o columnas)
de una tabla que hace referencia a una columna de clave principal (o columnas). Los elementos Proper tyRef
especifican a qué columnas se hace referencia. El elemento principal especifica las columnas de clave principal a
las que hacen referencia las columnas que se especifican en el elemento dependiente .
El elemento dependiente puede tener los elementos secundarios siguientes (en el orden mostrado):
PropertyRef (uno o varios)
Elementos Annotation (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento dependiente .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Rol Sí El mismo valor que el atributo role (si


se usa) del elemento end
correspondiente; de lo contrario, el
nombre de la tabla que contiene la
columna de referencia.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento dependiente .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que utiliza un elemento ReferentialConstraint
para especificar las columnas que participan en la restricción de clave externa de FK _ CustomerOrders . El
elemento dependiente especifica la columna CustomerID de la tabla Order como el extremo dependiente de
la restricción.
<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="[Link]" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="[Link]" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>

Documentation (Elemento) (SSDL)


El elemento Documentation del lenguaje de definición de esquemas de almacenamiento (SSDL) se puede usar
para proporcionar información sobre un objeto que se define en un elemento primario.
El elemento Documentation puede tener los elementos secundarios siguientes (en el orden mostrado):
Summar y : breve descripción del elemento primario. (cero o un elemento).
LongDescription : una descripción amplia del elemento primario. (cero o un elemento).
Atributos aplicables
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
Documentation . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio
de nombres XML reservado para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres
completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra el elemento Documentation como un elemento secundario de un elemento
EntityType.

<EntityType Name="Customers">
<Documentation>
<Summary>Summary here.</Summary>
<LongDescription>Long description here.</LongDescription>
</Documentation>
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>

End (Elemento) (SSDL)


El elemento End en el lenguaje de definición de esquemas de almacenamiento (SSDL) especifica la tabla y el
número de filas de un extremo de una restricción FOREIGN KEY en la base de datos subyacente. El elemento
End puede ser un elemento secundario del elemento Association o del elemento AssociationSet. En cada caso,
los posibles elementos secundarios y atributos aplicables son diferentes.
Elemento End como elemento secundario del elemento Association
Un elemento End (como elemento secundario del elemento Association ) especifica la tabla y el número de
filas al final de una restricción FOREIGN KEY con los atributos Type y Multiplicity , respectivamente. Los
extremos de una restricción de clave externa se definen como parte de una asociación SSDL, la cual debe tener
exactamente dos extremos.
Un elemento End puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
OnDelete (cero o un elemento)
Elementos Annotation (cero o más elementos)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento End cuando es el elemento
secundario de un elemento Association .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Tipo Sí El nombre completo del conjunto de


entidades SSDL que está en el extremo
de la restricción de clave externa.

Rol No El valor del atributo role en el


elemento principal o dependiente del
elemento ReferentialConstraint
correspondiente (si se usa).

Multiplicidad Sí 1 , 0.. 1 o, * dependiendo del número


de filas que puedan estar al final de la
restricción FOREIGN KEY.
1 indica que existe exactamente una
fila en el extremo de la restricción de
clave externa.
0.. 1 indica que hay cero o una fila en
el extremo de la restricción de clave
externa.
* indica que hay cero, una o más filas
en el extremo de la restricción de clave
externa.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento final . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que define la restricción de clave externa de FK _
CustomerOrders . Los valores de multiplicidad especificados en cada elemento final indican que muchas
filas de la tabla Orders se pueden asociar a una fila de la tabla Customers , pero solo se puede asociar una fila
de la tabla Customers a una fila de la tabla Orders . Además, el elemento aldelete indica que todas las filas de
la tabla Orders que hacen referencia a una fila determinada de la tabla Customers se eliminarán si se elimina
la fila de la tabla Customers .
<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="[Link]" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="[Link]" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>

Elemento End como elemento secundario del elemento AssociationSet


El elemento End (como elemento secundario del elemento AssociationSet ) especifica una tabla en un
extremo de una restricción FOREIGN KEY en la base de datos subyacente.
Un elemento End puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o uno)
Elementos Annotation (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento End cuando es el elemento
secundario de un elemento AssociationSet .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

EntitySet Sí El nombre del conjunto de entidades


SSDL que está en el extremo de la
restricción de clave externa.

Rol No El valor de uno de los atributos de rol


especificados en un elemento final del
elemento Association correspondiente.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento final . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer con un elemento AssociationSet con dos
elementos End :
<EntityContainer Name="ExampleModelStoreContainer">
<EntitySet Name="Customers"
EntityType="[Link]"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="[Link]"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="[Link].FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>

EntityContainer (Elemento) (SSDL)


Un elemento EntityContainer en el lenguaje de definición de esquemas de almacenamiento (SSDL) describe la
estructura del origen de datos subyacente en una aplicación Entity Framework: los conjuntos de entidades SSDL
(definidos en elementos EntitySet) representan las tablas de una base de datos, los tipos de entidad SSDL
(definidos en los elementos EntityType) representan las filas de una tabla, y los conjuntos de asociaciones
(definidos en los elementos AssociationSet) Un contenedor de entidades del modelo de almacenamiento se
asigna a un contenedor de entidades del modelo de conceptual a través del elemento EntityContainerMapping.
Un elemento EntityContainer puede tener cero o un elemento de documentación. Si hay un elemento de
documentación , debe preceder a todos los demás elementos secundarios.
Un elemento EntityContainer puede tener cero o más de los elementos secundarios siguientes (en el orden
mostrado):
EntitySet
AssociationSet
Elementos Annotation
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntityContainer .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre del contenedor de entidades.


Este nombre no puede contener
puntos (.).

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
EntityContainer . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de
nombres XML reservado para SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos
idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer que define dos conjuntos de entidades y un
conjunto de asociaciones. Observe que los nombres de los tipos de entidad y tipos de asociación están
calificados mediante el nombre del espacio de nombres del modelo conceptual.
<EntityContainer Name="ExampleModelStoreContainer">
<EntitySet Name="Customers"
EntityType="[Link]"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="[Link]"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="[Link].FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>

EntitySet (Elemento) (SSDL)


Un elemento EntitySet en el lenguaje de definición de esquemas de almacenamiento (SSDL) representa una
tabla o una vista en la base de datos subyacente. Un elemento EntityType de SSDL representa una fila de la tabla
o vista. El atributo EntityType de un elemento EntitySet especifica el tipo de entidad SSDL determinado que
representa las filas de un conjunto de entidades SSDL. La asignación entre un conjunto de entidades CSDL y un
conjunto de entidades SSDL se especifica en un elemento EntitySetMapping.
El elemento EntitySet puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
DefiningQuery (cero o un elemento)
Elementos Annotation
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntitySet .

NOTE
Algunos atributos (que no se enumeran aquí) pueden estar calificados con el alias de almacén . El Asistente para
actualizar modelo utiliza estos atributos al actualizar un modelo.

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre del conjunto de entidades.

EntityType Sí El nombre completo del tipo de


entidad para el que el conjunto de
entidades contiene las instancias.

Esquema No El esquema de base de datos.

Table No La tabla de base de datos.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento EntitySet . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer que tiene dos elementos EntitySet y un
elemento AssociationSet :

<EntityContainer Name="ExampleModelStoreContainer">
<EntitySet Name="Customers"
EntityType="[Link]"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="[Link]"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="[Link].FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>

EntityType (Elemento) (SSDL)


Un elemento EntityType en el lenguaje de definición de esquemas de almacenamiento (SSDL) representa una
fila de una tabla o vista de la base de datos subyacente. Un elemento EntitySet de SSDL representa la tabla o
vista donde están las filas. El atributo EntityType de un elemento EntitySet especifica el tipo de entidad SSDL
determinado que representa las filas de un conjunto de entidades SSDL. La asignación entre un tipo de entidad
SSDL y un tipo de entidad CSDL se especifica en un elemento EntityTypeMapping.
El elemento EntityType puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o un elemento)
Key (cero o un elemento)
Elementos Annotation
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento EntityType .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del tipo de entidad. Este


valor normalmente es igual que el
nombre de la tabla en la que el tipo de
entidad representa una fila. Este valor
no puede contener ningún punto (.).

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento EntityType . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType con dos propiedades:
<EntityType Name="Customers">
<Documentation>
<Summary>Summary here.</Summary>
<LongDescription>Long description here.</LongDescription>
</Documentation>
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>

Function (Elemento) (SSDL)


El elemento function del lenguaje de definición de esquemas de almacenamiento (SSDL) especifica un
procedimiento almacenado que existe en la base de datos subyacente.
El elemento function puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o uno)
Parámetro (cero o más)
CommandText (cero o uno)
ReturnType (cero o más)
Elementos Annotation (cero o más)
Un tipo de valor devuelto para una función debe especificarse con el elemento ReturnType o el atributo
ReturnType (consulte a continuación), pero no ambos.
Los procedimientos almacenados que se especifican en el modelo de almacenamiento se pueden importar en el
modelo conceptual de una aplicación. Para obtener más información, vea consultar con procedimientos
almacenados. El elemento de función también se puede utilizar para definir funciones personalizadas en el
modelo de almacenamiento.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento de función .

NOTE
Algunos atributos (que no se enumeran aquí) pueden estar calificados con el alias de almacén . El Asistente para
actualizar modelo utiliza estos atributos al actualizar un modelo.

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre del procedimiento


almacenado.

ReturnType No El tipo de valor devuelto del


procedimiento almacenado.

Agregada No True si el procedimiento almacenado


devuelve un valor agregado; en caso
contrario, false .

BuiltIn No True si la función es una función


integrada1 ; en caso contrario, false .
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

StoreFunctionName No Nombre del procedimiento


almacenado.

NiladicFunction No True si la función es una función


niládicas2 ; De lo contrario, false .

IsComposable No True si la función es una función que


admite composición3 ; De lo contrario,
false .

ParameterTypeSemantics No La enumeración que define la


semántica de tipos que se utiliza para
resolver sobrecargas de función. La
enumeración se define en el manifiesto
del proveedor por cada definición de
función. El valor predeterminado es
AllowImplicitConversion .

Esquema No El nombre del esquema donde se


define el procedimiento almacenado.

1 una función integrada es una función que se define en la base de datos. Para obtener
información sobre las
funciones que se definen en el modelo de almacenamiento, vea CommandText (elemento) (SSDL).
2 una función niládicas es una función que no acepta parámetros y, cuando se llama, no requiere paréntesis.

3 dos funciones son comparables si la salida de una función puede ser la entrada para la otra función.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento de función . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento function que corresponde al procedimiento almacenado
UpdateOrderQuantity . El procedimiento almacenado acepta dos parámetros y no devuelve ningún valor.

<Function Name="UpdateOrderQuantity"
Aggregate="false"
BuiltIn="false"
NiladicFunction="false"
IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="orderId" Type="int" Mode="In" />
<Parameter Name="newQuantity" Type="int" Mode="In" />
</Function>

Key (Elemento) (SSDL)


El elemento key del lenguaje de definición de esquemas de almacenamiento (SSDL) representa la clave
principal de una tabla en la base de datos subyacente. Key es un elemento secundario de un elemento
EntityType, que representa una fila de una tabla. La clave principal se define en el elemento key haciendo
referencia a uno o varios elementos de propiedad que se definen en el elemento EntityType .
El elemento key puede tener los elementos secundarios siguientes (en el orden mostrado):
PropertyRef (uno o varios)
Elementos Annotation
No hay atributos aplicables al elemento key .
Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType con una clave que hace referencia a una propiedad:

<EntityType Name="Customers">
<Documentation>
<Summary>Summary here.</Summary>
<LongDescription>Long description here.</LongDescription>
</Documentation>
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>

OnDelete (Elemento) (SSDL)


El elemento aldelete del lenguaje de definición de esquemas de almacenamiento (SSDL) refleja el
comportamiento de la base de datos cuando se elimina una fila que participa en una restricción FOREIGN KEY.
Si la acción se establece en Cascade , también se eliminarán las filas que hacen referencia a una fila que se va a
eliminar. Si la acción se establece en None , las filas que hacen referencia a una fila que se va a eliminar también
no se eliminan. Un elemento aldelete es un elemento secundario de un elemento end.
Un elemento aldelete puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o uno)
Elementos Annotation (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento aleliminar .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Acción Sí Cascade o None . (El valor


restringido es válido, pero tiene el
mismo comportamiento que
ninguno ).

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento aleliminar . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que define la restricción de clave externa de FK _
CustomerOrders . El elemento aldelete indica que se eliminarán todas las filas de la tabla Orders que hagan
referencia a una fila determinada de la tabla Customers si se elimina la fila de la tabla Customers .

<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="[Link]" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="[Link]" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>

Parameter (Elemento) (SSDL)


El elemento Parameter del lenguaje de definición de esquemas de almacenamiento (SSDL) es un elemento
secundario del elemento function que especifica los parámetros de un procedimiento almacenado en la base de
datos.
El elemento Parameter puede tener los elementos secundarios siguientes (en el orden mostrado):
Documentation (cero o uno)
Elementos Annotation (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Parameter .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre del parámetro.

Tipo Sí El tipo de parámetro.

Modo No In , out o INOUT dependiendo de si el


parámetro es un parámetro de
entrada, de salida o de entrada/salida.

MaxLength No Longitud máxima permitida del


parámetro.

Precisión No La precisión del parámetro.

Escala No La escala del parámetro.

SRID No Identificador de referencia del sistema


espacial. Válido solo para los
parámetros de los tipos espaciales.
Para obtener más información, vea
SRID y SRID (SQL Server).
NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Parameter . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento function que tiene dos elementos Parameter que especifican
parámetros de entrada:

<Function Name="UpdateOrderQuantity"
Aggregate="false"
BuiltIn="false"
NiladicFunction="false"
IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="orderId" Type="int" Mode="In" />
<Parameter Name="newQuantity" Type="int" Mode="In" />
</Function>

Principal (Elemento) (SSDL)


El elemento principal del lenguaje de definición de esquemas de almacenamiento (SSDL) es un elemento
secundario del elemento ReferentialConstraint que define el extremo principal de una restricción FOREIGN KEY
(también denominada restricción referencial). El elemento principal especifica la columna (o columnas) de
clave principal de una tabla a la que hace referencia otra columna (o columnas). Los elementos Proper tyRef
especifican a qué columnas se hace referencia. El elemento dependiente especifica las columnas que hacen
referencia a las columnas de clave principal que se especifican en el elemento principal .
El elemento principal puede tener los elementos secundarios siguientes (en el orden mostrado):
PropertyRef (uno o varios)
Elementos Annotation (cero o más)
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento principal .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Rol Sí El mismo valor que el atributo role (si


se usa) del elemento end
correspondiente; de lo contrario, el
nombre de la tabla que contiene la
columna a la que se hace referencia.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento principal . Sin
embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado para
CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Association que utiliza un elemento ReferentialConstraint
para especificar las columnas que participan en la restricción de clave externa de FK _ CustomerOrders . El
elemento principal especifica la columna CustomerID de la tabla Customer como el extremo principal de la
restricción.

<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="[Link]" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="[Link]" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>

Property (Elemento) (SSDL)


El elemento Proper ty del lenguaje de definición de esquemas de almacenamiento (SSDL) representa una
columna de una tabla en la base de datos subyacente. Los elementos de propiedad son elementos secundarios
de elementos EntityType, que representan las filas de una tabla. Cada elemento de propiedad definido en un
elemento EntityType representa una columna.
Un elemento Proper ty no puede tener elementos secundarios.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Proper ty .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí El nombre de la columna


correspondiente.

Tipo Sí El tipo de la columna correspondiente.

Admisión de valores NULL No True (valor predeterminado) o false ,


dependiendo de si la columna
correspondiente puede tener un valor
null.

DefaultValue No El valor predeterminado de la columna


correspondiente.

MaxLength No La longitud máxima de la columna


correspondiente.

FixedLength No True o false , dependiendo de si el


valor de columna correspondiente se
almacenará como una cadena de
longitud fija.
N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Precisión No La precisión de la columna


correspondiente.

Escala No La escala de la columna


correspondiente.

Unicode No True o false , dependiendo de si el


valor de columna correspondiente se
almacenará como una cadena Unicode.

Intercalación No Cadena que especifica la secuencia de


intercalación que se usará en el origen
de datos.

SRID No Identificador de referencia del sistema


espacial. Solo es válido para las
propiedades de los tipos espaciales.
Para obtener más información, vea
SRID y SRID (SQL Server).

StoreGeneratedPattern No None , Identity (si el valor de la


columna correspondiente es una
identidad que se genera en la base de
datos) o calculado (si el valor de la
columna correspondiente se calcula en
la base de datos). No es válido para las
propiedades RowType.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento de propiedad .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType con dos elementos Proper ty secundarios:

<EntityType Name="Customers">
<Documentation>
<Summary>Summary here.</Summary>
<LongDescription>Long description here.</LongDescription>
</Documentation>
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>

PropertyRef (Elemento) (SSDL)


El elemento Proper tyRef en el lenguaje de definición de esquemas de almacenamiento (SSDL) hace referencia
a una propiedad definida en un elemento EntityType para indicar que la propiedad realizará uno de los roles
siguientes:
Formar parte de la clave principal de la tabla que el EntityType representa. Se pueden usar uno o varios
elementos Proper tyRef para definir una clave principal. Para obtener más información, vea Key (Elemento).
Ser el extremo dependiente o principal de una restricción referencial. Para obtener más información, vea
ReferentialConstraint (Elemento).
El elemento Proper tyRef solo puede tener los siguientes elementos secundarios:
Documentation (cero o uno)
Elementos Annotation
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Proper tyRef .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Nombre Sí Nombre de la propiedad a la que se


hace referencia.

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento Proper tyRef .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para CSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se muestra un elemento Proper tyRef que se usa para definir una clave principal
haciendo referencia a una propiedad que se define en un elemento EntityType .

<EntityType Name="Customers">
<Documentation>
<Summary>Summary here.</Summary>
<LongDescription>Long description here.</LongDescription>
</Documentation>
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>

ReferentialConstraint (Elemento) (SSDL)


El elemento ReferentialConstraint del lenguaje de definición de esquemas de almacenamiento (SSDL)
representa una restricción de clave externa (también denominada restricción de integridad referencial) en la
base de datos subyacente. Los extremos principal y dependiente de la restricción se especifican mediante los
elementos secundarios Principal y Dependent, respectivamente. La referencia a las columnas que participan en
los extremos principal y dependiente se realiza mediante elementos PropertyRef.
El elemento ReferentialConstraint es un elemento secundario opcional del elemento Association. Si no se
utiliza un elemento ReferentialConstraint para asignar la restricción FOREIGN KEY especificada en el
elemento Association , se debe usar un elemento AssociationSetMapping para hacerlo.
El elemento ReferentialConstraint puede tener los siguientes elementos secundarios:
Documentation (cero o uno)
Principal (exactamente uno)
Dependent (exactamente uno)
Elementos Annotation (cero o más)
Atributos aplicables
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento
ReferentialConstraint . Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún
espacio de nombres XML reservado para SSDL. Dos atributos personalizados cualesquiera no pueden tener
nombres completos idénticos.
Ejemplo
En el ejemplo siguiente se muestra un elemento Association que utiliza un elemento ReferentialConstraint
para especificar las columnas que participan en la restricción FOREIGN KEY externa de **FK _ ** :

<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="[Link]" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="[Link]" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>

ReturnType (elemento) (SSDL)


El elemento ReturnType del lenguaje de definición de esquemas de almacenamiento (SSDL) especifica el tipo
de valor devuelto para una función que se define en un elemento de función . Un tipo de valor devuelto de
función también se puede especificar con un atributo ReturnType .
El tipo de valor devuelto de una función se especifica con el atributo Type o el elemento ReturnType .
El elemento ReturnType puede tener los siguientes elementos secundarios:
CollectionType (uno)

NOTE
Se puede aplicar cualquier número de atributos de anotación (atributos XML personalizados) al elemento ReturnType .
Sin embargo, es posible que los atributos personalizados no pertenezcan a ningún espacio de nombres XML reservado
para SSDL. Dos atributos personalizados cualesquiera no pueden tener nombres completos idénticos.

Ejemplo
En el ejemplo siguiente se utiliza una función que devuelve una colección de filas.
<Function Name="GetProducts" IsComposable="true" Schema="dbo">
<ReturnType>
<CollectionType>
<RowType>
<Property Name="ProductID" Type="int" Nullable="false" />
<Property Name="CategoryID" Type="bigint" Nullable="false" />
<Property Name="ProductName" Type="nvarchar" MaxLength="40" Nullable="false" />
<Property Name="UnitPrice" Type="money" />
<Property Name="Discontinued" Type="bit" />
</RowType>
</CollectionType>
</ReturnType>
</Function>

RowType (elemento) (SSDL)


Un elemento RowType en el lenguaje de definición de esquemas de almacenamiento (SSDL) define una
estructura sin nombre como un tipo de valor devuelto para una función definida en el almacén.
Un elemento RowType es el elemento secundario del elemento CollectionType :
Un elemento RowType puede tener los siguientes elementos secundarios:
Property (uno o varios)
Ejemplo
En el ejemplo siguiente se muestra una función de almacenamiento que usa un elemento CollectionType para
especificar que la función devuelve una colección de filas (tal y como se especifica en el elemento RowType ).

<Function Name="GetProducts" IsComposable="true" Schema="dbo">


<ReturnType>
<CollectionType>
<RowType>
<Property Name="ProductID" Type="int" Nullable="false" />
<Property Name="CategoryID" Type="bigint" Nullable="false" />
<Property Name="ProductName" Type="nvarchar" MaxLength="40" Nullable="false" />
<Property Name="UnitPrice" Type="money" />
<Property Name="Discontinued" Type="bit" />
</RowType>
</CollectionType>
</ReturnType>
</Function>

Schema (Elemento) (SSDL)


El elemento Schema en el lenguaje de definición de esquemas de almacenamiento (SSDL) es el elemento raíz
de una definición de modelo de almacenamiento. Contiene las definiciones para los objetos, las funciones y los
contenedores que conforman un modelo de almacenamiento.
El elemento Schema puede contener cero o más de los siguientes elementos secundarios:
Asociación
EntityType
EntityContainer
Función
El elemento Schema usa el atributo namespace para definir el espacio de nombres para el tipo de entidad y
los objetos de asociación en un modelo de almacenamiento. Dentro de un espacio de nombres, no puede haber
dos objetos con el mismo nombre.
Un espacio de nombres del modelo de almacenamiento es diferente del espacio de nombres XML del elemento
Schema . Un espacio de nombres del modelo de almacenamiento (tal y como se define en el atributo de
espacio de nombres ) es un contenedor lógico para tipos de entidad y tipos de asociación. El espacio de
nombres XML (indicado por el atributo xmlns ) de un elemento Schema es el espacio de nombres
predeterminado para los elementos secundarios y los atributos del elemento Schema . Los espacios de
nombres XML del formulario [Link] (donde YYYY y mm
representan un año y un mes respectivamente) se reservan para SSDL. No puede haber elementos y atributos
personalizados en espacios de nombres que tengan este formato.
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Schema .

N O M B RE DEL AT RIB UTO ES O B L IGATO RIO VA L UE

Espacio de nombres Sí El espacio de nombres del modelo de


almacenamiento. El valor del atributo
namespace se usa para formar el
nombre completo de un tipo. Por
ejemplo, si un EntityType
denominado Customer está en el
espacio de nombres ExampleModel.
Store, el nombre completo del
EntityType es ExampleModel. Store.
Customer.
Las siguientes cadenas no se pueden
usar como el valor del atributo
namespace : System , Transient o
EDM . El valor del atributo
namespace no puede ser el mismo
que el valor del atributo namespace
del elemento Schema de CSDL.

Alias No Un identificador usado en lugar del


nombre del espacio de nombres. Por
ejemplo, si un EntityType
denominado Customer está en el
espacio de nombres ExampleModel.
Store y el valor del atributo alias es
StorageModel, puede usar
StorageModel. Customer como
nombre completo del EntityType.

Proveedor Sí El proveedor de datos.

ProviderManifestToken Sí Un token que indica al proveedor qué


manifiesto del proveedor debe
devolver. No se define ningún formato
para el token. El proveedor define los
valores para el token. Para obtener
información sobre los tokens del
manifiesto del proveedor de SQL
Server, vea SqlClient para Entity
Framework.

Ejemplo
En el ejemplo siguiente se muestra un elemento Schema que contiene un elemento EntityContainer , dos
elementos EntityType y un elemento Association .
<Schema Namespace="[Link]"
Alias="Self" Provider="[Link]"
ProviderManifestToken="2008"
xmlns="[Link]
<EntityContainer Name="ExampleModelStoreContainer">
<EntitySet Name="Customers"
EntityType="[Link]"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="[Link]"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="[Link].FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>
<EntityType Name="Customers">
<Documentation>
<Summary>Summary here.</Summary>
<LongDescription>Long description here.</LongDescription>
</Documentation>
<Key>
<PropertyRef Name="CustomerId" />
</Key>
<Property Name="CustomerId" Type="int" Nullable="false" />
<Property Name="Name" Type="nvarchar(max)" Nullable="false" />
</EntityType>
<EntityType Name="Orders" xmlns:c="[Link]
<Key>
<PropertyRef Name="OrderId" />
</Key>
<Property Name="OrderId" Type="int" Nullable="false"
c:CustomAttribute="someValue"/>
<Property Name="ProductId" Type="int" Nullable="false" />
<Property Name="Quantity" Type="int" Nullable="false" />
<Property Name="CustomerId" Type="int" Nullable="false" />
<c:CustomElement>
Custom data here.
</c:CustomElement>
</EntityType>
<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="[Link]" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="[Link]" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
<Function Name="UpdateOrderQuantity"
Aggregate="false"
BuiltIn="false"
NiladicFunction="false"
IsComposable="false"
ParameterTypeSemantics="AllowImplicitConversion"
Schema="dbo">
<Parameter Name="orderId" Type="int" Mode="In" />
<Parameter Name="newQuantity" Type="int" Mode="In" />
</Function>
<Function Name="UpdateProductInOrder" IsComposable="false">
<Function Name="UpdateProductInOrder" IsComposable="false">
<CommandText>
UPDATE Orders
SET ProductId = @productId
WHERE OrderId = @orderId;
</CommandText>
<Parameter Name="productId"
Mode="In"
Type="int"/>
<Parameter Name="orderId"
Mode="In"
Type="int"/>
</Function>
</Schema>

Atributos de anotación
Los atributos de anotación (Annotation) del lenguaje de definición de esquemas de almacenamiento (SSDL) son
atributos XML personalizados del modelo de almacenamiento que proporcionan metadatos adicionales sobre
los elementos del modelo de almacenamiento. Además de tener una estructura XML válida, las siguientes
restricciones se aplican a los atributos de anotación:
Los atributos de anotación no deben estar en ningún espacio de nombres XML reservado para SSDL.
Dos atributos de anotación cualesquiera no pueden tener el mismo nombre completo.
Se pueden aplicar varios atributos de anotación a un elemento SSDL determinado. Se puede tener acceso a los
metadatos contenidos en los elementos Annotation en tiempo de ejecución mediante el uso de clases en el
espacio de nombres System. Data. Metadata. Edm.
Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType que tiene un atributo Annotation aplicado a la
propiedad OrderID . En el ejemplo también se muestra un elemento Annotation agregado al elemento
EntityType .

<EntityType Name="Orders" xmlns:c="[Link]


<Key>
<PropertyRef Name="OrderId" />
</Key>
<Property Name="OrderId" Type="int" Nullable="false"
c:CustomAttribute="someValue"/>
<Property Name="ProductId" Type="int" Nullable="false" />
<Property Name="Quantity" Type="int" Nullable="false" />
<Property Name="CustomerId" Type="int" Nullable="false" />
<c:CustomElement>
Custom data here.
</c:CustomElement>
</EntityType>

Annotation (Elementos) (SSDL)


Los elementos Annotation del lenguaje de definición de esquemas de almacenamiento (SSDL) son elementos
XML personalizados del modelo de almacenamiento que proporcionan metadatos adicionales sobre el modelo
de almacenamiento. Además de tener una estructura XML válida, las siguientes restricciones se aplican a los
elementos Annotation:
Los elementos Annotation no deben estar en un espacio de nombres de XML que esté reservado para SSDL.
Los nombres completos de dos elementos Annotation cualesquiera no deben ser los mismos.
Los elementos Annotation deben aparecer después de todos los demás elementos secundarios de un
elemento SSDL determinado.
Más de un elemento Annotation puede ser un elemento secundario de un elemento SSDL determinado. A partir
de la .NET Framework versión 4, se puede tener acceso a los metadatos contenidos en los elementos Annotation
en tiempo de ejecución mediante el uso de clases en el espacio de nombres System. Data. Metadata. Edm.
Ejemplo
En el ejemplo siguiente se muestra un elemento EntityType que tiene un elemento Annotation
(CustomElement ). En el ejemplo también se muestra un atributo de anotación aplicado a la propiedad
OrderID .

<EntityType Name="Orders" xmlns:c="[Link]


<Key>
<PropertyRef Name="OrderId" />
</Key>
<Property Name="OrderId" Type="int" Nullable="false"
c:CustomAttribute="someValue"/>
<Property Name="ProductId" Type="int" Nullable="false" />
<Property Name="Quantity" Type="int" Nullable="false" />
<Property Name="CustomerId" Type="int" Nullable="false" />
<c:CustomElement>
Custom data here.
</c:CustomElement>
</EntityType>

Facetas (SSDL)
Las facetas del lenguaje de definición de esquema de almacenamiento (SSDL) representan restricciones sobre
los tipos de columna que se especifican en elementos Property. Las caras se implementan como atributos XML
en los elementos de propiedad .
En la tabla siguiente se describen las facetas que se admiten en SSDL:

FA C ETA DESC RIP C IÓ N

Intercalación Especifica la secuencia de intercalación (o secuencia de


orden) que se va a usar cuando se realicen las operaciones
de comparación y ordenación sobre los valores de la
propiedad.

FixedLength Especifica si la longitud del valor de la columna puede variar.

MaxLength Especifica la longitud máxima del valor de la columna.

Precisión Para las propiedades de tipo decimal, especifica el número


de dígitos que puede tener un valor de propiedad. Para las
propiedades de tipo Time , DateTime y DateTimeOffset ,
especifica el número de dígitos para la parte fraccionaria de
los segundos del valor de la columna.

Escala Especifica el número de dígitos que puede haber a la derecha


del separador de decimales para el valor de la columna.

Unicode Indica si el valor de la columna está almacenado como


Unicode.
Definir el diseñador de consultas-EF
12/03/2021 • 10 minutes to read

En este tutorial se muestra cómo agregar una consulta de definición y un tipo de entidad correspondiente a un
modelo mediante el diseñador de EF. Una consulta de definición se usa normalmente para proporcionar una
funcionalidad similar a la que proporciona una vista de base de datos, pero la vista se define en el modelo, no en
la base de datos. Una consulta de definición permite ejecutar una instrucción SQL que se especifica en el
elemento DefiningQuer y de un archivo. edmx. Para obtener más información, vea DefiningQuer y en la
especificación de SSDL.
Al utilizar las consultas de definición, también debe definir un tipo de entidad en el modelo. El tipo de entidad se
utiliza para exponer los datos expuestos por la consulta de definición. Tenga en cuenta que los datos que se
exponen a través de este tipo de entidad son de solo lectura.
Las consultas con parámetros no se pueden ejecutar como consultas de definición. Sin embargo, los datos se
pueden actualizar asignando las funciones de inserción, actualización y eliminación del tipo de entidad que los
muestra a los procedimientos almacenados. Para obtener más información, vea Insert, Update y DELETE con
procedimientos almacenados.
En este tema se muestra cómo realizar las siguientes tareas.
Agregar una consulta de definición
Agregar un tipo de entidad al modelo
Asignar la consulta de definición al tipo de entidad

Requisitos previos
Para completar este tutorial, necesitará:
Una versión reciente de Visual Studio.
La base de datos de ejemplo School.

Configurar el proyecto
En este tutorial se usa Visual Studio 2012 o una versión más reciente.
Abra Visual Studio.
En el menú Archivo , seleccione Nuevo y haga clic en Proyecto .
En el panel izquierdo, haga clic en Visual # C y, a continuación, seleccione la plantilla aplicación de
consola .
Escriba DefiningQuer ySample como nombre del proyecto y haga clic en Aceptar .

Creación de un modelo basado en la base de datos School


Haga clic con el botón secundario en el nombre del proyecto en Explorador de soluciones, seleccione
Agregar y, a continuación, haga clic en nuevo elemento .
Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model
en el panel Plantillas.
Escriba DefiningQuer yModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
En el cuadro de diálogo elegir contenido del modelo, seleccione generar desde la base de datos y, a
continuación, haga clic en siguiente .
Haga clic en nueva conexión. En el cuadro de diálogo Propiedades de conexión, escriba el nombre del
servidor (por ejemplo, (LocalDB) \ mssqllocaldb ), seleccione el método de autenticación,
escriba School como nombre de la base de datos y, a continuación, haga clic en Aceptar . El cuadro de
diálogo elegir la conexión de datos se actualiza con la configuración de conexión de la base de datos.
En el cuadro de diálogo elija los objetos de base de datos, compruebe el nodo tablas . Se agregarán
todas las tablas al modelo School .
Haga clic en Finalizar .
En Explorador de soluciones, haga clic con el botón secundario en el archivo DefiningQuer yModel.
edmx y seleccione abrir con....
Seleccione Editor XML (texto) .

Haga clic en sí si se le solicita el mensaje siguiente:

Agregar una consulta de definición


En este paso, usaremos el editor XML para agregar una consulta de definición y un tipo de entidad a la sección
SSDL del archivo. edmx.
Agregue un elemento EntitySet a la sección SSDL del archivo. edmx (línea 5 a 13). Especifique lo siguiente:
Solo Name se especifican los atributos name y EntityType del elemento EntitySet .
El nombre completo del tipo de entidad se usa en el atributo EntityType .
La instrucción SQL que se va a ejecutar se especifica en el elemento DefiningQuer y .

<!-- SSDL content -->


<edmx:StorageModels>
<Schema Namespace="[Link]" Alias="Self" Provider="[Link]"
ProviderManifestToken="2008"
xmlns:store="[Link]
xmlns="[Link]
<EntityContainer Name="SchoolModelStoreContainer">
<EntitySet Name="GradeReport" EntityType="[Link]">
<DefiningQuery>
SELECT CourseID, Grade, FirstName, LastName
FROM StudentGrade
JOIN
(SELECT * FROM Person WHERE EnrollmentDate IS NOT NULL) AS p
ON StudentID = [Link]
</DefiningQuery>
</EntitySet>
<EntitySet Name="Course" EntityType="[Link]" store:Type="Tables" Schema="dbo" />

Agregue el elemento EntityType a la sección SSDL del archivo. edmx. como se muestra a continuación.
Tenga en cuenta lo siguiente:
El valor del atributo Name corresponde al valor del atributo EntityType en el elemento EntitySet
anterior, aunque el nombre completo del tipo de entidad se usa en el atributo EntityType .
Los nombres de propiedad corresponden a los nombres de columna devueltos por la instrucción SQL
en el elemento DefiningQuer y (arriba).
En este ejemplo, la clave de entidad consta de tres propiedades para garantizar un valor de clave
único.

<EntityType Name="GradeReport">
<Key>
<PropertyRef Name="CourseID" />
<PropertyRef Name="FirstName" />
<PropertyRef Name="LastName" />
</Key>
<Property Name="CourseID"
Type="int"
Nullable="false" />
<Property Name="Grade"
Type="decimal"
Precision="3"
Scale="2" />
<Property Name="FirstName"
Type="nvarchar"
Nullable="false"
MaxLength="50" />
<Property Name="LastName"
Type="nvarchar"
Nullable="false"
MaxLength="50" />
</EntityType>

NOTE
Si posteriormente ejecuta el cuadro de diálogo Asistente para actualizar modelo , se sobrescribirán los cambios
realizados en el modelo de almacenamiento, incluida la definición de las consultas.
Agregar un tipo de entidad al modelo
En este paso se agregará el tipo de entidad al modelo conceptual mediante el diseñador de EF. Tenga en cuenta
lo siguiente:
El nombre de la entidad corresponde al valor del atributo EntityType en el elemento EntitySet anterior.
Los nombres de propiedad corresponden a los nombres de columna devueltos por la instrucción SQL en el
elemento DefiningQuer y anterior.
En este ejemplo, la clave de entidad consta de tres propiedades para garantizar un valor de clave único.
Abra el modelo en el diseñador de EF.
Haga doble clic en DefiningQueryModel. edmx.
Por ejemplo , en el siguiente mensaje:

Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo.
Haga clic con el botón secundario en la superficie del diseñador y seleccione Agregar nueva - > entidad....
Especifique GradeRepor t para el nombre de entidad y el CourseID para la propiedad de clave .
Haga clic con el botón secundario en la entidad GradeRepor t y seleccione Agregar nueva - > propiedad
escalar .
Cambie el nombre predeterminado de la propiedad a FirstName .
Agregue otra propiedad escalar y especifique LastName como nombre.
Agregue otra propiedad escalar y especifique grade como el nombre.
En la ventana propiedades , cambie la propiedad tipo de grado a decimal .
Seleccione las propiedades FirstName y LastName .
En la ventana propiedades , cambie el valor de la propiedad EntityKey a true .
Como resultado, se han agregado los siguientes elementos a la sección CSDL del archivo. edmx.

<EntitySet Name="GradeReport" EntityType="[Link]" />

<EntityType Name="GradeReport">
. . .
</EntityType>

Asignar la consulta de definición al tipo de entidad


En este paso, usaremos la ventana detalles de la asignación para asignar los tipos de entidad conceptual y de
almacenamiento.
Haga clic con el botón secundario en la entidad GradeRepor t en la superficie de diseño y seleccione
asignación de tabla .
Se muestra la ventana detalles de la asignación .
Seleccione GradeRepor t en la lista desplegable ** < Agregar una > tabla o vista** (que se encuentra en la
tabla s).
Aparecen las asignaciones predeterminadas entre el tipo de entidad conceptual y Storage GradeRepor t .

Como resultado, el elemento EntitySetMapping se agrega a la sección de asignación del archivo. edmx.

<EntitySetMapping Name="GradeReports">
<EntityTypeMapping TypeName="IsTypeOf([Link])">
<MappingFragment StoreEntitySet="GradeReport">
<ScalarProperty Name="LastName" ColumnName="LastName" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="Grade" ColumnName="Grade" />
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>

Realice la compilación de la aplicación.

Llamar a la consulta de definición en el código


Ahora puede ejecutar la consulta de definición mediante el tipo de entidad GradeRepor t .

using (var context = new SchoolEntities())


{
var report = [Link]();
[Link]("{0} {1} got {2}",
[Link], [Link], [Link]);
}
Procedimientos almacenados con varios conjuntos
de resultados
12/03/2021 • 10 minutes to read

A veces, al utilizar procedimientos almacenados, deberá devolver más de un conjunto de resultados. Este
escenario se usa normalmente para reducir el número de recorridos de ida y vuelta de base de datos necesarios
para crear una sola [Link] de EF5, Entity Framework permitiría que se llamara al procedimiento
almacenado, pero solo devolvería el primer conjunto de resultados al código de llamada.
En este artículo se muestran dos formas de usar para tener acceso a más de un conjunto de resultados de un
procedimiento almacenado en Entity Framework. Una que usa solo código y funciona con Code First y el
diseñador de EF y otro que solo funciona con EF Designer. Las herramientas y la compatibilidad con API para
esto deberían mejorar en versiones futuras de Entity Framework.

Modelo
En los ejemplos de este artículo se usa un blog básico y un modelo de publicaciones en el que un blog tiene
muchas publicaciones y una publicación pertenece a un solo blog. Usaremos un procedimiento almacenado en
la base de datos que devuelva todos los blogs y publicaciones, algo parecido a esto:

CREATE PROCEDURE [dbo].[GetAllBlogsAndPosts]


AS
SELECT * FROM [Link]
SELECT * FROM [Link]

Obtener acceso a varios conjuntos de resultados con código


Podemos ejecutar usar código para emitir un comando SQL sin formato para ejecutar nuestro procedimiento
almacenado. La ventaja de este enfoque es que funciona tanto con Code First como con el diseñador EF.
Para conseguir que funcionen varios conjuntos de resultados, es necesario colocarlos en la API de ObjectContext
mediante la interfaz IObjectContextAdapter.
Una vez que tenemos un ObjectContext, podemos usar el método translate para traducir los resultados de
nuestro procedimiento almacenado en entidades que se pueden seguir y usar en EF como normal. En el ejemplo
de código siguiente se muestra esto en acción.
using (var db = new BloggingContext())
{
// If using Code First we need to make sure the model is built before we open the connection
// This isn't required for models created with the EF Designer
[Link](force: false);

// Create a SQL command to execute the sproc


var cmd = [Link]();
[Link] = "[dbo].[GetAllBlogsAndPosts]";

try
{

[Link]();
// Run the sproc
var reader = [Link]();

// Read Blogs from the first result set


var blogs = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Blog>(reader, "Blogs", [Link]);

foreach (var item in blogs)


{
[Link]([Link]);
}

// Move to second result set and read Posts


[Link]();
var posts = ((IObjectContextAdapter)db)
.ObjectContext
.Translate<Post>(reader, "Posts", [Link]);

foreach (var item in posts)


{
[Link]([Link]);
}
}
finally
{
[Link]();
}
}

El método translate acepta el lector que se ha recibido cuando se ejecuta el procedimiento, un nombre de
EntitySet y un MergeOption. El nombre de EntitySet será el mismo que el de la propiedad DbSet en el contexto
derivado. La enumeración MergeOption controla cómo se administran los resultados si la misma entidad ya
existe en la memoria.
Aquí se recorre en iteración la colección de blogs antes de que se llame a NextResult, lo que es importante con
el código anterior, ya que se debe consumir el primer conjunto de resultados antes de pasar al siguiente
conjunto de resultados.
Una vez que se llama a los dos métodos de traducción, EF realiza el seguimiento de las entidades blog y post de
la misma manera que cualquier otra entidad, por lo que se puede modificar o eliminar y guardar como normal.

NOTE
EF no tiene en cuenta ninguna asignación al crear entidades mediante el método translate. Simplemente coincidentes con
los nombres de columna del conjunto de resultados con los nombres de propiedad de las clases.
NOTE
Si tiene habilitada la carga diferida, el acceso a la propiedad postes en una de las entidades del blog, EF se conectará a la
base de datos para cargar de forma diferida todas las publicaciones, aunque ya se hayan cargado todas. Esto se debe a
que EF no puede saber si ha cargado o no todas las publicaciones o si hay más en la base de datos. Si desea evitar esto,
tendrá que deshabilitar la carga diferida.

Varios conjuntos de resultados con configurado en EDMX


NOTE
Debe tener como destino .NET Framework 4,5 para poder configurar varios conjuntos de resultados en EDMX. Si el
destino es .NET 4,0, puede usar el método basado en código que se muestra en la sección anterior.

Si usa el diseñador de EF, también puede modificar el modelo para que conozca los diferentes conjuntos de
resultados que se devolverán. Lo que hay que saber antes de la mano es que las herramientas no tienen un
conjunto de resultados múltiple, por lo que tendrá que editar manualmente el archivo edmx. La edición del
archivo edmx como este funcionará, pero también interrumpirá la validación del modelo en VS. Por lo tanto, si
valida el modelo, siempre se producirán errores.
Para ello, debe agregar el procedimiento almacenado al modelo como lo haría para una sola consulta de
conjunto de resultados.
Una vez hecho esto, debe hacer clic con el botón derecho en el modelo y seleccionar abrir con. a
continuación, XML

Una vez que tenga el modelo abierto como XML, debe realizar los siguientes pasos:
Busque el tipo complejo y la importación de función en el modelo:
<!-- CSDL content -->
<edmx:ConceptualModels>

...

<FunctionImport Name="GetAllBlogsAndPosts"
ReturnType="Collection(BlogModel.GetAllBlogsAndPosts_Result)" />

...

<ComplexType Name="GetAllBlogsAndPosts_Result">
<Property Type="Int32" Name="BlogId" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" MaxLength="255" />
<Property Type="String" Name="Description" Nullable="true" />
</ComplexType>

...

</edmx:ConceptualModels>

Quitar el tipo complejo


Actualice la importación de la función para que se asigne a las entidades; en nuestro caso, tendrá un aspecto
similar al siguiente:

<FunctionImport Name="GetAllBlogsAndPosts">
<ReturnType EntitySet="Blogs" Type="Collection([Link])" />
<ReturnType EntitySet="Posts" Type="Collection([Link])" />
</FunctionImport>

Esto indica al modelo que el procedimiento almacenado devolverá dos colecciones, una de las entradas del blog
y una de las entradas post.
Busque el elemento de asignación de función:

<!-- C-S mapping content -->


<edmx:Mappings>

...

<FunctionImportMapping FunctionImportName="GetAllBlogsAndPosts"
FunctionName="[Link]">
<ResultMapping>
<ComplexTypeMapping TypeName="BlogModel.GetAllBlogsAndPosts_Result">
<ScalarProperty Name="BlogId" ColumnName="BlogId" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Description" ColumnName="Description" />
</ComplexTypeMapping>
</ResultMapping>
</FunctionImportMapping>

...

</edmx:Mappings>

Reemplace la asignación de resultados por una para cada entidad que se va a devolver, como la siguiente:
<ResultMapping>
<EntityTypeMapping TypeName ="[Link]">
<ScalarProperty Name="BlogId" ColumnName="BlogId" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Description" ColumnName="Description" />
</EntityTypeMapping>
</ResultMapping>
<ResultMapping>
<EntityTypeMapping TypeName="[Link]">
<ScalarProperty Name="BlogId" ColumnName="BlogId" />
<ScalarProperty Name="PostId" ColumnName="PostId"/>
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="Text" ColumnName="Text" />
</EntityTypeMapping>
</ResultMapping>

También es posible asignar los conjuntos de resultados a tipos complejos, como el que se crea de forma
predeterminada. Para ello, cree un nuevo tipo complejo, en lugar de quitarlos, y use los tipos complejos en todos
los lugares en los que haya usado los nombres de entidad en los ejemplos anteriores.
Una vez que se hayan cambiado estas asignaciones, puede guardar el modelo y ejecutar el código siguiente
para usar el procedimiento almacenado:

using (var db = new BlogEntities())


{
var results = [Link]();

foreach (var result in results)


{
[Link]("Blog: " + [Link]);
}

var posts = [Link]<Post>();

foreach (var result in posts)


{
[Link]("Post: " + [Link]);
}

[Link]();
}

NOTE
Si edita manualmente el archivo edmx para el modelo, se sobrescribirá si alguna vez vuelve a generar el modelo desde la
base de datos.

Resumen
Aquí hemos mostrado dos métodos diferentes para tener acceso a varios conjuntos de resultados mediante
Entity Framework. Ambos son igualmente válidos en función de su situación y preferencias, y debe elegir el que
mejor se adapte a sus circunstancias. Se prevé que la compatibilidad con varios conjuntos de resultados se
mejorará en versiones futuras de Entity Framework y que ya no será necesario realizar los pasos descritos en
este documento.
Funciones de Table-Valued (TVF)
12/03/2021 • 7 minutes to read

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

En el tutorial de vídeo y paso a paso se muestra cómo asignar funciones con valores de tabla (TVF) mediante el
Entity Framework Designer. También se muestra cómo llamar a una TVF desde una consulta LINQ.
TVF actualmente solo se admiten en el flujo de trabajo Database First.
La compatibilidad con TVF se presentó en Entity Framework versión 5. Tenga en cuenta que para usar las nuevas
características, como las funciones con valores de tabla, las enumeraciones y los tipos espaciales, debe tener
como destino .NET Framework 4,5. Visual Studio 2012 tiene como destino .NET 4,5 de forma predeterminada.
TVF son muy similares a los procedimientos almacenados con una diferencia clave: el resultado de una TVF es
ajustable. Esto significa que los resultados de una TVF se pueden usar en una consulta LINQ mientras que los
resultados de un procedimiento almacenado no pueden.

Visualización del vídeo


Presentada por : Julia Kornich
WMV | MP4 | WMV (zip)

Requisitos previos
Para completar este tutorial, necesitará:
Instale la base de datos School.
Tener una versión reciente de Visual Studio

Configurar el proyecto
1. Apertura de Visual Studio
2. En el menú archivo , seleccione nuevo y, a continuación, haga clic en proyecto .
3. En el panel izquierdo, haga clic en **Visual C # **y, a continuación, seleccione la plantilla de consola .
4. Escriba TVF como el nombre del proyecto y haga clic en Aceptar .

Agregar una función TVF a la base de datos


Seleccionar vista- > Explorador de objetos de SQL Ser ver
Si LocalDB no está en la lista de servidores: haga clic con el botón derecho en SQL Ser ver y seleccione
Agregar SQL Ser ver usar la autenticación de Windows predeterminada para conectarse al servidor de
LocalDB.
Expandir el nodo LocalDB
En el nodo bases de datos, haga clic con el botón secundario en el nodo de base de datos School y
seleccione nueva consulta.
En el editor de T-SQL, pegue la siguiente definición de TVF

CREATE FUNCTION [dbo].[GetStudentGradesForCourse]

(@CourseID INT)

RETURNS TABLE

RETURN
SELECT [EnrollmentID],
[CourseID],
[StudentID],
[Grade]
FROM [dbo].[StudentGrade]
WHERE CourseID = @CourseID

Haga clic con el botón secundario del mouse en el editor de T-SQL y seleccione Ejecutar .
La función GetStudentGradesForCourse se agrega a la base de datos School

Creación de un modelo
1. Haga clic con el botón secundario en el nombre del proyecto en Explorador de soluciones, seleccione
Agregar y, a continuación, haga clic en nuevo elemento .
2. Seleccione datos en el menú de la izquierda y, a continuación, seleccione [Link] Entity Data Model en
el panel plantillas .
3. Escriba TVFModel. edmx como nombre de archivo y, a continuación, haga clic en Agregar .
4. En el cuadro de diálogo elegir contenido del modelo, seleccione generar desde la base de datos y, a
continuación, haga clic en siguiente .
5. Haga clic en nueva conexión entrar (LocalDB) \ mssqllocaldb en el cuadro de texto nombre de servidor,
escriba School como nombre de la base de datos haga clic en Aceptar .
6. En el cuadro de diálogo elija los objetos de base de datos, en el nodo tablas , seleccione las
tablas Person , StudentGrade y Course .
7. Seleccione la función GetStudentGradesForCourse que se encuentra en el nodo procedimientos
almacenados y funciones . tenga en cuenta que, a partir de Visual Studio 2012, Entity Designer le permite
importar por lotes los procedimientos almacenados y las funciones.
8. Haga clic en Finalizar
9. Se muestra el diseñador de entidades, que proporciona una superficie de diseño para editar el modelo. Todos
los objetos que seleccionó en el cuadro de diálogo Elija los objetos de base de datos se agregan al
modelo.
10. De forma predeterminada, la forma de resultado de cada procedimiento almacenado importado o función se
convertirá automáticamente en un nuevo tipo complejo en el modelo de entidad. Pero queremos asignar los
resultados de la función GetStudentGradesForCourse a la entidad StudentGrade: haga clic con el botón
derecho en la superficie de diseño y seleccione Explorador de modelos en el explorador de modelos,
seleccione impor taciones de función y, a continuación, haga doble clic en la
función GetStudentGradesForCourse en el cuadro de diálogo Editar importación de función,
seleccione entidades y elija StudentGrade

Conservar y recuperar datos


Abra el archivo donde se define el método Main. Agregue el código siguiente a la función main.
En el código siguiente se muestra cómo crear una consulta que utiliza una función con valores de tabla. La
consulta proyecta los resultados en un tipo anónimo que contiene el título del curso relacionado y los
estudiantes relacionados con un grado mayor o igual que 3,5.

using (var context = new SchoolEntities())


{
var CourseID = 4022;
var Grade = 3.5M;

// Return all the best students in the Microeconomics class.


var students = from s in [Link](CourseID)
where [Link] >= Grade
select new
{
[Link],
[Link]
};

foreach (var result in students)


{
[Link](
"Couse: {0}, Student: {1} {2}",
[Link],
[Link],
[Link]);
}
}

Compile y ejecute la aplicación. El programa produce el siguiente resultado:

Couse: Microeconomics, Student: Arturo Anand


Couse: Microeconomics, Student: Carson Bryant

Resumen
En este tutorial, hemos visto cómo asignar funciones con valores de tabla (TVF) mediante el Entity Framework
Designer. También se muestra cómo llamar a una función TVF desde una consulta LINQ.
Métodos abreviados de teclado Entity Framework
Designer
12/03/2021 • 15 minutes to read

Esta página proporciona una lista de shorcuts de teclado disponibles en las distintas pantallas de la Entity
Framework Tools para Visual Studio.

Asistente para Entity Data Model de [Link]


Paso 1: elegir el contenido del modelo

A C C ESO DIREC TO A C C IÓ N N OTA S

Alt + n Pasar a la siguiente pantalla No está disponible para todas las


selecciones del contenido del modelo.

Alt + f Finalice el asistente. No está disponible para todas las


selecciones del contenido del modelo.

Alt + w Cambiar el foco a "¿Qué debe contener


el modelo?" .

Paso dos: elegir la conexión


A C C ESO DIREC TO A C C IÓ N N OTA S

Alt + n Pasar a la siguiente pantalla

ALT + p Pasar a la pantalla anterior

Alt + w Cambiar el foco a "¿Qué debe contener


el modelo?" .

Alt + c Abra la ventana "propiedades de Permite la definición de una nueva


conexión". conexión de base de datos.

Alt + e Excluir datos confidenciales de la


cadena de conexión

ALT + i Incluir datos confidenciales en la


cadena de conexión

Alt + s Alternar la opción "Guardar


configuración de conexión en
[Link]"

Paso tres: elegir la versión


A C C ESO DIREC TO A C C IÓ N N OTA S

Alt + n Pasar a la siguiente pantalla

ALT + p Pasar a la pantalla anterior

Alt + w Cambiar el foco a la selección de la Permite especificar una versión


versión de Entity Framework diferente de Entity Framework para su
uso en el proyecto.

Paso cuatro: elegir los objetos y la configuración de la base de datos


A C C ESO DIREC TO A C C IÓ N N OTA S

Alt + f Finalice el asistente.

ALT + p Pasar a la pantalla anterior

Alt + w Cambiar el foco al panel de selección Permite especificar los objetos de base
de objetos de base de datos de datos a los que se va a aplicar
ingeniería inversa.

Alt + s Alternar la opción "pluralización o


singularización de los nombres de
objeto generados"

Alt + k Alternar la opción "incluir columnas de No está disponible para todas las
clave externa en el modelo" selecciones del contenido del modelo.

ALT + i Alternar la opción "importar funciones No está disponible para todas las
y procedimientos almacenados selecciones del contenido del modelo.
seleccionados en el modelo de
entidad"

Alt + m Cambia el foco al campo de texto No está disponible para todas las
"espacio de nombres del modelo". selecciones del contenido del modelo.

Espacia Alternar selección en elemento Si el elemento tiene elementos


secundarios, todos los elementos
secundarios también se alternarán
A C C ESO DIREC TO A C C IÓ N N OTA S

Left Contraer árbol secundario

Right Expandir árbol secundario

Up (Arriba) Navegar al elemento anterior en el


árbol

Bajar Navegar al siguiente elemento en el


árbol

Superficie del diseñador de EF

A C C ESO DIREC TO A C C IÓ N N OTA S

Espacio/Entrar Alternar selección Alterna la selección en el objeto que


tiene el foco.

Esc Cancelar selección Cancela la selección actual.

CTRL+A Seleccionar todo Selecciona todas las formas en la


superficie de diseño.

Flecha arriba Subir Sube la entidad seleccionada un


incremento de la cuadrícula.
Si está en una lista, se desplaza al
subcampo relacionado anterior.
A C C ESO DIREC TO A C C IÓ N N OTA S

Flecha abajo Bajar Baja una entidad seleccionada un


incremento de la cuadrícula.
Si se encuentra en una lista, se
desplaza al siguiente subcampo
relacionado.

Flecha izquierda Moverse a la izquierda Mueve la entidad seleccionada a la


izquierda un incremento de la
cuadrícula.
Si está en una lista, se desplaza al
subcampo relacionado anterior.

Flecha derecha Moverse a la derecha Mueve la entidad seleccionada a la


derecha un incremento de la
cuadrícula.
Si se encuentra en una lista, se
desplaza al siguiente subcampo
relacionado.

Mayús + Flecha izquierda Tamaño de la forma izquierda Reduce el ancho de la entidad


seleccionada en un incremento de la
cuadrícula.

Mayús + Flecha derecha Tamaño de la forma derecha Aumenta el ancho de la entidad


seleccionada en un incremento de la
cuadrícula.

Inicio Primer elemento del mismo nivel Mueve el foco y la selección al primer
objeto en la superficie de diseño en el
mismo nivel del mismo nivel.

Fin Último elemento del mismo nivel Mueve el foco y la selección al último
objeto en la superficie de diseño en el
mismo nivel del mismo nivel.

CTRL+Inicio Primer elemento del mismo nivel (foco) Igual que el primer elemento del
mismo nivel, pero mueve el foco en
lugar de mover el foco y la selección.

CTRL+Fin Último elemento del mismo nivel (foco) Igual que el último elemento del
mismo nivel, pero mueve el foco en
lugar de mover el foco y la selección.

Pestaña Siguiente elemento del mismo nivel Mueve el foco y la selección al


siguiente objeto en la superficie de
diseño en el mismo nivel del mismo
nivel.

Mayús+Tab Anterior del mismo nivel Mueve el foco y la selección al objeto


anterior en la superficie de diseño en el
mismo nivel del mismo nivel.

Alt + Ctrl + Tab Siguiente elemento del mismo nivel Igual que el siguiente elemento del
(foco) mismo nivel, pero mueve el foco en
lugar de mover el foco y la selección.
A C C ESO DIREC TO A C C IÓ N N OTA S

Alt + Ctrl + Mayús + Tab Anterior del mismo nivel (enfoque) Igual que el elemento anterior del
mismo nivel, pero mueve el foco en
lugar de mover el foco y la selección.

< Ascende Se desplaza al siguiente objeto en la


superficie de diseño un nivel superior
de la jerarquía. Si no hay formas por
encima de esta forma en la jerarquía
(es decir, el objeto se coloca
directamente en la superficie de
diseño), se selecciona el diagrama.

> Descendientes Se desplaza al siguiente objeto


contenido en la superficie de diseño un
nivel por debajo de este en la jerarquía.
Si no hay ningún objeto contenido, se
trata de una operación no operativa.

Ctrl + < Ascend (Focus) Igual que el comando Ascend, pero


mueve el foco sin selección.

Ctrl + > Descendente (foco) Igual que el comando descendente,


pero mueve el foco sin selección.

Mayús + fin Seguir a conectado Desde una entidad, se mueve a una


entidad a la que está conectada esta
entidad.

Resguardo Eliminar Elimine un objeto o un conector del


diagrama.

Complemento Insertar Agrega una nueva propiedad a una


entidad cuando se selecciona el
encabezado de compartimiento
"propiedades escalares" o una
propiedad en sí.

AvPág Desplazar diagrama hacia arriba Desplaza la superficie de diseño hacia


arriba, en incrementos iguales al 75%
del alto de la superficie de diseño
actualmente visible.

Av Pág Desplazar diagrama hacia abajo Desplaza la superficie de diseño hacia


abajo.

Mayús + Av Pág Desplazar diagrama a la derecha Desplaza la superficie de diseño a la


derecha.

Mayús + re pág Desplazar diagrama a la izquierda Desplaza la superficie de diseño a la


izquierda.

F2 Acceso al modo de edición Método abreviado de teclado estándar


para entrar en el modo de edición de
un control de texto.
A C C ESO DIREC TO A C C IÓ N N OTA S

Mayús + F10 Mostrar menú contextual Método abreviado de teclado estándar


para mostrar el menú contextual de un
elemento seleccionado.

Control + Mayús + clic con el Zoom semántico Acerca el área de la vista de diagrama
botón primario del mouse debajo del puntero del mouse.
Control + Mayús + MouseWheel
hacia delante

Control + Mayús + clic con el Alejar semántica Aleja el área de la vista de diagrama
botón secundario del mouse debajo del puntero del mouse. Vuelve
Control + Mayús + MouseWheel a centrar el diagrama cuando aleja el
hacia atrás centro del diagrama actual.

Control + Mayús + ' + ' Acercar Acerca el centro de la vista de


Control + MouseWheel For ward diagrama.

Control + Mayús + '-' Alejar Aleja el área en la que se hace clic en la


Control + MouseWheel hacia vista de diagrama. Vuelve a centrar el
atrás diagrama cuando aleja el centro del
diagrama actual.

Control + Mayús + dibujar un Área de zoom Acerca centrada en el área que ha


rectángulo con el botón primario seleccionado. Cuando mantenga
del mouse hacia abajo presionadas las teclas Control +
Mayús, verá que el cursor cambia a
una lupa, lo que le permite definir el
área de zoom.

Tecla del menú contextual + m ' Abrir la ventana detalles de la Abre la ventana detalles de la
asignación asignación para editar las asignaciones
de la entidad seleccionada

Detalles de la asignación (ventana)


A C C ESO DIREC TO A C C IÓ N N OTA S

Pestaña Cambiar contexto Cambia entre el área de la ventana


principal y la barra de herramientas de
la izquierda.

Teclas de dirección Navegación Subir y bajar filas, o de derecha a


izquierda entre las columnas del área
de la ventana principal. Desplácese
entre los botones de la barra de
herramientas de la izquierda.

Entrar Seleccionar Selecciona un botón de la barra de


Espacia herramientas de la izquierda.

Alt + flecha abajo Abrir lista Desplegar una lista si se selecciona una
celda que tenga una lista desplegable.

Entrar Seleccionar lista Selecciona un elemento de una lista


desplegable.

Esc Cerrar lista Cierra una lista desplegable.

Navegación de Visual Studio


Entity Framework también proporciona una serie de acciones que pueden tener los métodos abreviados de
teclado personalizados asignados (no se asignan accesos directos de forma predeterminada). Para crear estos
métodos abreviados personalizados, haga clic en el menú herramientas y, a continuación, en [Link]
entorno, elija [Link]ácese hacia abajo en la lista en el centro hasta que pueda seleccionar el comando que
desee, escriba el acceso directo en el cuadro de texto "presionar teclas de método abreviado" y haga clic en
asignar. Los métodos abreviados posibles son los siguientes:

A C C ESO DIREC TO

OtherContextMenus. MicrosoftDataEntityDesignContext. Add. ComplexProper ty. ComplexTypes

[Link]

[Link] t

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. AddEnumType

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. Association

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. ComplexProper ty

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. ComplexType

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. Entity

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. FunctionImpor t

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. inheritance


A C C ESO DIREC TO

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. NavigationProper ty

OtherContextMenus. MicrosoftDataEntityDesignContext. AddNew. ScalarProper ty

[Link]

[Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. Close

OtherContextMenus. MicrosoftDataEntityDesignContext. Collapse

[Link] ttoEnum

OtherContextMenus. MicrosoftDataEntityDesignContext. diagram. CollapseAll

OtherContextMenus. MicrosoftDataEntityDesignContext. diagram. ExpandAll

OtherContextMenus. MicrosoftDataEntityDesignContext. diagram. Expor tasImage

OtherContextMenus. MicrosoftDataEntityDesignContext. diagram. LayoutDiagram

OtherContextMenus. MicrosoftDataEntityDesignContext. Edit

OtherContextMenus. MicrosoftDataEntityDesignContext. EntityKey

OtherContextMenus. MicrosoftDataEntityDesignContext. Expand

OtherContextMenus. MicrosoftDataEntityDesignContext. FunctionImpor tMapping

[Link]

[Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. Grid. ShowGrid

OtherContextMenus. MicrosoftDataEntityDesignContext. Grid. SnaptoGrid

[Link]

[Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. ModelBrowser

[Link]

[Link] [Link]

[Link] ties.Down5
A C C ESO DIREC TO

[Link] [Link]

[Link] [Link]

[Link] [Link]

[Link] ties.Up5

[Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. Open

OtherContextMenus. MicrosoftDataEntityDesignContext. refactorizar. MovetoNewComplexType

[Link]

[Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. Rename

OtherContextMenus. MicrosoftDataEntityDesignContext. ScalarProper tyFormat. DisplayName

[Link] [Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. Select. BaseType

OtherContextMenus. MicrosoftDataEntityDesignContext. Select. Entity

OtherContextMenus. MicrosoftDataEntityDesignContext. Select. Proper ty

OtherContextMenus. MicrosoftDataEntityDesignContext. Select. SubType

OtherContextMenus. MicrosoftDataEntityDesignContext. SelectAll

[Link]

[Link]

[Link]

[Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. TableMapping

[Link]

OtherContextMenus. MicrosoftDataEntityDesignContext. Validate

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 10


A C C ESO DIREC TO

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 100

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 125

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 150

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 200

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 25

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 300

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 33

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 400

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 50

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 66

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. 75

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. Custom

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. zoom

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. ZoomOut

OtherContextMenus. MicrosoftDataEntityDesignContext. zoom. ZoomtoFit

[Link]

[Link]
Consulta y búsqueda de entidades
12/03/2021 • 6 minutes to read

En este tema se tratan las distintas formas de consultar datos mediante Entity Framework, incluidos LINQ y el
método Find. Las técnicas que se muestran en este tema se aplican igualmente a los modelos creados con Code
First y EF Designer.

Búsqueda de entidades mediante una consulta


DbSet e IDbSet implementan IQueryable, por lo que pueden usarse como punto de partida para escribir una
consulta LINQ en la base de datos. Este no es el lugar adecuado para una explicación detallada sobre LINQ, pero
aquí tiene un par de ejemplos sencillos:

using (var context = new BloggingContext())


{
// Query for all blogs with names starting with B
var blogs = from b in [Link]
where [Link]("B")
select b;

// Query for the Blog named [Link] Blog


var blog = [Link]
.Where(b => [Link] == "[Link] Blog")
.FirstOrDefault();
}

Tenga en cuenta que DbSet e IDbSet siempre crean las consultas en la base de datos, lo que siempre conlleva un
viaje de ida y vuelta a la base de datos, aun cuando las entidades devueltas ya existan en el contexto. Una
consulta se ejecuta en la base de datos cuando:
Se enumera mediante una instrucción foreach (C#) o For Each (Visual Basic).
Se enumera mediante una operación de recopilación como ToArray, ToDictionary o ToList.
Los operadores LINQ, como First o Any, se especifican en la parte más externa de la consulta.
Se llama a los métodos siguientes: el método de extensión Load en DbSet, [Link] y
[Link].
Cuando se devuelven los resultados de la base de datos, los objetos que no existen en el contexto se adjuntan a
él. Si un objeto ya está en el contexto, se devuelve el objeto existente (los valores actual y original de las
propiedades del objeto en la entrada no se sobrescriben con valores de la base de datos).
Cuando se realiza una consulta, las entidades que se han agregado al contexto pero aún no se han guardado en
la base de datos no se devuelven como parte del conjunto de resultados. Para obtener los datos que se
encuentran en el contexto, vea Datos locales.
Si una consulta no devuelve filas de la base de datos, el resultado será una colección vacía, en lugar de null .

Búsqueda de entidades mediante claves principales


El método Find de DbSet usa el valor de clave principal para intentar buscar una entidad cuyo seguimiento
realiza el contexto. Si la entidad no se encuentra en el contexto, se envía una consulta a la base de datos para
buscar la entidad allí. Si la entidad no se encuentra en el contexto ni en la base de datos, se devuelve null.
Find se diferencia del uso de una consulta en dos aspectos importantes:
Solo se realiza un viaje de ida y vuelta a la base de datos si la entidad con la clave especificada no se
encuentra en el contexto.
Find devuelve entidades que están en estado Added. Es decir, Find devuelve entidades que se han agregado
al contexto pero que aún no se han guardado en la base de datos.
Búsqueda de una entidad por clave principal
En el código siguiente se muestran algunos usos de Find:

using (var context = new BloggingContext())


{
// Will hit the database
var blog = [Link](3);

// Will return the same instance without hitting the database


var blogAgain = [Link](3);

[Link](new Blog { Id = -1 });

// Will find the new blog even though it does not exist in the database
var newBlog = [Link](-1);

// Will find a User which has a string primary key


var user = [Link]("johndoe1987");
}

Búsqueda de una entidad por clave principal compuesta


Entity Framework permite que las entidades tengan claves compuestas, que son claves que se componen de
más de una propiedad. Por ejemplo, podría tener una entidad BlogSettings que representara la configuración de
un usuario para un blog determinado. Dado que un usuario solo tendría una entidad BlogSettings para cada
blog, podría optar por convertir la clave principal de BlogSettings en una combinación de BlogId y Username. El
código siguiente intenta encontrar la entidad BlogSettings con BlogId = 3 y Username = "johndoe1987":

using (var context = new BloggingContext())


{
var settings = [Link](3, "johndoe1987");
}

Observe que con las claves compuestas hay que usar ColumnAttribute o la API fluida para especificar un orden
de las propiedades de la clave compuesta. La llamada a Find debe usar este orden al especificar los valores que
forman la clave.
El método de carga
12/03/2021 • 2 minutes to read

Hay varios escenarios en los que puede que desee cargar entidades de la base de datos en el contexto sin hacer
nada inmediatamente con esas entidades. Un buen ejemplo es la carga de entidades para el enlace de datos
como se describe en datos locales. Una forma habitual de hacerlo es escribir una consulta LINQ y, a
continuación, llamar a ToList en ella, solo para descartar inmediatamente la lista creada. El método de extensión
de carga funciona igual que ToList, salvo que evita la creación de la lista por completo.
Las técnicas que se muestran en este tema se aplican igualmente a los modelos creados con Code First y EF
Designer.
A continuación se muestran dos ejemplos del uso de load. La primera se toma de un Windows Forms aplicación
de enlace de datos donde la carga se utiliza para consultar las entidades antes de enlazarse a la colección local,
como se describe en datos locales:

protected override void OnLoad(EventArgs e)


{
[Link](e);

_context = new ProductContext();

_context.[Link]();
[Link] = _context.[Link]();
}

En el segundo ejemplo se muestra el uso de LOAD para cargar una colección filtrada de entidades relacionadas,
como se describe en carga de entidades relacionadas:

using (var context = new BloggingContext())


{
var blog = [Link](1);

// Load the posts with the 'entity-framework' tag related to a given blog
[Link](blog)
.Collection(b => [Link])
.Query()
.Where(p => [Link]("entity-framework"))
.Load();
}
Datos locales
12/03/2021 • 17 minutes to read

La ejecución de una consulta LINQ directamente en una DbSet siempre enviará una consulta a la base de datos,
pero se puede tener acceso a los datos que están actualmente en memoria mediante la propiedad DbSet. local.
También puede acceder a la información adicional EF realiza el seguimiento de las entidades mediante los
métodos DbContext. entry y DbContext. ChangeTracker. entrys. Las técnicas que se muestran en este tema se
aplican igualmente a los modelos creados con Code First y EF Designer.

Usar local para examinar datos locales


La propiedad local de DbSet proporciona acceso sencillo a las entidades del conjunto cuyo seguimiento realiza
actualmente el contexto y que no se han marcado como eliminados. El acceso a la propiedad local nunca hace
que se envíe una consulta a la base de datos. Esto significa que normalmente se utiliza después de que ya se
haya realizado una consulta. El método de extensión de carga se puede usar para ejecutar una consulta de modo
que el contexto realice un seguimiento de los resultados. Por ejemplo:

using (var context = new BloggingContext())


{
// Load all blogs from the database into the context
[Link]();

// Add a new blog to the context


[Link](new Blog { Name = "My New Blog" });

// Mark one of the existing blogs as Deleted


[Link]([Link](1));

// Loop over the blogs in the context.


[Link]("In Local: ");
foreach (var blog in [Link])
{
[Link](
"Found {0}: {1} with state {2}",
[Link],
[Link],
[Link](blog).State);
}

// Perform a query against the database.


[Link]("\nIn DbSet query: ");
foreach (var blog in [Link])
{
[Link](
"Found {0}: {1} with state {2}",
[Link],
[Link],
[Link](blog).State);
}
}

Si teníamos dos blogs en la base de datos: ' [Link] blog ' con BlogId de 1 y ' el blog de Visual Studio ' con un
BlogId de 2-podríamos esperar el siguiente resultado:
In Local:
Found 0: My New Blog with state Added
Found 2: The Visual Studio Blog with state Unchanged

In DbSet query:
Found 1: [Link] Blog with state Deleted
Found 2: The Visual Studio Blog with state Unchanged

Esto ilustra tres puntos:


El nuevo blog ' mi nuevo blog ' está incluido en la colección local aunque aún no se ha guardado en la base
de datos. Este blog tiene una clave principal de cero porque la base de datos aún no ha generado una clave
real para la entidad.
El "blog de [Link]" no se incluye en la colección local aunque el contexto siga realizando el seguimiento.
Esto se debe a que se ha quitado de DbSet y, por tanto, se ha marcado como eliminado.
Cuando se usa DbSet para realizar una consulta, el blog marcado para su eliminación ([Link] blog) se
incluye en los resultados y el nuevo blog (mi nuevo blog) que todavía no se ha guardado en la base de datos
no se incluye en los resultados. Esto se debe a que DbSet está realizando una consulta en la base de datos y
los resultados devueltos siempre reflejan lo que hay en la base de datos.

Usar local para agregar y quitar entidades del contexto


La propiedad local en DbSet devuelve un ObservableCollection con eventos enlazados de modo que permanece
sincronizado con el contenido del contexto. Esto significa que se pueden agregar o quitar entidades de la
colección local o de DbSet. También significa que las consultas que incorporan nuevas entidades al contexto
darán lugar a que la colección local se actualice con esas entidades. Por ejemplo:
using (var context = new BloggingContext())
{
// Load some posts from the database into the context
[Link](p => [Link]("entity-framework")).Load();

// Get the local collection and make some changes to it


var localPosts = [Link];
[Link](new Post { Name = "What's New in EF" });
[Link]([Link](1));

// Loop over the posts in the context.


[Link]("In Local after entity-framework query: ");
foreach (var post in [Link])
{
[Link](
"Found {0}: {1} with state {2}",
[Link],
[Link],
[Link](post).State);
}

var post1 = [Link](1);


[Link](
"State of post 1: {0} is {1}",
[Link],
[Link](post1).State);

// Query some more posts from the database


[Link](p => [Link]("[Link]").Load();

// Loop over the posts in the context again.


[Link]("\nIn Local after [Link] query: ");
foreach (var post in [Link])
{
[Link](
"Found {0}: {1} with state {2}",
[Link],
[Link],
[Link](post).State);
}
}

Suponiendo que tenemos algunas entradas etiquetadas con "Entity-Framework" y "[Link]", la salida puede
tener un aspecto similar al siguiente:

In Local after entity-framework query:


Found 3: EF Designer Basics with state Unchanged
Found 5: EF Code First Basics with state Unchanged
Found 0: What's New in EF with state Added
State of post 1: EF Beginners Guide is Deleted

In Local after [Link] query:


Found 3: EF Designer Basics with state Unchanged
Found 5: EF Code First Basics with state Unchanged
Found 0: What's New in EF with state Added
Found 4: [Link] Beginners Guide with state Unchanged

Esto ilustra tres puntos:


La nueva publicación ' What's New in EF ' que se agregó a la colección local se hace seguimiento por el
contexto en el estado Added. Por lo tanto, se insertará en la base de datos cuando se llame a SaveChanges.
La publicación que se quitó de la colección local (guía para principiantes de EF) ahora está marcada como
eliminada en el contexto. Por lo tanto, se eliminará de la base de datos cuando se llame a SaveChanges.
La publicación adicional (guía para principiantes de [Link]) cargada en el contexto con la segunda consulta
se agrega automáticamente a la colección local.
Una cuestión final que hay que tener en cuenta sobre local es que, dado que es un rendimiento de
ObservableCollection no es excelente para un gran número de entidades. Por lo tanto, si está tratando con miles
de entidades en el contexto, puede que no sea aconsejable usar local.

Usar local para el enlace de datos de WPF


La propiedad local en DbSet se puede usar directamente para el enlace de datos en una aplicación WPF porque
es una instancia de ObservableCollection. Tal como se describe en las secciones anteriores, esto significa que
permanecerá sincronizada automáticamente con el contenido del contexto y el contenido del contexto
permanecerá sincronizado automáticamente con él. Tenga en cuenta que debe rellenar previamente la colección
local con datos para que sea todo lo que se va a enlazar a, ya que local nunca genera una consulta de base de
datos.
No es un lugar adecuado para un ejemplo de enlace de datos de WPF completo, pero los elementos clave son:
Configuración de un origen de enlace
Enlazarlo a la propiedad local del conjunto
Rellene el local mediante una consulta a la base de datos.

Enlace de WPF a propiedades de navegación


Si está realizando el enlace de datos principal/detalle, puede enlazar la vista de detalle a una propiedad de
navegación de una de las entidades. Una manera fácil de realizar este trabajo es usar ObservableCollection para
la propiedad de navegación. Por ejemplo:

public class Blog


{
private readonly ObservableCollection<Post> _posts =
new ObservableCollection<Post>();

public int BlogId { get; set; }


public string Name { get; set; }

public virtual ObservableCollection<Post> Posts


{
get { return _posts; }
}
}

Usar local para limpiar entidades en SaveChanges


En la mayoría de los casos, las entidades quitadas de una propiedad de navegación no se marcarán
automáticamente como eliminadas en el contexto. Por ejemplo, si quita un objeto post de la colección blog.
posts, esa publicación no se eliminará automáticamente cuando se llame a SaveChanges. Si necesita que se
elimine, puede que necesite encontrar estas entidades pendientes y marcarlas como eliminadas antes de llamar
a SaveChanges o como parte de un SaveChanges invalidado. Por ejemplo:
public override int SaveChanges()
{
foreach (var post in [Link]())
{
if ([Link] == null)
{
[Link](post);
}
}

return [Link]();
}

El código anterior usa la colección local para buscar todas las publicaciones y marca cualquier que no tenga una
referencia de blog como eliminada. La llamada a ToList es necesaria porque, de lo contrario, se modificará la
colección mediante la llamada a Remove mientras se está enumerando. En la mayoría de las demás situaciones,
puede realizar consultas directamente en la propiedad local sin usar ToList primero.

Usar local y ToBindingList para el enlace de datos Windows Forms


Windows Forms no admite el enlace de datos de plena fidelidad mediante ObservableCollection directamente.
Sin embargo, todavía puede usar la propiedad local DbSet para el enlace de datos con el fin de obtener todas las
ventajas descritas en las secciones anteriores. Esto se logra mediante el método de extensión ToBindingList, que
crea una implementación de IBindingList respaldada por el ObservableCollection local.
No es un lugar adecuado para un ejemplo de enlace de datos completo Windows Forms pero los elementos
clave son:
Configuración de un origen de enlace de objeto
Enlácelo a la propiedad local del conjunto mediante local. ToBindingList ()
Rellenar local mediante una consulta a la base de datos

Obtención de información detallada sobre las entidades sometidas a


seguimiento
Muchos de los ejemplos de esta serie usan el método entry para devolver una instancia de DbEntityEntry para
una entidad. Este objeto de entrada actúa como punto de partida para recopilar información sobre la entidad,
como su estado actual, así como para realizar operaciones en la entidad, como cargar explícitamente una
entidad relacionada.
Los métodos de entrada devuelven objetos DbEntityEntry para muchas o todas las entidades de las que el
contexto realiza un seguimiento. Esto le permite recopilar información o realizar operaciones en muchas
entidades en lugar de en una sola entrada. Por ejemplo:
using (var context = new BloggingContext())
{
// Load some entities into the context
[Link]();
[Link]();
[Link]();

// Make some changes


[Link](1).Title = "The New [Link] Blog";
[Link]([Link](2));
[Link](new Author { Name = "Jane Doe" });
[Link](1).Username = "johndoe1987";

// Look at the state of all entities in the context


[Link]("All tracked entities: ");
foreach (var entry in [Link]())
{
[Link](
"Found entity of type {0} with state {1}",
[Link]([Link]()).Name,
[Link]);
}

// Find modified entities of any type


[Link]("\nAll modified entities: ");
foreach (var entry in [Link]()
.Where(e => [Link] == [Link]))
{
[Link](
"Found entity of type {0} with state {1}",
[Link]([Link]()).Name,
[Link]);
}

// Get some information about just the tracked blogs


[Link]("\nTracked blogs: ");
foreach (var entry in [Link]<Blog>())
{
[Link](
"Found Blog {0}: {1} with original Name {2}",
[Link],
[Link],
[Link](p => [Link]).OriginalValue);
}

// Find all people (author or reader)


[Link]("\nPeople: ");
foreach (var entry in [Link]<IPerson>())
{
[Link]("Found Person {0}", [Link]);
}
}

Observamos que estamos introduciendo una clase Author y Reader en el ejemplo. ambas clases implementan la
interfaz IPerson.
public class Author : IPerson
{
public int AuthorId { get; set; }
public string Name { get; set; }
public string Biography { get; set; }
}

public class Reader : IPerson


{
public int ReaderId { get; set; }
public string Name { get; set; }
public string Username { get; set; }
}

public interface IPerson


{
string Name { get; }
}

Supongamos que tenemos los siguientes datos en la base de datos:


Blog con BlogId = 1 y name = ' [Link] blog '
Blog con BlogId = 2 y name = "blog de Visual Studio"
Blog con BlogId = 3 y name = ' .NET Framework blog '
Autor con AuthorId = 1 y name = ' Joe Bloggs '
Lector con ReaderId = 1 y name = ' John Doe '
La salida de la ejecución del código sería:

All tracked entities:


Found entity of type Blog with state Modified
Found entity of type Blog with state Deleted
Found entity of type Blog with state Unchanged
Found entity of type Author with state Unchanged
Found entity of type Author with state Added
Found entity of type Reader with state Modified

All modified entities:


Found entity of type Blog with state Modified
Found entity of type Reader with state Modified

Tracked blogs:
Found Blog 1: The New [Link] Blog with original Name [Link] Blog
Found Blog 2: The Visual Studio Blog with original Name The Visual Studio Blog
Found Blog 3: .NET Framework Blog with original Name .NET Framework Blog

People:
Found Person John Doe
Found Person Joe Bloggs
Found Person Jane Doe

En estos ejemplos se muestran varios puntos:


Los métodos de entrada devuelven entradas para entidades en todos los Estados, incluido Deleted. Compare
esto con el local, que excluye las entidades eliminadas.
Se devuelven las entradas de todos los tipos de entidad cuando se usa el método de entradas no genéricas.
Cuando se usa el método de entradas genéricas, las entradas solo se devuelven para las entidades que son
instancias del tipo genérico. Se usó anteriormente para obtener entradas de todos los blogs. También se
usaba para obtener entradas para todas las entidades que implementan IPerson. Esto demuestra que el tipo
genérico no tiene que ser un tipo de entidad real.
LINQ to Objects se puede utilizar para filtrar los resultados devueltos. Se usó anteriormente para buscar
entidades de cualquier tipo, siempre y cuando se modifiquen.
Tenga en cuenta que las instancias de DbEntityEntry siempre contienen una entidad que no es NULL. Las
entradas de relación y las entradas de código auxiliar no se representan como instancias de DbEntityEntry, por
lo que no es necesario filtrarlas.
consultas de no seguimiento
12/03/2021 • 2 minutes to read

En ocasiones, es posible que desee obtener las entidades de una consulta, pero el contexto no puede realizar el
seguimiento de esas entidades. Esto puede dar lugar a un mejor rendimiento cuando se consulta un gran
número de entidades en escenarios de solo lectura. Las técnicas que se muestran en este tema se aplican
igualmente a los modelos creados con Code First y EF Designer.
Un nuevo método de extensión AsNoTracking permite ejecutar cualquier consulta de esta manera. Por ejemplo:

using (var context = new BloggingContext())


{
// Query for all blogs without tracking them
var blogs1 = [Link]();

// Query for some blogs without tracking them


var blogs2 = [Link]
.Where(b => [Link](".NET"))
.AsNoTracking()
.ToList();
}
Consultas SQL sin formato (EF6)
12/03/2021 • 4 minutes to read

Entity Framework permite realizar consultas con LINQ con las clases de entidad. Sin embargo, puede haber
ocasiones en las que desee ejecutar consultas utilizando SQL sin formato directamente en la base de datos. Esto
incluye llamar a procedimientos almacenados, que pueden ser útiles para Code First modelos que actualmente
no admiten la asignación a procedimientos almacenados. Las técnicas que se muestran en este tema se aplican
igualmente a los modelos creados con Code First y EF Designer.

Escribir consultas SQL para entidades


El método SqlQuery en DbSet permite escribir una consulta SQL sin formato que devolverá las instancias de la
entidad. El contexto realizará un seguimiento de los objetos devueltos tal como lo haría si se devolvieran
mediante una consulta LINQ. Por ejemplo:

using (var context = new BloggingContext())


{
var blogs = [Link]("SELECT * FROM [Link]").ToList();
}

Tenga en cuenta que, al igual que para las consultas LINQ, la consulta no se ejecuta hasta que se enumeran los
resultados; en el ejemplo anterior, esto se realiza con la llamada a ToList.
Se debe tener cuidado cuando las consultas SQL sin procesar se escriben por dos motivos. En primer lugar, se
debe escribir la consulta para asegurarse de que solo devuelve entidades que son realmente del tipo solicitado.
Por ejemplo, al usar características como la herencia, es fácil escribir una consulta que creará entidades que son
del tipo CLR incorrecto.
En segundo lugar, algunos tipos de consultas SQL sin procesar exponen posibles riesgos de seguridad,
especialmente en torno a ataques por inyección de SQL. Asegúrese de usar los parámetros de la consulta de la
manera correcta para protegerse frente a estos ataques.
Cargar entidades desde procedimientos almacenados
Puede usar DbSet. SqlQuery para cargar entidades de los resultados de un procedimiento almacenado. Por
ejemplo, el código siguiente llama a DBO. Procedimiento GetBlogs en la base de datos:

using (var context = new BloggingContext())


{
var blogs = [Link]("[Link]").ToList();
}

También puede pasar parámetros a un procedimiento almacenado con la sintaxis siguiente:

using (var context = new BloggingContext())


{
var blogId = 1;

var blogs = [Link]("[Link] @p0", blogId).Single();


}
Escribir consultas SQL para tipos que no son de entidad
Una consulta SQL que devuelve instancias de cualquier tipo, incluidos los tipos primitivos, se puede crear con el
método SqlQuery en la clase de base de datos. Por ejemplo:

using (var context = new BloggingContext())


{
var blogNames = [Link]<string>(
"SELECT Name FROM [Link]").ToList();
}

Nunca se realizará el seguimiento de los resultados devueltos de SqlQuery en la base de datos en el contexto,
incluso si los objetos son instancias de un tipo de entidad.

Enviar comandos sin formato a la base de datos


Los comandos que no son de consulta se pueden enviar a la base de datos mediante el método
ExecuteSqlCommand en la base de datos. Por ejemplo:

using (var context = new BloggingContext())


{
[Link](
"UPDATE [Link] SET Name = 'Another Name' WHERE BlogId = 1");
}

Tenga en cuenta que los cambios realizados en los datos de la base de datos mediante ExecuteSqlCommand son
opacos en el contexto hasta que las entidades se cargan o se vuelven a cargar desde la base de datos.
Parámetros de salida
Si se utilizan parámetros de salida, sus valores no estarán disponibles hasta que los resultados se hayan leído
por completo. Esto se debe al comportamiento subyacente de DbDataReader, consulte recuperación de datos
mediante un DataReader para obtener más detalles.
Carga de entidades relacionadas
12/03/2021 • 10 minutes to read

Entity Framework admite tres maneras de cargar datos relacionados: carga diligente, carga diferida y carga
explícita. Las técnicas que se muestran en este tema se aplican igualmente a los modelos creados con Code First
y EF Designer.

Cargando diligentemente
La carga diligente es el proceso por el cual una consulta para un tipo de entidad también carga las entidades
relacionadas como parte de la consulta. La carga diligente se logra mediante el uso del método include. Por
ejemplo, las consultas siguientes cargarán blogs y todas las entradas relacionadas con cada blog.

using (var context = new BloggingContext())


{
// Load all blogs and related posts.
var blogs1 = [Link]
.Include(b => [Link])
.ToList();

// Load one blog and its related posts.


var blog1 = [Link]
.Where(b => [Link] == "[Link] Blog")
.Include(b => [Link])
.FirstOrDefault();

// Load all blogs and related posts


// using a string to specify the relationship.
var blogs2 = [Link]
.Include("Posts")
.ToList();

// Load one blog and its related posts


// using a string to specify the relationship.
var blog2 = [Link]
.Where(b => [Link] == "[Link] Blog")
.Include("Posts")
.FirstOrDefault();
}

NOTE
Include es un método de extensión en el espacio de nombres System. Data. Entity, por lo que debe asegurarse de que
está usando ese espacio de nombres.

Carga de varios niveles diligentemente


También es posible cargar diligentemente varios niveles de entidades relacionadas. En las consultas siguientes
se muestran ejemplos de cómo hacerlo para las propiedades de navegación de colección y de referencia.
using (var context = new BloggingContext())
{
// Load all blogs, all related posts, and all related comments.
var blogs1 = [Link]
.Include(b => [Link](p => [Link]))
.ToList();

// Load all users, their related profiles, and related avatar.


var users1 = [Link]
.Include(u => [Link])
.ToList();

// Load all blogs, all related posts, and all related comments
// using a string to specify the relationships.
var blogs2 = [Link]
.Include("[Link]")
.ToList();

// Load all users, their related profiles, and related avatar


// using a string to specify the relationships.
var users2 = [Link]
.Include("[Link]")
.ToList();
}

NOTE
Actualmente no es posible filtrar las entidades relacionadas que se cargan. Incluir siempre incluirá todas las entidades
relacionadas.

Carga diferida
La carga diferida es el proceso por el que una entidad o colección de entidades se carga automáticamente desde
la base de datos la primera vez que se tiene acceso a una propiedad que hace referencia a la entidad o
entidades. Al usar tipos de entidad POCO, la carga diferida se consigue creando instancias de tipos de proxy
derivados y, a continuación, reemplazando las propiedades virtuales para agregar el enlace de carga. Por
ejemplo, al usar la clase de entidad de blog que se define a continuación, se cargarán las publicaciones
relacionadas la primera vez que se tenga acceso a la propiedad de navegación posts:

public class Blog


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

public virtual ICollection<Post> Posts { get; set; }


}

Desactivación de la carga diferida para la serialización


La carga diferida y la serialización no se combinan bien y, si no tiene cuidado, puede finalizar la consulta de toda
la base de datos, solo porque está habilitada la carga diferida. La mayoría de los serializadores funcionan
mediante el acceso a cada propiedad en una instancia de un tipo. El acceso de propiedad desencadena la carga
diferida, por lo que se serializan más entidades. En esas entidades se tiene acceso a las propiedades de, e incluso
se cargan más entidades. Se recomienda desactivar la carga diferida antes de serializar una entidad. Se muestra
cómo hacerlo en las secciones siguientes.
Desactivar la carga diferida para propiedades de navegación específicas
La carga diferida de la colección de publicaciones se puede desactivar haciendo que la propiedad postes no sea
virtual:

public class Blog


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

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


}

La carga de la colección de publicaciones se puede seguir usando la carga diligente (consulte la carga rápida
anterior) o el método Load (vea carga explícita a continuación).
Desactivar la carga diferida para todas las entidades
La carga diferida se puede desactivar para todas las entidades en el contexto estableciendo una marca en la
propiedad de configuración. Por ejemplo:

public class BloggingContext : DbContext


{
public BloggingContext()
{
[Link] = false;
}
}

La carga de entidades relacionadas todavía se puede lograr mediante la carga diligente (consulte la carga rápida
anterior) o el método Load (vea carga explícita a continuación).

Cargar explícitamente
Incluso con la carga diferida deshabilitada, todavía es posible cargar de forma diferida las entidades
relacionadas, pero debe realizarse con una llamada explícita. Para ello, use el método Load en la entrada de la
entidad relacionada. Por ejemplo:

using (var context = new BloggingContext())


{
var post = [Link](2);

// Load the blog related to a given post.


[Link](post).Reference(p => [Link]).Load();

// Load the blog related to a given post using a string.


[Link](post).Reference("Blog").Load();

var blog = [Link](1);

// Load the posts related to a given blog.


[Link](blog).Collection(p => [Link]).Load();

// Load the posts related to a given blog


// using a string to specify the relationship.
[Link](blog).Collection("Posts").Load();
}
NOTE
El método de referencia debe usarse cuando una entidad tiene una propiedad de navegación a otra entidad única. Por
otro lado, el método de colección debe usarse cuando una entidad tiene una propiedad de navegación a una colección de
otras entidades.

Aplicar filtros al cargar explícitamente entidades relacionadas


El método de consulta proporciona acceso a la consulta subyacente que utilizará Entity Framework al cargar las
entidades relacionadas. Después, puede usar LINQ para aplicar filtros a la consulta antes de ejecutarlo con una
llamada a un método de extensión LINQ como ToList, Load, etc. El método de consulta se puede utilizar con
propiedades de navegación de referencia y de colección, pero es muy útil para las colecciones en las que se
puede usar para cargar solo parte de la colección. Por ejemplo:

using (var context = new BloggingContext())


{
var blog = [Link](1);

// Load the posts with the 'entity-framework' tag related to a given blog.
[Link](blog)
.Collection(b => [Link])
.Query()
.Where(p => [Link]("entity-framework"))
.Load();

// Load the posts with the 'entity-framework' tag related to a given blog
// using a string to specify the relationship.
[Link](blog)
.Collection("Posts")
.Query()
.Where(p => [Link]("entity-framework"))
.Load();
}

Cuando se usa el método de consulta, suele ser mejor desactivar la carga diferida para la propiedad de
navegación. Esto se debe a que, de lo contrario, es posible que el mecanismo de carga diferida cargue
automáticamente toda la colección antes o después de que se haya ejecutado la consulta filtrada.

NOTE
Aunque la relación se puede especificar como una cadena en lugar de una expresión lambda, el IQueryable devuelto no es
genérico cuando se usa una cadena y, por lo tanto, el método Cast suele ser necesario antes de que se pueda hacer nada
útil con él.

Usar Query para contar entidades relacionadas sin cargarlas


A veces resulta útil saber el número de entidades relacionadas con otra entidad de la base de datos sin incurrir
realmente en el costo de cargar todas esas entidades. Se puede utilizar el método de consulta con el método
Count de LINQ para hacerlo. Por ejemplo:
using (var context = new BloggingContext())
{
var blog = [Link](1);

// Count how many posts the blog has.


var postCount = [Link](blog)
.Collection(b => [Link])
.Query()
.Count();
}
Guardado de datos con Entity Framework 6
12/03/2021 • 2 minutes to read

En esta sección puede encontrar información sobre las capacidades de seguimiento de cambios de EF y lo que
sucede cuando se llama a SaveChanges para almacenar los cambios realizados en los objetos en la base de
datos.
Detección automática de cambios
12/03/2021 • 3 minutes to read

Al usar la mayoría de las entidades POCO, la determinación de cómo ha cambiado una entidad (y, por lo tanto,
las actualizaciones que se deben enviar a la base de datos) se controla mediante el algoritmo de detección de
cambios. Detectar cambios funciona detectando las diferencias entre los valores de propiedad actuales de la
entidad y los valores de propiedad originales que se almacenan en una instantánea cuando se consulta o se
adjunta la entidad. Las técnicas que se muestran en este tema se aplican igualmente a los modelos creados con
Code First y EF Designer.
De forma predeterminada, Entity Framework realiza la detección automática de cambios cuando se llama a los
métodos siguientes:
[Link]
DbSet. local
DbSet. Add
DbSet. AddRange
DbSet. Remove
DbSet. RemoveRange
DbSet. Attach
[Link]
DbContext. GetValidationErrors
[Link]
DbChangeTracker. entradas

Deshabilitación de la detección automática de cambios


Si está realizando un seguimiento de muchas entidades en el contexto y llama a uno de estos métodos muchas
veces en un bucle, puede obtener mejoras de rendimiento significativas desactivando la detección de cambios
durante el bucle. Por ejemplo:

using (var context = new BloggingContext())


{
try
{
[Link] = false;

// Make many calls in a loop


foreach (var blog in aLotOfBlogs)
{
[Link](blog);
}
}
finally
{
[Link] = true;
}
}

No olvide volver a habilitar la detección de cambios después del bucle: hemos usado try/finally para asegurarse
de que siempre se vuelve a habilitar aunque el código del bucle produzca una excepción.
Una alternativa a deshabilitar y volver a habilitar consiste en dejar la detección automática de los cambios
desactivados en todo momento y en el contexto de llamada. ChangeTracker. DetectChanges explícitamente o
usar los proxies de seguimiento de cambios diligentemente. Ambas opciones están avanzadas y pueden
introducir fácilmente errores sutiles en la aplicación, por lo que deben usarse con cuidado.
Si necesita agregar o quitar muchos objetos de un contexto, considere la posibilidad de usar DbSet. AddRange y
DbSet. RemoveRange. Estos métodos detectan automáticamente los cambios solo una vez después de que se
completen las operaciones de agregar o quitar.
Trabajar con Estados de entidad
12/03/2021 • 11 minutes to read

En este tema se explica cómo agregar y adjuntar entidades a un contexto y cómo Entity Framework procesa
estas durante el SaveChanges. Entity Framework se encarga del seguimiento del estado de las entidades
mientras están conectadas a un contexto, pero en escenarios desconectados o de N niveles, puede permitir que
EF sepa en qué estado deben estar las entidades. Las técnicas que se muestran en este tema se aplican
igualmente a los modelos creados con Code First y EF Designer.

Estados de la entidad y SaveChanges


Una entidad puede estar en uno de cinco Estados, tal y como se define en la enumeración EntityState. Estos
estados son:
Agregado: el contexto está realizando el seguimiento de la entidad, pero aún no existe en la base de datos
Unchanged: el contexto realiza un seguimiento de la entidad y existe en la base de datos, y sus valores de
propiedad no han cambiado con respecto a los valores de la base de datos.
Modificado: el contexto está realizando el seguimiento de la entidad y existe en la base de datos, y se han
modificado algunos o todos sus valores de propiedad.
Eliminado: el contexto realiza un seguimiento de la entidad y existe en la base de datos, pero se ha marcado
para su eliminación de la base de datos la próxima vez que se llame a SaveChanges.
Detached: el contexto no está realizando el seguimiento de la entidad
SaveChanges realiza diferentes acciones para entidades en diferentes Estados:
SaveChanges no toca las entidades sin modificar. Las actualizaciones no se envían a la base de datos para
entidades en el estado sin cambios.
Las entidades agregadas se insertan en la base de datos y, a continuación, se vuelven sin cambios cuando se
devuelve SaveChanges.
Las entidades modificadas se actualizan en la base de datos y, a continuación, se vuelven sin cambios cuando
se devuelve SaveChanges.
Las entidades eliminadas se eliminan de la base de datos y, a continuación, se desasocian del contexto.
En los ejemplos siguientes se muestran las maneras en las que se puede cambiar el estado de una entidad o un
gráfico de entidades.

Agregar una nueva entidad al contexto


Se puede Agregar una nueva entidad al contexto llamando al método Add en DbSet. Esto coloca la entidad en el
estado agregado, lo que significa que se insertará en la base de datos la próxima vez que se llame a
SaveChanges. Por ejemplo:

using (var context = new BloggingContext())


{
var blog = new Blog { Name = "[Link] Blog" };
[Link](blog);
[Link]();
}

Otra manera de agregar una nueva entidad al contexto es cambiar su estado a agregado. Por ejemplo:
using (var context = new BloggingContext())
{
var blog = new Blog { Name = "[Link] Blog" };
[Link](blog).State = [Link];
[Link]();
}

Por último, puede Agregar una nueva entidad al contexto al enlazarla a otra entidad de la que ya se está
realizando el seguimiento. Esto podría ser agregando la nueva entidad a la propiedad de navegación de
colección de otra entidad o estableciendo una propiedad de navegación de referencia de otra entidad para que
apunte a la nueva entidad. Por ejemplo:

using (var context = new BloggingContext())


{
// Add a new User by setting a reference from a tracked Blog
var blog = [Link](1);
[Link] = new User { UserName = "johndoe1987" };

// Add a new Post by adding to the collection of a tracked Blog


[Link](new Post { Name = "How to Add Entities" });

[Link]();
}

Tenga en cuenta que, para todos estos ejemplos, si la entidad que se va a agregar tiene referencias a otras
entidades de las que todavía no se ha realizado un seguimiento, estas nuevas entidades también se agregarán al
contexto y se insertarán en la base de datos la próxima vez que se llame a SaveChanges.

Asociar una entidad existente al contexto


Si tiene una entidad que sabe que ya existe en la base de datos pero en la que no se realiza el seguimiento
actualmente por el contexto, puede indicar al contexto que realice el seguimiento de la entidad mediante el
método Attach en DbSet. La entidad estará en el estado Unchanged en el contexto. Por ejemplo:

var existingBlog = new Blog { BlogId = 1, Name = "[Link] Blog" };

using (var context = new BloggingContext())


{
[Link](existingBlog);

// Do some more work...

[Link]();
}

Tenga en cuenta que no se realizarán cambios en la base de datos si se llama a SaveChanges sin realizar
ninguna otra manipulación de la entidad adjunta. Esto se debe a que la entidad está en el estado Unchanged.
Otra manera de asociar una entidad existente al contexto es cambiar su estado a sin cambios. Por ejemplo:
var existingBlog = new Blog { BlogId = 1, Name = "[Link] Blog" };

using (var context = new BloggingContext())


{
[Link](existingBlog).State = [Link];

// Do some more work...

[Link]();
}

Tenga en cuenta que para ambos ejemplos, si la entidad que se va a asociar tiene referencias a otras entidades
de las que todavía no se ha realizado un seguimiento, estas nuevas entidades también se adjuntarán al contexto
en el estado sin cambios.

Adjuntar una entidad existente pero modificada al contexto


Si tiene una entidad que sabe que ya existe en la base de datos, pero en la que se pueden realizar cambios,
puede indicar al contexto que adjunte la entidad y establezca su estado en modificado. Por ejemplo:

var existingBlog = new Blog { BlogId = 1, Name = "[Link] Blog" };

using (var context = new BloggingContext())


{
[Link](existingBlog).State = [Link];

// Do some more work...

[Link]();
}

Al cambiar el estado a modificado, todas las propiedades de la entidad se marcarán como modificadas y todos
los valores de propiedad se enviarán a la base de datos cuando se llame a SaveChanges.
Tenga en cuenta que si la entidad que se va a asociar tiene referencias a otras entidades de las que todavía no se
ha realizado un seguimiento, estas nuevas entidades se adjuntarán al contexto en el estado Unchanged (no se
modificarán automáticamente). Si tiene varias entidades que deben marcarse como modificadas, debe
establecer el estado de cada una de estas entidades de forma individual.

Cambiar el estado de una entidad de la que se ha realizado un


seguimiento
Puede cambiar el estado de una entidad de la que ya se realiza el seguimiento estableciendo la propiedad State
en su entrada. Por ejemplo:

var existingBlog = new Blog { BlogId = 1, Name = "[Link] Blog" };

using (var context = new BloggingContext())


{
[Link](existingBlog);
[Link](existingBlog).State = [Link];

// Do some more work...

[Link]();
}

Tenga en cuenta que la llamada a Add o attach para una entidad de la que ya se ha realizado un seguimiento
también se puede usar para cambiar el estado de la entidad. Por ejemplo, al llamar a attach para una entidad
que está actualmente en el estado agregado, se cambiará su estado a sin cambios.

Insertar o actualizar patrón


Un patrón común para algunas aplicaciones es agregar una entidad como nueva (lo que da como resultado una
inserción de base de datos) o adjuntar una entidad como existente y marcarla como modificada (lo que da como
resultado una actualización de la base de datos) en función del valor de la clave principal. Por ejemplo, al utilizar
las claves principales de enteros generados por la base de datos, es habitual tratar una entidad con una clave
cero como nueva y una entidad con una clave distinta de cero como existente. Este patrón se puede lograr
estableciendo el estado de la entidad en función de una comprobación del valor de la clave principal. Por
ejemplo:

public void InsertOrUpdate(Blog blog)


{
using (var context = new BloggingContext())
{
[Link](blog).State = [Link] == 0 ?
[Link] :
[Link];

[Link]();
}
}

Tenga en cuenta que al cambiar el estado a modificado, todas las propiedades de la entidad se marcarán como
modificadas y todos los valores de propiedad se enviarán a la base de datos cuando se llame a SaveChanges.
Trabajar con valores de propiedad
12/03/2021 • 17 minutes to read

En la mayoría de los casos, Entity Framework se encargará del seguimiento del estado, los valores originales y
los valores actuales de las propiedades de las instancias de la entidad. Sin embargo, puede haber algunos casos,
como escenarios desconectados, donde desea ver o manipular la información que EF tiene sobre las
propiedades. Las técnicas que se muestran en este tema se aplican igualmente a los modelos creados con Code
First y EF Designer.
Entity Framework realiza un seguimiento de dos valores para cada propiedad de una entidad de la que se ha
realizado un seguimiento. El valor actual es, como el nombre indica, el valor actual de la propiedad en la entidad.
El valor original es el valor que tenía la propiedad cuando la entidad se consultaba desde la base de datos o se
adjuntó al contexto.
Hay dos mecanismos generales para trabajar con valores de propiedad:
El valor de una propiedad única se puede obtener de una manera fuertemente tipada mediante el método de
propiedad.
Los valores de todas las propiedades de una entidad se pueden leer en un objeto DbPropertyValues. A
continuación, DbPropertyValues actúa como un objeto similar a un diccionario para permitir la lectura y el
establecimiento de los valores de propiedad. Los valores de un objeto DbPropertyValues se pueden
establecer a partir de los valores de otro objeto DbPropertyValues o de los valores de otro objeto, como otra
copia de la entidad o un objeto de transferencia de datos simple (DTO).
En las secciones siguientes se muestran ejemplos del uso de los dos mecanismos anteriores.

Obtener y establecer el valor actual o original de una propiedad


individual
En el ejemplo siguiente se muestra cómo se puede leer el valor actual de una propiedad y establecerla en un
nuevo valor:

using (var context = new BloggingContext())


{
var blog = [Link](3);

// Read the current value of the Name property


string currentName1 = [Link](blog).Property(u => [Link]).CurrentValue;

// Set the Name property to a new value


[Link](blog).Property(u => [Link]).CurrentValue = "My Fancy Blog";

// Read the current value of the Name property using a string for the property name
object currentName2 = [Link](blog).Property("Name").CurrentValue;

// Set the Name property to a new value using a string for the property name
[Link](blog).Property("Name").CurrentValue = "My Boring Blog";
}

Use la propiedad OriginalValue en lugar de la propiedad CurrentValue para leer o establecer el valor original.
Tenga en cuenta que el valor devuelto se escribe como "objeto" cuando se utiliza una cadena para especificar el
nombre de la propiedad. Por otro lado, el valor devuelto está fuertemente tipado si se utiliza una expresión
lambda.
Al establecer el valor de la propiedad de este modo, solo se marcará la propiedad como modificada si el nuevo
valor es diferente del valor anterior.
Cuando se establece un valor de propiedad de esta manera, el cambio se detecta automáticamente aunque
AutoDetectChanges esté desactivado.

Obtener y establecer el valor actual de una propiedad no asignada


También se puede leer el valor actual de una propiedad que no está asignada a la base de datos. Un ejemplo de
una propiedad no asignada podría ser una propiedad RssLink en el blog. Este valor se puede calcular en función
de BlogId y, por tanto, no es necesario almacenarlo en la base de datos. Por ejemplo:

using (var context = new BloggingContext())


{
var blog = [Link](1);
// Read the current value of an unmapped property
var rssLink = [Link](blog).Property(p => [Link]).CurrentValue;

// Use a string to specify the property name


var rssLinkAgain = [Link](blog).Property("RssLink").CurrentValue;
}

También se puede establecer el valor actual si la propiedad expone un establecedor.


Leer los valores de las propiedades sin asignar es útil al realizar Entity Framework la validación de las
propiedades no asignadas. Por el mismo motivo, los valores actuales se pueden leer y establecer para las
propiedades de entidades a las que el contexto no realiza un seguimiento actualmente. Por ejemplo:

using (var context = new BloggingContext())


{
// Create an entity that is not being tracked
var blog = new Blog { Name = "[Link] Blog" };

// Read and set the current value of Name as before


var currentName1 = [Link](blog).Property(u => [Link]).CurrentValue;
[Link](blog).Property(u => [Link]).CurrentValue = "My Fancy Blog";
var currentName2 = [Link](blog).Property("Name").CurrentValue;
[Link](blog).Property("Name").CurrentValue = "My Boring Blog";
}

Tenga en cuenta que los valores originales no están disponibles para las propiedades no asignadas o para las
propiedades de entidades de las que el contexto no realiza un seguimiento.

Comprobar si una propiedad está marcada como modificada


En el ejemplo siguiente se muestra cómo comprobar si una propiedad individual está marcada como
modificada:
using (var context = new BloggingContext())
{
var blog = [Link](1);

var nameIsModified1 = [Link](blog).Property(u => [Link]).IsModified;

// Use a string for the property name


var nameIsModified2 = [Link](blog).Property("Name").IsModified;
}

Los valores de las propiedades modificadas se envían como actualizaciones a la base de datos cuando se llama a
SaveChanges.

Marcar una propiedad como modificada


En el ejemplo siguiente se muestra cómo forzar que una propiedad individual se marque como modificada:

using (var context = new BloggingContext())


{
var blog = [Link](1);

[Link](blog).Property(u => [Link]).IsModified = true;

// Use a string for the property name


[Link](blog).Property("Name").IsModified = true;
}

Marcar una propiedad como modificada obliga a que se envíe una actualización a la base de datos para la
propiedad cuando se llama a SaveChanges incluso si el valor actual de la propiedad es el mismo que el valor
original.
Actualmente no es posible restablecer una propiedad individual para que no se modifique una vez que se ha
marcado como modificada. Esto es algo que tenemos previsto admitir en una versión futura.

Leer los valores actuales, originales y de base de datos para todas las
propiedades de una entidad
En el ejemplo siguiente se muestra cómo leer los valores actuales, los valores originales y los valores reales de
la base de datos para todas las propiedades asignadas de una entidad.
using (var context = new BloggingContext())
{
var blog = [Link](1);

// Make a modification to Name in the tracked entity


[Link] = "My Cool Blog";

// Make a modification to Name in the database


[Link]("update [Link] set Name = 'My Boring Blog' where Id = 1");

// Print out current, original, and database values


[Link]("Current values:");
PrintValues([Link](blog).CurrentValues);

[Link]("\nOriginal values:");
PrintValues([Link](blog).OriginalValues);

[Link]("\nDatabase values:");
PrintValues([Link](blog).GetDatabaseValues());
}

public static void PrintValues(DbPropertyValues values)


{
foreach (var propertyName in [Link])
{
[Link]("Property {0} has value {1}",
propertyName, values[propertyName]);
}
}

Los valores actuales son los valores que las propiedades de la entidad contienen actualmente. Los valores
originales son los valores que se leyeron de la base de datos cuando se realizó la consulta de la entidad. Los
valores de la base de datos son los valores que están almacenados actualmente en la base de datos. Obtener los
valores de la base de datos es útil cuando los valores de la base de datos pueden haber cambiado desde la
consulta de la entidad, por ejemplo, cuando otro usuario ha realizado una edición simultánea en la base de
datos.

Establecer valores actuales o originales de otro objeto


Los valores actuales o originales de una entidad de la que se ha realizado un seguimiento se pueden actualizar
mediante la copia de los valores de otro objeto. Por ejemplo:
using (var context = new BloggingContext())
{
var blog = [Link](1);
var coolBlog = new Blog { Id = 1, Name = "My Cool Blog" };
var boringBlog = new BlogDto { Id = 1, Name = "My Boring Blog" };

// Change the current and original values by copying the values from other objects
var entry = [Link](blog);
[Link](coolBlog);
[Link](boringBlog);

// Print out current and original values


[Link]("Current values:");
PrintValues([Link]);

[Link]("\nOriginal values:");
PrintValues([Link]);
}

public class BlogDto


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

La ejecución del código anterior se imprimirá:

Current values:
Property Id has value 1
Property Name has value My Cool Blog

Original values:
Property Id has value 1
Property Name has value My Boring Blog

Esta técnica se usa a veces al actualizar una entidad con valores obtenidos de una llamada de servicio o un
cliente en una aplicación de n niveles. Tenga en cuenta que el objeto utilizado no tiene que ser del mismo tipo
que la entidad, siempre y cuando tenga propiedades cuyos nombres coincidan con los de la entidad. En el
ejemplo anterior, se usa una instancia de BlogDTO para actualizar los valores originales.
Tenga en cuenta que solo las propiedades que se establecen en valores diferentes cuando se copian del otro
objeto se marcan como modificadas.

Establecer los valores actuales o originales de un diccionario


Los valores actuales o originales de una entidad de la que se ha realizado un seguimiento se pueden actualizar
mediante la copia de los valores de un diccionario u otra estructura de datos. Por ejemplo:
using (var context = new BloggingContext())
{
var blog = [Link](1);

var newValues = new Dictionary<string, object>


{
{ "Name", "The New [Link] Blog" },
{ "Url", "[Link]/adonet" },
};

var currentValues = [Link](blog).CurrentValues;

foreach (var propertyName in [Link])


{
currentValues[propertyName] = newValues[propertyName];
}

PrintValues(currentValues);
}

Use la propiedad OriginalValues en lugar de la propiedad CurrentValues para establecer los valores originales.

Establecer valores actuales o originales de un diccionario mediante la


propiedad
Una alternativa al uso de CurrentValues o OriginalValues, como se muestra arriba, es usar el método de
propiedad para establecer el valor de cada propiedad. Esto puede ser preferible cuando es necesario establecer
los valores de las propiedades complejas. Por ejemplo:

using (var context = new BloggingContext())


{
var user = [Link]("johndoe1987");

var newValues = new Dictionary<string, object>


{
{ "Name", "John Doe" },
{ "[Link]", "Redmond" },
{ "[Link]", "Washington" },
{ "[Link]", "WA" },
};

var entry = [Link](user);

foreach (var propertyName in [Link])


{
[Link](propertyName).CurrentValue = newValues[propertyName];
}
}

En el ejemplo anterior se tiene acceso a las propiedades complejas mediante nombres con puntos. Para conocer
otras formas de obtener acceso a propiedades complejas, vea las dos secciones más adelante en este tema
específicamente sobre las propiedades complejas.

Crear un objeto clonado que contenga valores actual, original o de


base de datos
El objeto DbPropertyValues devuelto de CurrentValues, OriginalValues o GetDatabaseValues se puede usar para
crear un clon de la entidad. Este clon contendrá los valores de propiedad del objeto DbPropertyValues que se
usa para crearlo. Por ejemplo:
using (var context = new BloggingContext())
{
var blog = [Link](1);

var clonedBlog = [Link](blog).GetDatabaseValues().ToObject();


}

Tenga en cuenta que el objeto devuelto no es la entidad y el contexto no realiza su seguimiento. El objeto
devuelto tampoco tiene ninguna relación establecida en otros objetos.
El objeto clonado puede ser útil para resolver problemas relacionados con las actualizaciones simultáneas en la
base de datos, especialmente cuando se usa una interfaz de usuario que implique el enlace de datos a objetos
de un tipo determinado.

Obtener y establecer los valores actuales o originales de propiedades


complejas
El valor de un objeto complejo completo se puede leer y establecer mediante el método de propiedad, tal como
puede ser para una propiedad primitiva. Además, puede explorar en profundidad el objeto complejo y leer o
establecer las propiedades de ese objeto, o incluso un objeto anidado. Estos son algunos ejemplos:
using (var context = new BloggingContext())
{
var user = [Link]("johndoe1987");

// Get the Location complex object


var location = [Link](user)
.Property(u => [Link])
.CurrentValue;

// Get the nested State complex object using chained calls


var state1 = [Link](user)
.ComplexProperty(u => [Link])
.Property(l => [Link])
.CurrentValue;

// Get the nested State complex object using a single lambda expression
var state2 = [Link](user)
.Property(u => [Link])
.CurrentValue;

// Get the nested State complex object using a dotted string


var state3 = [Link](user)
.Property("[Link]")
.CurrentValue;

// Get the value of the Name property on the nested State complex object using chained calls
var name1 = [Link](user)
.ComplexProperty(u => [Link])
.ComplexProperty(l => [Link])
.Property(s => [Link])
.CurrentValue;

// Get the value of the Name property on the nested State complex object using a single lambda
expression
var name2 = [Link](user)
.Property(u => [Link])
.CurrentValue;

// Get the value of the Name property on the nested State complex object using a dotted string
var name3 = [Link](user)
.Property("[Link]")
.CurrentValue;
}

Use la propiedad OriginalValue en lugar de la propiedad CurrentValue para obtener o establecer un valor
original.
Tenga en cuenta que se puede utilizar la propiedad o el método ComplexProperty para tener acceso a una
propiedad compleja. Sin embargo, se debe usar el método ComplexProperty si desea explorar en profundidad el
objeto complejo con llamadas adicionales a propiedades o ComplexProperty.

Usar DbPropertyValues para tener acceso a propiedades complejas


Cuando se usa CurrentValues, OriginalValues o GetDatabaseValues para obtener todos los valores actuales,
originales o de la base de datos de una entidad, los valores de las propiedades complejas se devuelven como
objetos DbPropertyValues anidados. Estos objetos anidados se pueden usar para obtener valores del objeto
complejo. Por ejemplo, el método siguiente imprimirá los valores de todas las propiedades, incluidos los valores
de las propiedades complejas y las propiedades complejas anidadas.
public static void WritePropertyValues(string parentPropertyName, DbPropertyValues propertyValues)
{
foreach (var propertyName in [Link])
{
var nestedValues = propertyValues[propertyName] as DbPropertyValues;
if (nestedValues != null)
{
WritePropertyValues(parentPropertyName + propertyName + ".", nestedValues);
}
else
{
[Link]("Property {0}{1} has value {2}",
parentPropertyName, propertyName,
propertyValues[propertyName]);
}
}
}

Para imprimir todos los valores de propiedad actuales, se llamaría al método de la siguiente manera:

using (var context = new BloggingContext())


{
var user = [Link]("johndoe1987");

WritePropertyValues("", [Link](user).CurrentValues);
}
Controlar los conflictos de simultaneidad (EF6)
12/03/2021 • 9 minutes to read

La simultaneidad optimista implica un intento optimista de guardar la entidad en la base de datos, con la
esperanza de que los datos no hayan cambiado desde que se cargó la entidad. Si se da cuenta de que los datos
han cambiado, se produce una excepción y debe resolver el conflicto antes de intentar volver a guardar. En este
tema se explica cómo controlar dichas excepciones en Entity Framework. Las técnicas que se muestran en este
tema se aplican igualmente a los modelos creados con Code First y EF Designer.
Esta publicación no es el lugar adecuado para una descripción completa de la simultaneidad optimista. En las
secciones siguientes se presupone cierto conocimiento de la resolución de simultaneidad y se muestran
patrones para tareas comunes.
Muchos de estos patrones hacen uso de los temas que se describen en trabajar con valores de propiedad.
La resolución de problemas de simultaneidad cuando se usan asociaciones independientes (donde la clave
externa no está asignada a una propiedad de la entidad) es mucho más difícil que cuando se usan asociaciones
de clave externa. Por lo tanto, si va a realizar la resolución de simultaneidad en la aplicación, se recomienda que
asigne siempre las claves externas a las entidades. Todos los ejemplos siguientes suponen que está usando
asociaciones de clave externa.
SaveChanges genera una DbUpdateConcurrencyException cuando se detecta una excepción de simultaneidad
optimista al intentar guardar una entidad que utiliza asociaciones de clave externa.

Resolver excepciones de simultaneidad optimista con Reload (base de


datos gana)
El método Reload se puede usar para sobrescribir los valores actuales de la entidad con los valores ahora en la
base de datos. La entidad se suele devolver al usuario de alguna forma y debe intentar realizar de nuevo los
cambios y volver a guardar. Por ejemplo:

using (var context = new BloggingContext())


{
var blog = [Link](1);
[Link] = "The New [Link] Blog";

bool saveFailed;
do
{
saveFailed = false;

try
{
[Link]();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;

// Update the values of the entity that failed to save from the store
[Link]().Reload();
}

} while (saveFailed);
}
Una buena forma de simular una excepción de simultaneidad es establecer un punto de interrupción en la
llamada a SaveChanges y, a continuación, modificar una entidad que se guarda en la base de datos mediante
otra herramienta como SQL Server Management Studio. También puede insertar una línea antes de
SaveChanges para actualizar la base de datos directamente mediante SqlCommand. Por ejemplo:

[Link](
"UPDATE [Link] SET Name = 'Another Name' WHERE BlogId = 1");

El método de entradas de DbUpdateConcurrencyException devuelve las instancias de DbEntityEntry para las


entidades que no se pudieron actualizar. (Actualmente, esta propiedad siempre devuelve un valor único para los
problemas de simultaneidad. Puede devolver varios valores para las excepciones de actualización generales).
Una alternativa para algunas situaciones podría ser obtener entradas para todas las entidades que pueden tener
que volver a cargarse desde la base de datos y llamar a recargar para cada una de ellas.

Resolver excepciones de simultaneidad optimista como cliente WINS


El ejemplo anterior que usa la recarga se denomina a veces Database WINS o Store WINS porque los valores de
la entidad se sobrescriben con los valores de la base de datos. En ocasiones, es posible que desee hacer lo
contrario y sobrescribir los valores de la base de datos con los valores actualmente en la entidad. Esto se
denomina a veces cliente WINS y se puede hacer obteniendo los valores de la base de datos actual y
estableciéndolo como valores originales de la entidad. (Vea trabajar con valores de propiedad para obtener
información sobre los valores actuales y originales). Por ejemplo:

using (var context = new BloggingContext())


{
var blog = [Link](1);
[Link] = "The New [Link] Blog";

bool saveFailed;
do
{
saveFailed = false;
try
{
[Link]();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;

// Update original values from the database


var entry = [Link]();
[Link]([Link]());
}

} while (saveFailed);
}

Resolución personalizada de excepciones de simultaneidad optimista


En ocasiones, es posible que desee combinar los valores que hay actualmente en la base de datos con los
valores de la entidad. Normalmente, esto requiere una lógica personalizada o una interacción del usuario. Por
ejemplo, puede presentar un formulario al usuario que contiene los valores actuales, los valores de la base de
datos y un conjunto predeterminado de valores resueltos. Después, el usuario editaría los valores resueltos
según sea necesario y serían estos valores resueltos que se guardan en la base de datos. Esto se puede hacer
mediante los objetos DbPropertyValues devueltos desde CurrentValues y GetDatabaseValues en la entrada de la
entidad. Por ejemplo:
using (var context = new BloggingContext())
{
var blog = [Link](1);
[Link] = "The New [Link] Blog";

bool saveFailed;
do
{
saveFailed = false;
try
{
[Link]();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;

// Get the current entity values and the values in the database
var entry = [Link]();
var currentValues = [Link];
var databaseValues = [Link]();

// Choose an initial set of resolved values. In this case we


// make the default be the values currently in the database.
var resolvedValues = [Link]();

// Have the user choose what the resolved values should be


HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues);

// Update the original values with the database values and


// the current values with whatever the user choose.
[Link](databaseValues);
[Link](resolvedValues);
}
} while (saveFailed);
}

public void HaveUserResolveConcurrency(DbPropertyValues currentValues,


DbPropertyValues databaseValues,
DbPropertyValues resolvedValues)
{
// Show the current, database, and resolved values to the user and have
// them edit the resolved values to get the correct resolution.
}

Resolución personalizada de excepciones de simultaneidad optimista


mediante objetos
El código anterior usa instancias de DbPropertyValues para pasar los valores actuales, de base de datos y
resueltos. A veces puede ser más fácil usar instancias de su tipo de entidad para esto. Esto se puede hacer
mediante los métodos ToObject y SetValues de DbPropertyValues. Por ejemplo:
using (var context = new BloggingContext())
{
var blog = [Link](1);
[Link] = "The New [Link] Blog";

bool saveFailed;
do
{
saveFailed = false;
try
{
[Link]();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;

// Get the current entity values and the values in the database
// as instances of the entity type
var entry = [Link]();
var databaseValues = [Link]();
var databaseValuesAsBlog = (Blog)[Link]();

// Choose an initial set of resolved values. In this case we


// make the default be the values currently in the database.
var resolvedValuesAsBlog = (Blog)[Link]();

// Have the user choose what the resolved values should be


HaveUserResolveConcurrency((Blog)[Link],
databaseValuesAsBlog,
resolvedValuesAsBlog);

// Update the original values with the database values and


// the current values with whatever the user choose.
[Link](databaseValues);
[Link](resolvedValuesAsBlog);
}

} while (saveFailed);
}

public void HaveUserResolveConcurrency(Blog entity,


Blog databaseValues,
Blog resolvedValues)
{
// Show the current, database, and resolved values to the user and have
// them update the resolved values to get the correct resolution.
}
Trabajar con transacciones
07/04/2021 • 15 minutes to read

NOTE
Solo EF6 y versiones posteriores : las características, las API, etc. que se tratan en esta página se han incluido a partir
de Entity Framework 6. Si usa una versión anterior, no se aplica parte o la totalidad de la información.

En este documento se describe el uso de transacciones en EF6, incluidas las mejoras que hemos agregado desde
EF5 para facilitar el trabajo con transacciones.

Qué hace EF de forma predeterminada


En todas las versiones de Entity Framework, siempre que ejecute SaveChanges () para insertar, actualizar o
eliminar en la base de datos, el marco de trabajo encapsulará esa operación en una transacción. Esta transacción
dura solo el tiempo suficiente para ejecutar la operación y, a continuación, se completa. Al ejecutar otra
operación de este tipo, se inicia una nueva transacción.
A partir de EF6 [Link] () , de forma predeterminada, se ajustará el comando en una
transacción si aún no hay ninguna presente. Hay sobrecargas de este método que le permiten invalidar este
comportamiento si lo desea. Además, en la ejecución EF6 de procedimientos almacenados incluidos en el
modelo a través de API como [Link] () hace lo mismo (salvo que no se puede
reemplazar el comportamiento predeterminado en ese momento).
En cualquier caso, el nivel de aislamiento de la transacción es cualquier nivel de aislamiento en el que el
proveedor de base de datos considere su configuración predeterminada. De forma predeterminada, por
ejemplo, en SQL Server este es READ COMMITTED.
Entity Framework no encapsula las consultas en una transacción.
Esta funcionalidad predeterminada es adecuada para muchos usuarios y, si es así, no es necesario hacer nada
diferente en EF6; solo tiene que escribir el código como lo hizo siempre.
Sin embargo, algunos usuarios requieren un mayor control sobre sus transacciones; esto se trata en las
secciones siguientes.

Cómo funcionan las API


Antes de EF6 Entity Framework insista en abrir la propia conexión de base de datos (se produjo una excepción si
se pasa una conexión que ya estaba abierta). Puesto que una transacción solo se puede iniciar en una conexión
abierta, esto significaba que la única forma en que un usuario podía encapsular varias operaciones en una
transacción era usar TransactionScope o usar la propiedad ObjectContext. Connection y comenzar a llamar a
Open () y BeginTransaction () directamente en el objeto EntityConnection devuelto. Además, las llamadas
API que contacten con la base de datos generarán un error si hubiera iniciado una transacción en la conexión de
base de datos subyacente por su cuenta.

NOTE
La limitación de aceptar solo conexiones cerradas se quitó en Entity Framework 6. Para obtener más información, consulte
Administración de conexiones.
A partir de EF6, el marco de trabajo ahora proporciona:
1. Database. BeginTransaction () : método más sencillo para que un usuario inicie y complete transacciones
en un DbContext existente, lo que permite combinar varias operaciones dentro de la misma transacción y,
por lo tanto, todas confirmadas o revertidas como una. También permite al usuario especificar más
fácilmente el nivel de aislamiento para la transacción.
2. Database. UseTransaction () : permite que DbContext use una transacción que se inició fuera del Entity
Framework.
Combinar varias operaciones en una transacción dentro del mismo contexto
Database. BeginTransaction () tiene dos invalidaciones: una que toma un IsolationLevel explícito y otra que
no toma ningún argumento y usa el valor de IsolationLevel predeterminado del proveedor de base de datos
subyacente. Ambas invalidaciones devuelven un objeto DbContextTransaction que proporciona los métodos
Commit () y Rollback () que realizan la confirmación y reversión en la transacción del almacén subyacente.
La DbContextTransaction está pensada para ser eliminada una vez que se ha confirmado o revertido. Una
manera fácil de lograrlo es usar (...) {. ..} sintaxis que llamará automáticamente a Dispose () cuando se
complete el bloque Using:

using System;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace TransactionsExamples
{
class TransactionsExample
{
static void StartOwnTransactionWithinContext()
{
using (var context = new BloggingContext())
{
using (var dbContextTransaction = [Link]())
{
[Link](
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'"
);

var query = [Link](p => [Link] >= 5);


foreach (var post in query)
{
[Link] += "[Cool Blog]";
}

[Link]();

[Link]();
}
}
}
}
}
NOTE
Iniciar una transacción requiere que la conexión del almacén subyacente esté abierta. Por tanto, si se llama a Database.
BeginTransaction (), se abrirá la conexión si aún no está abierta. Si DbContextTransaction abrió la conexión, se cerrará
cuando se llame a Dispose ().

Pasar una transacción existente al contexto


A veces, desea una transacción que es incluso más amplia en el ámbito y que incluye operaciones en la misma
base de datos pero fuera de EF completamente. Para ello, debe abrir la conexión e iniciar la transacción usted
mismo y, a continuación, indicar a EF a) que use la conexión de base de datos ya abierta y b) para usar la
transacción existente en esa conexión.
Para ello, debe definir y usar un constructor en la clase de contexto que herede de uno de los constructores
DbContext que llevan i) un parámetro de conexión existente y II) el valor booleano contextOwnsConnection.

NOTE
La marca contextOwnsConnection debe establecerse en false cuando se llama en este escenario. Esto es importante a
medida que informa Entity Framework que no debería cerrar la conexión cuando se realiza con ella (por ejemplo, vea la
línea 4 a continuación):

using (var conn = new SqlConnection("..."))


{
[Link]();
using (var context = new BloggingContext(conn, contextOwnsConnection: false))
{
}
}

Además, debe iniciar la transacción usted mismo (incluido el valor de IsolationLevel si desea evitar la
configuración predeterminada) y dejar que Entity Framework sepa que hay una transacción existente ya iniciada
en la conexión (consulte la línea 33 a continuación).
Después, puede ejecutar operaciones de base de datos directamente en el SqlConnection o en DbContext. Todas
estas operaciones se ejecutan dentro de una transacción. Usted asume la responsabilidad de confirmar o
revertir la transacción y de llamar a Dispose () en ella, así como para cerrar y eliminar la conexión a la base de
datos. Por ejemplo:
using System;
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace TransactionsExamples
{
class TransactionsExample
{
static void UsingExternalTransaction()
{
using (var conn = new SqlConnection("..."))
{
[Link]();

using (var sqlTxn = [Link]([Link]))


{
var sqlCommand = new SqlCommand();
[Link] = conn;
[Link] = sqlTxn;
[Link] =
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
[Link]();

using (var context =


new BloggingContext(conn, contextOwnsConnection: false))
{
[Link](sqlTxn);

var query = [Link](p => [Link] >= 5);


foreach (var post in query)
{
[Link] += "[Cool Blog]";
}
[Link]();
}

[Link]();
}
}
}
}
}

Borrar la transacción
Puede pasar null a Database. UseTransaction () para borrar el conocimiento de Entity Framework de la
transacción actual. Entity Framework no confirmará ni revertirá la transacción existente al hacerlo, por lo que
debe usar con cuidado y solo si está seguro de que es lo que desea hacer.
Errores en UseTransaction
Verá una excepción de Database. UseTransaction () si pasa una transacción cuando:
Entity Framework ya tiene una transacción existente
Entity Framework ya está funcionando en TransactionScope
El objeto de conexión en la transacción pasada es NULL. Es decir, la transacción no está asociada a una
conexión; normalmente es un signo de que ya se ha completado la transacción.
El objeto de conexión de la transacción pasada no coincide con la conexión del Entity Framework.
Usar transacciones con otras características
En esta sección se describe cómo interactúan las transacciones anteriores con:
Resistencia de conexión
Métodos asincrónicos
Transacciones TransactionScope
Resistencia de conexión
La nueva característica de resistencia de conexión no funciona con las transacciones iniciadas por el usuario.
Para obtener más información, consulte reintento de estrategias de ejecución.
Programación asincrónica
El enfoque descrito en las secciones anteriores no necesita más opciones ni valores de configuración para
trabajar con los métodos de consulta y guardado asíncronos. Pero tenga en cuenta que, en función de lo que
haga dentro de los métodos asincrónicos, esto puede dar lugar a transacciones de ejecución prolongada, lo que
a su vez puede provocar interbloqueos o bloqueos que son incorrectos para el rendimiento de la aplicación
global.
Transacciones TransactionScope
Antes de EF6, la manera recomendada de proporcionar transacciones de ámbito mayor era usar un objeto
TransactionScope:
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace TransactionsExamples
{
class TransactionsExample
{
static void UsingTransactionScope()
{
using (var scope = new TransactionScope([Link]))
{
using (var conn = new SqlConnection("..."))
{
[Link]();

var sqlCommand = new SqlCommand();


[Link] = conn;
[Link] =
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
[Link]();

using (var context =


new BloggingContext(conn, contextOwnsConnection: false))
{
var query = [Link](p => [Link] > 5);
foreach (var post in query)
{
[Link] += "[Cool Blog]";
}
[Link]();
}
}

[Link]();
}
}
}
}

SqlConnection y Entity Framework usarían ambas transacciones ambiente TransactionScope y, por tanto, se
confirmarán juntas.
A partir de .NET 4.5.1 TransactionScope, se ha actualizado para que también funcione con métodos asincrónicos
mediante el uso de la enumeración TransactionScopeAsyncFlowOption :
using [Link];
using [Link];
using [Link];
using [Link];
using [Link];

namespace TransactionsExamples
{
class TransactionsExample
{
public static void AsyncTransactionScope()
{
using (var scope = new TransactionScope([Link]))
{
using (var conn = new SqlConnection("..."))
{
await [Link]();

var sqlCommand = new SqlCommand();


[Link] = conn;
[Link] =
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'";
await [Link]();

using (var context = new BloggingContext(conn, contextOwnsConnection: false))


{
var query = [Link](p => [Link] > 5);
foreach (var post in query)
{
[Link] += "[Cool Blog]";
}

await [Link]();
}
}

[Link]();
}
}
}
}

Todavía existen algunas limitaciones en el enfoque de TransactionScope:


Requiere .NET 4.5.1 o superior para trabajar con métodos asincrónicos.
No se puede usar en escenarios en la nube a menos que esté seguro de que tiene una sola conexión (los
escenarios en la nube no admiten transacciones distribuidas).
No se puede combinar con el enfoque Database. UseTransaction () de las secciones anteriores.
Se producirán excepciones si se emite cualquier DDL y no se han habilitado las transacciones distribuidas a
través del servicio MSDTC.
Ventajas del enfoque de TransactionScope:
Actualizará automáticamente una transacción local a una transacción distribuida si realiza más de una
conexión a una base de datos determinada o combina una conexión con una base de datos con una conexión
a una base de datos diferente en la misma transacción (Nota: debe tener el servicio MSDTC configurado para
permitir que las transacciones distribuidas funcionen).
Facilidad de codificación. Si prefiere que la transacción sea ambiente y se trate implícitamente en segundo
plano en lugar de hacerlo explícitamente bajo el control, el enfoque de TransactionScope puede adaptarse
mejor.
En Resumen, con las nuevas API Database. BeginTransaction () y Database. UseTransaction () anteriores, el
enfoque de TransactionScope ya no es necesario para la mayoría de los usuarios. Si sigue usando
TransactionScope, tenga en cuenta las limitaciones anteriores. Se recomienda usar el enfoque descrito en las
secciones anteriores, siempre que sea posible.
Validación de datos
12/03/2021 • 15 minutes to read

NOTE
EF 4.1 en adelante solo : las características, las API, etc. que se describen en esta página se introdujeron en Entity
Framework 4,1. Si usa una versión anterior, parte o toda la información no se aplica.

El contenido de esta página se adapta a un artículo escrito originalmente por Julia Lerman (
[Link] ).
Entity Framework proporciona una gran variedad de características de validación que se pueden transmitir a
través de una interfaz de usuario para la validación del lado cliente o se pueden usar para la validación del lado
servidor. Cuando se usa Code First, se pueden especificar validaciones mediante las configuraciones de
anotación o de API fluida. Las validaciones adicionales, y más complejas, se pueden especificar en el código y
funcionarán si el modelo es el primero de Code, Model First o Database.

El modelo
Mostraré las validaciones con un par sencillo de clases: blog y post.

public class Blog


{
public int Id { get; set; }
public string Title { get; set; }
public string BloggerName { get; set; }
public DateTime DateCreated { get; set; }
public virtual ICollection<Post> Posts { get; set; }
}

public class Post


{
public int Id { get; set; }
public string Title { get; set; }
public DateTime DateCreated { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public ICollection<Comment> Comments { get; set; }
}

Anotaciones de datos
Code First usa anotaciones del [Link] ensamblado como un medio para
configurar clases Code First. Entre estas anotaciones se encuentran las que proporcionan reglas como Required
, MaxLength y MinLength . Varias aplicaciones cliente de .NET también reconocen estas anotaciones, por
ejemplo, [Link] MVC. Puede lograr la validación del lado cliente y del lado servidor con estas anotaciones. Por
ejemplo, puede forzar que la propiedad título del blog sea una propiedad necesaria.

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

Sin ningún cambio de código o marcado adicional en la aplicación, una aplicación MVC existente realizará la
validación del lado cliente, incluso generando dinámicamente un mensaje usando los nombres de la propiedad
y de las anotaciones.

En el método posterior de esta vista de creación, Entity Framework se usa para guardar el nuevo blog en la base
de datos, pero la validación del lado cliente de MVC se desencadena antes de que la aplicación llegue a ese
código.
Sin embargo, la validación del lado cliente no es una prueba de viñetas. Los usuarios pueden afectar a las
características de su explorador o peor aún, un pirata informático podría usar algún truco para evitar las
validaciones de la interfaz de usuario. Sin embargo, Entity Framework también reconocerá la Required
anotación y la validará.
Una manera sencilla de probarlo es deshabilitar la característica de validación del lado cliente de MVC. Puede
hacerlo en el archivo de [Link] de la aplicación MVC. La sección appSettings tiene una clave para
ClientValidationEnabled. Si se establece esta clave en false, impedirá que la interfaz de usuario realice
validaciones.

<appSettings>
<add key="ClientValidationEnabled"value="false"/>
...
</appSettings>

Incluso con la validación del lado cliente deshabilitada, obtendrá la misma respuesta en la aplicación. El mensaje
de error "el campo título es obligatorio" se mostrará como antes. A excepción de ahora, será el resultado de la
validación del lado servidor. Entity Framework realizará la validación en la Required anotación (antes de que se
genere un INSERT comando para enviarlo a la base de datos) y devolverá el error a MVC, que mostrará el
mensaje.

API fluida
Puede usar la API fluida de Code First en lugar de anotaciones para obtener el mismo cliente & la validación del
lado servidor. En lugar de usar Required , lo mostraré con una validación de MaxLength.
Las configuraciones de la API fluida se aplican como Code First está compilando el modelo a partir de las clases.
Puede insertar las configuraciones invalidando el método OnModelCreating de la clase DbContext. A
continuación se muestra una configuración que especifica que la propiedad BloggerName no puede tener más
de 10 caracteres.
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)


{
[Link]<Blog>().Property(p => [Link]).HasMaxLength(10);
}
}

Los errores de validación que se produzcan en función de las configuraciones de la API fluida no llegarán
automáticamente a la interfaz de usuario, pero puede capturarlos en el código y, a continuación, responder a
ellos en consecuencia.
Este es un código de error de control de excepciones en la clase BlogController de la aplicación que captura ese
error de validación cuando Entity Framework intenta guardar un blog con un BloggerName que supere el
máximo de 10 caracteres.

[HttpPost]
public ActionResult Edit(int id, Blog blog)
{
try
{
[Link](blog).State = [Link];
[Link]();
return RedirectToAction("Index");
}
catch (DbEntityValidationException ex)
{
var error = [Link]().[Link]();
[Link]([Link], [Link]);
return View();
}
}

La validación no se vuelve a pasar automáticamente a la vista, que es la razón por la que se usa el código
adicional que usa [Link] . Esto garantiza que los detalles del error lo convierten en la vista
que utilizará a continuación el ValidationMessageFor HtmlHelper para mostrar el error.

@[Link](model => [Link])

IValidatableObject
IValidatableObject es una interfaz que reside en [Link] . Aunque no forma
parte de la API de Entity Framework, todavía puede aprovecharla para la validación del lado servidor en las
clases de Entity Framework. IValidatableObject proporciona un Validate método que Entity Framework
llamará durante SaveChanges o se puede llamar a sí mismo en cualquier momento en el que desee validar las
clases.
Configuraciones como Required y MaxLength realizan la validación en un único campo. En el Validate método
puede tener una lógica aún más compleja, por ejemplo, comparando dos campos.
En el ejemplo siguiente, la Blog clase se ha ampliado para implementar IValidatableObject y, a continuación,
proporciona una regla que Title y BloggerName no coinciden.
public class Blog : IValidatableObject
{
public int Id { get; set; }

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

public string BloggerName { get; set; }


public DateTime DateCreated { get; set; }
public virtual ICollection<Post> Posts { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)


{
if (Title == BloggerName)
{
yield return new ValidationResult(
"Blog Title cannot match Blogger Name",
new[] { nameof(Title), nameof(BloggerName) });
}
}
}

El ValidationResult constructor toma un objeto string que representa el mensaje de error y una matriz de
objetos string que representan los nombres de miembro que están asociados a la validación. Dado que esta
validación comprueba Title y BloggerName , se devuelven ambos nombres de propiedad.
A diferencia de la validación proporcionada por la API fluida, el resultado de la validación será reconocido por la
vista y el controlador de excepción que se usó anteriormente para agregar el error ModelState no es necesario.
Dado que he establecido ambos nombres de propiedad en ValidationResult , el HtmlHelpers de MVC muestra
el mensaje de error para ambas propiedades.

DbContext. ValidateEntity
DbContext tiene un método reemplazable denominado ValidateEntity . Cuando llame a SaveChanges , Entity
Framework llamará a este método para cada entidad en su caché cuyo estado no sea Unchanged . Puede colocar
la lógica de validación directamente aquí o incluso usar este método para llamar a, por ejemplo, el
[Link] método agregado en la sección anterior.

A continuación se muestra un ejemplo de una ValidateEntity invalidación que valida Post las nuevas para
asegurarse de que el título de la publicación no se ha usado ya. Primero se comprueba si la entidad es una
publicación y se agrega su estado. Si ese es el caso, busca en la base de datos para ver si ya existe una
publicación con el mismo título. Si ya hay una publicación existente, se crea un nuevo DbEntityValidationResult
.
DbEntityValidationResult aloja un DbEntityEntry y un ICollection<DbValidationErrors> para una sola entidad.
Al principio de este método, DbEntityValidationResult se crea una instancia de y, a continuación, se agregan los
errores detectados a su ValidationErrors colección.

protected override DbEntityValidationResult ValidateEntity (


[Link] entityEntry,
IDictionary<object, object> items)
{
var result = new DbEntityValidationResult(entityEntry, new List<DbValidationError>());

if ([Link] is Post post && [Link] == [Link])


{
// Check for uniqueness of post title
if ([Link](p => [Link] == [Link]).Any())
{
[Link](
new [Link](
nameof(Title),
"Post title must be unique."));
}
}

if ([Link] > 0)
{
return result;
}
else
{
return [Link](entityEntry, items);
}
}

Desencadenar explícitamente la validación


Una llamada a SaveChanges desencadena todas las validaciones que se describen en este artículo. Pero no es
necesario que confíe en SaveChanges . Puede que prefiera validar cualquier otro lugar de la aplicación.
[Link] desencadenará todas las validaciones, las definidas por las anotaciones o la API
fluida, la validación creada en IValidatableObject (por ejemplo, [Link] ) y las validaciones realizadas en
el [Link] método.
El código siguiente llamará a GetValidationErrors en la instancia actual de DbContext . ValidationErrors se
agrupan por tipo de entidad en DbEntityValidationResult . El código recorre en iteración las
DbEntityValidationResult s devueltas por el método y, a continuación, a través de cada una de ellas
DbValidationError .

foreach (var validationResult in [Link]())


{
foreach (var error in [Link])
{
[Link](
"Entity Property: {0}, Error {1}",
[Link],
[Link]);
}
}

Otras consideraciones al usar la validación


Estos son algunos aspectos que se deben tener en cuenta al usar la validación de Entity Framework:
La carga diferida está deshabilitada durante la validación
EF validará las anotaciones de datos en propiedades no asignadas (propiedades que no están asignadas a
una columna en la base de datos)
La validación se realiza después de que se detecten los cambios durante SaveChanges . Si realiza cambios
durante la validación, es su responsabilidad notificar al seguimiento de cambios
DbUnexpectedValidationException se produce si se producen errores durante la validación
Las caras que Entity Framework incluye en el modelo (longitud máxima, requerida, etc.) provocarán la
validación, incluso si no hay ninguna anotación de datos en las clases o si se usó el diseñador de EF para
crear el modelo.
Reglas de prioridad:
Las llamadas a la API fluida invalidan las anotaciones de datos correspondientes
Orden de ejecución:
La validación de la propiedad tiene lugar antes de la validación de tipos
La validación de tipos solo se produce si la validación de la propiedad se realiza correctamente
Si una propiedad es compleja, su validación también incluirá:
Validación de nivel de propiedad en las propiedades de tipo complejo
Validación del nivel de tipo en el tipo complejo, incluida IValidatableObject la validación en el tipo
complejo

Resumen
La API de validación en Entity Framework se reproduce muy bien con la validación del lado cliente en MVC, pero
no tiene que depender de la validación del lado cliente. Entity Framework se encargará de la validación en el
lado del servidor para las anotaciones o configuraciones que haya aplicado con la API fluida de Code First.
También vio una serie de puntos de extensibilidad para personalizar el comportamiento si usa la
IValidatableObject interfaz o pulsa en el [Link] método. Y estos dos últimos métodos de
validación están disponibles a través de DbContext , independientemente de que use el flujo de trabajo Code
First, Model First o Database First para describir el modelo conceptual.
Entity Framework blogs
12/03/2021 • 2 minutes to read

Además de la documentación del producto, estos blogs pueden ser una fuente de información útil sobre Entity
Framework:

Blogs del equipo de EF


Blog de .NET: etiqueta Entity Framework
Blog de [Link] (ya no está en uso)
Blog de diseño de EF (ya no está en uso)

Bloggers del equipo de EF actuales y anteriores


Arthur Vickers
Brice Lambson
Diego Vega
Rowan Miller
Pawel Kadluczka
Alex James
Zlatko Michailov

Bloggers de la comunidad EF
Julia Lerman
Shawn Wildermuth
Casos prácticos de Microsoft para Entity Framework
12/03/2021 • 7 minutes to read

En los casos prácticos de esta página se resaltan algunos proyectos de producción reales que han empleado
Entity Framework.

NOTE
En el sitio web de Microsoft ya no están disponibles las versiones detalladas de estos casos de estudio. Por lo tanto, se
han quitado los vínculos.

Epopeya
La epopeya es una empresa de software global de gran tamaño (con más de 400 desarrolladores) que
desarrolla soluciones de planeamiento de recursos empresariales (ERP) para empresas de más de 150 países.
Su producto insignia, Epicr 9, se basa en una arquitectura de Service-Oriented (SOA) mediante el .NET
Framework. Se enfrenta a numerosas solicitudes de clientes para proporcionar compatibilidad con Language
Integrated Query (LINQ) y también para reducir la carga en sus servidores de SQL back-end, el equipo decidió
actualizar a Visual Studio 2010 y el .NET Framework 4,0. Con el Entity Framework 4,0, pudieron alcanzar estos
objetivos y simplificar enormemente el desarrollo y el mantenimiento. En concreto, la compatibilidad con T4
enriquecida del Entity Framework les permitió tomar el control completo de su código generado y compilar
automáticamente características de ahorro de rendimiento como las consultas precompiladas y el
almacenamiento en caché.

"Hemos realizado algunas pruebas de rendimiento recientemente con el código existente y pudimos reducir
las solicitudes a SQL Server por un 90 por ciento. Esto se debe a [Link] Entity Framework 4. " – Erik
Johnson, Vicepresidente, investigación del producto

Soluciones de veracidad
Habiendo adquirido un sistema de software de planeación de eventos que iba a ser difícil de mantener y
extenderse a través de las soluciones de veracidad a largo plazo que usaba Visual Studio 2010 para volver a
escribirlo como una aplicación de Internet eficaz y fácil de usar basada en Silverlight 4. Con .NET RIA Services,
podían crear rápidamente una capa de servicio sobre el Entity Framework que evitaba la duplicación de código
y permitía la validación común y la lógica de autenticación en los niveles.

"Se vendió en el Entity Framework la primera vez que se presentó y el Entity Framework 4 ha demostrado
ser aún mejor. Se han mejorado las herramientas y es más fácil manipular los archivos. edmx que definen el
modelo conceptual, el modelo de almacenamiento y la asignación entre esos modelos... Con el Entity
Framework, puedo conseguir que el nivel de acceso a datos funcione en un día y compilarlo a medida que
avance. La Entity Framework es nuestra capa de acceso a datos de facto; No sé por qué nadie lo usaría ". –
Joe McBride, Desarrollador Senior

NEC muestra soluciones de América


NEC deseaba entrar en el mercado de publicidad basada en el lugar digital con una solución para beneficiar a
los anunciantes y propietarios de la red, así como para aumentar sus ingresos. Para ello, inició un par de
aplicaciones web que automatizan los procesos manuales necesarios en una campaña de ad tradicional. Los
sitios se crearon con [Link], Silverlight 3, AJAX y WCF, junto con los Entity Framework en la capa de acceso a
datos para comunicarse con SQL Server 2008.

"Con SQL Server, pensamos que podríamos obtener el rendimiento que necesitábamos para servir a los
anunciantes y redes con información en tiempo real y la confiabilidad para garantizar que la información de
nuestras aplicaciones críticas siempre estuviera disponible"-Mike Corcoran, Director de ti

Dimensiones de Darwin
Gracias a una amplia gama de tecnologías de Microsoft, el equipo de Darwin ha establecido la creación de un
portal de Avatar en línea para que los consumidores puedan utilizarla con el fin de crear atractivas avatares
reales para su uso en juegos, animaciones y páginas de redes sociales. Con las ventajas de productividad del
Entity Framework y la extracción de componentes como Windows Workflow Foundation (WF) y Windows
Server AppFabric (una memoria caché de aplicaciones en memoria altamente escalable), el equipo pudo ofrecer
un producto sorprendente en un 35% menos tiempo de desarrollo. A pesar de que los miembros del equipo se
dividen en varios países, el equipo sigue un proceso de desarrollo ágil con versiones semanales.

"Intentamos no crear tecnología para la tecnología. Como inicio, es fundamental que se aproveche la
tecnología que ahorra tiempo y dinero. .NET era la elección para el desarrollo rápido y rentable ". – Zachary
Olsen, arquitecto

Silverware
Con más de 15 años de experiencia en el desarrollo de soluciones de punto de venta (POS) para grupos de
restaurante pequeños y medianas, el equipo de desarrollo de silverware se ha establecido para mejorar su
producto con características de nivel empresarial más para atraer cadenas de restaurante más grandes. Con la
versión más reciente de las herramientas de desarrollo de Microsoft, podían crear la nueva solución cuatro
veces más rápido que antes. Las nuevas características clave como LINQ y el Entity Framework facilitan el
traslado de Crystal Reports a SQL Server 2008 y SQL Server Reporting Services (SSRS) para sus necesidades de
informes y almacenamiento de datos.

"La administración eficaz de los datos es fundamental para el éxito de SilverWare, y por eso se decidió
adoptar SQL Reporting". -Nicholas Romanidis, Director de ingeniería de ti/software
Contribuir a Entity Framework 6
12/03/2021 • 2 minutes to read

Entity Framework 6 se desarrolla con un modelo de código abierto en GitHub. Aunque el enfoque principal del
equipo de Entity Framework en Microsoft es agregar nuevas características a Entity Framework Core y no
esperamos que se agreguen características principales a Entity Framework 6, seguimos aceptando
contribuciones.
En el caso de las contribuciones del producto, comience en la página wiki de colaboración en nuestro repositorio
de github.
Para obtener información sobre las contribuciones de documentación, empiece por leer la Guía de contribución
en nuestro repositorio de documentación.
Obtener ayuda con Entity Framework
12/03/2021 • 2 minutes to read

Preguntas sobre el uso de EF


La mejor manera de obtener ayuda sobre el uso de Entity Framework es publicar una pregunta en Stack
Overflow mediante la etiqueta Entity-Framework .
Si no está familiarizado con Stack Overflow, asegúrese de leer las directricespara realizar preguntas. En concreto,
no utilice Stack Overflow para informar de errores, formular preguntas de la hoja de ruta o sugerir nuevas
características.

Informes de errores y solicitudes de características


Si ha encontrado un error que cree que debería corregirse, que tiene una característica que le gustaría ver
implementada, o una pregunta a la que no encontró una respuesta, cree un problema en el repositorio de
github de EF6.
Entity Framework Glosario
12/03/2021 • 6 minutes to read

Code First
Crear un modelo de Entity Framework mediante código. El modelo puede tener como destino una base de datos
existente o una nueva.

Context
Una clase que representa una sesión con la base de datos, lo que permite consultar y guardar datos. Un contexto
se deriva de la clase DbContext o ObjectContext.

Convención (Code First)


Una regla que Entity Framework usa para inferir la forma de su modelo a partir de las clases.

Database First
Crear un modelo de Entity Framework, mediante el diseñador de EF, que tiene como destino una base de datos
existente.

Carga diligente
Patrón de carga de datos relacionados donde una consulta para un tipo de entidad también carga las entidades
relacionadas como parte de la consulta.

EF Designer
Diseñador visual de Visual Studio que permite crear un modelo de Entity Framework mediante cuadros y líneas.

Entidad
Clase u objeto que representa datos de aplicación como clientes, productos y pedidos.

Entity Data Model


Modelo que describe las entidades y las relaciones entre ellas. EF usa EDM para describir el modelo conceptual
en el que los programas de desarrollador. EDM se basa en el modelo de relación de entidades introducido por
Dr. Peter Chen. El EDM se desarrolló originalmente con el objetivo principal de convertirse en el modelo de
datos común en un conjunto de tecnologías de desarrollador y de servidor de Microsoft. EDM también se usa
como parte del protocolo OData.

Carga explícita
Patrón de carga de datos relacionados en los que los objetos relacionados se cargan llamando a una API.

API fluida
Una API que se puede usar para configurar un modelo de Code First.
Asociación de clave externa
Asociación entre entidades donde una propiedad que representa la clave externa se incluye en la clase de la
entidad dependiente. Por ejemplo, Product contiene una propiedad CategoryId.

Relación de identificación
Relación donde la clave principal de la entidad principal también forma parte de la clave principal de la entidad
dependiente. En este tipo de relación, la entidad dependiente no puede existir sin la entidad principal.

Asociación independiente
Asociación entre entidades en las que no hay ninguna propiedad que represente la clave externa en la clase de
la entidad dependiente. Por ejemplo, una clase de producto contiene una relación con la categoría pero no tiene
la propiedad CategoryId. Entity Framework realiza un seguimiento del estado de la Asociación
independientemente del estado de las entidades en los dos extremos de la asociación.

Carga diferida
Patrón de carga de datos relacionados en los que los objetos relacionados se cargan automáticamente cuando
se tiene acceso a una propiedad de navegación.

Model First
Crear un modelo de Entity Framework, mediante el diseñador de EF, que se utiliza para crear una nueva base de
datos.

Propiedad de navegación
Propiedad de una entidad que hace referencia a otra entidad. Por ejemplo, el producto contiene una propiedad
de navegación categoría y la categoría contiene una propiedad de navegación productos.

POCO
Acrónimo de Plain-Old objeto CLR. Una clase de usuario simple que no tiene dependencias con ningún marco
de trabajo. En el contexto de EF, una clase de entidad que no se deriva de EntityObject, implementa las interfaces
o incluye cualquier atributo definido en EF. Estas clases de entidad que se desacoplan del marco de persistencia
también se consideran "que ignoran la persistencia".

Inverso de relación
Extremo opuesto de una relación, por ejemplo, product. Categoría y categoría. Manuales.

Entidad de seguimiento propio


Una entidad creada a partir de una plantilla de generación de código que ayuda con el desarrollo de N niveles.

Tabla por tipo concreto (TPC)


Método de asignación de la herencia en la que cada tipo no abstracto de la jerarquía se asigna a una tabla
independiente en la base de datos.

Tabla por jerarquía (TPH)


Método de asignación de la herencia en la que todos los tipos de la jerarquía se asignan a la misma tabla de la
base de datos. Se utiliza una o varias columnas de discriminador para identificar a qué tipo está asociada cada
fila.

Tabla por tipo (TPT)


Método de asignación de la herencia en la que las propiedades comunes de todos los tipos de la jerarquía se
asignan a la misma tabla de la base de datos, pero las propiedades exclusivas de cada tipo se asignan a una
tabla independiente.

Detección de tipos
El proceso de identificar los tipos que deben formar parte de un modelo de Entity Framework.
Base de datos de ejemplo School
12/03/2021 • 15 minutes to read

Este tema contiene el esquema y los datos de la base de datos School. La base de datos School de ejemplo se
usa en varios lugares de la documentación Entity Framework.

NOTE
El servidor de base de datos que se instala con Visual Studio es diferente en función de la versión de Visual Studio que
use. Vea las versiones de Visual Studio para obtener más información sobre lo que se debe usar.

Estos son los pasos para crear la base de datos:


Apertura de Visual Studio
Vista -> de Explorador de ser vidores
Haga clic con el botón derecho en conexiones de datos -> Agregar conexión...
Si no se ha conectado a una base de datos desde Explorador de servidores antes de que tenga que
seleccionar Microsoft SQL Ser ver como origen de datos
Conéctese a LocalDB o a SQL Express, en función de la que haya instalado.
Escriba School como nombre de la base de datos
Seleccione Aceptar y se le preguntará si desea crear una nueva base de datos, seleccione sí .
La nueva base de datos aparecerá ahora en Explorador de servidores
Si usa Visual Studio 2012 o una versión más reciente
Haga clic con el botón derecho en la base de datos en el Explorador de servidores y seleccione Nueva
consulta
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione Ejecutar .
Si usa Visual Studio 2010
Seleccionar datos del -> Editor de Transact SQL -> nueva conexión de consulta...
Escriba .\SQLEXPRESS como el nombre del servidor y haga clic en Aceptar .
Seleccione la base de datos STESample en la lista desplegable de la parte superior del editor de
consultas.
Copie el siguiente código SQL en la nueva consulta, haga clic con el botón derecho en la consulta y
seleccione ejecutar SQL .

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

-- Create the Department table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[Department]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Department]([DepartmentID] [int] NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Budget] [money] NOT NULL,
[StartDate] [datetime] NOT NULL,
[Administrator] [int] NULL,
CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
CONSTRAINT [PK_Department] PRIMARY KEY CLUSTERED
(
[DepartmentID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO

-- Create the Person table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[Person]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Person]([PersonID] [int] IDENTITY(1,1) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[HireDate] [datetime] NULL,
[EnrollmentDate] [datetime] NULL,
[Discriminator] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_School.Student] PRIMARY KEY CLUSTERED
(
[PersonID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO

-- Create the OnsiteCourse table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[OnsiteCourse]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[OnsiteCourse]([CourseID] [int] NOT NULL,
[Location] [nvarchar](50) NOT NULL,
[Days] [nvarchar](50) NOT NULL,
[Time] [smalldatetime] NOT NULL,
CONSTRAINT [PK_OnsiteCourse] PRIMARY KEY CLUSTERED
(
[CourseID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO

-- Create the OnlineCourse table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[OnlineCourse]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[OnlineCourse]([CourseID] [int] NOT NULL,
[URL] [nvarchar](100) NOT NULL,
CONSTRAINT [PK_OnlineCourse] PRIMARY KEY CLUSTERED
(
[CourseID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO

--Create the StudentGrade table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[StudentGrade]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[StudentGrade]([EnrollmentID] [int] IDENTITY(1,1) NOT NULL,
[CourseID] [int] NOT NULL,
[StudentID] [int] NOT NULL,
[Grade] [decimal](3, 2) NULL,
CONSTRAINT [PK_StudentGrade] PRIMARY KEY CLUSTERED
(
[EnrollmentID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO
GO

-- Create the CourseInstructor table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[CourseInstructor]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[CourseInstructor]([CourseID] [int] NOT NULL,
[PersonID] [int] NOT NULL,
CONSTRAINT [PK_CourseInstructor] PRIMARY KEY CLUSTERED
(
[CourseID] ASC,
[PersonID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO

-- Create the Course table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[Course]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[Course]([CourseID] [int] NOT NULL,
[Title] [nvarchar](100) NOT NULL,
[Credits] [int] NOT NULL,
[DepartmentID] [int] NOT NULL,
CONSTRAINT [PK_School.Course] PRIMARY KEY CLUSTERED
(
[CourseID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO

-- Create the OfficeAssignment table.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[OfficeAssignment]')
AND type in (N'U'))
BEGIN
CREATE TABLE [dbo].[OfficeAssignment]([InstructorID] [int] NOT NULL,
[Location] [nvarchar](50) NOT NULL,
[Timestamp] [timestamp] NOT NULL,
CONSTRAINT [PK_OfficeAssignment] PRIMARY KEY CLUSTERED
(
[InstructorID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]) ON [PRIMARY]
END
GO

-- Define the relationship between OnsiteCourse and Course.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_OnsiteCourse_Course]')
AND parent_object_id = OBJECT_ID(N'[dbo].[OnsiteCourse]'))
ALTER TABLE [dbo].[OnsiteCourse] WITH CHECK ADD
CONSTRAINT [FK_OnsiteCourse_Course] FOREIGN KEY([CourseID])
REFERENCES [dbo].[Course] ([CourseID])
GO
ALTER TABLE [dbo].[OnsiteCourse] CHECK
CONSTRAINT [FK_OnsiteCourse_Course]
GO

-- Define the relationship between OnlineCourse and Course.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_OnlineCourse_Course]')
AND parent_object_id = OBJECT_ID(N'[dbo].[OnlineCourse]'))
ALTER TABLE [dbo].[OnlineCourse] WITH CHECK ADD
CONSTRAINT [FK_OnlineCourse_Course] FOREIGN KEY([CourseID])
REFERENCES [dbo].[Course] ([CourseID])
GO
ALTER TABLE [dbo].[OnlineCourse] CHECK
CONSTRAINT [FK_OnlineCourse_Course]
CONSTRAINT [FK_OnlineCourse_Course]
GO

-- Define the relationship between StudentGrade and Course.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_StudentGrade_Course]')
AND parent_object_id = OBJECT_ID(N'[dbo].[StudentGrade]'))
ALTER TABLE [dbo].[StudentGrade] WITH CHECK ADD
CONSTRAINT [FK_StudentGrade_Course] FOREIGN KEY([CourseID])
REFERENCES [dbo].[Course] ([CourseID])
GO
ALTER TABLE [dbo].[StudentGrade] CHECK
CONSTRAINT [FK_StudentGrade_Course]
GO

--Define the relationship between StudentGrade and Student.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_StudentGrade_Student]')
AND parent_object_id = OBJECT_ID(N'[dbo].[StudentGrade]'))
ALTER TABLE [dbo].[StudentGrade] WITH CHECK ADD
CONSTRAINT [FK_StudentGrade_Student] FOREIGN KEY([StudentID])
REFERENCES [dbo].[Person] ([PersonID])
GO
ALTER TABLE [dbo].[StudentGrade] CHECK
CONSTRAINT [FK_StudentGrade_Student]
GO

-- Define the relationship between CourseInstructor and Course.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_CourseInstructor_Course]')
AND parent_object_id = OBJECT_ID(N'[dbo].[CourseInstructor]'))
ALTER TABLE [dbo].[CourseInstructor] WITH CHECK ADD
CONSTRAINT [FK_CourseInstructor_Course] FOREIGN KEY([CourseID])
REFERENCES [dbo].[Course] ([CourseID])
GO
ALTER TABLE [dbo].[CourseInstructor] CHECK
CONSTRAINT [FK_CourseInstructor_Course]
GO

-- Define the relationship between CourseInstructor and Person.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_CourseInstructor_Person]')
AND parent_object_id = OBJECT_ID(N'[dbo].[CourseInstructor]'))
ALTER TABLE [dbo].[CourseInstructor] WITH CHECK ADD
CONSTRAINT [FK_CourseInstructor_Person] FOREIGN KEY([PersonID])
REFERENCES [dbo].[Person] ([PersonID])
GO
ALTER TABLE [dbo].[CourseInstructor] CHECK
CONSTRAINT [FK_CourseInstructor_Person]
GO

-- Define the relationship between Course and Department.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_Course_Department]')
AND parent_object_id = OBJECT_ID(N'[dbo].[Course]'))
ALTER TABLE [dbo].[Course] WITH CHECK ADD
CONSTRAINT [FK_Course_Department] FOREIGN KEY([DepartmentID])
REFERENCES [dbo].[Department] ([DepartmentID])
GO
ALTER TABLE [dbo].[Course] CHECK CONSTRAINT [FK_Course_Department]
GO

--Define the relationship between OfficeAssignment and Person.


IF NOT EXISTS (SELECT * FROM sys.foreign_keys
WHERE object_id = OBJECT_ID(N'[dbo].[FK_OfficeAssignment_Person]')
AND parent_object_id = OBJECT_ID(N'[dbo].[OfficeAssignment]'))
ALTER TABLE [dbo].[OfficeAssignment] WITH CHECK ADD
CONSTRAINT [FK_OfficeAssignment_Person] FOREIGN KEY([InstructorID])
REFERENCES [dbo].[Person] ([PersonID])
GO
GO
ALTER TABLE [dbo].[OfficeAssignment] CHECK
CONSTRAINT [FK_OfficeAssignment_Person]
GO

-- Create InsertOfficeAssignment stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[InsertOfficeAssignment]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[InsertOfficeAssignment]
@InstructorID int,
@Location nvarchar(50)
AS
INSERT INTO [Link] (InstructorID, Location)
VALUES (@InstructorID, @Location);
IF @@ROWCOUNT > 0
BEGIN
SELECT [Timestamp] FROM OfficeAssignment
WHERE InstructorID=@InstructorID;
END
'
END
GO

--Create the UpdateOfficeAssignment stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[UpdateOfficeAssignment]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[UpdateOfficeAssignment]
@InstructorID int,
@Location nvarchar(50),
@OrigTimestamp timestamp
AS
UPDATE OfficeAssignment SET Location=@Location
WHERE InstructorID=@InstructorID AND [Timestamp]=@OrigTimestamp;
IF @@ROWCOUNT > 0
BEGIN
SELECT [Timestamp] FROM OfficeAssignment
WHERE InstructorID=@InstructorID;
END
'
END
GO

-- Create the DeleteOfficeAssignment stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[DeleteOfficeAssignment]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[DeleteOfficeAssignment]
@InstructorID int
AS
DELETE FROM OfficeAssignment
WHERE InstructorID=@InstructorID;
'
END
GO

-- Create the DeletePerson stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[DeletePerson]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[DeletePerson]
@PersonID int
AS
DELETE FROM Person WHERE PersonID = @PersonID;
'
END
GO

-- Create the UpdatePerson stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[UpdatePerson]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[UpdatePerson]
@PersonID int,
@LastName nvarchar(50),
@FirstName nvarchar(50),
@HireDate datetime,
@EnrollmentDate datetime,
@Discriminator nvarchar(50)
AS
UPDATE Person SET LastName=@LastName,
FirstName=@FirstName,
HireDate=@HireDate,
EnrollmentDate=@EnrollmentDate,
Discriminator=@Discriminator
WHERE PersonID=@PersonID;
'
END
GO

-- Create the InsertPerson stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[InsertPerson]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[InsertPerson]
@LastName nvarchar(50),
@FirstName nvarchar(50),
@HireDate datetime,
@EnrollmentDate datetime,
@Discriminator nvarchar(50)
AS
INSERT INTO [Link] (LastName,
FirstName,
HireDate,
EnrollmentDate,
Discriminator)
VALUES (@LastName,
@FirstName,
@HireDate,
@EnrollmentDate,
@Discriminator);
SELECT SCOPE_IDENTITY() as NewPersonID;
'
END
GO

-- Create GetStudentGrades stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[GetStudentGrades]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[GetStudentGrades]
@StudentID int
AS
SELECT EnrollmentID, Grade, CourseID, StudentID FROM [Link]
WHERE StudentID = @StudentID
'
END
GO

-- Create GetDepartmentName stored procedure.


IF NOT EXISTS (SELECT * FROM [Link]
WHERE object_id = OBJECT_ID(N'[dbo].[GetDepartmentName]')
AND type in (N'P', N'PC'))
BEGIN
EXEC dbo.sp_executesql @statement = N'
CREATE PROCEDURE [dbo].[GetDepartmentName]
@ID int,
@Name nvarchar(50) OUTPUT
AS
SELECT @Name = Name FROM Department
WHERE DepartmentID = @ID
'
END
GO

-- Insert data into the Person table.


USE School
GO
SET IDENTITY_INSERT [Link] ON
GO
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (1, 'Abercrombie', 'Kim', '1995-03-11', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (2, 'Barzdukas', 'Gytis', null, '2005-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (3, 'Justice', 'Peggy', null, '2001-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (4, 'Fakhouri', 'Fadi', '2002-08-06', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (5, 'Harui', 'Roger', '1998-07-01', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (6, 'Li', 'Yan', null, '2002-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (7, 'Norman', 'Laura', null, '2003-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (8, 'Olivotto', 'Nino', null, '2005-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (9, 'Tang', 'Wayne', null, '2005-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (10, 'Alonso', 'Meredith', null, '2002-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (11, 'Lopez', 'Sophia', null, '2004-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (12, 'Browning', 'Meredith', null, '2000-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (13, 'Anand', 'Arturo', null, '2003-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (14, 'Walker', 'Alexandra', null, '2000-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (15, 'Powell', 'Carson', null, '2004-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (16, 'Jai', 'Damien', null, '2001-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (17, 'Carlson', 'Robyn', null, '2005-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (18, 'Zheng', 'Roger', '2004-02-12', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (19, 'Bryant', 'Carson', null, '2001-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (20, 'Suarez', 'Robyn', null, '2004-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (21, 'Holt', 'Roger', null, '2004-09-01', 'Student');
VALUES (21, 'Holt', 'Roger', null, '2004-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (22, 'Alexander', 'Carson', null, '2005-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (23, 'Morgan', 'Isaiah', null, '2001-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (24, 'Martin', 'Randall', null, '2005-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (25, 'Kapoor', 'Candace', '2001-01-15', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (26, 'Rogers', 'Cody', null, '2002-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (27, 'Serrano', 'Stacy', '1999-06-01', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (28, 'White', 'Anthony', null, '2001-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (29, 'Griffin', 'Rachel', null, '2004-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (30, 'Shan', 'Alicia', null, '2003-09-01', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (31, 'Stewart', 'Jasmine', '1997-10-12', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (32, 'Xu', 'Kristen', '2001-7-23', null, 'Instructor');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (33, 'Gao', 'Erica', null, '2003-01-30', 'Student');
INSERT INTO [Link] (PersonID, LastName, FirstName, HireDate, EnrollmentDate, Discriminator)
VALUES (34, 'Van Houten', 'Roger', '2000-12-07', null, 'Instructor');
GO
SET IDENTITY_INSERT [Link] OFF
GO

-- Insert data into the Department table.


INSERT INTO [Link] (DepartmentID, [Name], Budget, StartDate, Administrator)
VALUES (1, 'Engineering', 350000.00, '2007-09-01', 2);
INSERT INTO [Link] (DepartmentID, [Name], Budget, StartDate, Administrator)
VALUES (2, 'English', 120000.00, '2007-09-01', 6);
INSERT INTO [Link] (DepartmentID, [Name], Budget, StartDate, Administrator)
VALUES (4, 'Economics', 200000.00, '2007-09-01', 4);
INSERT INTO [Link] (DepartmentID, [Name], Budget, StartDate, Administrator)
VALUES (7, 'Mathematics', 250000.00, '2007-09-01', 3);
GO

-- Insert data into the Course table.


INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (1050, 'Chemistry', 4, 1);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (1061, 'Physics', 4, 1);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (1045, 'Calculus', 4, 7);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (2030, 'Poetry', 2, 2);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (2021, 'Composition', 3, 2);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (2042, 'Literature', 4, 2);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (4022, 'Microeconomics', 3, 4);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (4041, 'Macroeconomics', 3, 4);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (4061, 'Quantitative', 2, 4);
INSERT INTO [Link] (CourseID, Title, Credits, DepartmentID)
VALUES (3141, 'Trigonometry', 4, 7);
GO

-- Insert data into the OnlineCourse table.


INSERT INTO [Link] (CourseID, URL)
VALUES (2030, '[Link]
VALUES (2030, '[Link]
INSERT INTO [Link] (CourseID, URL)
VALUES (2021, '[Link]
INSERT INTO [Link] (CourseID, URL)
VALUES (4041, '[Link]
INSERT INTO [Link] (CourseID, URL)
VALUES (3141, '[Link]

--Insert data into OnsiteCourse table.


INSERT INTO [Link] (CourseID, Location, Days, [Time])
VALUES (1050, '123 Smith', 'MTWH', '11:30');
INSERT INTO [Link] (CourseID, Location, Days, [Time])
VALUES (1061, '234 Smith', 'TWHF', '13:15');
INSERT INTO [Link] (CourseID, Location, Days, [Time])
VALUES (1045, '121 Smith','MWHF', '15:30');
INSERT INTO [Link] (CourseID, Location, Days, [Time])
VALUES (4061, '22 Williams', 'TH', '11:15');
INSERT INTO [Link] (CourseID, Location, Days, [Time])
VALUES (2042, '225 Adams', 'MTWH', '11:00');
INSERT INTO [Link] (CourseID, Location, Days, [Time])
VALUES (4022, '23 Williams', 'MWF', '9:00');

-- Insert data into the CourseInstructor table.


INSERT INTO [Link](CourseID, PersonID)
VALUES (1050, 1);
INSERT INTO [Link](CourseID, PersonID)
VALUES (1061, 31);
INSERT INTO [Link](CourseID, PersonID)
VALUES (1045, 5);
INSERT INTO [Link](CourseID, PersonID)
VALUES (2030, 4);
INSERT INTO [Link](CourseID, PersonID)
VALUES (2021, 27);
INSERT INTO [Link](CourseID, PersonID)
VALUES (2042, 25);
INSERT INTO [Link](CourseID, PersonID)
VALUES (4022, 18);
INSERT INTO [Link](CourseID, PersonID)
VALUES (4041, 32);
INSERT INTO [Link](CourseID, PersonID)
VALUES (4061, 34);
GO

--Insert data into the OfficeAssignment table.


INSERT INTO [Link](InstructorID, Location)
VALUES (1, '17 Smith');
INSERT INTO [Link](InstructorID, Location)
VALUES (4, '29 Adams');
INSERT INTO [Link](InstructorID, Location)
VALUES (5, '37 Williams');
INSERT INTO [Link](InstructorID, Location)
VALUES (18, '143 Smith');
INSERT INTO [Link](InstructorID, Location)
VALUES (25, '57 Adams');
INSERT INTO [Link](InstructorID, Location)
VALUES (27, '271 Williams');
INSERT INTO [Link](InstructorID, Location)
VALUES (31, '131 Smith');
INSERT INTO [Link](InstructorID, Location)
VALUES (32, '203 Williams');
INSERT INTO [Link](InstructorID, Location)
VALUES (34, '213 Smith');

-- Insert data into the StudentGrade table.


INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2021, 2, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2030, 2, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2021, 3, 3);
VALUES (2021, 3, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2030, 3, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2021, 6, 2.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2042, 6, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2021, 7, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2042, 7, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2021, 8, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (2042, 8, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4041, 9, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4041, 10, null);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4041, 11, 2.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4041, 12, null);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4061, 12, null);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 14, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 13, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4061, 13, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4041, 14, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 15, 2.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 16, 2);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 17, null);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 19, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4061, 20, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4061, 21, 2);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 22, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4041, 22, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4061, 22, 2.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (4022, 23, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1045, 23, 1.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1061, 24, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1061, 25, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1050, 26, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1061, 26, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1061, 27, 3);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1045, 28, 2.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1050, 28, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1061, 29, 4);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1050, 30, 3.5);
INSERT INTO [Link] (CourseID, StudentID, Grade)
VALUES (1061, 30, 4);
GO
Extensiones de & de Entity Framework Tools
12/03/2021 • 2 minutes to read

IMPORTANT
Las extensiones se compilan mediante una variedad de orígenes y no se mantienen como parte de Entity Framework. Al
considerar una extensión de terceros, evalúe la calidad, las licencias, la compatibilidad, el soporte técnico, etc., a fin de
asegurarse de que cumple sus requisitos.

Entity Framework ha sido un popular O/RM durante muchos años. Estos son algunos ejemplos de herramientas
y extensiones gratuitas y de pago desarrolladas para ello:
EF Power Tools Community Edition
Analizador de EF
Generador de perfiles ORM
LINQPad
LLBLGen Pro
Huagati DBML/EDMX Tools
Entity Developer

También podría gustarte