Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Entity Framework
EF Core y EF6
Comparar EF Core y EF6
Portabilidad de EF6 a EF Core
Información general
Portabilidad de un modelo basado en EDMX
Portabilidad de un modelo basado en código
EF6 y EF Core en la misma aplicación
Entity Framework Core
Información general
Versiones y planeamiento (plan de desarrollo)
Versiones actuales y planeadas
Proceso de planeamiento de versiones
EF Core 5.0
Plan de alto nivel
Novedades
EF Core 3.0
Nuevas características
Últimos cambios
EF Core 2.2
EF Core 2.1
EF Core 2.0
EF Core 1.1
EF Core 1.0
Actualización desde versiones anteriores
De 1.0 RC1 a RC2
De 1.0 RC2 a RTM
De 1.x a 2.0
Introducción
Tutorial de EF Core
Instalación de EF Core
Tutorial de ASP.NET Core >>
Aspectos básicos
Cadenas de conexión
Registro
Resistencia de la conexión
Prueba
Información general
Pruebas con SQLite
Pruebas con InMemory
Configuración de un DbContext
Tipos de referencia que aceptan valores NULL
Crear un modelo
Información general
Tipos de entidades
Propiedades de entidad
Claves
Valores generados
Tokens de simultaneidad
Propiedades reemplazadas
Relaciones
Índices
Herencia
Secuencias
Campos de respaldo
Conversiones de valores
Propagación de datos
Constructores de tipos de entidad
División de tablas
Tipos de entidad en propiedad
Tipos de entidad sin llave
Alternancia de modelos con el mismo DbContext
Datos espaciales
Administración de esquemas de base de datos
Información general
Migraciones
Información general
Entornos de equipo
Operaciones personalizadas
Uso de un proyecto independiente
Varios proveedores
Tabla de historial personalizada
Creación y eliminación de API
Utilización de técnicas de ingeniería inversa (scaffolding)
Consultar datos
Información general
Diferencias entre la evaluación de cliente y servidor
Diferencias entre seguimiento y sin seguimiento
Operadores de consulta complejos
Carga de datos relacionados
Consultas asincrónicas
Consultas SQL sin formato
Filtros de consulta global
Etiquetas de consulta
Funcionamiento de las consultas
Guardar datos
Información general
Guardado básico
Datos relacionados
Eliminación en cascada
Conflictos de simultaneidad
Transacciones
Guardado asincrónico
Entidades desconectadas
Valores explícitos para propiedades generadas
Implementaciones de .NET compatibles
Proveedores de bases de datos
Información general
Microsoft SQL Server
Información general
Tablas optimizadas para memoria
Especificación de opciones de Azure SQL Database
SQLite
Información general
Limitaciones de SQLite
Cosmos
Información general
Trabajo con datos no estructurados
Limitaciones de Cosmos
InMemory (para pruebas)
Escritura de un proveedor de base de datos
Cambios que afectan al proveedor
Herramientas y extensiones
Referencia de la línea de comandos
Información general
Consola del Administrador de paquetes (Visual Studio)
CLI de .NET Core
Creación de DbContext en tiempo de diseño
Servicios en tiempo de diseño
Referencia de la API de EF Core >>
Entity Framework 6
Información general
Novedades
Información general
Versiones anteriores
Actualización a EF6
Versiones de Visual Studio
Introducción
Aspectos básicos
Obtener Entity Framework
Trabajar con DbContext
Descripción de las relaciones
Consulta asincrónica y guardado
Configuración
Basada en código
Archivo config
Cadenas de conexión
Resolución de dependencias
Administración de conexiones
Resistencia de la conexión
Lógica de reintento
Errores de confirmación de transacciones
Enlace de datos
WinForms
WPF
Entidades desconectadas
Información general
Entidades de autoseguimiento
Información general
Tutorial
Registro e intercepción
Rendimiento
Consideraciones sobre el rendimiento (notas del producto)
Uso de NGEN
Uso de vistas generadas previamente
Proveedores
Información general
Modelo de proveedor de EF6
Compatibilidad de elementos espaciales con los proveedores
Uso de servidores proxy
Pruebas con EF6
Uso de la simulación
Escritura de duplicados de pruebas propios
Capacidad de prueba con EF4 (artículo)
Crear un modelo
Información general
Uso de Code First
Workflows
Con una base de datos nueva
Con una base de datos existente
Anotaciones de datos
DbSets
Tipos de datos
Enumeraciones
Espacial
Convenciones
Convenciones integradas
Convenciones personalizadas
Convenciones de modelo
Configuración de Fluent
Relaciones
Tipos y propiedades
Uso en Visual Basic
Asignación de procedimientos almacenados
Migraciones
Información general
Migraciones automáticas
Trabajo con bases de datos existentes
Personalización del historial de migraciones
Uso de Migrate.exe
Migraciones en entornos de equipo
Uso de EF Designer
Workflows
Model-First
Database-First
Tipos de datos
Tipos complejos
Enumeraciones
Espacial
División de asignaciones
División de entidades
División de tablas
Asignaciones de herencia
Tabla por jerarquía
Tabla por tipo
Asignación de procedimientos almacenados
Consultar
Actualizar
Asignación de relaciones
Varios diagramas
Selección de la versión del entorno de ejecución
Generación de código
Información general
ObjectContext heredado
Avanzado
Formato de archivo EDMX
Definición de consulta
Varios conjuntos de resultados
Funciones con valores de tabla
Accesos directos del teclado
Consultar datos
Información general
Load (Método)
Datos locales
Consultas de seguimiento y no seguimiento
Uso de consultas SQL sin formato
Consulta de datos relacionados
Guardar datos
Información general
seguimiento de cambios
Detección de cambios automática
Estado de la entidad
Valores de propiedad
Control de conflictos de simultaneidad
Uso de transacciones
Validación de datos
Recursos adicionales
Blogs
Casos prácticos
Contribuciones
Obtener ayuda
Glosario
Base de datos de ejemplo School
Herramientas y extensiones
Licencias
EF5
Chino simplificado
Chino tradicional
Alemán
Inglés
Español
Francés
Italiano
Japonés
Coreano
Ruso
EF6
Versión preliminar
Chino simplificado
Chino tradicional
Alemán
Inglés
Español
Francés
Italiano
Japonés
Coreano
Ruso
Referencia de la API de EF6 >>
Documentación de Entity Framework
Entity Framework
Entity Framework 6
EF 6 es una tecnología de acceso a datos probada con muchos años de características
y estabilización.
Elección
Descubra qué versión de EF es adecuada en su caso.
Portabilidad a EF Core
Guía sobre la portabilidad de una aplicación existente de EF 6 a EF Core.
EF Core
todo
Introducción
Información general
Crear un modelo
Consultar datos
Guardar datos
Tutoriales
más…
Referencia de API
DbContext
DbSet<TEntity>
más…
EF 6
Introducción
Aprenda a acceder a los datos con Entity Framework 6.
Referencia de API
Examine la API de Entity Framework 6, organizada por espacio de nombres.
Comparar EF Core y EF6
08/04/2020 • 9 minutes to read • Edit Online
EF Core
Entity Framework Core (EF Core) es un asignador de base de datos de objeto moderno para .NET. Admite consultas
LINQ, seguimiento de cambios, actualizaciones y migraciones de esquemas.
EF Core funciona con SQL Server o SQL Azure, SQLite, Azure Cosmos DB, MySQL, PostgreSQL y muchas otras
bases de datos a través de un modelo de complemento de proveedor de bases de datos.
EF6
Entity Framework 6 (EF6) es un asignador relacional de objetos diseñado para .NET Framework, pero compatible
con .NET Core. EF6 es un producto estable y compatible, pero ya no se desarrolla activamente.
Comparación de características
EF Core ofrece nuevas características que no se implementarán en EF6. Sin embargo, no todas las características de
EF6 están implementadas actualmente en EF Core.
En las tablas siguientes se comparan las características disponibles en EF Core y EF6. Se trata de una comparación
general en la que no se muestran todas las características ni se explican las diferencias entre una misma
característica en las distintas versiones de EF.
La columna EF Core indica la versión del producto en la que la característica apareció por primera vez.
Creación de un modelo
C A RA C T ERÍST IC A EF 6. 4 EF C O RE
Convenciones Sí 1.0
Herencia: tabla por tipo (TPT) Sí Planeado para la versión 5.0 (n.º 2266)
C A RA C T ERÍST IC A EF 6. 4 EF C O RE
Herencia: tabla por clase concreta (TPC) Sí Stretch para la versión 5.0 (n.º 3170) (1)
Actualizar modelo desde base de datos Parcial En el trabajo pendiente (n.º 831)
Crear modelo desde base de datos: Sí No hay soporte técnico planeado (2)
Asistente de VS
Consulta de datos
C A RA C T ERÍST IC A EF 6. 4 EF C O RE
Guardado de datos
C A RA C T ERÍST IC A EF 6. 4 EF C O RE
Transacciones Sí 1.0
Otras características
C A RA C T ERÍST IC A EF 6. 4 EF C O RE
Migraciones Sí 1.0
Interceptores Sí 3.0
MySQL Sí 1.0
PostgreSQL Sí 1.0
Oracle Sí 1.0
SQLite Sí 1.0
DB2 Sí 1.0
Firebird Sí 2.0
1 Es probable que no se logren los objetivos de Stretch para una versión determinada. Sin embargo, si las cosas
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.
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.
DbSet.Add/Attach y comportamiento del grafo
En EF6, llamar DbSet.Add() 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. DbSet.Attach() 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 DbSet.Add y sin cambios para
DbSet.Attach ).
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.
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;"
Microsoft.EntityFrameworkCore.SqlServer
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.
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.
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:
Si traslada una aplicación existente que tiene varios modelos de EF, puede elegir trasladar de manera selectiva
algunos de ellos a EF Core y seguir usando EF6 para los demás.
Entity Framework Core
08/04/2020 • 3 minutes to read • Edit Online
Entity Framework (EF) Core es una versión ligera, extensible, de código abierto y multiplataforma de la popular
tecnología de acceso a datos Entity Framework.
EF Core puede servir como asignador relacional de objetos (O/RM), lo que permite a los desarrolladores de .NET
trabajar con una base de datos mediante objetos .NET y eliminar la mayoría del código de acceso a los datos que
normalmente deben escribir.
EF Core es compatible con muchos motores de base de datos; vea Proveedores de bases de datos para más
información.
El modelo
Con EF Core, el acceso a datos se realiza mediante un modelo. Un modelo se compone de clases de entidad y un
objeto de contexto que representa una sesión con la base de datos, lo que permite consultar y guardar los datos.
Vea Creación de un modelo para más información.
Puede generar un modelo a partir de una base de datos existente, codificar manualmente un modelo para que
coincida con la base de datos o usar migraciones de EF para crear una base de datos a partir del modelo y que
evolucione a medida que cambia el modelo.
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
namespace Intro
{
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
Consultas
Las instancias de las clases de entidad se recuperan de la base de datos mediante Language Integrated Query
(LINQ). Vea Consulta de datos para más información.
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.
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
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).
EF Core 5.0
La siguiente versión estable planeada es EF Core 5.0 , programada para noviembre de 2020.
Se ha creado un plan de alto nivel para EF Core 5.0 siguiendo el proceso de planeamiento de versiones
documentado.
Sus comentarios sobre la planeación son importantes. La mejor manera de indicar la importancia de un problema
es votar (pulgar arriba ) por ese problema en GitHub. Estos datos se introducen en el proceso de planeación de
la próxima versión.
¡Obténgalo ahora!
Los paquetes de EF Core 5.0 están disponibles ahora como
Compilaciones diarias
Todas las características y correcciones de errores más recientes. Normalmente muy estable; se ejecutan
más de 57 000 pruebas en cada compilación.
Versiones preliminares en NuGet
Van a la zaga de las compilaciones diarias, pero están probadas para trabajar con las versiones
preliminares de ASP.NET Core y .NET Core correspondientes.
Usar las versiones preliminares o las compilaciones diarias es una excelente manera de detectar problemas y
proporcionar comentarios cuanto antes. Cuanto antes recibamos esos comentarios, más probable será que puedan
procesarse antes de la siguiente versión oficial.
Proceso de planeamiento de versiones
08/04/2020 • 12 minutes to read • Edit Online
A menudo nos preguntan cómo se eligen características específicas para incluirlas en una versión concreta. En
este documento se describe el proceso que usamos. El proceso evoluciona continuamente a medida que
encontramos mejores formas de planeación, pero las ideas generales siguen siendo las mismas.
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.
Temas
Hemos extraído algunas áreas o temas importantes que formarán la base de las grandes inversiones en EF
Core 5.0.
Inclusión filtrada
Jefe de desarrollo: @maumar
Seguimiento realizado por #1833
Talla de camiseta: M
Estado: En curso
La inclusión filtrada es una característica muy solicitada (aproximadamente 317 votos; en segunda posición) que
no requiere demasiado trabajo y que creemos que desbloqueará o facilitará escenarios que actualmente requieren
filtros de nivel de modelo o consultas más complejas.
Rendimiento
Jefe de desarrollo: @roji
Seguimiento por problemas etiquetados con area-perf en el hito 5.0
Talla de camiseta: L
Estado: En curso
Para EF Core, el plan es mejorar nuestro conjunto de pruebas comparativas de rendimiento y realizar mejoras de
rendimiento dirigidas al tiempo de ejecución. Además, tenemos previsto completar la nueva API de procesamiento
por lotes de ADO.NET, que se ha creado como prototipo durante el ciclo de versiones de 3.0. En el nivel de
ADO.NET 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 ADO.NET y EF Core, y
otros diagnósticos según corresponda.
Documentación de Microsoft.Data.Sqlite
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 ADO.NET Microsoft.Data.Sqlite. Tenemos previsto documentar
completamente este proveedor como parte de la versión 5.0.
Documentación general
Jefe de documentación: @ajcvickers
Seguimiento mediante problemas en el repositorio de documentación del hito 5.0
Talla de camiseta: L
Estado: En curso
Ya se ha iniciado el proceso de actualización de la documentación de las versiones 3.0 y 3.1. También se está
trabajando en:
Una revisión de la documentación de introducción para que sea más fácil de seguir
La reorganización de la documentación para facilitar la búsqueda y la adición de referencias cruzadas
La incorporación de más detalles y aclaraciones a la documentación existente
La actualización de los ejemplos y la incorporación de otros nuevos
Corrección de errores
Seguimiento por problemas etiquetados con type-bug en el hito 5.0
Desarrolladores: @roji, @maumar, @bricelam, @smitpatel, @AndriySvyryd, @ajcvickers
Talla de camiseta: L
Estado: En curso
En el momento de escribir este documento, se han evaluado 135 errores para corregirlos en la versión 5.0 (ya se
han corregido 62), pero hay una superposición significativa con la sección anterior Mejoras generales de
consultas.
La velocidad de entrada (problemas que acaban como trabajo en un hito) fue de aproximadamente 23 problemas
al mes en el transcurso de la versión 3.0. No todos se tendrán que corregir en la versión 5.0. Como estimación
aproximada, tenemos previsto corregir unos 150 problemas adicionales para la versión 5.0.
Mejoras menores
Seguimiento por problemas etiquetados con type-enhancement en el hito 5.0
Desarrolladores: @roji, @maumar, @bricelam, @smitpatel, @AndriySvyryd, @ajcvickers
Talla de camiseta: L
Estado: En curso
Además de las características más importantes descritas antes, también hay muchas mejoras más pequeñas
programadas para la versión 5.0 a fin de corregir los "elementos excluidos". Tenga en cuenta que muchas de estas
mejoras también se incluyen en los temas más generales descritos antes.
Nivel inferior
Seguimiento por problemas etiquetados con consider-for-next-release
Se trata de correcciones de errores y mejoras no programadas actualmente para la versión 5.0, pero que se
considerarán objetivos de extensión en función del progreso realizado en el trabajo anterior.
Además, durante la planeación siempre se tienen en cuenta los problemas más votados. Excluir cualquiera de
estos problemas de una versión siempre es complicado, pero necesitamos un plan realista para los recursos que
tenemos.
Comentarios
Sus comentarios sobre la planeación son importantes. La mejor manera de indicar la importancia de un problema
es votar (pulgar) por ese problema en GitHub. Estos datos se introducirán después en el proceso de planeación de
la próxima versión.
Novedades en EF Core 5.0
27/03/2020 • 9 minutes to read • Edit Online
EF Core 5.0 está actualmente en desarrollo. Esta página contendrá información general sobre los cambios
interesantes introducidos en cada versión preliminar.
Esta página no duplica el plan para EF Core 5.0. En el plan se describen los temas generales relativos a EF Core 5.0,
incluido todo lo que estamos planeando incluir antes de publicar la versión final.
A medida que se publique el contenido, se agregarán vínculos que redirigirán de esta página a la documentación
oficial.
Versión preliminar 1
Registro sencillo
Esta característica agrega funcionalidad similar a Database.Log en EF6. Es decir, proporciona una manera sencilla
de obtener registros de EF Core sin necesidad de configurar ningún tipo de plataforma de registro externa.
La documentación preliminar se incluye en el estado semanal de EF del 5 de diciembre de 2019.
En el problema n.º 2085 se realiza el seguimiento de la documentación adicional.
Forma sencilla de generar contenido SQL
EF Core 5.0 presenta el método de extensión ToQueryString que devolverá el contenido SQL que EF Core generará
al ejecutar una consulta LINQ.
La documentación preliminar se incluye en el estado semanal de EF del 9 de enero de 2020.
En el problema n.º 1331 se realiza el seguimiento de la documentación adicional.
Uso de un atributo de C# para indicar que una entidad no tiene clave
Ahora se pueden configurar los tipos de entidad para indicar que no tienen clave mediante el nuevo valor
KeylessAttribute . Por ejemplo:
[Keyless]
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public int Zip { get; set; }
}
Revisión de LINQ
LINQ permite escribir consultas a la base de datos en el lenguaje .NET que prefiera, con lo que se aprovecha la
información de tipo enriquecido para ofrecer la comprobación de IntelliSense y de tipos en tiempo de compilación.
Pero LINQ también permite escribir un número ilimitado de consultas complicadas que contienen expresiones
arbitrarias (llamadas a métodos u operaciones). Cómo controlar todas esas combinaciones es el principal desafío
para los proveedores LINQ.
En EF Core 3,0, hemos rediseñado nuestro proveedor LINQ para habilitar la conversión de más patrones de
consulta en SQL, la generación de consultas eficientes en más casos y la prevención de que las consultas ineficaces
no se detecten. El nuevo proveedor LINQ es la base sobre la que podremos ofrecer nuevas funcionalidades de
consulta y mejoras de rendimiento en futuras versiones, sin interrumpir las aplicaciones y los proveedores de datos
existentes.
Evaluación de cliente restringida
El cambio de diseño más importante tiene que ver con la forma en que manejamos las expresiones LINQ que no se
pueden convertir a parámetros ni traducir a SQL.
En las versiones anteriores, EF Core identificada qué partes de una consulta se podían traducir a SQL y ejecutaba el
resto de la consulta en el cliente. Este tipo de ejecución en el lado cliente es una opción interesante en algunas
situaciones, pero en muchos otros casos puede dar lugar a consultas ineficaces.
Por ejemplo, si EF Core 2.2 no podía traducir un predicado en una llamada a Where() , ejecutaba una instrucción
SQL sin filtro, transfería todas las filas de la base de datos y luego las filtraba en memoria:
Esta operación puede ser aceptable si la base de datos contiene pocas filas, pero puede dar lugar a problemas de
rendimiento considerables o incluso errores en la aplicación si la base de datos contiene muchas filas.
En EF Core 3.0 hemos restringido la evaluación de cliente para que solo suceda en la proyección de nivel superior
(fundamentalmente, la última llamada a Select() ). Cuando EF Core 3.0 detecta expresiones que no se pueden
traducir en ningún otro lugar de la consulta, produce una excepción en tiempo de ejecución.
Para evaluar una condición de predicado en el cliente como en el ejemplo anterior, los desarrolladores ahora tienen
que cambiar explícitamente la evaluación de la consulta a LINQ to Objects:
Consulte la documentación sobre cambios importantes para más detalles sobre cómo esto puede afectar a las
aplicaciones existentes.
Instrucción SQL única por consulta LINQ
Otro aspecto del diseño que cambió significativamente en la versión 3.0 es que ahora siempre se genera una única
instrucción SQL por cada consulta LINQ. En versiones anteriores, se usaba para generar varias instrucciones SQL en
ciertos casos, llamadas Include() traducidas en las propiedades de navegación de la colección y consultas
traducidas que seguían determinados patrones con subconsultas. Aunque en ocasiones este diseño resultaba
práctico y, en el caso de Include() , incluso ayudaba a evitar el envío de datos redundantes a través de la conexión,
la implementación era compleja y se producían algunos comportamientos considerablemente ineficaces (consultas
N+1). Había situaciones en las que los datos devueltos en varias consultas eran incoherentes en potencia.
De forma similar a la evaluación del cliente, si EF Core 3.0 no puede convertir una consulta LINQ en una única
instrucción SQL, se inicia una excepción en tiempo de ejecución. Pero hicimos que EF Core fuera capaz de traducir
muchos de los patrones comunes que solían generar varias consultas en una sola consulta con JOIN.
var orders =
from o in context.Orders
where o.Status == OrderStatus.Pending
select o;
Consulte Trabajar con tipos de referencia que aceptan valores NULL en la documentación de EF Core para más
detalles.
services.AddDbContext(b => b
.UseSqlServer(connectionString)
.AddInterceptors(new HintCommandInterceptor()));
Y la herramienta ahora anulará automáticamente los tipos de scaffold para vistas y tablas sin claves:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Names>(entity =>
{
entity.HasNoKey();
entity.ToView("Names");
});
modelBuilder.Entity<Things>(entity =>
{
entity.HasNoKey();
});
}
[Owned]
public class OrderDetails
{
public int Id { get; set; }
public string ShippingAddress { get; set; }
}
Características pospuestas
Algunas características planeadas originalmente para EF Core 3.0 se pospusieron para versiones futuras:
Capacidad de omitir partes de un modelo en migraciones, con seguimiento realizado a través del problema nº
2725.
Entidades contenedoras de propiedades, de las que se realiza un seguimiento a través de dos problemas
independientes: nº 9914 sobre las entidades de tipo compartido y nº 13610 sobre la compatibilidad con la
asignación de propiedades indizadas.
Cambios importantes incluidos en EF Core 3.0
08/04/2020 • 88 minutes to read • Edit Online
Es posible que los siguientes cambios de API y comportamiento interrumpan las aplicaciones actuales cuando se
actualicen a la versión 3.0.0. Los cambios que esperamos que solo afecten a proveedores de base de datos se
documentan en Cambios para proveedores.
Resumen
C A M B IO IM P O RTA N T E IM PA C TO
EF Core 3.0 tiene como destino .NET Standard 2.1, y no .NET Alto
Standard 2.0
Todas las entidades que compartan una tabla con una Bajo
columna de token de simultaneidad tienen que asignarla a
una propiedad
LogQueryPossibleExceptionWithAggregateOperator ha Bajo
cambiado de nombre
IMPORTANT
EF Core 3.1 vuelve a tener como objetivo a .NET Standard 2.0. Esto reincorpora la compatibilidad con .NET Framework.
También se puede obtener una herramienta local cuando se restauran las dependencias de un proyecto que la
declara como una dependencia de herramientas mediante un archivo de manifiesto de herramientas.
FromSql, ExecuteSql y ExecuteSqlAsync han cambiado de nombre
Problema de seguimiento n.º 10996
Compor tamiento anterior
Antes de EF Core 3.0, estos nombres de métodos se sobrecargaban para funcionar tanto con una cadena normal
como con una cadena que se debería interpolar en SQL y parámetros.
Compor tamiento nuevo
A partir de la versión EF Core 3.0, use FromSqlRaw , ExecuteSqlRaw y ExecuteSqlRawAsync para crear una consulta
con parámetros donde los parámetros se pasan por separado de la cadena de consulta. Por ejemplo:
context.Products.FromSqlRaw(
"SELECT * FROM Products WHERE Name = {0}",
product.Name);
context.Products.FromSqlInterpolated(
$"SELECT * FROM Products WHERE Name = {product.Name}");
Tenga en cuenta que las dos consultas anteriores producirán el mismo código SQL parametrizado con los mismos
parámetros SQL.
Por qué
Las sobrecargas del método como esta facilitan las llamadas accidentales al método de cadena sin procesar
cuando la intención era llamar al método de cadena interpolada y viceversa. Esto podría resultar en consultas que
no se parametrizan cuando deberían.
Mitigaciones
Haga el cambio para usar los nuevos nombres de métodos.
Cuando el método FromSql se usa con un procedimiento almacenado no se puede redactar
Problema de seguimiento n.° 15392
Compor tamiento anterior
Antes de EF Core 3.0, el método FromSql intentaba detectar si se podía redactar en el código SQL pasado. Cuando
el código SQL no se podía redactar, como un procedimiento almacenado, realizaba la evaluación de cliente. La
consulta siguiente funcionaba al ejecutar el procedimiento almacenado en el servidor y aplicar FirstOrDefault en
el lado cliente.
context.Products.FromSqlRaw("[dbo].[Ten Most Expensive Products]").FirstOrDefault();
Esta consulta devolverá la misma instancia de Category para cada elemento Product asociado con la categoría
determinada.
Compor tamiento nuevo
A partir de EF Core 3.0, se crean distintas instancias de la entidad si se encuentra una entidad con un tipo e
identificador determinados en varias ubicaciones del gráfico devuelto. Por ejemplo, la consulta anterior ahora
devolverá una nueva instancia de Category para cada elemento Product cuando haya dos productos asociados a
la misma categoría.
Por qué
La resolución de las identidades (es decir, el hecho de determinar que una entidad tiene los mismos tipo e
identificador que la entidad encontrada) agrega más rendimiento y sobrecarga de memoria. Este enfoque suele
ser contrario a por qué las consultas sin seguimiento se usan en primer lugar. Además, aunque la resolución de las
identidades a veces puede resultar útil, no es necesaria si las entidades se van a serializar y enviar a un cliente,
algo habitual para las consultas sin seguimiento.
Mitigaciones
Si se requiere la resolución de identidad, use una consulta de seguimiento.
La ejecución de consultas se registra en el nivel de depuración Revertido
Problema de seguimiento n.º 14523
Revertimos este cambio porque la nueva configuración de EF Core 3.0 permite a la aplicación especificar el nivel
de registro para cualquier evento. Por ejemplo, para cambiar el registro de SQL a Debug , configure el nivel de
forma explícita en OnConfiguring o AddDbContext :
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedNever();
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;
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:
modelBuilder.Entity<Order>.OwnsOne(e => e.Details, eb =>
{
eb.WithOwner()
.HasForeignKey(e => e.AlternateId)
.HasConstraintName("FK_OrderDetails");
eb.ToTable("OrderDetails");
eb.HasKey(e => e.AlternateId);
eb.HasIndex(e => e.Id);
eb.HasData(
new OrderDetails
{
AlternateId = 1,
Id = -1
});
});
Además, la llamada a Entity() , HasOne() o Set() con un tipo de propiedad de destino ahora iniciará una
excepción.
Por qué
Este cambio se ha realizado para crear una separación más clara entre la configuración del propio tipo de
propiedad y la relación con el tipo de propiedad. A su vez, esto elimina la ambigüedad y la confusión de métodos
como HasForeignKey .
Mitigaciones
Cambie la configuración de las relaciones de tipo de propiedad para usar la nueva superficie de API, como se
muestra en el ejemplo anterior.
Ahora, las entidades dependientes que comparten la tabla con la entidad de seguridad son opcionales
Problema de seguimiento n.º 9005
Compor tamiento anterior
Considere el modelo siguiente:
Antes de EF Core 3.0, si OrderDetails era propiedad de Order o estaba asignado explícitamente a la misma tabla,
siempre era necesaria una instancia de OrderDetails al agregar un elemento Order nuevo.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core permite agregar Order sin OrderDetails y asigna todas las propiedades
OrderDetails a excepción de la clave principal a columnas que aceptan valores NULL. Al realizar consultas, EF
Core establece OrderDetails en null si ninguna de las propiedades necesarias tiene un valor o si no tiene
propiedades necesarias más allá de la clave principal y todas las propiedades son null .
Mitigaciones
Si el modelo tiene una tabla que comparte dependencias con todas las columnas opcionales, pero la navegación
que apunta a ella no se espera que sea null , la aplicación debería modificarse para controlar los casos en los que
la navegación sea null . Si esto no es posible, debería agregarse una propiedad necesaria al tipo de entidad o, al
menos, una entidad debería tener un valor distinto a null asignado.
Todas las entidades que compartan una tabla con una columna de token de simultaneidad tienen que asignarla
a una propiedad
Problema de seguimiento n.º 14154
Compor tamiento anterior
Considere el modelo siguiente:
Antes de EF Core 3.0, si OrderDetails era propiedad de Order o estaba asignado explícitamente a la misma tabla,
si solo se actualizaba OrderDetails , no se actualizaba el valor Version en el cliente y se producía un error en la
próxima actualización.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core propaga el nuevo valor Version en Order si posee OrderDetails . En caso
contrario, se produce una excepción durante la validación del modelo.
Por qué
Este cambio se realizó para evitar un valor de token de simultaneidad obsoleto cuando solo se actualiza una de las
entidades asignadas a la misma tabla.
Mitigaciones
Todas las entidades que comparten la tabla deben incluir una propiedad que se asigna a la columna del token de
simultaneidad. Es posible crear una en estado reemplazado:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<OrderDetails>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
}
Las entidades en propiedad no se pueden consultar sin el propietario mediante una consulta de seguimiento
Problema de seguimiento n.º 18876
Compor tamiento anterior
Antes de EF Core 3.0, las entidades en propiedad se podían consultar como cualquier otra navegación.
Ahora, las propiedades heredadas de tipos sin asignar se asignan a una única columna para todos los tipos
derivados
Problema de seguimiento n.º 13998
Compor tamiento anterior
Considere el modelo siguiente:
public abstract class EntityBase
{
public int Id { get; set; }
}
Antes de EF Core 3.0, la propiedad ShippingAddress se asignaba a columnas distintas para BulkOrder y Order de
forma predeterminada.
Compor tamiento nuevo
A partir de la versión3.0, EF Core solo crea una columna para ShippingAddress .
Por qué
El comportamiento anterior no era el esperado.
Mitigaciones
Todavía se puede asignar explícitamente la propiedad a columnas separadas en los tipos derivados:
La convención de propiedad de clave externa ya no coincide con el mismo nombre que la propiedad de
entidad de seguridad
Problema de seguimiento n.º 13274
Compor tamiento anterior
Considere el modelo siguiente:
public class Customer
{
public int CustomerId { get; set; }
public ICollection<Order> Orders { get; set; }
}
Antes de EF Core 3.0, se podía usar la propiedad CustomerId para la clave externa por convención. Pero si Order
es un tipo de propiedad, entonces esto convertiría también a CustomerId en la clave principal, algo que no suele
ser lo esperado.
Compor tamiento nuevo
A partir de la versión 3.0, EF Core no intenta usar las propiedades de claves externas por convención si tienen el
mismo nombre que la propiedad de entidad de seguridad. Los patrones de nombre de tipo de entidad de
seguridad concatenado con el nombre de propiedad de la entidad de seguridad y de nombre de navegación
concatenado con el nombre de propiedad de la entidad de seguridad todavía se hacen coincidir. Por ejemplo:
Por qué
Este cambio se ha realizado para evitar definir erróneamente una propiedad de clave principal en el tipo de
propiedad.
Mitigaciones
Si la propiedad se ha diseñado para ser la clave externa y, por tanto, parte de la clave principal, se debe configurar
explícitamente como tal.
Ahora, la conexión de base de datos se cierra si ya no se usa antes de que se complete TransactionScope
Problema de seguimiento n.º 14218
Compor tamiento anterior
Antes de EF Core 3.0, si el contexto abría la conexión dentro de TransactionScope , la conexión permanecía abierta
mientras el ámbito actual TransactionScope estuviese activo.
modelBuilder.UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.HasField("_id");
Los nombres de propiedades de solo campo deben coincidir con el nombre del campo
Compor tamiento anterior
Antes de EF Core 3.0, una propiedad podía especificarse con un valor de cadena y, si no había ninguna propiedad
con ese nombre en el tipo .NET, EF Core intentaba hacerla coincidir con un campo mediante reglas de convención.
modelBuilder
.Entity<Blog>()
.Property("Id");
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");
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 DbContext.Entry realiza una operación DetectChanges local
Problema de seguimiento n.º 13552
Compor tamiento anterior
Antes de EF Core 3.0, la llamada a DbContext.Entry provocaba que se detectaran cambios para todas las
entidades con seguimiento. Esto garantizaba que el estado expuesto en EntityEntry estuviera actualizado.
Compor tamiento nuevo
A partir de EF Core 3.0, ahora la llamada a DbContext.Entry 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 ChangeTracker.AutoDetectChangesEnabled se establece en false incluso esta detección de cambios
local se deshabilitará.
Otros métodos que provocan la detección de cambios (como ChangeTracker.Entries 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 context.Entry .
Mitigaciones
Llame a ChangeTracker.DetectChanges() de forma explícita antes de llamar a Entry para garantizar el
comportamiento anterior a la versión 3.0.
El cliente no genera las claves de matriz de cadena y byte de forma predeterminada
Problema de seguimiento n.º 14617
Compor tamiento anterior
Antes de EF Core 3.0, se podían usar las propiedades de clave string y byte[] sin tener que establecer de forma
explícita un valor distinto de NULL. En ese caso, el valor de clave se generaba en el cliente como un GUID, que se
serializaba en bytes para byte[] .
Compor tamiento nuevo
A partir de EF Core 3.0, se iniciará una excepción en la que indica que no se ha establecido ningún valor de clave.
Por qué
Este cambio se ha realizado porque los valores string / byte[] generados por el cliente no suelen ser útiles, y el
comportamiento predeterminado dificultaba razonar sobre los valores de clave generados de una forma habitual.
Mitigaciones
Se puede obtener el comportamiento anterior a la versión 3.0 si se especifica de forma explícita que las
propiedades de clave deben usar los valores generados si no se establece ningún otro valor distinto de NULL. Por
ejemplo, con la API fluida:
modelBuilder
.Entity<Blog>()
.Property(e => e.Id)
.ValueGeneratedOnAdd();
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public string Id { get; set; }
modelBuilder.Entity<Samurai>().HasOne("Entrance").WithOne();
El código parece relacionar Samurai con otro tipo de entidad mediante la propiedad de navegación Entrance ,
que puede ser privada.
En realidad, este código intenta crear una relación con algún tipo de entidad denominada Entrance sin ninguna
propiedad de navegación.
Compor tamiento nuevo
A partir de EF Core 3.0, el código anterior ahora hace lo que parecía que debía hacer antes.
Por qué
El comportamiento anterior era muy confuso, especialmente al leer el código de configuración y al buscar errores.
Mitigaciones
Esto solo interrumpirá las aplicaciones que configuran de manera explícita las relaciones con cadenas para
nombres de tipos y sin especificar explícitamente la propiedad de navegación. Esto no es habitual. El
comportamiento anterior se puede obtener al pasar de manera explícita null para el nombre de la propiedad de
navegación. Por ejemplo:
modelBuilder.Entity<Samurai>().HasOne("Some.Entity.Type.Name", null).WithOne();
El tipo de valor devuelto para varios métodos asincrónicos se ha cambiado de Task a ValueTask
Problema de seguimiento n.º 15184
Compor tamiento anterior
Antes, los siguientes métodos asincrónicos devolvían Task<T> :
DbContext.FindAsync()
DbSet.FindAsync()
DbContext.AddAsync()
DbSet.AddAsync()
ValueGenerator.NextValueAsync() (y las clases derivadas)
Compor tamiento nuevo
Dichos métodos ahora devuelven ValueTask<T> durante el mismo T que antes.
Por qué
Este cambio reduce el número de asignaciones de montones que se producen al invocar estos métodos, lo que
mejora el rendimiento general.
Mitigaciones
Las aplicaciones que simplemente esperen las API anteriores solo necesitan recompilarse, sin que sea necesario
realizar cambios en el código fuente. Un uso más complejo (p. ej., pasar el valor Task devuelto a Task.WhenAny() )
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.
Por qué
Este cambio se ha realizado para consolidar la API para índices con Include en un mismo lugar para todos los
proveedores de base de datos.
Mitigaciones
Use la API nueva, como se ha mostrado anteriormente.
Cambios en la API de metadatos
Problema de seguimiento n.º 214
Compor tamiento nuevo
Las siguientes propiedades se han convertido en métodos de extensión:
IEntityType.QueryFilter -> GetQueryFilter()
IEntityType.DefiningQuery -> GetDefiningQuery()
IProperty.IsShadowProperty -> IsShadowProperty()
IProperty.BeforeSaveBehavior -> GetBeforeSaveBehavior()
IProperty.AfterSaveBehavior -> GetAfterSaveBehavior()
Por qué
Este cambio simplifica la implementación de las interfaces mencionadas anteriormente.
Mitigaciones
Use los nuevos métodos de extensión.
Cambios en la API de metadatos específicos del proveedor
Problema de seguimiento n.º 214
Compor tamiento nuevo
Los métodos de extensión específicos del proveedor se simplificarán:
IProperty.Relational().ColumnName -> IProperty.GetColumnName()
IEntityType.SqlServer().IsMemoryOptimized -> IEntityType.IsMemoryOptimized()
PropertyBuilder.UseSqlServerIdentityColumn() -> PropertyBuilder.UseIdentityColumn()
Por qué
Este cambio simplifica la implementación de los métodos de extensión mencionados anteriormente.
Mitigaciones
Use los nuevos métodos de extensión.
EF Core ya no envía pragma para el cumplimiento de SQLite FK
Problema de seguimiento n.º 12151
Compor tamiento anterior
Antes de EF Core 3.0, EF Core enviaba PRAGMA foreign_keys = 1 cuando se abría una conexión con SQLite.
Compor tamiento nuevo
A partir de EF Core 3.0, EF Core ya no envía PRAGMA foreign_keys = 1 cuando se abre una conexión con SQLite.
Por qué
Este cambio se ha realizado porque en EF Core se usa SQLitePCLRaw.bundle_e_sqlite3 de forma predeterminada,
lo que a su vez significa que el cumplimiento de CD está activado de forma predeterminada y no es necesario
habilitarlo explícitamente cada vez que se abra una conexión.
Mitigaciones
Las claves externas se habilitan de forma predeterminada en SQLitePCLRaw.bundle_e_sqlite3, que en EF Core se
usa de forma predeterminada. Para otros casos, las claves externas se pueden habilitar mediante la especificación
de Foreign Keys=True en la cadena de conexión.
Microsoft.EntityFrameworkCore.Sqlite ahora depende de SQLitePCLRaw.bundle_e_sqlite3
Compor tamiento anterior
Antes de EF Core 3.0, en EF Core se usaba SQLitePCLRaw.bundle_green .
Compor tamiento nuevo
A partir de EF Core 3.0, en EF Core se usa SQLitePCLRaw.bundle_e_sqlite3 .
Por qué
Este cambio se ha realizado para que la versión de SQLite que se usa en iOS sea coherente con otras plataformas.
Mitigaciones
Para usar la versión nativa de SQLite en iOS, configure Microsoft.Data.Sqlite para usar otra agrupación
SQLitePCLRaw .
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 => e.GuidProperty)
.HasConversion(
g => g.ToByteArray(),
b => new Guid(b));
Microsoft.Data.Sqlite sigue siendo capaz de leer valores GUID de ambas columnas BLOB y TEXT. Sin embargo,
dado que el formato predeterminado de los parámetros y las constantes ha cambiado, seguramente deberá
realizar alguna acción en la mayoría de casos que impliquen el uso de valores GUID.
Ahora los valores char se almacenan como TEXT en SQLite
Problema de seguimiento n.º 15020
Compor tamiento anterior
Anteriormente los valores char se almacenaban como valores INTEGER en SQLite. Por ejemplo, un valor char de A
se almacenaba como el valor entero 65.
Compor tamiento nuevo
Ahora, los valores char se almacenan como TEXT.
Por qué
El almacenamiento de valores como TEXT es más natural y mejora la compatibilidad de la base de datos con otras
tecnologías.
Mitigaciones
Puede migrar las bases de datos existentes al nuevo formato ejecutando SQL de la siguiente forma.
UPDATE MyTable
SET CharColumn = char(CharColumn)
WHERE typeof(CharColumn) = 'integer';
En EF Core, también puede seguir usando el comportamiento anterior configurando un convertidor de valores en
estas propiedades.
modelBuilder
.Entity<MyEntity>()
.Property(e => e.CharProperty)
.HasConversion(
c => (long)c,
i => (char)i);
Microsoft.Data.Sqlite también puede leer valores de caracteres tanto de columnas INTEGER como de columnas
TEXT, por lo que es posible que no deba hacer nada dependiendo de su caso.
Ahora los id. de migración se generan usando el calendario de la referencia cultural invariable
Problema de seguimiento n.º 12978
Compor tamiento anterior
Los identificadores de migración se generaban de forma involuntaria con el calendario de la referencia cultural
actual.
Compor tamiento nuevo
Ahora los id. de migración siempre se generan usando el calendario de la referencia cultural invariable
(gregoriano).
Por qué
El orden de las migraciones es importante al actualizar la base de datos o al solucionar conflictos de combinación.
Al usar el calendario invariable, se evitan problemas de ordenación que pueden producirse si los miembros del
equipo tienen distintos calendarios del sistema.
Mitigaciones
Esta cambio afecta a todas las personas que usan un calendario no gregoriano en el que el año sea superior al del
calendario gregoriano (como el calendario budista tailandés). Los id. de migración existentes deberán actualizarse
para que las migraciones nuevas se ordenen después de las existentes.
Puede ver el id. de migración en el atributo Migration de los archivos de diseñador de la migración.
[DbContext(typeof(MyDbContext))]
-[Migration("25620318122820_MyMigration")]
+[Migration("20190318122820_MyMigration")]
partial class MyMigration
{
UPDATE __EFMigrationsHistory
SET MigrationId = CONCAT(LEFT(MigrationId, 4) - 543, SUBSTRING(MigrationId, 4, 150))
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.
IRelationalDatabaseCreator.HasTables/HasTablesAsync se han hecho públicos
Problema de seguimiento n.° 15997
Compor tamiento anterior
Antes de EF Core 3.0, estos métodos estaban protegidos.
Compor tamiento nuevo
Desde EF Core 3.0, estos métodos son públicos.
Por qué
EF usa estos métodos para determinar si se ha creado una base de datos, pero está vacía. Esto también puede
resultar útil fuera de EF al determinar si se deben aplicar migraciones o no.
Mitigaciones
Cambie la accesibilidad de cualquier invalidación.
Microsoft.EntityFrameworkCore.Design es ahora un paquete DevelopmentDependency
Problema de seguimiento n.° 11506
Compor tamiento anterior
Antes de EF Core 3.0, Microsoft.EntityFrameworkCore.Design era un paquete NuGet regular con un ensamblado al
que podían hacer referencia los proyectos que dependían de él.
Compor tamiento nuevo
Desde EF Core 3.0, es un paquete DevelopmentDependency. Esto significa que la dependencia no fluirá de manera
transitiva en otros proyectos y que ya no puede, de forma predeterminada, hacer referencia a su ensamblado.
Por qué
Este paquete solo está destinado a usarse en tiempo de diseño. Las aplicaciones implementadas no deben hacer
referencia al mismo. Hacer que el paquete sea DevelopmentDependency refuerza esta recomendación.
Mitigaciones
Si tiene que hacer referencia a este paquete para invalidar el comportamiento en tiempo de diseño de EF Core,
puede actualizar los metadatos de elementos PackageReference del proyecto.
modelBuilder
.Entity<User>()
.HasOne(e => e.CreatedBy)
.WithMany();
modelBuilder
.Entity<User>()
.HasOne(e => e.UpdatedBy)
.WithMany();
DbFunction.Schema es NULL o la cadena vacía lo configura para estar en el esquema predeterminado del
modelo
Problema de seguimiento n.º 12757
Compor tamiento anterior
Una función DbFunction configurada con el esquema como una cadena vacía se trataba como una función
integrada sin un esquema. Por ejemplo, el código siguiente asignará la función CLR DatePart a la función
integrada DATEPART en SqlServer.
modelBuilder
.HasDbFunction(typeof(MyContext).GetMethod(nameof(MyContext.DatePart)))
.HasTranslation(args => SqlFunctionExpression.Create("DatePart", args, typeof(int?), null));
Novedades de EF Core 2.2
08/04/2020 • 4 minutes to read • Edit Online
using NetTopologySuite.Geometries;
namespace MyApp
{
public class Friend
{
[Key]
public string Name { get; set; }
[Required]
public Point Location { get; set; }
}
}
var nearestFriends =
(from f in context.Friends
orderby f.Location.Distance(myLocation) descending
select f).Take(5).ToList();
Para obtener más información sobre esta característica, consulte la documentación sobre tipos espaciales.
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 context.Friends.TagWith(@"This is my spatial query!")
orderby f.Location.Distance(myLocation) descending
select f).Take(5).ToList();
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,
Microsoft.EntityFrameworkCore.Proxies, 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.
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.
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 :
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.
Consulte la sección sobre Include con tipos derivados para obtener más información sobre el tema.
System.Transactions
Se ha agregado la posibilidad de trabajar con características de System.Transactions 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 System.Transactions para obtener más información sobre el tema.
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:
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; }
}
Paquete Microsoft.EntityFrameworkCore.Abstractions
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í.
TIP
Si encuentra alguna incompatibilidad inesperada o algún problema en las nuevas características o si tiene comentarios sobre
ellas, notifíquelos mediante nuestro rastreador de problemas.
Nuevas características de EF Core 2.0
08/04/2020 • 18 minutes to read • Edit Online
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:
modelBuilder.Entity<Product>()
.HasOne(e => e.Details).WithOne(e => e.Product)
.HasForeignKey<ProductDetails>(e => e.Id);
modelBuilder.Entity<Product>().ToTable("Products");
modelBuilder.Entity<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:
modelBuilder.Entity<Order>().OwnsOne(p => p.OrderDetails, cb =>
{
cb.OwnsOne(c => c.BillingAddress);
cb.OwnsOne(c => c.ShippingAddress);
});
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:
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 :
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 context.Posts
where BloggingContext.PostReadCount(p.Id) > 5
select p;
...
// OnModelCreating
builder.ApplyConfiguration(new CustomerConfiguration());
Alto rendimiento
Agrupación de DbContext
El patrón básico para usar EF Core en una aplicación de ASP.NET 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:
services.AddDbContextPool<BloggingContext>(
options => options.UseSqlServer(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
ADO.NET y tiene la ventaja de ahorrar algunos de los costos de inicialización de la instancia de DbContext.
Limitaciones
El nuevo método presenta algunas limitaciones con respecto a lo que se puede hacer en el método
OnConfiguring() de DbContext.
WARNING
Evite el uso de la agrupación de DbContext si mantiene su propio estado (por ejemplo, campos privados) en la clase derivada
DbContext que no debe compartirse con otras solicitudes. EF Core solo restablece el estado del que es consciente antes de
agregar una instancia de DbContext al grupo.
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 DbContext.Attach o DbSet.Attach 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. DbContext.Update y DbSet.Update funcionan de la misma manera, salvo que
las entidades con una clave establecida se marcan como Modified en lugar de Unchanged .
Consultar
Traducción de LINQ mejorada
Permite que más consultas se ejecuten correctamente, con más lógica evaluada en la base de datos (en lugar de en
memoria) y menos datos innecesariamente recuperados de la base de datos.
Mejoras de GroupJoin
Este trabajo mejora el SQL que se genera para las combinaciones agrupadas. Las combinaciones agrupadas suelen
ser un resultado de subconsultas en propiedades de navegación opcionales.
Interpolación de cadenas en FromSql y ExecuteSqlCommand
C# 6 presentó la interpolación de cadenas, una característica que permite insertar expresiones de C# directamente
en literales de cadena, lo que proporciona una forma útil de compilar cadenas en tiempo de ejecución. En EF Core
2.0 se ha agregado compatibilidad especial con las cadenas interpoladas a las dos API principales que aceptan
cadenas SQL sin formato: FromSql y ExecuteSqlCommand . Esta nueva compatibilidad permite que la interpolación de
cadenas de C# se use de forma "segura". Es decir, de una forma que protege frente a errores de inserción de SQL
comunes que pueden producirse al crear SQL de forma dinámica en tiempo de ejecución.
Este es un ejemplo:
var city = "London";
var contactTitle = "Sales Representative";
En este ejemplo hay dos variables insertadas en la cadena de formato SQL. EF Core genera el SQL siguiente:
SELECT *
FROM ""Customers""
WHERE ""City"" = @p0
AND ""ContactTitle"" = @p1
EF.Functions.Like ()
Se ha agregado la propiedad EF.Functions, 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 context.Customers
where EF.Functions.Like(c.Name, "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.
Otros
Traslado del proveedor de SQLite de ADO.NET a SQLitePCL.raw
Esto proporciona una solución más robusta en Microsoft.Data.Sqlite para distribuir archivos binarios nativos de
SQLite en distintas plataformas.
Solo un proveedor por modelo
Mejora considerablemente la forma en que los proveedores pueden interactuar con el modelo y simplifica el
funcionamiento de las convenciones, las anotaciones y las API fluidas con distintos proveedores.
EF Core 2.0 ahora compila un elemento IModel diferente para cada proveedor que se va a usar. Esto suele ser
transparente para la aplicación. Esto ha permitido una simplificación de las API de metadatos de nivel inferior, de
modo que cualquier acceso a conceptos de metadatos relacionales comunes siempre se realiza mediante una
llamada a .Relational en lugar de a .SqlServer , .Sqlite , etc.
Registro y diagnóstico consolidados
Los mecanismos de registro (basados en ILogger) y diagnóstico (basados en DiagnosticSource) ahora comparten
más código.
Los identificadores de evento de los mensajes enviados a un elemento ILogger han cambiado en 2.0. Los
identificadores de evento ahora son únicos en el código de EF Core. Ahora, estos mensajes también siguen el
patrón estándar de registro estructurado que usa, por ejemplo, MVC.
Las categorías de registrador también han cambiado. Ahora hay un conjunto conocido de categorías a las que se
accede a través de DbLoggerCategory.
Los eventos de DiagnosticSource ahora usan los mismos nombres de identificador de evento que los mensajes de
ILogger correspondientes.
Características nuevas en EF Core 1.1
08/04/2020 • 2 minutes to read • Edit Online
Modelado
Asignación de campos
Permite configurar un campo de respaldo para una propiedad. Puede resultar útil en las propiedades de solo
lectura o en los datos que tienen métodos Get/Set en lugar de una propiedad.
Asignación a tablas optimizadas para memoria en SQL Server
Puede especificar que la tabla a la que está asignada una entidad está optimizada para memoria. Cuando use EF
Core para crear y mantener una base de datos basada en el modelo (ya sea con migraciones o
Database.EnsureCreated() ), 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.
DbSet.Find
Proporciona una manera sencilla de capturar una entidad en función de su valor de clave principal.
Otros
Resistencia de la conexión
Reintenta automáticamente los comandos de base de datos erróneos. Esto resulta especialmente útil cuando se
realizan conexiones a SQL Azure, donde los errores transitorios son comunes.
Reemplazo de servicio simplificado
Facilita el reemplazo de servicios internos que EF usa.
Características incluidas en EF Core 1.0
08/04/2020 • 8 minutes to read • Edit Online
Plataformas
.NET Framework 4.5.1
Incluye la consola, WPF, WinForms, ASP.NET 4, etc.
.NET Standard 1.3
Incluye ASP.NET 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 DbContext.Entry y DbContext.ChangeTracker .
Adjuntar grafos o entidades desasociados
La nueva API DbContext.AttachGraph ayuda a volver a adjuntar entidades a un contexto para guardar las entidades
nuevas o modificadas.
Guardar datos
Funcionalidad básica de guardado
Permite que los cambios en las instancias de la entidad se conserven en la base de datos.
Simultaneidad optimista
Impide sobrescribir los cambios realizados por otro usuario desde que se capturaron de la base de datos.
Característica SaveChanges asincrónica
Puede liberar el subproceso actual para que procese otras solicitudes mientras la base de datos procesa los
comandos que se emiten desde SaveChanges .
Transacciones de bases de datos
Es decir, SaveChanges siempre es atómica (lo que significa que siempre se completa correctamente o que no se
realiza ningún cambio en la base de datos). También hay API relacionadas con transacciones que permiten
compartir las transacciones entre las instancias de contexto, etc.
Relacional: procesamiento de instrucciones por lotes
Proporciona un mejor rendimiento mediante el procesamiento por lotes de varios comandos
INSERT/UPDATE/DELETE en un solo ciclo de ida y vuelta a la base de datos.
Consultar
Compatibilidad básica con LINQ
Proporciona la capacidad de usar LINQ para recuperar datos de la base de datos.
Evaluación combinada de cliente/servidor
Permite que las consultas contengan una lógica que no se puede evaluar en la base de datos y, por lo tanto, se debe
evaluar después de que los datos se recuperan en la memoria.
NoTracking
Las consultas permiten ejecutar más rápido las consultas cuando el contexto no necesita supervisar los cambios
realizados en las instancias de entidad (esto es útil si los resultados son de solo lectura).
Carga diligente
Proporciona los métodos Include y ThenInclude para identificar los datos relacionados que se deben capturar
cuando se realizan las consultas.
Consulta asincrónica
Puede liberar el subproceso actual (y los recursos asociados) para que procese otras solicitudes mientras la base de
datos procesa la consulta.
Consultas SQL sin formato
Proporciona el método DbSet.FromSql para usar consultas SQL sin procesar para capturar datos. Estas consultas
también se pueden componer mediante LINQ.
En este artículo se proporcionan instrucciones para mover una aplicación compilada con paquetes RC1 a RC2.
Tendrá que quitar por completo los paquetes RC1 y, a continuación, instalar los RC2. Esta es la
asignación para algunos paquetes comunes.
PA Q UET E RC 1 EQ UIVA L EN T ES DE RC 2
Espacios de nombres
Junto con los nombres de paquete, los espacios de nombres cambiaron de Microsoft.Data.Entity.* a
Microsoft.EntityFrameworkCore.* . Puede controlar este cambio con una búsqueda/reemplazo de
using Microsoft.Data.Entity con using Microsoft.EntityFrameworkCore .
Si desea adoptar la nueva estrategia de nomenclatura, se recomienda completar correctamente el resto de los
pasos de actualización y, a continuación, quitar el código y crear una migración para aplicar el cambio de nombre
de la tabla.
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
También debe agregar un constructor, al contexto derivado, que toma las opciones de contexto y las pasa al
constructor base. Esto es necesario porque hemos quitado algunas de las ideas mágicas que snuck en segundo
plano:
Pasar un IServiceProvider
Si tiene código RC1 que pasa un IServiceProvider al contexto, ahora se mueve a DbContextOptions , en lugar de ser
un parámetro de constructor independiente. Utilice DbContextOptionsBuilder.UseInternalServiceProvider(...) para
establecer el proveedor de servicios.
Prueba
El escenario más común para hacerlo era controlar el ámbito de una base de datos inmemory al realizar las
pruebas. Vea el artículo sobre las pruebas actualizadas para obtener un ejemplo de cómo hacerlo con RC2.
Resolver servicios internos desde el proveedor de servicios de aplicación (solo proyectos de ASP.NET Core )
Si tiene una aplicación ASP.NET Core y desea que EF resuelva los servicios internos del proveedor de servicios de
aplicación, hay una sobrecarga de AddDbContext que le permite configurar lo siguiente:
services.AddEntityFrameworkSqlServer()
.AddDbContext<ApplicationDbContext>((serviceProvider, options) =>
options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"])
.UseInternalServiceProvider(serviceProvider));
WARNING
Se recomienda permitir que EF administre internamente sus propios servicios, a menos que tenga un motivo para combinar
los servicios de EF internos en el proveedor de servicios de aplicación. La razón principal por la que puede querer hacer esto
es usar el proveedor de servicios de aplicación para reemplazar los servicios que EF usa internamente
"tools": {
"Microsoft.EntityFrameworkCore.Tools": {
"version": "1.0.0-preview1-final",
"imports": [
"portable-net45+win8+dnxcore50",
"portable-net45+win8"
]
}
}
TIP
Si usa Visual Studio, ahora puede usar la consola del administrador de paquetes para ejecutar comandos EF para proyectos de
ASP.NET Core (esto no se admitía en RC1). Todavía tiene que registrar los comandos en la sección tools de project.json
para hacerlo.
La solución consiste en importar manualmente el perfil portátil "portable-net451 + win8". Esto obliga a NuGet a
tratar estos binarios que coinciden con este proporcionado como un marco compatible con .NET Standard, aunque
no lo sean. Aunque "portable-net451 + win8" no es 100% compatible con .NET Standard, es lo suficientemente
compatible para la transición de PCL a .NET Standard. Las importaciones se pueden quitar cuando las dependencias
de EF finalmente se actualizan a .NET Standard.
Se pueden agregar varios marcos a "Imports" en la sintaxis de la matriz. Otras importaciones pueden ser
necesarias si agrega bibliotecas adicionales al proyecto.
{
"frameworks": {
"netcoreapp1.0": {
"imports": ["dnxcore50", "portable-net451+win8"]
}
}
}
En este artículo se proporcionan instrucciones para mover una aplicación compilada con los paquetes de RC2 a
1.0.0 RTM.
Versiones de paquetes
Los nombres de los paquetes de nivel superior que se instalarían normalmente en una aplicación no cambiaban
entre RC2 y RTM.
Debe actualizar los paquetes instalados a las versiones de RTM:
Los paquetes en tiempo de ejecución (por ejemplo, Microsoft.EntityFrameworkCore.SqlServer ) cambiaron de
1.0.0-rc2-final a 1.0.0 .
{
"frameworks": {
"netcoreapp1.0": {
"imports": ["dnxcore50", "portable-net451+win8"]
}
}
}
NOTE
A partir de la versión 1,0 RTM, el SDK de .net Core ya no admite project.json ni desarrollar aplicaciones de .net Core con
Visual Studio 2015. Se recomienda migrar de project.json a csproj. Si usa Visual Studio, se recomienda que actualice a visual
studio 2017.
Debe agregar manualmente redirecciones de enlace al proyecto de UWP. Cree un archivo denominado App.config
en la carpeta raíz del proyecto y agregue redireccionamientos a las versiones de ensamblado correctas.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.IO.FileSystem.Primitives"
publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="4.0.0.0"
newVersion="4.0.1.0"/>
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Threading.Overlapped"
publicKeyToken="b03f5f7f11d50a3a"
culture="neutral" />
<bindingRedirect oldVersion="4.0.0.0"
newVersion="4.0.1.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Actualización de las aplicaciones de las versiones
anteriores a EF Core 2,0
11/03/2020 • 16 minutes to read
Hemos aprovechado la oportunidad de refinar significativamente las API y los comportamientos existentes en 2,0.
Hay algunas mejoras que pueden requerir la modificación del código de aplicación existente, aunque creemos que,
para la mayoría de las aplicaciones, el impacto será bajo, en la mayoría de los casos solo requiere volver a compilar
y cambios guiados mínimos para reemplazar las API obsoletas.
La actualización de una aplicación existente a EF Core 2,0 puede requerir:
1. Actualización de la implementación de .NET de destino de la aplicación a una que admite .NET Standard 2,0.
Vea implementaciones de .net compatibles para obtener más detalles.
2. Identifique un proveedor para la base de datos de destino que sea compatible con EF Core 2,0. Consulte EF
Core 2,0 requiere un proveedor de base de datos 2,0 .
3. Actualizar todos los paquetes de EF Core (tiempo de ejecución y herramientas) a 2,0. Consulte instalación de
EF Core para obtener más detalles.
4. Realice los cambios de código necesarios para compensar los cambios importantes descritos en el resto de
este documento.
No se encontró ningún constructor sin parámetros en ' ApplicationContext '. Agregue un constructor sin
parámetros a ' ApplicationContext ' o agregue una implementación de '
IDesignTimeDbContextFactory<ApplicationContext>' en el mismo ensamblado que ' ApplicationContext '
Se ha agregado un nuevo enlace en tiempo de diseño a la plantilla predeterminada de ASP.NET Core 2.0. El método
Program.BuildWebHost estático permite a EF Core tener acceso al proveedor de servicios de la aplicación en tiempo
de diseño. Si está actualizando una aplicación ASP.NET Core 1. x, deberá actualizar la clase Program para que se
parezca a lo siguiente.
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace AspNetCoreDotNetCore2._0App
{
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
La adopción de este nuevo patrón al actualizar las aplicaciones a 2,0 es muy recomendable y es necesaria para que
funcionen las características del producto, como Entity Framework Core las migraciones. La otra alternativa común
es implementar IDesignTimeDbContextFactory<TContext > .
IDbContextFactory cambiado
Con el fin de admitir diversos patrones de aplicación y proporcionar a los usuarios un mayor control sobre cómo se
utiliza su DbContext en tiempo de diseño, en el pasado, siempre se ofrecía la interfaz IDbContextFactory<TContext> .
En tiempo de diseño, las herramientas de EF Core detectarán las implementaciones de esta interfaz en el proyecto y
la usarán para crear objetos DbContext .
Esta interfaz tenía un nombre muy general que engaña a algunos usuarios para intentar volver a usarlo para otros
escenarios de creación de DbContext . No estaban protegidos cuando las herramientas de EF intentaban usar su
implementación en tiempo de diseño y provocaban que se produjera un error en los comandos como
Update-Database o dotnet ef database update .
Con el fin de comunicar la semántica sólida en tiempo de diseño de esta interfaz, se le ha cambiado el nombre a
IDesignTimeDbContextFactory<TContext> .
En la versión 2,0, el IDbContextFactory<TContext> todavía existe pero está marcado como obsoleto.
DbContextFactoryOptions quitado
Debido a los cambios ASP.NET 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.
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:
modelBuilder.Entity<User>().ToTable(
Database.IsSqlServer() ? "SqlServerName" : "OtherName");
Tenga en cuenta que este cambio solo se aplica a las API/metadatos que se definen para todos los proveedores
relacionales. La API y los metadatos siguen siendo los mismos cuando son específicos de un solo proveedor. Por
ejemplo, los índices clúster son específicos de SQL Server, por lo que se deben seguir ForSqlServerIsClustered y
.SqlServer().IsClustered() .
optionsBuilder.UseInMemoryDatabase("MyDatabase");
Esto crea o usa una base de datos con el nombre "base de datos". Si se llama de nuevo a UseInMemoryDatabase con
el mismo nombre, se usará la misma base de datos en memoria, lo que permite que varias instancias de contexto lo
compartan.
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer"
Version="2.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools"
Version="2.0.0"
PrivateAssets="All" />
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet"
Version="2.0.0" />
Introducción a EF Core
08/04/2020 • 7 minutes to read • Edit Online
En este tutorial se crea una aplicación de consola de .NET Core que realiza el acceso a datos en una base de datos
SQLite mediante Entity Framework Core.
Puede seguir el tutorial con Visual Studio en Windows o mediante la CLI de .NET Core en Windows, macOS o
Linux.
Vea un ejemplo de este artículo en GitHub.
Requisitos previos
Instale el software siguiente:
CLI de .NET Core
Visual Studio
SDK de .NET Core.
namespace EFGetStarted
{
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
EF Core también puede aplicar ingeniería inversa en un modelo desde una base de datos existente.
Sugerencia: En una aplicación real, lo habitual sería colocar cada clase en un archivo independiente y la cadena de
conexión, en un archivo de configuración o una variable de entorno. Para que el tutorial sea sencillo, todo está
incluido en un archivo.
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.
namespace EFGetStarted
{
class Program
{
static void Main()
{
using (var db = new BloggingContext())
{
// Create
Console.WriteLine("Inserting a new blog");
db.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
db.SaveChanges();
// Read
Console.WriteLine("Querying for a blog");
var blog = db.Blogs
.OrderBy(b => b.BlogId)
.First();
// Update
Console.WriteLine("Updating the blog and adding a post");
blog.Url = "https://devblogs.microsoft.com/dotnet";
blog.Posts.Add(
new Post
{
Title = "Hello World",
Content = "I wrote an app using EF Core!"
});
db.SaveChanges();
// Delete
Console.WriteLine("Delete the blog");
db.Remove(blog);
db.SaveChanges();
}
}
}
}
Ejecutar la aplicación
CLI de .NET Core
Visual Studio
dotnet run
Pasos siguientes
Siga el tutorial de ASP.NET Core para usar EF Core en una aplicación web.
Obtenga más información sobre las expresiones de consulta LINQ.
Configure su modelo para especificar aspectos como requerido y longitud máxima.
Use Migraciones para actualizar el esquema de la base de datos después de cambiar el modelo.
Instalación de Entity Framework Core
08/04/2020 • 10 minutes to read • Edit Online
Prerequisites
EF Core es una biblioteca de .NET Standard 2.0. Por este motivo, EF Core requiere una implementación de
.NET que admita .NET Standard 2.0 para poder ejecutarse. Otras bibliotecas de .NET Standard 2.0 también
pueden hacer referencia a EF Core.
Por ejemplo, puede usar EF Core para desarrollar aplicaciones que tengan como destino .NET Core. La
compilación de aplicaciones de .NET Core requiere el SDK de .NET Core. También puede usar un entorno de
desarrollo como Visual Studio, Visual Studio para Mac o Visual Studio Code. Para obtener más información,
vea Introducción a .NET Core.
Puede usar EF Core para desarrollar aplicaciones en Windows con Visual Studio. Se recomienda usar la
última versión de Visual Studio.
EF Core puede ejecutarse en otras implementaciones de .NET, como Xamarin y .NET Native. Pero en la
práctica, estas implementaciones tienen limitaciones de runtime que podrían afectar el rendimiento de EF
Core en su aplicación. Para obtener más información, vea Implementaciones de .NET compatibles con EF
Core.
Por último, los diferentes proveedores de bases de datos pueden requerir versiones de motores de bases de
datos, implementaciones de .NET o sistemas operativos específicas. Asegúrese de que esté disponible un
proveedor de bases de datos de EF Core que admita el entorno adecuado para su aplicación.
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
Microsoft.EntityFrameworkCore.SqlServer 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 Microsoft.EntityFrameworkCore.SqlServer
Aunque puede usar los comandos de dotnet ef desde la consola del Administrador de paquetes, le
recomendamos que use las herramientas de la consola del Administrador de paquetes en Visual Studio:
Trabajan automáticamente con el proyecto actual seleccionado en la PMC de Visual Studio sin necesidad de
cambiar manualmente entre directorios.
Abren automáticamente los archivos generados por los comandos de Visual Studio una vez completado el
comando.
Obtención de las herramientas de la CLI de .NET Core
Las herramientas de la CLI de .NET Core requieren el SDK de .NET Core, tal como se indica en Requisitos previos.
Los comandos de dotnet ef están incluidos en las versiones actuales del SDK de .NET Core, pero es necesario
instalar el paquete Microsoft.EntityFrameworkCore.Design para habilitarlos en un proyecto específico:
Install-Package Microsoft.EntityFrameworkCore.Tools
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.
<connectionStrings>
<add name="BloggingDatabase"
connectionString="Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;" />
</connectionStrings>
</configuration>
TIP
El valor providerName no es necesario EF Core en las cadenas de conexión almacenadas en el archivo app. config porque el
proveedor de base de datos se configura mediante código.
Después, puede leer la cadena de conexión mediante el ConfigurationManager API en el método de OnConfiguring
del contexto. Es posible que tenga que agregar una referencia al ensamblado de System.Configuration Framework
para poder usar esta API.
optionsBuilder.UseSqlServer(ConfigurationManager.ConnectionStrings["BloggingDatabase"].ConnectionString);
}
}
ASP.NET Core
En ASP.NET Core el sistema de configuración es muy flexible y la cadena de conexión puede almacenarse en
appsettings.json , 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 ASP.net Core para obtener más detalles. En el ejemplo
siguiente se muestra la cadena de conexión almacenada en appsettings.json .
{
"ConnectionStrings": {
"BloggingDatabase": "Server=
(localdb)\\mssqllocaldb;Database=EFGetStarted.ConsoleApp.NewDb;Trusted_Connection=True;"
},
}
Normalmente, el contexto se configura en Startup.cs con la cadena de conexión que se lee de la configuración.
Tenga en cuenta que el método GetConnectionString() busca un valor de configuración cuya clave sea
ConnectionStrings:<connection string name> . Debe importar el espacio de nombres Microsoft. Extensions.
Configuration para usar este método de extensión.
TIP
Puede ver un ejemplo de este artículo en GitHub.
Otras aplicaciones
El registro de EF Core requiere un ILoggerFactory que se configura a su vez con uno o más proveedores de
registro. Los proveedores comunes se incluyen en los siguientes paquetes:
Microsoft. Extensions. Logging. Console: un registrador de consola simple.
Microsoft. Extensions. Logging. AzureAppServices: admite las características ' registros de diagnóstico ' y ' flujo
de registro ' de los servicios de App de Azure.
Microsoft. Extensions. Logging. Debug: inicia sesión en un monitor de depurador con System. Diagnostics.
Debug. WriteLine ().
Microsoft. Extensions. Logging. EventLog: registra en el registro de eventos de Windows.
Microsoft. Extensions. Logging. EventSource: admite EventSource/EventListener.
Microsoft. Extensions. Logging. TraceSource: registra en un agente de escucha de seguimiento mediante
System.Diagnostics.TraceSource.TraceEvent() .
Después de instalar los paquetes adecuados, la aplicación debe crear una instancia singleton/global de un
LoggerFactory. Por ejemplo, mediante el registrador de consola:
Versión 3.x
Versión 2.x
Esta instancia singleton/global se debe registrar con EF Core en el DbContextOptionsBuilder . Por ejemplo:
La resistencia de conexión reintenta automáticamente los comandos de base de datos con errores. La característica
se puede usar con cualquier base de datos proporcionando una "estrategia de ejecución", que encapsula la lógica
necesaria para detectar errores y volver a ejecutar los comandos. Los proveedores de EF Core pueden proporcionar
estrategias de ejecución adaptadas a sus condiciones de error de base de datos específica y las directivas de
reintento óptima.
Como ejemplo, el proveedor de SQL Server incluye una estrategia de ejecución que se adapta específicamente a
SQL Server (incluido SQL Azure). Es consciente de los tipos de excepción que se pueden reintentar y tienen valores
predeterminados razonables para el número máximo de reintentos, el retraso entre reintentos, etc.
Cuando se configuran las opciones para el contexto, se especifica una estrategia de ejecución. Normalmente, se
encuentra en el método OnConfiguring del contexto derivado:
La solución consiste en invocar manualmente la estrategia de ejecución con un delegado que representa todo lo
que se debe ejecutar. Si se produce un error transitorio, la estrategia de ejecución invoca al delegado de nuevo.
strategy.Execute(() =>
{
using (var context = new BloggingContext())
{
using (var transaction = context.Database.BeginTransaction())
{
context.Blogs.Add(new Blog {Url = "http://blogs.msdn.com/dotnet"});
context.SaveChanges();
transaction.Commit();
}
}
});
}
strategy.Execute(() =>
{
using (var context2 = new BloggingContext())
{
using (var transaction = new TransactionScope())
{
context2.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context2.SaveChanges();
context1.SaveChanges();
transaction.Complete();
}
}
});
}
Este método inicia y confirma una transacción y también acepta una función en el parámetro verifySucceeded que
se invoca cuando se produce un error transitorio durante la confirmación de la transacción.
using (var db = new BloggingContext())
{
var strategy = db.Database.CreateExecutionStrategy();
strategy.ExecuteInTransaction(db,
operation: context =>
{
context.SaveChanges(acceptAllChangesOnSuccess: false);
},
verifySucceeded: context => context.Blogs.AsNoTracking().Any(b => b.BlogId == blogToAdd.BlogId));
db.ChangeTracker.AcceptAllChanges();
}
NOTE
Aquí SaveChanges se invoca con acceptAllChangesOnSuccess establecido en false para evitar cambiar el estado de la
entidad Blog a Unchanged si SaveChanges se realiza correctamente. Esto permite volver a intentar la misma operación si
se produce un error en la confirmación y se revierte la transacción.
strategy.ExecuteInTransaction(db,
operation: context =>
{
context.SaveChanges(acceptAllChangesOnSuccess: false);
},
verifySucceeded: context => context.Transactions.AsNoTracking().Any(t => t.Id == transaction.Id));
db.ChangeTracker.AcceptAllChanges();
db.Transactions.Remove(transaction);
db.SaveChanges();
}
NOTE
Asegúrese de que el contexto utilizado para la comprobación tenga definida una estrategia de ejecución, ya que es probable
que se produzca un error en la conexión durante la comprobación si se produjo un error durante la confirmación de la
transacción.
Pruebas de código que usa EF Core
09/04/2020 • 10 minutes to read
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.
Pruebas unitarias
Imagine que va a probar una parte de la lógica de negocio que pueda necesitar usar algunos datos de una base de
datos, pero que no supone probar propiamente las interacciones de la base de datos. Una opción es usar un doble
de prueba como simulacro o imitación.
Los dobles de prueba se usan para las pruebas internas de EF Core. Pero nunca se intentan simular DbContext o
IQueryable. Hacerlo es difícil, engorroso y delicado. No lo haga.
En su lugar, se usa la base de datos en memoria siempre que se realizan pruebas unitarias de algo que usa
DbContext. En este caso, el uso de la base de datos en memoria es adecuado porque la prueba no depende del
comportamiento de la base de datos. Pero no lo haga para probar consultas o actualizaciones reales de la base de
datos.
Vea Pruebas con InMemory para obtener instrucciones específicas de EF Core sobre el uso de la base de datos en
memoria para las pruebas unitarias.
Pruebas con SQLite
11/03/2020 • 5 minutes to read
SQLite tiene un modo en memoria que le permite usar SQLite para escribir pruebas en una base de datos
relacional, sin la sobrecarga de las operaciones de base de datos reales.
TIP
Puede ver el ejemplo de este artículo en github
using System.Collections.Generic;
using System.Linq;
namespace BusinessLogic
{
public class BlogService
{
private BloggingContext _context;
Preparar el contexto
Evite configurar dos proveedores de bases de datos
En las pruebas, va a configurar externamente el contexto para usar el proveedor de inmemory. Si está
configurando un proveedor de base de datos invalidando OnConfiguring en el contexto, deberá agregar código
condicional para asegurarse de que solo se configura el proveedor de base de datos si aún no se ha configurado
ninguno.
TIP
Si usa ASP.NET Core, no necesitará este código, ya que el proveedor de la base de datos se configura fuera del contexto (en
Startup.cs).
TIP
DbContextOptions<TContext> indica al contexto toda su configuración, como la base de datos a la que se va a conectar.
Este es el mismo objeto que se genera al ejecutar el método de configuración en el contexto.
Escribir pruebas
La clave para realizar pruebas con este proveedor es la posibilidad de indicar al contexto que use SQLite y
controlar el ámbito de la base de datos en memoria. El ámbito de la base de datos se controla mediante la
apertura y el cierre de la conexión. La base de datos está en el ámbito de la duración de la conexión abierta.
Normalmente, desea una base de datos limpia para cada método de prueba.
TIP
Para usar SqliteConnection() y el método de extensión .UseSqlite() , haga referencia al paquete NuGet Microsoft.
EntityFrameworkCore. SQLite.
using BusinessLogic;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using Xunit;
namespace EFTesting.TestProject.SQLite
{
{
public class BlogServiceTests
{
[Fact]
public void Add_writes_to_database()
{
// In-memory database only exists while the connection is open
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlite(connection)
.Options;
// Use a separate instance of the context to verify correct data was saved to database
using (var context = new BloggingContext(options))
{
Assert.Equal(1, context.Blogs.Count());
Assert.Equal("https://example.com", context.Blogs.Single().Url);
}
}
finally
{
connection.Close();
}
}
[Fact]
public void Find_searches_url()
{
// In-memory database only exists while the connection is open
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlite(connection)
.Options;
// Insert seed data into the database using one instance of the context
using (var context = new BloggingContext(options))
{
context.Blogs.Add(new Blog { Url = "https://example.com/cats" });
context.Blogs.Add(new Blog { Url = "https://example.com/catfish" });
context.Blogs.Add(new Blog { Url = "https://example.com/dogs" });
context.SaveChanges();
}
El proveedor de inmemory es útil cuando se desea probar los componentes mediante algo que se aproxima a la
conexión a la base de datos real, sin la sobrecarga de las operaciones de base de datos reales.
TIP
Puede ver un ejemplo de este artículo en GitHub.
TIP
Para muchos propósitos de prueba, estas diferencias no serán importantes. Sin embargo, si desea probar algo que se
comporta más como una verdadera base de datos relacional, considere el uso del modo en memoria de SQLite.
namespace BusinessLogic
{
public class BlogService
{
private BloggingContext _context;
Preparar el contexto
Evite configurar dos proveedores de bases de datos
En las pruebas, va a configurar externamente el contexto para usar el proveedor de inmemory. Si está
configurando un proveedor de base de datos invalidando OnConfiguring en el contexto, deberá agregar código
condicional para asegurarse de que solo se configura el proveedor de base de datos si aún no se ha configurado
ninguno.
TIP
Si usa ASP.NET Core, no necesitará este código ya que el proveedor de base de datos ya está configurado fuera del contexto
(en Startup.cs).
TIP
DbContextOptions<TContext> indica al contexto toda su configuración, como la base de datos a la que se va a conectar.
Este es el mismo objeto que se genera al ejecutar el método de configuración en el contexto.
Escribir pruebas
La clave para probar con este proveedor es la capacidad de indicar al contexto que use el proveedor de inmemory
y controlar el ámbito de la base de datos en memoria. Normalmente, desea una base de datos limpia para cada
método de prueba.
Este es un ejemplo de una clase de prueba que usa la base de datos inmemory. Cada método de prueba especifica
un nombre de base de datos único, lo que significa que cada método tiene su propia base de datos inmemory.
TIP
Para usar el método de extensión .UseInMemoryDatabase() , haga referencia al paquete NuGet Microsoft.
EntityFrameworkCore. inmemory.
using BusinessLogic;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using Xunit;
namespace EFTesting.TestProject.InMemory
{
public class BlogServiceTests
{
[Fact]
public void Add_writes_to_database()
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "Add_writes_to_database")
.Options;
// Use a separate instance of the context to verify correct data was saved to database
using (var context = new BloggingContext(options))
{
Assert.Equal(1, context.Blogs.Count());
Assert.Equal("https://example.com", context.Blogs.Single().Url);
}
}
[Fact]
public void Find_searches_url()
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseInMemoryDatabase(databaseName: "Find_searches_url")
.Options;
// Insert seed data into the database using one instance of the context
using (var context = new BloggingContext(options))
{
context.Blogs.Add(new Blog { Url = "https://example.com/cats" });
context.Blogs.Add(new Blog { Url = "https://example.com/catfish" });
context.Blogs.Add(new Blog { Url = "https://example.com/dogs" });
context.SaveChanges();
}
En este artículo se muestran los patrones básicos para configurar un DbContext a través de un DbContextOptions
para conectarse a una base de datos mediante un proveedor de EF Core específico y comportamientos opcionales.
Configuración de DbContextOptions
DbContext debe tener una instancia de DbContextOptions para poder realizar cualquier trabajo. La instancia de
DbContextOptions contiene información de configuración como:
Proveedor de base de datos que se va a usar, normalmente seleccionado mediante la invocación de un método
como UseSqlServer o UseSqlite . Estos métodos de extensión requieren el paquete de proveedor
correspondiente, como Microsoft.EntityFrameworkCore.SqlServer o Microsoft.EntityFrameworkCore.Sqlite . Los
métodos se definen en el espacio de nombres Microsoft.EntityFrameworkCore .
Cualquier cadena de conexión o identificador de la instancia de base de datos que sea necesario, normalmente
se pasa como argumento al método de selección de proveedor mencionado anteriormente
Cualquier selector de comportamiento opcional de nivel de proveedor, normalmente también encadenado
dentro de la llamada al método de selección de proveedor.
Los selectores de comportamiento de EF Core general, normalmente encadenados después o antes del método
de selector de proveedor
En el ejemplo siguiente se configura el DbContextOptions para utilizar el proveedor de SQL Server, una conexión
contenida en la variable connectionString , un tiempo de espera de comando de nivel de proveedor y un selector
de comportamiento de EF Core que hace que todas las consultas ejecutadas en el DbContext sin seguimiento de
forma predeterminada:
optionsBuilder
.UseSqlServer(connectionString, providerOptions=>providerOptions.CommandTimeout(60))
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
NOTE
Los métodos de selector de proveedor y otros métodos de selector de comportamiento mencionados anteriormente son
métodos de extensión en DbContextOptions o clases de opciones específicas del proveedor. Para tener acceso a estos
métodos de extensión, puede que necesite tener un espacio de nombres (normalmente Microsoft.EntityFrameworkCore )
en el ámbito e incluir dependencias de paquetes adicionales en el proyecto.
TIP
El constructor base de DbContext también acepta la versión no genérica de DbContextOptions , pero no se recomienda usar
la versión no genérica para las aplicaciones con varios tipos de contexto.
Ahora la aplicación puede pasar el DbContextOptions al crear una instancia de un contexto, como se indica a
continuación:
Configuración
También puede inicializar la DbContextOptions dentro del propio contexto. Aunque puede usar esta técnica para la
configuración básica, normalmente tendrá que obtener determinados detalles de configuración del exterior, por
ejemplo, una cadena de conexión de base de datos. Esto puede hacerse con una API de configuración o con
cualquier otro medio.
Para inicializar DbContextOptions dentro del contexto, invalide el método OnConfiguring y llame a los métodos en
el DbContextOptionsBuilder proporcionado:
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
Una aplicación simplemente puede crear una instancia de este tipo de contexto sin pasar nada a su constructor:
TIP
Este enfoque no se presta a las pruebas, a menos que las pruebas tengan como destino la base de datos completa.
Esto requiere agregar un argumento de constructor al tipo DbContext que acepte DbContextOptions<TContext> .
Código de contexto:
...
}
Cuando EF Core detecta un intento de usar una instancia de DbContext simultáneamente, verá un
InvalidOperationException con un mensaje similar al siguiente:
Se inició una segunda operación en este contexto antes de que se completara una operación anterior. Esto se
debe normalmente a distintos subprocesos que usan la misma instancia de DbContext; sin embargo, no se
garantiza que los miembros de instancia sean seguros para subprocesos.
Cuando el acceso simultáneo no se detecta, puede dar lugar a un comportamiento indefinido, a bloqueos de la
aplicación y a daños en los datos.
Hay errores comunes que pueden provocar involuntariamente el acceso simultáneo en la misma instancia de
DbContext :
Se olvida esperar la finalización de una operación asincrónica antes de iniciar cualquier otra operación en el
mismo DbContext
Los métodos asincrónicos permiten a EF Core iniciar las operaciones que tienen acceso a la base de datos sin
bloqueos. Pero si el autor de la llamada no espera la finalización de uno de estos métodos y continúa realizando
otras operaciones en el DbContext , el estado de la DbContext puede ser (y probablemente estará) dañado.
Esperar siempre EF Core métodos asincrónicos inmediatamente.
Compartir instancias de DbContext implícitamente entre varios subprocesos mediante la inserción de
dependencias
El método de extensión AddDbContext registra los tipos de DbContext con una duración de ámbito de forma
predeterminada.
Esto es seguro frente a problemas de acceso simultáneos en la mayoría de las aplicaciones ASP.NET Core porque
solo hay un subproceso que ejecuta cada solicitud de cliente en un momento dado, y dado que cada solicitud
obtiene un ámbito de inyección de dependencia independiente (y, por tanto, una instancia de DbContext
independiente). Para el modelo de hospedaje de servidor increíble, se usa una solicitud lógica para mantener el
circuito de usuario increíble y, por lo tanto, solo hay una instancia de DbContext con ámbito disponible por circuito
de usuario si se usa el ámbito de inyección predeterminado.
Cualquier código que ejecute explícitamente varios subprocesos en paralelo debe asegurarse de que no se tiene
acceso a las instancias de DbContext simultáneamente.
Mediante la inserción de dependencias, esto se puede lograr registrando el contexto como ámbito y creando
ámbitos (mediante IServiceScopeFactory ) para cada subproceso, o registrando el DbContext como transitorio
(mediante la sobrecarga de AddDbContext que toma un parámetro ServiceLifetime ).
Más lecturas
Lea inserción de dependencias para obtener más información sobre el uso de di.
Lea pruebas para obtener más información.
Trabajar con tipos de referencia que aceptan valores
NULL
11/03/2020 • 10 minutes to read
C#8 presentó una nueva característica denominada tipos de referencia que aceptan valores NULL, lo que permite
anotar tipos de referencia, lo que indica si es válido que contengan null o not. Si no está familiarizado con esta
característica, se recomienda que se familiarice con ella leyendo los C# documentos.
En esta página se presenta la compatibilidad de EF Core con tipos de referencia que aceptan valores NULL y se
describen los procedimientos recomendados para trabajar con ellos.
NOTE
Tenga cuidado al habilitar los tipos de referencia que aceptan valores NULL en un proyecto existente: las propiedades de tipo
de referencia que se configuraron anteriormente como opcional ahora se configurarán según sea necesario, a menos que se
anoten explícitamente para que acepten valores NULL. Al administrar un esquema de base de datos relacional, esto puede
provocar que se generen migraciones que modifiquen la nulabilidad de la columna de la base de datos.
DbContext y DbSet
Cuando se habilitan los tipos de referencia que C# aceptan valores NULL, el compilador emite advertencias para
cualquier propiedad no inicializada que no acepte valores NULL, ya que estos contendrían el valor null. Como
resultado, la práctica común de definir un DbSet que no acepta valores NULL en un contexto generará ahora una
advertencia. Sin embargo, EF Core siempre inicializa todas las propiedades DbSet en tipos derivados de
DbContext, por lo que se garantiza que nunca serán null, incluso si el compilador no es consciente de esto. Por lo
tanto, se recomienda mantener las propiedades de DbSet que no admitan valores NULL, lo que le permite tener
acceso a ellas sin comprobaciones nulas, y para silenciar las advertencias del compilador estableciéndolo
explícitamente en NULL con la ayuda del operador null-permisivo (!):
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 PropertyAccessMode.Field 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 (!):
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.
Se produce un problema similar al incluir varios niveles de relaciones entre las navegaciones opcionales:
Si tiene que hacer esto mucho y los tipos de entidad en cuestión son principalmente (o exclusivamente) usados en
consultas de EF Core, considere la posibilidad de hacer que las propiedades de navegación no acepten valores
NULL y configurarlas como opcionales a través de la API fluida o las anotaciones de datos. Esto quitará todas las
advertencias del compilador manteniendo la relación opcional; sin embargo, si las entidades se recorren fuera de
EF Core, puede observar valores NULL, aunque las propiedades se anotan como que no aceptan valores NULL.
Limitaciones
La ingeniería inversa no admite C# C# actualmente 8 tipos de referencia que aceptan valores NULL (NRTs): EF
Core siempre genera código que supone que la característica está desactivada. Por ejemplo, las columnas de
texto que aceptan valores NULL se scaffolding como una propiedad con el tipo string , no string? , con la API
fluida o las anotaciones de datos que se usan para configurar si una propiedad es obligatoria o no. Puede editar
el código con scaffolding y reemplazarlo con anotaciones de C# nulabilidad. El seguimiento de la
compatibilidad con scaffolding para tipos de referencia que aceptan valores NULL se realiza mediante el
problema #15520.
La superficie de la API pública de EF Core todavía no se ha anotado para la nulabilidad (la API pública es "null-
desconocen"), lo que a veces es difícil de usar cuando la característica NRT está activada. Esto incluye
principalmente los operadores Async LINQ expuestos por EF Core, como FirstOrDefaultAsync. Tenemos
previsto abordar esto para la versión 5,0.
Creación y configuración de un modelo
08/04/2020 • 2 minutes to read
Entity Framework usa un conjunto de convenciones para compilar un modelo basado en la forma de las clases de
entidad. Puede especificar una configuración adicional para complementar o reemplazar lo que se ha detectado
por convención.
Este artículo trata de la configuración que se puede aplicar a un modelo para cualquier almacén de datos y que se
puede aplicar al elegir como destino cualquier base de datos relacional. Los proveedores también pueden habilitar
la configuración específica de un almacén de datos determinado. Para obtener documentación sobre la
configuración específica del proveedor, vea la sección Proveedores de bases de datos .
TIP
Puede ver un ejemplo de este artículo en GitHub.
using Microsoft.EntityFrameworkCore;
namespace EFModeling.FluentAPI.Required
{
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
#region Required
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Blog>()
.Property(b => b.Url)
.IsRequired();
}
#endregion
}
namespace EFModeling.DataAnnotations.Required
{
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
#region Required
public class Blog
{
public int BlogId { get; set; }
[Required]
public string Url { get; set; }
}
#endregion
}
Tipos de entidad
11/03/2020 • 4 minutes to read
La inclusión de un DbSet de un tipo en el contexto significa que se incluye en el modelo de EF Core. normalmente
hacemos referencia a este tipo como una entidad. EF Core puede leer y escribir instancias de entidad desde y hacia
la base de datos, y si está utilizando una base de datos relacional, EF Core puede crear tablas para las entidades a
través de migraciones.
[NotMapped]
public class BlogMetadata
{
public DateTime LoadedFromDatabase { get; set; }
}
Nombre de la tabla
Por Convención, cada tipo de entidad se configurará para asignarse a una tabla de base de datos con el mismo
nombre que la propiedad DbSet que expone la entidad. Si no existe ningún DbSet para la entidad especificada, se
utiliza el nombre de clase.
Puede configurar manualmente el nombre de la tabla:
Anotaciones de datos
API fluida
[Table("blogs")]
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
}
Esquema de tabla
Al utilizar una base de datos relacional, las tablas se crean por Convención en el esquema predeterminado de la
base de datos. Por ejemplo, Microsoft SQL Server usará el esquema de dbo (SQLite no admite esquemas).
Puede configurar las tablas que se van a crear en un esquema específico de la siguiente manera:
Anotaciones de datos
API fluida
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:
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.
[NotMapped]
public DateTime LoadedFromDatabase { get; set; }
}
Nombres de columna
Por Convención, cuando se utiliza una base de datos relacional, las propiedades de entidad se asignan a las
columnas de la tabla que tienen el mismo nombre que la propiedad.
Si prefiere configurar las columnas con nombres diferentes, puede hacerlo de la siguiente manera:
Anotaciones de datos
API fluida
También puede configurar las columnas para especificar un tipo de datos exacto para una columna. Por ejemplo, el
código siguiente configura Url como una cadena no Unicode con una longitud máxima de 200 y Rating como
decimal con precisión de 5 y la escala de 2 :
Anotaciones de datos
API fluida
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
Se recomienda el uso de tipos de referencia que aceptan valores NULL, ya que C# fluye la nulabilidad expresada en
el código para EF Core modelo y en la base de datos, e obvia el uso de las anotaciones de datos o la API fluida para
expresar el mismo concepto dos veces.
NOTE
Tenga cuidado al habilitar los tipos de referencia que aceptan valores NULL en un proyecto existente: las propiedades de tipo
de referencia que se configuraron anteriormente como opcional ahora se configurarán según sea necesario, a menos que se
anoten explícitamente para que acepten valores NULL. Al administrar un esquema de base de datos relacional, esto puede
provocar que se generen migraciones que modifiquen la nulabilidad de la columna de la base de datos.
Para obtener más información sobre los tipos de referencia que aceptan valores NULL y cómo usarlos con EF Core,
consulte la página de documentación dedicada para esta característica.
Configuración explícita
Una propiedad que sería opcional por Convención se puede configurar para que sea necesaria de la siguiente
manera:
Anotaciones de datos
API fluida
Una clave actúa como identificador único para cada instancia de la entidad. La mayoría de las entidades de EF
tienen una sola clave, que se asigna al concepto de una clave principal en las bases de datos relacionales (para
entidades sin claves, vea entidadessin clave). Las entidades pueden tener claves adicionales más allá de la clave
principal (consulte claves alternativas para obtener más información).
Por Convención, una propiedad denominada Id o <type name>Id se configurará como la clave principal de una
entidad.
class Car
{
public string Id { get; set; }
class Truck
{
public string TruckId { get; set; }
NOTE
Los tipos de entidad de propiedad usan reglas diferentes para definir claves.
Puede configurar una única propiedad para que sea la clave principal de una entidad, como se indica a
continuación:
Anotaciones de datos
API fluida
class Car
{
[Key]
public string LicensePlate { get; set; }
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)
{
modelBuilder.Entity<Car>()
.HasKey(c => new { c.State, c.LicensePlate });
}
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.
También puede configurar una sola propiedad para que sea una clave alternativa:
También puede configurar varias propiedades para que sean una clave alternativa (conocida como clave alternativa
compuesta):
Por último, por Convención, el índice y la restricción que se introducen para una clave alternativa se denominarán
AK_<type name>_<property name> (para las claves alternativas compuestas <property name> se convierte en una lista
de nombres de propiedad separados por un carácter de subrayado). Puede configurar el nombre del índice de la
clave alternativa y la restricción UNIQUE:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.HasAlternateKey(c => c.LicensePlate)
.HasName("AlternateKey_LicensePlate");
}
Valores generados
11/03/2020 • 10 minutes to read
WARNING
La forma en que se genera el valor para las entidades agregadas dependerá del proveedor de base de datos que se use. Los
proveedores de bases de datos pueden configurar automáticamente la generación de valores para algunos tipos de
propiedad, pero otros pueden requerir que se configure manualmente cómo se genera el valor.
Por ejemplo, al usar SQL Server, se generarán automáticamente valores para las propiedades de GUID (mediante el
algoritmo GUID secuencial SQL Server). Sin embargo, si especifica que se genere una propiedad DateTime en Add, debe
configurar una manera de que se generen los valores. Una manera de hacerlo es configurar un valor predeterminado de
GETDATE() , vea valores predeterminados.
UPDATE dbo.Blogs
SET LastUpdated = GETDATE()
WHERE BlogId = @Id
END
WARNING
Esto solo permite que EF sepa que los valores se generan para las entidades agregadas, no garantiza que EF configurará el
mecanismo real para generar valores. Vea la sección valor generado al agregar para obtener más detalles.
Valores predeterminados
En las bases de datos relacionales, una columna se puede configurar con un valor predeterminado. Si se inserta
una fila sin un valor para esa columna, se usará el valor predeterminado.
Puede configurar un valor predeterminado en una propiedad:
También puede especificar un fragmento de SQL que se usa para calcular el valor predeterminado:
WARNING
Esto solo permite que EF sepa que se generan valores para entidades agregadas o actualizadas, no garantiza que EF
configure el mecanismo real para generar valores. Vea la sección valor generado al agregar o actualizar para obtener más
detalles.
Columnas calculadas
En algunas bases de datos relacionales, una columna se puede configurar para que se calcule su valor en la base
de datos, normalmente con una expresión que haga referencia a otras columnas:
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
[ConcurrencyCheck]
public string LastName { 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
[Timestamp]
public byte[] Timestamp { get; set; }
}
Propiedades reemplazadas
11/03/2020 • 4 minutes to read
Las propiedades de sombra son propiedades que no están definidas en la clase de entidad de .NET pero que se
definen para ese tipo de entidad en el modelo de EF Core. El valor y el estado de estas propiedades se mantienen
únicamente en el seguimiento de cambios. Las propiedades Shadow son útiles cuando hay datos en la base de
datos que no se deben exponer en los tipos de entidad asignados.
Si el nombre proporcionado al método Property coincide con el nombre de una propiedad existente (una
propiedad Shadow o una definida en la clase de entidad), el código configurará esa propiedad existente en lugar
de introducir una nueva propiedad Shadow.
context.Entry(myBlog).Property("LastUpdated").CurrentValue = DateTime.Now;
Se puede hacer referencia a las propiedades Shadow en consultas LINQ a través del método estático
EF.Property :
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; }
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.
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 .
En este ejemplo, la clave externa de la sombra se BlogId porque, si se antepone el nombre de navegación, sería
redundante.
NOTE
Si ya existe una propiedad con el mismo nombre, el nombre de la propiedad Shadow tendrá como sufijo un número.
Limitaciones
Cuando hay varias propiedades de navegación definidas entre dos tipos (es decir, más de un par de navegaciones
que apuntan entre sí), las relaciones representadas por las propiedades de navegación son ambiguas. Tendrá que
configurarlos manualmente para resolver la ambigüedad.
Eliminación en cascada
Por Convención, la eliminación en cascada se establecerá en Cascade para las relaciones necesarias y ClientSetNull
para las relaciones opcionales. Cascade significa que las entidades dependientes también se eliminan. ClientSetNull
significa que las entidades dependientes que no se cargan en la memoria permanecerán sin cambios y deben
eliminarse manualmente o actualizarse para que apunten a una entidad principal válida. En el caso de las entidades
que se cargan en memoria, EF Core intentará establecer las propiedades de clave externa en NULL.
Vea la sección relaciones obligatorias y opcionales para ver la diferencia entre las relaciones obligatorias y
opcionales.
Consulte eliminación en cascada para obtener más detalles sobre los distintos comportamientos de eliminación y
los valores predeterminados que usa la Convención.
Configuración manual
API fluida
Anotaciones de datos
Para configurar una relación en la API fluida, empiece por identificar las propiedades de navegación que componen
la relación. HasOne o HasMany identifica la propiedad de navegación en el tipo de entidad en el que se está
iniciando la configuración. A continuación, encadenar una llamada a WithOne o WithMany para identificar la
navegación inversa. HasOne / WithOne se utilizan para las propiedades de navegación de referencia y HasMany /
WithMany se utilizan para las propiedades de navegación de la colección.
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
Clave principal
Si desea que la clave externa haga referencia a una propiedad que no sea la clave principal, puede usar la API fluida
para configurar la propiedad de clave principal de la relación. La propiedad que se configura como clave principal
se configurará automáticamente como una clave alternativa.
Clave simple
Clave compuesta
class MyContext : DbContext
{
public DbSet<Car> Cars { get; set; }
NOTE
La llamada a IsRequired(false) también hace que la propiedad de clave externa sea opcional a menos que esté
configurada de otro modo.
Eliminación en cascada
Puede usar la API fluida para configurar explícitamente el comportamiento de eliminación en cascada para una
relación determinada.
Consulte la eliminación en cascada para obtener una explicación detallada de cada opción.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasOne(p => p.Blog)
.WithMany(b => b.Posts)
.OnDelete(DeleteBehavior.Cascade);
}
NOTE
EF elegirá una de las entidades como dependiente en función de su capacidad para detectar una propiedad de clave externa.
Si se elige la entidad equivocada como dependiente, puede usar la API fluida para corregir este problema.
Al configurar la relación con la API fluida, se usan los métodos HasOne y WithOne .
Al configurar la clave externa, debe especificar el tipo de entidad dependiente: Observe que el parámetro genérico
proporcionado a HasForeignKey en la siguiente lista. En una relación uno a varios, es evidente que la entidad con la
navegación de referencia es el dependiente y el que tiene la colección es la entidad de seguridad. Pero esto no es
así en una relación uno a uno; por lo tanto, la necesidad de definirla explícitamente.
class MyContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<BlogImage> BlogImages { get; set; }
Varios a varios
Todavía no se admiten las relaciones de varios a varios sin una clase de entidad para representar la tabla de
combinación. Sin embargo, puede representar una relación de varios a varios incluyendo una clase de entidad para
la tabla de combinación y asignando dos relaciones uno a varios independientes.
class MyContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId);
modelBuilder.Entity<PostTag>()
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId);
}
}
Los índices son un concepto común en muchos almacenes de datos. Aunque su implementación en el almacén de
datos puede variar, se usan para realizar búsquedas basadas en una columna (o conjunto de columnas) más
eficaces.
Los índices no se pueden crear con anotaciones de datos. Puede usar la API fluida para especificar un índice en una
sola columna de la siguiente manera:
NOTE
Por Convención, se crea un índice en cada propiedad (o conjunto de propiedades) que se usa como clave externa.
EF Core solo admite un índice por conjunto de propiedades distinto. Si usa la API fluida para configurar un índice en un
conjunto de propiedades que ya tiene definido un índice, ya sea por Convención o por configuración anterior, cambiará la
definición de ese índice. Esto resulta útil si desea seguir configurando un índice creado por la Convención.
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.
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:
Al usar el proveedor de SQL Server EF agrega un filtro de 'IS NOT NULL' para todas las columnas que aceptan
valores NULL que forman parte de un índice único. Para invalidar esta Convención, puede proporcionar un valor
null .
Columnas incluidas
Algunas bases de datos relacionales permiten configurar un conjunto de columnas que se incluyen en el índice,
pero que no forman parte de su "clave". Esto puede mejorar significativamente el rendimiento de las consultas
cuando todas las columnas de la consulta se incluyen en el índice como columnas de clave o sin clave, ya que no es
necesario tener acceso a la tabla en sí. Para obtener más información sobre SQL Server columnas incluidas, vea la
documentaciónde.
En el ejemplo siguiente, la columna Url forma parte de la clave de índice, por lo que cualquier filtrado de
consultas en esa columna puede utilizar el índice. Pero además, las consultas que solo tienen acceso a las columnas
Title y PublishedOn no tendrán que acceder a la tabla y se ejecutarán de forma más eficaz:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasIndex(p => p.Url)
.IncludeProperties(p => new
{
p.Title,
p.PublishedOn
});
}
Herencia
11/03/2020 • 5 minutes to read
EF puede asignar una jerarquía de tipos .NET a una base de datos. Esto le permite escribir las entidades .NET en el
código como de costumbre, con los tipos base y derivados, y hacer que EF cree sin problemas el esquema de base
de datos adecuado, las consultas de problemas, etc. Los detalles reales de cómo se asigna una jerarquía de tipos
dependen del proveedor; en esta página se describe la compatibilidad de herencia en el contexto de una base de
datos relacional.
En este momento, EF Core solo admite el patrón de tabla por jerarquía (TPH). TPH usa una sola tabla para
almacenar los datos de todos los tipos de la jerarquía y se usa una columna de discriminador para identificar qué
tipo representa cada fila.
NOTE
Los EF Core no admiten la tabla por tipo (TPT) y la tabla por tipo específico (TPC), que son compatibles con EF6. TPT es una
característica importante planeada para EF Core 5,0.
Este modelo se asignará al siguiente esquema de la base de datos (tenga en cuenta la columna discriminada
creada implícitamente, que identifica qué tipo de blog se almacena en cada fila):
NOTE
Las columnas de la base de datos se convierten en NULL automáticamente según sea necesario al usar la asignación TPH.
Por ejemplo, la columna RssUrl admite valores NULL porque las instancias de blog normales no tienen esa propiedad.
Si no desea exponer un DbSet para una o varias entidades de la jerarquía, también puede usar la API fluida para
asegurarse de que se incluyen en el modelo.
TIP
Si no se basa en las convenciones, puede especificar explícitamente el tipo base mediante HasBaseType . También puede usar
.HasBaseType((Type)null) para quitar un tipo de entidad de la jerarquía.
Configuración de discriminador
Puede configurar el nombre y el tipo de la columna discriminadora y los valores que se usan para identificar cada
tipo en la jerarquía:
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:
Por último, el discriminador también se puede asignar a una propiedad .NET normal en la entidad:
modelBuilder.Entity<Blog>()
.Property(e => e.BlogType)
.HasMaxLength(200)
.HasColumnName("blog_type");
}
Columnas compartidas
De forma predeterminada, cuando dos tipos de entidad del mismo nivel en la jerarquía tienen una propiedad con
el mismo nombre, se asignarán a dos columnas independientes. Sin embargo, si su tipo es idéntico, se pueden
asignar a la misma columna de base de datos:
public class MyContext : DbContext
{
public DbSet<BlogBase> Blogs { get; set; }
modelBuilder.Entity<RssBlog>()
.Property(b => b.Url)
.HasColumnName("Url");
}
}
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:
modelBuilder.Entity<Order>()
.Property(o => o.OrderNo)
.HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers");
}
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.
Los campos de respaldo permiten a EF leer o escribir en un campo en lugar de una propiedad. Esto puede ser útil
cuando se usa la encapsulación en la clase para restringir el uso de y/o mejorar la semántica en torno al acceso a
los datos por código de aplicación, pero el valor debe leerse o escribirse en la base de datos sin usar esas
restricciones o mejoras.
Configuración básica
Por Convención, se detectarán los campos siguientes como campos de respaldo para una propiedad determinada
(que se muestra en orden de prioridad).
_<camel-cased property name>
_<property name>
m_<camel-cased property name>
m_<property name>
En el ejemplo siguiente, la propiedad Url está configurada para tener _url como campo de respaldo:
Tenga en cuenta que los campos de respaldo solo se detectan para las propiedades que se incluyen en el modelo.
Para obtener más información sobre las propiedades que se incluyen en el modelo, vea incluir & excluyendo las
propiedades.
También puede configurar los campos de respaldo explícitamente, por ejemplo, si el nombre del campo no se
corresponde con las convenciones anteriores:
NOTE
Con EF Core 3,0, el modo de acceso de propiedad predeterminado cambió de PreferFieldDuringConstruction a
PreferField .
_validatedUrl = url;
}
}
EF intentará buscar una propiedad CLR con el nombre especificado o un campo si no se encuentra una propiedad.
Si no se encuentra ninguna propiedad ni un campo, se configurará una propiedad Shadow en su lugar.
Es posible que tenga que hacer referencia a una propiedad de solo campo desde las consultas LINQ, pero estos
campos suelen ser privados. Puede usar el método EF.Property(...) en una consulta LINQ para hacer referencia
al campo:
NOTE
Esta característica es nueva en EF Core 2.1.
Los convertidores de valores permiten convertir los valores de propiedad al leer o escribir en la base de datos.
Esta conversión puede ser de un valor a otro del mismo tipo (por ejemplo, cifrar cadenas) o de un valor de un
tipo a un valor de otro tipo (por ejemplo, convertir valores de enumeración en cadenas en la base de datos y
desde ellas).
Aspectos básicos
Los convertidores de valores se especifican en términos de un ModelClrType y un ProviderClrType . El tipo de
modelo es el tipo .NET de la propiedad en el tipo de entidad. El tipo de proveedor es el tipo .NET que entiende el
proveedor de base de datos. Por ejemplo, para guardar las enumeraciones como cadenas en la base de datos, el
tipo de modelo es el tipo de la enumeración y el tipo de proveedor es String . Estos dos tipos pueden ser iguales.
Las conversiones se definen utilizando dos Func árboles de expresión: uno de ModelClrType a ProviderClrType
y otro de ProviderClrType a ModelClrType . Los árboles de expresión se usan para que se puedan compilar en el
código de acceso a la base de datos para conversiones eficientes. En las conversiones complejas, el árbol de
expresión puede ser una llamada simple a un método que realiza la conversión.
A continuación, se pueden definir conversiones en OnModelCreating para almacenar los valores de enumeración
como cadenas (por ejemplo, "Donkey", "Mule",...) en la base de datos:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder
.Entity<Rider>()
.Property(e => e.Mount)
.HasConversion(
v => v.ToString(),
v => (EquineBeast)Enum.Parse(typeof(EquineBeast), v));
}
NOTE
Un valor null nunca se pasará a un convertidor de valores. Esto hace que la implementación de conversiones sea más
sencilla y permite que se compartan entre propiedades que aceptan valores NULL y que no aceptan valores NULL.
La clase ValueConverter
Al llamar a HasConversion como se muestra anteriormente, se creará una instancia de ValueConverter y se
establecerá en la propiedad. En su lugar, se puede crear el ValueConverter explícitamente. Por ejemplo:
modelBuilder
.Entity<Rider>()
.Property(e => e.Mount)
.HasConversion(converter);
Esto puede ser útil cuando varias propiedades usan la misma conversión.
NOTE
Actualmente no hay ninguna manera de especificar en un lugar que cada propiedad de un tipo determinado debe usar el
mismo convertidor de valores. Esta característica se considerará para futuras versiones.
Convertidores integrados
EF Core incluye un conjunto de clases de ValueConverter predefinidas, que se encuentran en el espacio de
nombres Microsoft.EntityFrameworkCore.Storage.ValueConversion . Dichos componentes son:
BoolToZeroOneConverter -bool a cero y uno
BoolToStringConverter -bool a cadenas como "Y" y "N"
BoolToTwoValuesConverter -bool a dos valores cualesquiera
matriz de bytes de BytesToStringConverter a una cadena codificada en Base64
conversiones de CastingConverter que requieren solo una conversión de tipos
CharToStringConverter -char a una cadena de un solo carácter
DateTimeOffsetToBinaryConverter -DateTimeOffset a un valor codificado en binario 64 bits
DateTimeOffsetToBytesConverter -DateTimeOffset a la matriz de bytes
DateTimeOffsetToStringConverter -DateTimeOffset a cadena
DateTimeToBinaryConverter : DateTime al valor de 64 bits, incluido DateTimeKind
DateTimeToStringConverter : fecha y hora en cadena
DateTimeToTicksConverter : fecha y hora en pasos
EnumToNumberConverter -enum al número subyacente
EnumToStringConverter -enum a cadena
GuidToBytesConverter -GUID a una matriz de bytes
GuidToStringConverter -GUID a cadena
NumberToBytesConverter : cualquier valor numérico en una matriz de bytes
NumberToStringConverter : cualquier valor numérico a cadena
StringToBytesConverter : cadena en bytes UTF8
TimeSpanToStringConverter -TimeSpan a String
TimeSpanToTicksConverter -TimeSpan a ticks
Observe que EnumToStringConverter se incluye en esta lista. Esto significa que no es necesario especificar la
conversión explícitamente, como se muestra anteriormente. En su lugar, use simplemente el convertidor
integrado:
modelBuilder
.Entity<Rider>()
.Property(e => e.Mount)
.HasConversion(converter);
Tenga en cuenta que todos los convertidores integrados no tienen estado y, por tanto, una sola instancia puede
compartirse de forma segura con varias propiedades.
Conversiones predefinidas
En el caso de las conversiones comunes para las que existe un convertidor integrado, no es necesario especificar
el convertidor explícitamente. En su lugar, solo tiene que configurar el tipo de proveedor que se debe usar y EF
usará automáticamente el convertidor integrado adecuado. La enumeración de las conversiones de cadenas se
usa como ejemplo anterior, pero EF lo hará automáticamente si se configura el tipo de proveedor:
modelBuilder
.Entity<Rider>()
.Property(e => e.Mount)
.HasConversion<string>();
Lo mismo se puede lograr especificando explícitamente el tipo de columna. Por ejemplo, si el tipo de entidad se
define de la manera siguiente:
[Column(TypeName = "nvarchar(24)")]
public EquineBeast Mount { get; set; }
}
A continuación, los valores de enumeración se guardarán como cadenas en la base de datos sin ninguna otra
configuración en OnModelCreating .
Limitaciones
Hay algunas limitaciones actuales conocidas del sistema de conversión de valores:
Como se indicó anteriormente, no se puede convertir null .
Actualmente no hay ninguna manera de distribuir una conversión de una propiedad a varias columnas o
viceversa.
El uso de conversiones de valores puede afectar a la capacidad de EF Core para traducir expresiones a SQL. Se
registrará una advertencia para dichos casos. La eliminación de estas limitaciones se está considerando en una
versión futura.
Propagación de datos
11/03/2020 • 7 minutes to read
La propagación de datos es el proceso de rellenar una base de datos con un conjunto inicial de datos.
Hay varias maneras de lograrlo en EF Core:
Datos de inicialización del modelo
Personalización de la migración manual
Lógica de inicialización personalizada
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.
Para agregar entidades que tienen una relación, es necesario especificar los valores de clave externa:
modelBuilder.Entity<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:
modelBuilder.Entity<Post>().HasData(
new { BlogId = 1, PostId = 2, Title = "Second post", Content = "Test 2" });
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 context.Database.EnsureCreated() para crear una nueva base de datos que contenga
los datos de inicialización, por ejemplo, para una base de datos de prueba o cuando se usa el proveedor en
memoria o cualquier base de datos que no sea de relación. Tenga en cuenta que si la base de datos ya existe,
EnsureCreated() no actualizará el esquema ni los datos de inicialización en la base de datos. En el caso de las
bases de datos relacionales, no debe llamar a EnsureCreated() si tiene previsto usar las migraciones.
Limitaciones de los datos de inicialización del modelo
Este tipo de datos de inicialización se administra mediante migraciones y el script para actualizar los datos que ya
están en la base de datos debe generarse sin necesidad de conectarse a la base de datos. Esto impone algunas
restricciones:
El valor de clave principal debe especificarse incluso si la base de datos lo genera normalmente. Se usará para
detectar los cambios de datos entre las migraciones.
Los datos previamente inicializados se quitarán si se cambia la clave principal de cualquier manera.
Por lo tanto, esta característica es muy útil para los datos estáticos que no se espera que cambien fuera de las
migraciones y no depende de nada más en la base de datos, por ejemplo códigos postales.
Si el escenario incluye alguno de los siguientes, se recomienda usar la lógica de inicialización personalizada que se
describe en la última sección:
Datos temporales para pruebas
Datos que dependen del estado de la base de datos
Datos que necesitan que la base de datos genere valores clave, incluidas las entidades que usan claves
alternativas como identidad.
Datos que requieren una transformación personalizada (que no se controlan mediante conversiones de
valores), como algunas operaciones hash de contraseñas.
Datos que requieren llamadas a la API externa, como ASP.NET Core roles de identidad y la creación de usuarios
migrationBuilder.InsertData(
table: "Blogs",
columns: new[] { "Url" },
values: new object[] { "http://generated.com" });
Lógica de inicialización personalizada
Una manera sencilla y eficaz de realizar la propagación de datos es usar DbContext.SaveChanges() antes de que la
lógica de la aplicación principal comience la ejecución.
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.
NOTE
Esta característica es nueva en EF Core 2.1.
A partir de EF Core 2,1, ahora es posible definir un constructor con parámetros y EF Core llamar a este constructor
al crear una instancia de la entidad. Los parámetros de constructor se pueden enlazar a propiedades asignadas o a
varios tipos de servicios para facilitar comportamientos como la carga diferida.
NOTE
A partir de EF Core 2,1, todos los enlaces de constructor son por Convención. La configuración de constructores específicos
que se va a usar está planeada para una versión futura.
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;
}
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;
modelBuilder.Entity<Post>(
b =>
{
b.HasKey("_id");
b.Property(e => e.Title);
b.Property(e => e.PostedOn);
});
}
Insertar servicios
EF Core también puede insertar "servicios" en el constructor de un tipo de entidad. Por ejemplo, se puede insertar
lo siguiente:
DbContext : la instancia de contexto actual, que también se puede escribir como el tipo de DbContext derivado.
ILazyLoader -el servicio de carga diferida, consulte la documentación sobre la carga diferida para obtener más
detalles.
Action<object, string> : un delegado de carga diferida; consulte la documentación sobre la carga diferida para
obtener más detalles.
IEntityType : los metadatos de EF Core asociados a este tipo de entidad
NOTE
A partir de EF Core 2,1, solo se pueden insertar los servicios conocidos por EF Core. Se está considerando la compatibilidad
con la inserción de servicios de aplicación en una versión futura.
Por ejemplo, un DbContext insertado se puede usar para tener acceso de forma selectiva a la base de datos para
obtener información sobre las entidades relacionadas sin cargarlas todas. En el ejemplo siguiente, se usa para
obtener el número de publicaciones en un blog sin cargar las entradas:
public class Blog
{
public Blog()
{
}
WARNING
Inyectar DbContext como esto se suele considerar un anti-patrón, ya que une los tipos de entidad directamente a EF Core.
Tenga en cuenta todas las opciones antes de usar la inserción de servicios como esta.
División de tablas
11/03/2020 • 3 minutes to read
EF Core permite asignar dos o más entidades a una sola fila. Esto se denomina División de tablas o uso
compartido de tablas.
Configuración
Para usar la división de tablas, los tipos de entidad deben asignarse a la misma tabla, tener las claves principales
asignadas a las mismas columnas y al menos una relación configurada entre la clave principal de un tipo de
entidad y otra en la misma tabla.
Un escenario común para la división de tablas es usar solo un subconjunto de las columnas de la tabla para un
mayor rendimiento o encapsulación.
En este ejemplo Order representa un subconjunto de DetailedOrder .
modelBuilder.Entity<DetailedOrder>(dob =>
{
dob.ToTable("Orders");
dob.Property(o => o.Status).HasColumnName("Status");
});
modelBuilder.Entity<Order>(ob =>
{
ob.ToTable("Orders");
ob.Property(o => o.Status).HasColumnName("Status");
ob.HasOne(o => o.DetailedOrder).WithOne()
.HasForeignKey<DetailedOrder>(o => o.Id);
});
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:
context.Add(new Order
{
Status = OrderStatus.Pending,
DetailedOrder = new DetailedOrder
{
Status = OrderStatus.Pending,
ShippingAddress = "221 B Baker St, London",
BillingAddress = "11 Wall Street, New York"
}
});
context.SaveChanges();
}
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:
modelBuilder.Entity<Order>()
.Property<byte[]>("Version").IsRowVersion().HasColumnName("Version");
modelBuilder.Entity<DetailedOrder>()
.Property(o => o.Version).IsRowVersion().HasColumnName("Version");
Tipos de entidad en propiedad
11/03/2020 • 16 minutes to read
EF Core permite a tipos de entidad del modelo que sólo pueden aparecer en las propiedades de navegación de
otros tipos de entidad. Se denominan tipos de entidad de propiedad. La entidad que contiene un tipo de entidad
propiedad es su propietario.
Las entidades propiedad son esencialmente parte del propietario y no pueden existir sin ella, son
conceptualmente similares a los agregados. Esto significa que la entidad propiedad es por definición en el lado
dependiente de la relación con el propietario.
Configuración explícita
Propiedad de entidad tipos nunca se incluyen por EF Core en el modelo por convención. Puede usar el método
OwnsOne en OnModelCreating o anotar el tipo con OwnedAttribute (novedad en EF Core 2,1) para configurar el
tipo como un tipo de propiedad.
En este ejemplo, StreetAddress es un tipo sin propiedad de identidad. Se usa como propiedad del tipo Order
para especificar la dirección de envío de un pedido en concreto.
Podemos usar el OwnedAttribute para tratarlo como una entidad propiedad cuando se hace referencia a él desde
otro tipo de entidad:
[Owned]
public class StreetAddress
{
public string Street { get; set; }
public string City { get; set; }
}
También es posible usar el método OwnsOne de OnModelCreating para especificar que la propiedad
ShippingAddress es una entidad propiedad del tipo de entidad Order y para configurar aspectos adicionales si
es necesario.
Si la propiedad ShippingAddress es privada en el tipo de Order , puede usar la versión de cadena del método
OwnsOne :
modelBuilder.Entity<Order>().OwnsOne(typeof(StreetAddress), "ShippingAddress");
De forma predeterminada, la clave principal que se usa para el tipo de propiedad al que se hace referencia a
través de la propiedad de navegación de ShippingCenters se ("DistributorId", "Id") donde "DistributorId"
es el FK y "Id" es un valor de int único.
Para configurar una llamada PK diferente HasKey :
modelBuilder.Entity<Distributor>().OwnsMany(p => p.ShippingCenters, a =>
{
a.WithOwner().HasForeignKey("OwnerId");
a.Property<int>("Id");
a.HasKey("Id");
});
NOTE
Antes de que el método EF Core 3,0 WithOwner() no existiera, se debe quitar esta llamada. Además, la clave principal no
se detectó automáticamente, por lo que siempre se especificó.
modelBuilder.Entity<Order>().OwnsOne(
o => o.ShippingAddress,
sa =>
{
sa.Property(p => p.Street).HasColumnName("ShipsToStreet");
sa.Property(p => p.City).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.
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.
Cada navegación a un tipo de propiedad define un tipo de entidad independiente con una configuración
completamente independiente.
Además de los tipos de propiedad anidados, un tipo de propiedad puede hacer referencia a una entidad normal,
puede ser el propietario o una entidad distinta, siempre y cuando la entidad propiedad esté en el lado
dependiente. Esta funcionalidad establece tipos de entidad de propiedad además de tipos complejos en EF6.
Es posible encadenar el método OwnsOne en una llamada fluida para configurar este modelo:
Observe la llamada WithOwner utilizada para configurar la propiedad de navegación que señala hacia atrás en el
propietario. Para configurar una navegación al tipo de entidad Owner que no forma parte de la relación de
propiedad WithOwner() se debe llamar a sin ningún argumento.
Es posible lograr el resultado mediante OwnedAttribute en OrderDetails y StreetAddress .
También es posible usar el TableAttribute para lograr esto, pero tenga en cuenta que esto produciría un error si
hay varias navegaciones al tipo de propiedad, ya que en ese caso se asignarían varios tipos de entidad a la misma
tabla.
Limitaciones
Algunas de estas limitaciones son fundamentales para el funcionamiento de los tipos de entidad de propiedad,
pero otras son restricciones que podríamos ser capaces de quitar en versiones futuras:
Restricciones por diseño
No se puede crear un DbSet<T> para un tipo de propiedad
No se puede llamar a Entity<T>() con un tipo de propiedad en ModelBuilder
Deficiencias actuales
Los tipos de entidad de propiedad no pueden tener jerarquías de herencia
Las navegaciones de referencia a tipos de entidad de propiedad no pueden ser null a menos que se asignen
explícitamente a una tabla independiente del propietario.
Los distintos propietarios no pueden compartir instancias de tipos de entidad con propiedad (este es un
escenario conocido para objetos de valor que no se pueden implementar mediante tipos de entidad de
propiedad)
Deficiencias en versiones anteriores
En EF Core 2,0, las navegaciones a tipos de entidad de propiedad no se pueden declarar en tipos de entidad
derivadas a menos que las entidades de propiedad se asignen explícitamente a una tabla independiente de la
jerarquía de propietarios. Esta limitación se ha eliminado en EF Core 2,1
En EF Core 2,0 y 2,1 solo se admiten las navegaciones de referencia a los tipos de propiedad. Esta limitación se
ha eliminado en EF Core 2,2
Tipos de entidad sin llave
11/03/2020 • 7 minutes to read
NOTE
Esta característica se agregó en EF Core 2,1 bajo el nombre de los tipos de consulta. En EF Core 3,0 se cambió el nombre
del concepto a tipos de entidad sin entrada.
Además de los tipos de entidad normales, un modelo de EF Core puede contener _tipos de entidad_sin clave, que
se pueden usar para realizar consultas de base de datos con datos que no contengan valores de clave.
Escenarios de uso
Algunos de los escenarios de uso principales de los tipos de entidad sin llave son:
Actúa como el tipo de valor devuelto para las consultas SQL sin procesar.
Asignación a vistas de base de datos que no contienen una clave principal.
Asignación de tablas que no tiene definida una clave principal.
Asignación de las consultas definidas en el modelo.
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.
A continuación, definimos una vista de base de datos simple que nos permitirá consultar el número de entradas
vinculadas a cada blog:
db.Database.ExecuteSqlRaw(
@"CREATE VIEW View_BlogPostCounts AS
SELECT b.Name, Count(p.PostId) as PostCount
FROM Blogs b
JOIN Posts p on p.BlogId = b.BlogId
GROUP BY b.Name");
A continuación, definimos una clase para contener el resultado de la vista de base de datos:
public class BlogPostsCount
{
public string BlogName { get; set; }
public int PostCount { get; set; }
}
A continuación, configuraremos el tipo de entidad sin llave en OnModelCreating con la API de HasNoKey .
Usamos la API de configuración fluida para configurar la asignación para el tipo de entidad sin llave:
TIP
Nota también hemos definido una propiedad de consulta de nivel de contexto (DbSet) para que actúe como raíz para las
consultas en este tipo.
Alternar entre varios modelos con el mismo tipo
DbContext
11/03/2020 • 2 minutes to read
El modelo integrado OnModelCreating puede utilizar una propiedad en el contexto para cambiar la forma en que se
compila el modelo. Por ejemplo, supongamos que desea configurar una entidad de forma diferente en función de
alguna propiedad:
Desafortunadamente, este código no funcionaría tal cual, ya que EF crea el modelo y se ejecuta OnModelCreating
una sola vez, con lo que se almacena en caché el resultado por motivos de rendimiento. Sin embargo, puede
enlazar con el mecanismo de almacenamiento en caché del modelo para que EF tenga en cuenta la propiedad que
genera distintos modelos.
IModelCacheKeyFactory
EF utiliza el IModelCacheKeyFactory para generar claves de caché para los modelos; de forma predeterminada, EF
supone que para un tipo de contexto dado, el modelo será el mismo, por lo que la implementación predeterminada
de este servicio devuelve una clave que solo contiene el tipo de contexto. Para generar diferentes modelos a partir
del mismo tipo de contexto, debe reemplazar el servicio IModelCacheKeyFactory con la implementación correcta. la
clave generada se comparará con otras claves del modelo mediante el método Equals , teniendo en cuenta todas
las variables que afectan al modelo:
La siguiente implementación tiene en cuenta el IgnoreIntProperty al generar una clave de caché del modelo:
NOTE
Esta característica se agregó en EF Core 2,2.
Los datos espaciales representan la ubicación física y la forma de los objetos. Muchas bases de datos proporcionan
compatibilidad con este tipo de datos, por lo que se puede indizar y consultar junto con otros datos. Entre los
escenarios comunes se incluyen las consultas de objetos dentro de una distancia determinada desde una ubicación
o la selección del objeto cuyo borde contiene una ubicación determinada. EF Core admite la asignación a tipos de
datos espaciales mediante la biblioteca espacial NetTopologySuite .
Instalación
Para usar los datos espaciales con EF Core, debe instalar el paquete NuGet de soporte adecuado. El paquete que
necesita instalar depende del proveedor que esté usando.
Microsoft.EntityFrameworkCore.InMemory NetTopologySuite
Ingeniería inversa
Los paquetes de NuGet espaciales también habilitan los modelos de ingeniería inversa con propiedades espaciales,
pero debe instalar el paquete antes de ejecutar Scaffold-DbContext o dotnet ef dbcontext scaffold . Si no lo hace,
recibirá advertencias sobre cómo no encontrar las asignaciones de tipos para las columnas y se omitirán las
columnas.
NetTopologySuite (NTS)
NetTopologySuite es una biblioteca espacial para .NET. EF Core permite la asignación a tipos de datos espaciales en
la base de datos mediante el uso de tipos NTS en el modelo.
Para habilitar la asignación a tipos espaciales a través de NTS, llame al método UseNetTopologySuite en el
generador de opciones DbContext del proveedor. Por ejemplo, con SQL Server le llamaría como esto.
optionsBuilder.UseSqlServer(
@"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=WideWorldImporters",
x => x.UseNetTopologySuite());
Hay varios tipos de datos espaciales. El tipo que use dependerá de los tipos de formas que desee permitir. Esta es la
jerarquía de tipos NTS que puede usar para las propiedades del modelo. Están ubicados en el espacio de nombres
NetTopologySuite.Geometries .
Geometría
Punto
LineString
Polygon
GeometryCollection
MultiPoint
MultiLineString
MultiPolygon
WARNING
La CircularString, CompoundCurve y CurePolygon no son compatibles con NTS.
El uso del tipo de geometría base permite que la propiedad especifique cualquier tipo de forma.
Las siguientes clases de entidad se pueden usar para asignar tablas en la base de datos de ejemplo Wide World
Importers.
Crear valores
Puede usar constructores para crear objetos Geometry; sin embargo, NTS recomienda el uso de un generador de
geometría en su lugar. Esto le permite especificar un valor predeterminado de SRID (el sistema de referencia
espacial que usan las coordenadas) y le proporciona el control sobre aspectos más avanzados, como el modelo de
precisión (usado durante los cálculos) y la secuencia de coordenadas (determina las coordenadas: dimensiones). y
las medidas--están disponibles).
NOTE
4326 hace referencia a WGS 84, un estándar que se usa en GPS y en otros sistemas geográficos.
Longitud y latitud
Las coordenadas en NTS están en términos de valores X e y. Para representar la longitud y la latitud, use X para
longitud e y para latitud. Tenga en cuenta que esto es hacia atrás desde el formato de latitude, longitude en el
que normalmente se ven estos valores.
SRID omitido durante las operaciones de cliente
NTS omite los valores de SRID durante las operaciones. Supone un sistema de coordenadas plano. Esto significa
que si especifica coordenadas en términos de longitud y latitud, algunos valores evaluados por el cliente como la
distancia, la longitud y el área estarán en grados, no en metros. Para valores más significativos, primero debe
proyectar las coordenadas en otro sistema de coordenadas mediante una biblioteca como ProjNet4GeoAPI antes
de calcular estos valores.
Si una operación es evaluada por el servidor mediante EF Core a través de SQL, la unidad del resultado se
determinará por la base de datos.
Este es un ejemplo del uso de ProjNet4GeoAPI para calcular la distancia entre dos ciudades.
[4326] = GeographicCoordinateSystem.WGS84.WKT,
return result;
return result;
}
Consulta de datos
En LINQ, los métodos y las propiedades NTS disponibles como funciones de base de datos se traducirán a SQL. Por
ejemplo, los métodos Distance y Contains se traducen en las siguientes consultas. En la tabla al final de este
artículo se muestran los miembros que son compatibles con varios proveedores de EF Core.
SQL Server
Si usa SQL Server, hay algunos aspectos adicionales que debe tener en cuenta.
Geografía o geometría
De forma predeterminada, las propiedades espaciales se asignan a geography columnas en SQL Server. Para usar
geometry , Configure el tipo de columna en el modelo.
WARNING
Los FullGlobe y los polígonos basados en ellos no son compatibles con NTS.
SQLite
A continuación se muestra información adicional para los usuarios que usan SQLite.
Instalación de SpatiaLite
En Windows, la biblioteca de mod_spatialite nativa se distribuye como una dependencia del paquete NuGet. Otras
plataformas deben instalarse por separado. Esto se suele hacer mediante un administrador de paquetes de
software. Por ejemplo, puede usar APT en Ubuntu y homebrew en MacOS.
# Ubuntu
apt-get install libsqlite3-mod-spatialite
# macOS
brew install libspatialite
Desafortunadamente, las versiones más recientes de PROJ (una dependencia de SpatiaLite) son incompatibles con
el paquete SQLitePCLRawpredeterminado de EF. Para solucionar este fin, puede crear un proveedor de
SQLitePCLRaw personalizado que use la biblioteca de SQLite del sistema, o bien puede instalar una compilación
personalizada de SpatiaLite deshabilitar la compatibilidad con proj.
./configure --disable-proj
make
make install
Configuración de SRID
En SpatiaLite, las columnas deben especificar un SRID por columna. El valor predeterminado de SRID es 0 .
Especifique otro SRID con el método ForSqliteHasSrid.
Dimensión
De forma similar a SRID, la dimensión de una columna (o las ordenadas) también se especifica como parte de la
columna. Las ordenadas predeterminadas son X e y. Habilite las ordenadas adicionales (Z y M) mediante el método
ForSqliteHasDimension.
modelBuilder.Entity<City>().Property(c => c.Location)
.ForSqliteHasDimension(Ordinates.XYZ);
Operaciones traducidas
En esta tabla se muestran los miembros de NTS que cada proveedor de EF Core traduce en SQL.
SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L
Geometry. Area ✔ ✔ ✔ ✔
Geometry. AsBinary () ✔ ✔ ✔ ✔
Geometry. astext () ✔ ✔ ✔ ✔
Geometry. Boundary ✔ ✔ ✔
Geometry. Buffer ✔ ✔ ✔ ✔
(Double)
Geometry. Buffer ✔ ✔
(Double, int)
Geometry. centroide ✔ ✔ ✔
Geometry. Contains ✔ ✔ ✔ ✔
(Geometry)
Geometry. ✔ ✔ ✔ ✔
ConvexHull ()
Geometry. CoveredBy ✔ ✔
(Geometry)
Geometry. cubiertas ✔ ✔
(Geometry)
Geometry. Crosses ✔ ✔ ✔
(Geometry)
Geometry. Difference ✔ ✔ ✔ ✔
(Geometry)
Geometry. Dimension ✔ ✔ ✔ ✔
Geometry. disunion ✔ ✔ ✔ ✔
(Geometry)
Geometry. Distance ✔ ✔ ✔ ✔
(Geometry)
Geometría. sobre ✔ ✔ ✔
SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L
Geometry. ✔
EqualsExact
(Geometry)
Geometry. ✔ ✔ ✔ ✔
EqualsTopologically
(Geometry)
Geometry. ✔ ✔ ✔ ✔
GeometryType
Geometry. ✔ ✔ ✔
GetGeometryN (int)
Geometry. ✔ ✔ ✔
InteriorPoint
Geometry. ✔ ✔ ✔ ✔
Intersection
(Geometry)
Geometry. Intersects ✔ ✔ ✔ ✔
(Geometry)
Geometry. IsEmpty ✔ ✔ ✔ ✔
Geometry. IsSimple ✔ ✔ ✔
Geometry. IsValid ✔ ✔ ✔ ✔
Geometry. ✔ ✔ ✔
IsWithinDistance
(Geometry, Double)
Geometry. length ✔ ✔ ✔ ✔
Geometry. ✔ ✔ ✔ ✔
NumGeometries
Geometry. NumPoints ✔ ✔ ✔ ✔
Geometry. ✔ ✔ ✔ ✔
OgcGeometryType
Geometry. superpone ✔ ✔ ✔ ✔
(Geometry)
Geometry. ✔ ✔ ✔
PointOnSurface
Geometry. Relate ✔ ✔ ✔
(Geometry, String)
SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L
Geometry. Reverse () ✔ ✔
Geometry. SRID ✔ ✔ ✔ ✔
Geometry. ✔ ✔ ✔ ✔
SymmetricDifference
(Geometry)
Geometry. ToBinary () ✔ ✔ ✔ ✔
Geometry. ToText () ✔ ✔ ✔ ✔
Geometry. toques ✔ ✔ ✔
(Geometry)
Geometry. Union () ✔ ✔
Geometry. Union ✔ ✔ ✔ ✔
(Geometry)
Geometry. Within ✔ ✔ ✔ ✔
(Geometry)
GeometryCollection. ✔ ✔ ✔ ✔
Count
GeometryCollection ✔ ✔ ✔ ✔
[int]
LineString. Count ✔ ✔ ✔ ✔
LineString. EndPoint ✔ ✔ ✔ ✔
LineString. GetPointN ✔ ✔ ✔ ✔
(int)
LineString. IsClosed ✔ ✔ ✔ ✔
LineString. IsRing ✔ ✔ ✔
LineString. StartPoint ✔ ✔ ✔ ✔
MultiLineString. ✔ ✔ ✔ ✔
IsClosed
Punto. M ✔ ✔ ✔ ✔
Point. X ✔ ✔ ✔ ✔
Punto. Y ✔ ✔ ✔ ✔
SQ L SERVER SQ L SERVER
N ET TO P O LO GY SUIT E ( GEO M ET RÍA ) ( GEO GRA F ÍA ) SQ L IT E N P GSQ L
Punto. Z ✔ ✔ ✔ ✔
Polygon. ExteriorRing ✔ ✔ ✔ ✔
Polygon. ✔ ✔ ✔ ✔
GetInteriorRingN (int)
Polygon. ✔ ✔ ✔ ✔
NumInteriorRings
Recursos adicionales
Datos espaciales en SQL Server
Página principal de SpatiaLite
Documentación espacial Npgsql
Documentación de PostGIS
Administración de esquemas de base de datos
08/04/2020 • 2 minutes to read
EF Core proporciona dos métodos principales para mantener sincronizados el esquema de la base de datos y el
modelo de EF Core. Para elegir entre los dos, decida si es el modelo de EF Core o el esquema de la base de datos el
origen verdadero.
Si quiere que el modelo de EF Core sea el origen verdadero, use Migraciones. Al realizar cambios en el modelo de
EF Core, este método aplica de forma incremental los cambios de esquema correspondientes a la base de datos
para que siga siendo compatible con el modelo de EF Core.
Si quiere que el esquema de la base de datos sea el origen verdadero, use Ingeniería inversa. Este método permite
aplicar la técnica de scaffolding a un elemento DbContext y a las clases de tipo de entidad mediante la aplicación de
ingeniería inversa al esquema de la base de datos de un modelo de EF Core.
NOTE
Las API de creación y eliminación también pueden crear el esquema de la base de datos a partir del modelo de EF Core. Pero
son principalmente para pruebas, creación de prototipos y otros escenarios donde la eliminación de la base de datos es
aceptable.
Migraciones
08/04/2020 • 12 minutes to read
Un modelo de datos cambia durante el desarrollo y deja de estar sincronizado con la base de datos. Puede
quitar la base de datos y dejar que EF cree una que coincida con el modelo, pero este procedimiento provoca
la pérdida de datos. La característica de migraciones de EF Core proporciona una manera de actualizar
incrementalmente el esquema de la base de datos para mantenerla sincronizada con el modelo de datos de la
aplicación al tiempo que se conservan los datos existentes en la base de datos.
Las migraciones incluyen herramientas de línea de comandos y API que facilitan las siguientes tareas:
Crear una migración. Generar código que puede actualizar la base de datos para sincronizarla con un
conjunto de cambios en el modelo.
Actualizar la base de datos. Aplicar las migraciones pendientes para actualizar el esquema de la base de
datos.
Personalizar el código de migración. A veces el código generado debe modificarse o complementarse.
Quitar una migración. Eliminar el código generado.
Revertir una migración. Deshacer los cambios de la base de datos.
Generar scripts SQL. Puede que necesite un script para actualizar una base de datos de producción o para
solucionar problemas con el código de migración.
Aplicar migraciones en tiempo de ejecución. Si las actualizaciones en tiempo de diseño y la ejecución de
scripts no son las mejores opciones, llame al método Migrate() .
TIP
Si DbContext está en un ensamblado diferente al del proyecto de inicio, puede especificar de manera explícita los
proyectos de destino e inicio tanto en las herramientas de la Consola del Administrador de paquetes como en las
herramientas de la CLI de .NET Core.
TIP
Puede mover los archivos de Migraciones y cambiar su espacio de nombres. Se crean nuevas migraciones como
elementos del mismo nivel de la última migración.
Tras aplicar scaffolding a la migración (código generado para ella), revise el código para mayor precisión y
agregue, quite o modifique todas las operaciones necesarias para aplicarla correctamente.
Por ejemplo, una migración podría contener las siguientes operaciones:
migrationBuilder.DropColumn(
name: "FirstName",
table: "Customer");
migrationBuilder.DropColumn(
name: "LastName",
table: "Customer");
migrationBuilder.AddColumn<string>(
name: "Name",
table: "Customer",
nullable: true);
Aunque estas operaciones hacen que el esquema de la base de datos sea compatible, no conservan los
nombres de cliente existentes. Para que sea mejor, vuelva a escribirla como se indica a continuación.
migrationBuilder.AddColumn<string>(
name: "Name",
table: "Customer",
nullable: true);
migrationBuilder.Sql(
@"
UPDATE Customer
SET Name = FirstName + ' ' + LastName;
");
migrationBuilder.DropColumn(
name: "FirstName",
table: "Customer");
migrationBuilder.DropColumn(
name: "LastName",
table: "Customer");
TIP
El proceso de scaffolding de la migración advierte si una operación puede ocasionar una pérdida de datos (como el
borrado de una columna). Si aparece dicha advertencia, asegúrese especialmente de revisar el código de las migraciones
para mayor precisión.
Migraciones vacías
A veces resulta útil agregar una migración sin realizar ningún cambio de modelo. En este caso, agregar una
nueva migración crea archivos de código con clases vacías. Puede personalizar esta migración para llevar a
cabo operaciones que no estén directamente relacionadas con el modelo de EF Core. Algunos aspectos que
podría querer administrar de esta manera son:
Búsqueda de texto completo
Funciones
Procedimientos almacenados
Desencadenadores
Vistas
Después de quitar la migración, puede realizar los cambios de modelo adicionales y volver a agregarla.
Con From y To
Se generará un script SQL de la migración de from a la migración de to especificada.
Puede usar un valor from que sea más reciente que el valor to para generar un script de reversión. Tome
nota de los posibles escenarios de pérdida de datos.
Hay varias opciones para este comando.
La migración from debe ser la última migración aplicada a la base de datos antes de ejecutar el script. Si no se
han aplicado migraciones, especifique 0 (es el valor predeterminado).
La migración to debe ser la última migración que se va a aplicar a la base de datos después de ejecutar el
script. El valor predeterminado es la última migración del proyecto.
Se puede generar un script idempotent de forma opcional. Este script solo aplica migraciones si aún no se
han aplicado a la base de datos. Es útil si no sabe exactamente cuál ha sido la última migración aplicada a la
base de datos o si va a implementar en varias bases de datos que pueden encontrarse en migraciones
diferentes.
Aplicar migraciones en tiempo de ejecución
Algunas aplicaciones pueden querer aplicar migraciones en tiempo de ejecución durante el inicio o la primera
ejecución. Para ello, se usa el método Migrate() .
Este método se basa en el servicio , que se puede usar para escenarios más avanzados. Use
IMigrator
myDbContext.GetInfrastructure().GetService<IMigrator>() para acceder a él.
myDbContext.Database.Migrate();
WARNING
Este método no es para todos. Aunque es excelente para las aplicaciones con una base de datos local, la mayoría de
las aplicaciones necesitan estrategias de implementación más sólidas, como la generación de scripts SQL.
No llame a EnsureCreated() antes de Migrate() . EnsureCreated() omite las migraciones para crear el
esquema, lo cual provoca un error de Migrate() .
Pasos siguientes
Para obtener más información, vea Referencia sobre las herramientas de Entity Framework Core (EF Core).
Migraciones en entornos de equipo
11/03/2020 • 3 minutes to read
Al trabajar con migraciones en entornos de equipo, preste especial atención al archivo de instantáneas del modelo.
Este archivo puede indicarle si la migración de su compañero de equipo se combina correctamente con la suya o si
necesita resolver un conflicto volviendo a crear la migración antes de compartirla.
Combinación
Al fusionar mediante combinación las migraciones de sus compañeros de equipo, puede obtener conflictos en el
archivo de instantánea del modelo. Si los dos cambios no están relacionados, la combinación es trivial y las dos
migraciones pueden coexistir. Por ejemplo, puede obtener un conflicto de fusión mediante combinación en la
configuración del tipo de entidad Customer, que tiene el siguiente aspecto:
<<<<<<< Mine
b.Property<bool>("Deactivated");
=======
b.Property<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.
b.Property<bool>("Deactivated");
b.Property<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
b.Property<string>("Username");
=======
b.Property<string>("Alias");
>>>>>>> Theirs
Si encuentra este tipo de conflicto, resuélvalos volviendo a crear la migración. Siga estos pasos:
1. Anular la combinación y revertir al directorio de trabajo antes de la fusión mediante combinación
2. Quitar la migración (pero mantener los cambios del modelo)
3. Combinar los cambios de su compañero en el directorio de trabajo
4. Volver a agregar la migración
Después de hacer esto, las dos migraciones se pueden aplicar en el orden correcto. En primer lugar, se aplica su
migración, cambiando el nombre de la columna a aliasy, a partir de ese momento, la migración lo cambia por
nombre de usuario.
La migración puede compartirse de forma segura con el resto del equipo.
Operaciones de migración personalizadas
11/03/2020 • 3 minutes to read
La API de MigrationBuilder permite realizar muchos tipos diferentes de operaciones durante una migración, pero
está lejos de ser exhaustiva. Sin embargo, la API también es extensible, lo que le permite definir sus propias
operaciones. Hay dos maneras de extender la API: mediante el método Sql() o mediante la definición de objetos
de MigrationOperation personalizados.
Para ilustrar, echemos un vistazo a la implementación de una operación que crea un usuario de base de datos
mediante cada enfoque. En nuestras migraciones, queremos habilitar la escritura del código siguiente:
migrationBuilder.CreateUser("SQLUser1", "Password");
Si las migraciones necesitan admitir varios proveedores de bases de datos, puede usar la propiedad
MigrationBuilder.ActiveProvider . Este es un ejemplo que admite tanto Microsoft SQL Server como PostgreSQL.
case "Microsoft.EntityFrameworkCore.SqlServer":
return migrationBuilder
.Sql($"CREATE USER {name} WITH PASSWORD = '{password}';");
}
return migrationBuilder;
}
Este enfoque solo funciona si conoce todos los proveedores en los que se va a aplicar la operación personalizada.
Uso de un MigrationOperation
Para desacoplar la operación personalizada de SQL, puede definir su propia MigrationOperation para
representarla. A continuación, la operación se pasa al proveedor para que pueda determinar el SQL adecuado que
se va a generar.
Con este enfoque, el método de extensión solo tiene que agregar una de estas operaciones a
MigrationBuilder.Operations .
return migrationBuilder;
}
Este enfoque requiere que cada proveedor sepa cómo generar SQL para esta operación en su servicio
IMigrationsSqlGenerator . Este es un ejemplo invalidando el generador del SQL Server para administrar la nueva
operación.
class MyMigrationsSqlGenerator : SqlServerMigrationsSqlGenerator
{
public MyMigrationsSqlGenerator(
MigrationsSqlGeneratorDependencies dependencies,
IMigrationsAnnotationProvider migrationsAnnotations)
: base(dependencies, migrationsAnnotations)
{
}
builder
.Append("CREATE USER ")
.Append(sqlHelper.DelimitIdentifier(operation.Name))
.Append(" WITH PASSWORD = ")
.Append(stringMapping.GenerateSqlLiteral(operation.Password))
.AppendLine(sqlHelper.StatementTerminator)
.EndCommand();
}
}
Es posible que desee almacenar las migraciones en un ensamblado diferente del que contiene el DbContext .
También puede usar esta estrategia para mantener varios conjuntos de migraciones, por ejemplo, una para el
desarrollo y otra para las actualizaciones de lanzamiento a lanzamiento.
Para hacer esto...
1. Cree una nueva biblioteca de clases.
2. Agregue una referencia al ensamblado DbContext.
3. Mueva las migraciones y los archivos de instantáneas de modelo a la biblioteca de clases.
TIP
Si no tiene ninguna migración existente, genere una en el proyecto que contiene el DbContext y muévala. Esto es
importante porque si el ensamblado de migraciones no contiene una migración existente, el comando Add-
Migration no podrá encontrar DbContext.
options.UseSqlServer(
connectionString,
x => x.MigrationsAssembly("MyApp.Migrations"));
<PropertyGroup>
<OutputPath>..\MyStartupProject\bin\$(Configuration)\</OutputPath>
</PropertyGroup>
Las herramientas de EF Core solo las migraciones de scaffolding para el proveedor activo. Sin embargo, a veces es
posible que desee usar más de un proveedor (por ejemplo Microsoft SQL Server y SQLite) con DbContext. Hay dos
formas de controlar esto con las migraciones. Puede mantener dos conjuntos de migraciones, uno para cada
proveedor, o combinarlos en un único conjunto que pueda funcionar en ambos.
NOTE
Dado que cada conjunto de migración usa sus propios tipos DbContext, este enfoque no requiere el uso de un ensamblado
de migración independiente.
TIP
No es necesario especificar el directorio de salida para las migraciones posteriores, ya que se crean como elementos del
mismo nivel que el último.
Un conjunto de migración
Si no le gusta tener dos conjuntos de migraciones, puede combinarlas manualmente en un único conjunto que se
puede aplicar a ambos proveedores.
Las anotaciones pueden coexistir ya que un proveedor omite cualquier anotación que no comprenda. Por ejemplo,
una columna de clave principal que funciona con Microsoft SQL Server y SQLite podría tener este aspecto.
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy",
SqlServerValueGenerationStrategy.IdentityColumn)
.Annotation("Sqlite:Autoincrement", true),
Si las operaciones solo se pueden aplicar en un proveedor (o son diferentes entre proveedores), use la propiedad
ActiveProvider para indicar qué proveedor está activo.
if (migrationBuilder.ActiveProvider == "Microsoft.EntityFrameworkCore.SqlServer")
{
migrationBuilder.CreateSequence(
name: "EntityFrameworkHiLoSequence");
}
Tabla de historial de migraciones personalizadas
11/03/2020 • 2 minutes to read
De forma predeterminada, EF Core realiza un seguimiento de las migraciones que se han aplicado a la base de
datos mediante su grabación en una tabla denominada __EFMigrationsHistory . Por varias razones, puede que
desee personalizar esta tabla para satisfacer mejor sus necesidades.
IMPORTANT
Si personaliza la tabla de historial de migraciones después de aplicar las migraciones, es responsable de actualizar la tabla
existente en la base de datos.
Otros cambios
Para configurar aspectos adicionales de la tabla, invalide y reemplace el servicio de IHistoryRepository específico
del proveedor. Este es un ejemplo de cómo cambiar el nombre de la columna MigrationId a ID en SQL Server.
WARNING
SqlServerHistoryRepository está dentro de un espacio de nombres interno y puede cambiar en futuras versiones.
class MyHistoryRepository : SqlServerHistoryRepository
{
public MyHistoryRepository(HistoryRepositoryDependencies dependencies)
: base(dependencies)
{
}
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.
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á.
TIP
También hay disponibles versiones asincrónicas de estos métodos.
La ingeniería inversa es el proceso de scaffolding de las clases de tipo de entidad y una clase DbContext basada
en un esquema de base de datos. Puede realizarse mediante el comando Scaffold-DbContext de las herramientas
de la consola del administrador de paquetes EF Core (PMC) o el comando dotnet ef dbcontext scaffold de las
herramientas de la interfaz de la línea de comandos (CLI) de .NET.
Instalación
Antes de la ingeniería inversa, deberá instalar las herramientas de PMC (solo en Visual Studio) o las
herramientasde la CLI. Vea los vínculos para obtener más información.
También necesitará instalar un proveedor de base de datos adecuado para el esquema de la base de datos al que
desea aplicar ingeniería inversa.
Cadena de conexión
El primer argumento del comando es una cadena de conexión a la base de datos. Las herramientas usarán esta
cadena de conexión para leer el esquema de la base de datos.
La forma de citar y escapar de la cadena de conexión depende del shell que use para ejecutar el comando.
Consulte la documentación de su shell para obtener información específica. Por ejemplo, PowerShell requiere que
se escape el carácter $ , pero no \ .
Nombre de proveedor
El segundo argumento es el nombre del proveedor. El nombre del proveedor suele ser el mismo que el nombre
del paquete NuGet del proveedor.
Especificar tablas
De forma predeterminada, se aplica ingeniería inversa a todas las tablas del esquema de la base de datos en tipos
de entidad. Puede limitar las tablas a las que se aplica ingeniería inversa mediante la especificación de esquemas
y tablas.
El parámetro -Schemas en PMC y la opción --schema de la CLI se pueden usar para incluir todas las tablas de un
esquema.
-Tables (PMC) y --table (CLI) se pueden usar para incluir tablas específicas.
Para incluir varias tablas en PMC, use una matriz.
Conservar nombres
Los nombres de tablas y columnas se han corregido para que coincidan mejor con las convenciones de
nomenclatura de .NET para tipos y propiedades de forma predeterminada. Si se especifica el modificador
-UseDatabaseNames en PMC o la opción --use-database-names de la CLI, se deshabilitará este comportamiento
para conservar los nombres de las bases de datos originales lo máximo posible. Los identificadores de .NET no
válidos seguirán siendo fijos y los nombres sintetizados, como las propiedades de navegación, seguirán
conforme a las convenciones de nomenclatura de .NET.
[Required]
[StringLength(160)]
public string Title { get; set; }
Nombre de DbContext
El nombre de la clase DbContext con scaffolding será el nombre de la base de datos con sufijo de contexto de
forma predeterminada. Para especificar otro, use -Context en PMC y --context en la CLI.
Funcionamiento
La ingeniería inversa comienza leyendo el esquema de la base de datos. Lee información acerca de las tablas,
columnas, restricciones e índices.
A continuación, usa la información de esquema para crear un modelo de EF Core. Las tablas se usan para crear
tipos de entidad. las columnas se usan para crear propiedades; y las claves externas se utilizan para crear
relaciones.
Por último, el modelo se usa para generar código. Las clases de tipo de entidad, la API fluida y las anotaciones de
datos correspondientes son scaffolding para volver a crear el mismo modelo desde la aplicación.
Limitaciones
No todo lo relacionado con un modelo se puede representar mediante un esquema de la base de datos. Por
ejemplo, la información sobre las jerarquías de herencia , los tipos de propiedad y la División de tablas
no están presentes en el esquema de la base de datos. Por este motivo, estas construcciones nunca se
aplicarán a ingeniería inversa.
Además, es posible que algunos tipos de columna no sean compatibles con el proveedor de EF Core. Estas
columnas no se incluirán en el modelo.
Puede definir tokens de simultaneidad en un modelo de EF Core para evitar que dos usuarios actualicen la
misma entidad al mismo tiempo. Algunas bases de datos tienen un tipo especial para representar este tipo de
columna (por ejemplo, rowversion en SQL Server), en cuyo caso se puede aplicar ingeniería inversa a esta
información; sin embargo, no se aplicarán ingeniería inversa a otros tokens de simultaneidad.
La C# característica 8 tipos de referencia que aceptan valores NULL no se admite actualmente en ingeniería
inversa: EF Core C# siempre genera código que supone que la característica está deshabilitada. Por ejemplo,
las columnas de texto que aceptan valores NULL se scaffolding como una propiedad con el tipo string , no
string? , con la API fluida o las anotaciones de datos que se usan para configurar si una propiedad es
obligatoria o no. Puede editar el código con scaffolding y reemplazarlo con anotaciones de C# nulabilidad. El
seguimiento de la compatibilidad con scaffolding para tipos de referencia que aceptan valores NULL se realiza
mediante el problema #15520.
Actualizar el modelo
Después de realizar cambios en la base de datos, puede que tenga que actualizar el modelo de EF Core para
reflejar los cambios. Si los cambios en la base de datos son sencillos, puede que sea más fácil realizar los cambios
manualmente en el modelo de EF Core. Por ejemplo, cambiar el nombre de una tabla o columna, quitar una
columna o actualizar el tipo de una columna son cambios triviales que se deben realizar en el código.
Sin embargo, los cambios más significativos no son tan sencillos como los que se realizan de forma manual. Un
flujo de trabajo común consiste en volver a aplicar ingeniería inversa del modelo de la base de datos mediante
-Force (PMC) o --force (CLI) para sobrescribir el modelo existente con uno actualizado.
Otra característica solicitada comúnmente es la posibilidad de actualizar el modelo de la base de datos a la vez
que se conserva la personalización, como cambiar el nombre, las jerarquías de tipos, etc. Use el #831 de
problemas para realizar el seguimiento del progreso de esta característica.
WARNING
Si vuelve a aplicar ingeniería inversa al modelo desde la base de datos, se perderán los cambios realizados en los archivos.
Consulta de datos
08/04/2020 • 2 minutes to read • Edit Online
Entity Framework Core usa Language Integrated Query (LINQ) para consultar datos de la base de datos. LINQ
permite usar C# (o el lenguaje .NET que prefiera) para escribir consultas fuertemente tipadas. Usa el contexto
derivado y las clases de entidad para hacer referencia a los objetos de base de datos. EF Core pasa una
representación de la consulta LINQ al proveedor de la base de datos. A su vez, los proveedores de la base de datos
la traducen al lenguaje de la consulta específico para la base de datos (por ejemplo, SQL para una base de datos
relacional).
TIP
Puede ver un ejemplo de este artículo en GitHub.
Los fragmentos de código siguientes muestran algunos ejemplos de cómo realizar tareas comunes con Entity
Framework Core.
Filtros
using (var context = new BloggingContext())
{
var blogs = context.Blogs
.Where(b => b.Url.Contains("dotnet"))
.ToList();
}
Lecturas adicionales
Obtenga más información sobre las expresiones de consulta LINQ.
Para más información sobre cómo se procesa una consulta en EF Core, vea Cómo funcionan las consultas.
Evaluación de cliente frente a servidor
08/04/2020 • 10 minutes to read • Edit Online
Como norma general, Entity Framework Core intenta evaluar una consulta en el servidor lo máximo posible. EF
Core convierte partes de la consulta en parámetros, que se pueden evaluar en el lado cliente. El resto de la consulta
( junto con los parámetros generados) se proporciona al proveedor de base de datos para determinar la consulta
de base de datos equivalente que se va a evaluar en el servidor. EF Core admite la evaluación de cliente parcial en
la proyección de nivel superior (fundamentalmente, la última llamada a Select() ). Si la proyección de nivel
superior de la consulta no se puede traducir en el servidor, EF Core capturará los datos necesarios del servidor y
evaluará las partes restantes de la consulta en el cliente. Si EF Core detecta una expresión, en cualquier lugar que
no sea la proyección de nivel superior, que no se puede traducir en el servidor, inicia una excepción en tiempo de
ejecución. Vea Funcionamiento de la consulta para saber cómo determina EF Core lo que no se puede traducir al
servidor.
NOTE
Antes de la versión 3.0, Entity Framework Core admitía la evaluación de cliente en cualquier parte de la consulta. Para
obtener más información, vea la sección sobre versiones anteriores.
TIP
Puede ver un ejemplo de este artículo en GitHub.
if (!url.StartsWith("http://"))
{
url = string.Concat("http://", 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.
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 DbContext.OnConfiguring , o bien en
Startup.cs si usa ASP.NET Core.
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() .
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.
context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
Si el conjunto de resultados contiene tipos de entidad que proceden de la composición LINQ, EF Core realizará un
seguimiento de ellos.
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.
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 = context.Blogs
.OrderByDescending(blog => blog.Rating)
.Select(blog => new
{
Id = blog.BlogId,
Url = StandardizeUrl(blog)
})
.ToList();
if (!url.StartsWith("http://"))
{
url = string.Concat("http://", 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 .
Siempre que los resultados de consulta contenían tipos de entidad sin clave, significaba que no se hacía un
seguimiento de la consulta completa. Esto quiere decir que tampoco se realizaba un seguimiento de los tipos
de entidad con claves que estaban en el resultado.
EF Coreó realizaba la resolución de identidades en consultas de no seguimiento. Se usaban referencias
débiles para mantener el seguimiento de entidades que ya se habían devuelto. Por lo tanto, si un conjunto de
resultados contenía la misma entidad varias veces, obtenía la misma instancia para cada caso. Sin embargo,
si un resultado anterior con la misma identidad se salía del ámbito y generaba un elemento no utilizado, EF
Core devolvía una nueva instancia.
Operadores de consulta complejos
08/04/2020 • 13 minutes to read • Edit Online
Language Integrated Query (LINQ) contiene muchos operadores complejos, que combinan varios orígenes de
datos o realizan procesamientos complejos. No todos los operadores de LINQ tienen traducciones adecuadas en el
lado servidor. En ocasiones, una consulta en un formato se traduce en el servidor, pero si se escribe en otro
formato, no se traduce aunque el resultado sea el mismo. En esta página se describen algunos de los operadores
complejos y sus variaciones admitidas. En futuras versiones, es posible que se reconozcan más patrones y se
agreguen sus correspondientes traducciones. También es importante tener en cuenta que la compatibilidad con la
traducción varía entre proveedores. Es posible que una consulta determinada, que se traduzca en SqlServer, no
funcione para bases de datos SQLite.
TIP
Puede ver un ejemplo de este artículo en GitHub.
Join
El operador Join de LINQ permite conectar dos orígenes de datos en función del selector de claves de cada origen,
lo que genera una tupla de valores cuando la clave coincide. Se traduce de forma natural a INNER JOIN en las bases
de datos relacionales. Aunque Join de LINQ tiene selectores de clave externa e interna, la base de datos requiere
una única condición de combinación. Por tanto, EF Core genera una condición de combinación que compara el
selector de clave externa con el selector de clave interna para determinar si son iguales. Además, si los selectores de
clave son tipos anónimos, EF Core genera una condición de combinación para comparar los componentes de
igualdad.
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 context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.PostId into grouping
select new { b, grouping };
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.
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.
GROUP BY
Los operadores GroupBy de LINQ crean un resultado de tipo IGrouping<TKey, TElement> , donde TKey y TElement
podrían ser cualquier tipo arbitrario. Además, IGrouping implementa IEnumerable<TElement> , lo que significa que
se puede redactar sobre este elemento con cualquier operador de LINQ después de la agrupación. Como ninguna
estructura de base de datos puede representar una instancia de IGrouping , en la mayoría de los casos los
operadores GroupBy no tienen ninguna traducción. Cuando se aplica un operador de agregado a cada grupo, lo
que devuelve un valor escalar, se puede traducir a GROUP BY de SQL en las bases de datos relacionales. GROUP BY
de SQL también es restrictivo. Requiere que se agrupe solo por valores escalares. La proyección solo puede
contener columnas de clave de agrupación o cualquier agregado aplicado en una columna. EF Core identifica este
patrón y lo traduce al servidor, como en el ejemplo siguiente:
var query = from p in context.Set<Post>()
group p by p.AuthorId into g
select new
{
g.Key,
Count = g.Count()
};
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.
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 context.Set<Blog>()
join p in context.Set<Post>()
on b.BlogId equals p.BlogId into grouping
from p in grouping.DefaultIfEmpty()
select new { b, p };
En el patrón anterior se crea una estructura compleja en el árbol de expresión. Por eso, EF Core requiere que se
reduzcan los resultados de agrupación del operador GroupJoin en un paso inmediatamente después del operador.
Aunque se use GroupJoin-DefaultIfEmpty-SelectMany, pero en otro patrón, es posible que no se identifique como
una combinación izquierda.
Carga de datos relacionados
08/04/2020 • 13 minutes to read • Edit Online
Entity Framework Core permite usar las propiedades de navegación del modelo para cargar las entidades
relacionados. Existen tres patrones de O/RM comunes que se usan para cargar los datos relacionados.
Carga diligente significa que los datos relacionados se cargan desde la base de datos como parte de la
consulta inicial.
Carga explícita significa que los datos relacionados se cargan de manera explícita desde la base de datos
más adelante.
Carga diferida significa que los datos relacionados se cargan de manera transparente desde la base de datos
cuando se accede a la propiedad de navegación.
TIP
Puede ver un ejemplo de este artículo en GitHub.
Carga diligente
Puede usar el método Include para especificar los datos relacionados que se incluirán en los resultados de la
consulta. En el ejemplo siguiente, las entradas relacionadas rellenarán la propiedad Posts de los blogs que se
devuelvan en los resultados.
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.
Puede encadenar varias llamadas en ThenInclude para continuar incluyendo más niveles de datos relacionados.
Puede combinar todo esto para incluir datos relacionados provenientes de varios niveles y varias raíces en la
misma consulta.
Es posible que quiera incluir varias entidades relacionadas para una de las entidades que se está incluyendo. Por
ejemplo, cuando consulte Blogs , incluye Posts y luego quiere incluir tanto Author como Tags de las Posts .
Para hacerlo, debe especificar cada inicio de ruta de acceso de inclusión en la raíz. Por ejemplo,
Blog -> Posts -> Author y Blog -> Posts -> Tags . Esto no significa que vaya a obtener combinaciones
redundantes; en la mayoría de los casos, EF consolidará las combinaciones al generar código SQL.
Cau t i on
Desde la versión 3.0.0, todas las instancias de Include producirán que se agregue una combinación JOIN
adicional a las consultas SQL generadas por los proveedores relacionales, mientras que las versiones anteriores
generaban consultas SQL adicionales. Esto puede cambiar significativamente el rendimiento de las consultas,
tanto para bien como para mal. En concreto, es posible que las consultas LINQ con un número excesivamente alto
de operadores Include deban dividirse en varias consultas LINQ independientes con el fin de evitar el problema
de explosión cartesiana.
Inclusión en tipos derivados
Puede incluir datos relacionados provenientes de las navegaciones que se definen solo en un tipo derivado con
Include y ThenInclude .
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
con el operador as
context.People.Include("School").ToList()
Carga explícita
Puede cargar de manera explícita una propiedad de navegación a través de la API DbContext.Entry(...) .
using (var context = new BloggingContext())
{
var blog = context.Blogs
.Single(b => b.BlogId == 1);
context.Entry(blog)
.Collection(b => b.Posts)
.Load();
context.Entry(blog)
.Reference(b => b.Owner)
.Load();
}
También puede cargar de manera explícita una propiedad de navegación si ejecuta una consulta independiente
que devuelve las entidades relacionadas. Si está habilitado el seguimiento de cambios, cuando se cargue una
entidad, EF Core establecerá automáticamente las propiedades de navegación de la entidad recién cargada para
hacer referencia a cualquier entidad ya cargada y establecerá las propiedades de navegación de las entidades ya
cargadas para hacer referencia a la entidad recién cargada.
Consulta de las entidades relacionadas
También puede obtener una consulta LINQ que represente el contenido de una propiedad de navegación.
Esto permite, entre otras acciones, ejecutar un operador de agregado en las entidades relacionadas sin cargarlas
en la memoria.
Carga diferida
La manera más simple de usar la carga diferida es instalar el paquete Microsoft.EntityFrameworkCore.Proxies 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 => b.UseLazyLoadingProxies()
.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 Post.Blog y Blog.Posts serán de carga diferida.
public Blog()
{
}
public Post()
{
}
Esto no requiere tipos de entidad de los cuales heredar ni propiedades de navegación para ser virtual y permite
que las instancias de entidad creadas con new se carguen de manera diferida una vez que se asocian a un
contexto. Sin embargo, requiere una referencia al servicio ILazyLoader , que está definido en el paquete
Microsoft.EntityFrameworkCore.Abstractions. Este paquete contiene un conjunto mínimo de tipos, por lo que es
muy poco el impacto al depender de él. Sin embargo, para evitar por completo depender de cualquier paquete de
EF Core en los tipos de entidad, es posible insertar el método ILazyLoader.Load como delegado. Por ejemplo:
public class Blog
{
private ICollection<Post> _posts;
public Blog()
{
}
public Post()
{
}
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.
Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'Blog' with type
'MyApplication.Models.Blog'. (Newtonsoft.Json.JsonSerializationException: se detectó un bucle con
autorreferencia para la propiedad "Blog" con el tipo "MyApplication.Models.Blog").
Si usa ASP.NET Core, puede configurar JSON.NET para que omita los ciclos que encuentre en el grafo del objeto.
Esto se hace en el método ConfigureServices(...) en Startup.cs .
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
Otra alternativa consiste en decorar una de las propiedades de navegación con el atributo [JsonIgnore] , que
indica a JSON.NET que no recorra esa propiedad de navegación mientras se serializa.
Consultas asincrónicas
08/04/2020 • 2 minutes to read • Edit Online
Las consultas asincrónicas evitan bloquear un subproceso mientras la consulta se ejecuta en la base de datos. Las
consultas asincrónicas son importantes para mantener una interfaz de usuario dinámica en las aplicaciones cliente
de gran tamaño. También pueden aumentar el rendimiento de las aplicaciones web donde liberan el subproceso
para atender otras solicitudes de las aplicaciones web. Para más información, consulte Programación asincrónica en
C#.
WARNING
EF Core no admite que varias operaciones en paralelo se ejecuten en la misma instancia de contexto. Siempre debe esperar
que se complete una operación antes de iniciar la siguiente. Habitualmente, para esto se usa la palabra clave await en cada
una de las operaciones asincrónicas.
Entity Framework Core proporciona un conjunto de métodos de extensión asincrónicos similares a los de LINQ, que
ejecutan una consulta y devuelven resultados. Entre los ejemplos se incluyen ToListAsync() , ToArrayAsync() ,
SingleAsync() . No hay versiones asincrónicas de algunos operadores de LINQ como Where(...) o OrderBy(...)
porque estos métodos solo generan el árbol de la expresión de LINQ y no hacen que la consulta se ejecute en la
base de datos.
IMPORTANT
Los métodos de extensión asincrónicos de EF Core se define en el espacio de nombres Microsoft.EntityFrameworkCore . Es
necesario importar este espacio de nombres para que los métodos estén disponibles.
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.
Las consultas SQL sin formato se pueden usar para ejecutar un procedimiento almacenado.
Paso de parámetros
WARNING
Use siempre la parametrización para las consultas SQL sin formato
Al indicar cualquier valor proporcionado por el usuario en una consulta SQL sin formato, debe tener cuidado para evitar
ataques por inyección de código SQL. Además de validar que dichos valores no contienen caracteres no válidos, use siempre
la parametrización que envía los valores separados del texto SQL.
En concreto, no pase nunca a $"" o FromSqlRaw una cadena concatenada o interpolada ( ExecuteSqlRaw ) con valores
proporcionados por el usuario sin validar. Los métodos FromSqlInterpolated y ExecuteSqlInterpolated permiten usar
la sintaxis de interpolación de cadenas de manera que se protege frente a los ataques por inyección de código SQL.
En el ejemplo siguiente se pasa un parámetro único a un procedimiento almacenado; para ello, se incluye un
marcador de posición de parámetro en la cadena de consulta SQL y se proporciona un argumento adicional.
Aunque esta sintaxis se pueda parecer a la de String.Format , 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";
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.
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:
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:
La redacción con LINQ requiere que la consulta SQL sin procesar se pueda redactar, ya que EF Core tratará el
código SQL proporcionado como una subconsulta. Las consultas SQL que se pueden redactar empiezan con la
palabra clave SELECT . Es más, el código SQL que se pasa no debe contener ningún carácter ni opción que no sea
válido en una subconsulta, como los siguientes:
Un punto y coma final
En SQL Server, una sugerencia en el nivel de consulta final (por ejemplo, OPTION (HASH JOIN) )
En SQL Server, una cláusula ORDER BY que no se usa con OFFSET 0 O BIEN TOP 100 PERCENT en la cláusula
SELECT
SQL Server no permite la redacción sobre llamadas a procedimientos almacenados, por lo que cualquier intento
de aplicar operadores de consulta adicionales a ese tipo de llamada producirá código SQL no válido. Use el
método AsEnumerable o AsAsyncEnumerable justo después de los métodos FromSqlRaw o FromSqlInterpolated
para asegurarse de que EF Core no intente redactar sobre un procedimiento almacenado.
Seguimiento de cambios
Las consultas que usan los métodos FromSqlRaw o FromSqlInterpolated siguen las mismas reglas de seguimiento
de cambios que las demás consultas LINQ en EF Core. Por ejemplo, si la consulta proyecta tipos de entidad, se
realizará un seguimiento de los resultados de forma predeterminada.
En el ejemplo siguiente se usa una consulta SQL sin formato que realiza una selección en una función con valores
de tabla (TVF) y después deshabilita el seguimiento de cambios con la llamada a AsNoTracking :
var searchTerm = ".NET";
Limitaciones
Existen algunas limitaciones que debe considerar al usar las consultas SQL sin formato:
La consulta SQL debe devolver datos para todas las propiedades del tipo de entidad.
Los nombres de las columnas del conjunto de resultados deben coincidir con los nombres de las columnas a
los que se asignan las propiedades. Tenga en cuenta que este comportamiento es diferente al de EF6. En EF6 se
omitía la asignación de propiedades y columnas para las consultas SQL sin formato, y los nombres de las
columnas del conjunto de resultados tenían que coincidir con los nombres de las propiedades.
La consulta SQL no puede contener datos relacionados. Sin embargo, en muchos casos puede redactar sobre la
consulta si usa el operador Include para devolver datos relacionados (consulte Inclusión de datos
relacionados).
Versiones anteriores
EF Core 2.2 y las versiones anteriores tenían dos sobrecargas de método denominadas FromSql , que se
comportaban de la misma manera que las sobrecargas FromSqlRaw y FromSqlInterpolated más recientes.
Resultaba sencillo llamar de forma accidental al método de cadena sin formato cuando la intención era llamar al
método de cadena interpolada y viceversa. La llamada accidental a la sobrecarga incorrecta podría generar como
resultado consultas que no se parametrizaban cuando debían.
Filtros de consulta global
08/04/2020 • 4 minutes to read • Edit Online
NOTE
Esta característica se incluyó por primera vez en EF Core 2.0.
Los filtros de consulta global son predicados de consulta LINQ (una expresión booleana que habitualmente se pasa
al operador de consulta LINQ Where) aplicados a los tipos de entidad del modelo de metadatos (habitualmente en
OnModelCreating). Estos filtros se aplican automáticamente a las consultas LINQ que implican a esos tipos de
entidad, incluidos aquellos a los que se hace referencia de forma indirecta, por ejemplo mediante el uso de Include
o de referencias de propiedad de navegación directas. Algunas aplicaciones comunes de esta característica son:
Eliminación temporal : un tipo de entidad define una propiedad IsDeleted.
Ser vicios multiinquilino : un tipo de entidad define una propiedad TenantId.
Ejemplo
En el ejemplo siguiente se muestra cómo usar los filtros de consulta global para implementar los comportamientos
de consulta de multiinquilino y de eliminación temporal en un simple modelo de creación de blogs.
TIP
Puede ver un ejemplo de este artículo en GitHub.
Tenga en cuenta la declaración de un campo tenantId en la entidad Blog. Se usará para asociar cada instancia de
blog con un inquilino específico. También hay definida una propiedad IsDeleted en el tipo de entidad Post. Se usa
para llevar un seguimiento de si una instancia Post se eliminó de manera temporal. Es decir, la instancia se marca
como eliminada sin quitar físicamente los datos subyacentes.
A continuación, configure los filtros de consulta en OnModelCreating con la API HasQueryFilter .
Las expresiones de predicado pasadas a las llamadas de HasQueryFilter ahora se aplicarán automáticamente a
cualquier consulta LINQ para esos tipos.
TIP
Tenga en cuenta el uso de un campo en el nivel de instancia de DbContext: _tenantId se usa para establecer el inquilino
actual. Los filtros de nivel de modelo usan el valor de la instancia de contexto correcta (es decir, la instancia que está
ejecutando la consulta).
NOTE
Actualmente no es posible definir varios filtros de consulta en la misma entidad. Solo se aplicará el último. Sin embargo,
puede definir un único filtro con varias condiciones mediante el operador lógico AND ( && en C#).
Deshabilitación de filtros
Los filtros se pueden deshabilitar para consultas LINQ individuales mediante el operador IgnoreQueryFilters() .
blogs = db.Blogs
.Include(b => b.Posts)
.IgnoreQueryFilters()
.ToList();
Limitaciones
Los filtros de consulta global tienen las limitaciones siguientes:
Solo se pueden definir filtros para el tipo de entidad raíz de una jerarquía de herencia.
Etiquetas de consulta
08/04/2020 • 2 minutes to read • Edit Online
NOTE
Esta característica es nueva en EF Core 2.2.
Esta característica ayuda a establecer la correlación de las consultas LINQ en el código con las consultas SQL
generadas capturadas en los registros. El usuario anota una consulta LINQ con el nuevo método TagWith() :
var nearestFriends =
(from f in context.Friends.TagWith("This is my spatial query!")
orderby f.Location.Distance(myLocation) descending
select f).Take(5).ToList();
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:
La siguiente consulta:
Se traduce en:
-- GetNearestFriends
-- Limit
También es posible utilizar cadenas de varias líneas como etiquetas de consulta. Por ejemplo:
var results = Limit(GetNearestFriends(myLocation), 25).TagWith(
@"This is a multi-line
string").ToList();
-- GetNearestFriends
-- Limit
-- This is a multi-line
-- string
Restricciones conocidas
Las etiquetas de consulta no se pueden parametrizar : EF Core siempre trata las etiquetas de consulta de la
consulta LINQ como literales de cadena que se incluyen en el código SQL generado. Las consultas compiladas que
toman las etiquetas de consulta como parámetros no están permitidas.
Funcionamiento de las consultas
08/04/2020 • 4 minutes to read • Edit Online
Entity Framework Core usa Language Integrated Query (LINQ) para consultar datos de la base de datos. LINQ
permite usar C# (o el lenguaje .NET que prefiera) para escribir consultas fuertemente tipadas basadas en el
contexto derivado y las clases de entidad.
WARNING
Valide siempre la entrada del usuario: aunque EF Core protege contra los ataques por inyección de código SQL con el
uso de parámetros y el escape de cadenas literales en consultas, no valida las entradas. Se debe realizar una validación
apropiada, según los requisitos de la aplicación, antes de que los valores de los orígenes que no son de confianza se usen en
consultas LINQ, se asignen a las propiedades de una entidad o se pasen a otras API de EF Core. Esto incluye cualquier
intervención del usuario que se use para construir consultas de manera dinámica. Incluso al usar LINQ, si acepta la
intervención del usuario para crear expresiones, necesita garantizar que solo se pueden construir las expresiones previstas.
Guardado de datos
08/04/2020 • 2 minutes to read • Edit Online
Cada instancia de contexto tiene un elemento ChangeTracker que es responsable de realizar el seguimiento de los
cambios que deben escribirse en la base de datos. Al realizar cambios en instancias de las clases de entidad, estos
cambios se registran en ChangeTracker y luego se escriben en la base de datos cuando se llama a SaveChanges . El
proveedor de base de datos es responsable de convertir los cambios en operaciones específicas de la base de
datos (por ejemplo, los comandos INSERT , UPDATE y DELETE de una base de datos relacional).
Guardado básico
08/04/2020 • 3 minutes to read • Edit Online
Obtenga información sobre cómo agregar, modificar y quitar datos mediante las clases de entidad y contexto.
TIP
Puede ver un ejemplo de este artículo en GitHub.
Agregar datos
Use el método DbSet.Add para agregar instancias nuevas de las clases de entidad. Los datos se insertarán en la
base de datos cuando llame a SaveChanges.
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 EntityEntry.State para establecer el estado de una sola
unidad. Por ejemplo, context.Entry(blog).State = EntityState.Modified .
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.
Eliminar datos
Use el método DbSet.Remove 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 = context.Blogs.First();
context.Blogs.Remove(blog);
context.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.
// update
var firstBlog = context.Blogs.First();
firstBlog.Url = "";
// remove
var lastBlog = context.Blogs.Last();
context.Blogs.Remove(lastBlog);
context.SaveChanges();
}
Guardado de datos relacionados
08/04/2020 • 4 minutes to read • Edit Online
Además de las entidades aisladas, también puede usar las relaciones definidas en el modelo.
TIP
Puede ver un ejemplo de este artículo en GitHub.
context.Blogs.Add(blog);
context.SaveChanges();
}
TIP
Use la propiedad EntityEntry.State para establecer el estado de una sola unidad. Por ejemplo,
context.Entry(blog).State = EntityState.Modified .
blog.Posts.Add(post);
context.SaveChanges();
}
post.Blog = blog;
context.SaveChanges();
}
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 = context.Blogs.Include(b => b.Posts).First();
var post = blog.Posts.First();
blog.Posts.Remove(post);
context.SaveChanges();
}
Eliminación en cascada
08/04/2020 • 23 minutes to read • Edit Online
En la terminología de las bases de datos, la eliminación en cascada se usa habitualmente para describir una
característica que permite eliminar una fila para desencadenar de manera automática la eliminación de las filas
relacionadas. Un concepto estrechamente relacionado que también abarcan los comportamientos de eliminación
de EF Core es la eliminación automática de una entidad secundaria cuando se interrumpe su relación con una
entidad primaria. Esto normalmente se conoce como la "eliminación de entidades huérfanas".
EF Core implementa varios comportamientos de eliminación distintos y permite configurar estos
comportamientos de las relaciones individuales. EF Core también implementa convenciones que configuran
automáticamente útiles comportamientos de eliminación predeterminados para cada relación en función de la
obligatoriedad de la relación.
Comportamientos de eliminación
Los comportamientos de eliminación se define en el tipo de enumerador DeleteBehavior y se pueden pasar a la
API fluida OnDelete para controlar si la eliminación de una entidad principal o primaria o la interrupción de la
relación con entidades dependientes o secundarias debería tener un efecto secundario en estas últimas.
Hay tres acciones que EF puede llevar a cabo cuando se elimina una entidad principal o primaria o cuando se
interrumpe la relación con una entidad secundaria:
Se puede eliminar la entidad secundaria o dependiente.
Los valores de clave externa de la entidad secundaria se pueden establecer en NULL.
La entidad secundaria se mantiene sin cambios.
NOTE
El comportamiento de eliminación configurado en el modelo EF Core solo se aplica cuando la entidad principal se elimina
mediante EF Core y las entidades dependientes se cargan en la memoria (es decir, en el caso de las entidades dependientes
con seguimiento). Es necesario configurar un comportamiento en cascada correspondiente en la base de datos para
garantizar que la acción necesaria se aplique a los datos a los que el contexto no hace seguimiento. Si usa EF Core para
crear la base de datos, este comportamiento en cascada se configurará automáticamente.
La segunda acción mencionada, establecer un valor de clave externa en NULL, no es válida si la clave externa no
admite un valor NULL. (Una clave externa que no admite un valor NULL equivale a una relación obligatoria). En
estos casos, EF Core hace seguimiento de que la propiedad de la clave externa se haya marcado como NULL hasta
que se llama a SaveChanges, momento en que se genera una excepción porque el cambio no puede durar hasta
la base de datos. Esto es similar a obtener una infracción de restricción de la base de datos.
Existen cuatro comportamientos de eliminación, los que se indican en las tablas siguientes.
Relaciones opcionales
En el caso de las relaciones opcionales (clave externa que admite un valor NULL) es posible guardar un valor de
clave externa NULL, lo que tiene como resultado los efectos siguientes:
EF EC TO EN L A EN T IDA D DEP EN DIEN T E EF EC TO EN L A EN T IDA D DEP EN DIEN T E
N O M B RE DEL C O M P O RTA M IEN TO O SEC UN DA RIA EN L A M EM O RIA O SEC UN DA RIA EN L A B A SE DE DATO S
Relaciones obligatorias
En el caso de las relaciones obligatorias (clave externa que no admite un valor NULL) no es posible guardar un
valor de clave externa NULL, lo que tiene como resultado los efectos siguientes:
En las tablas anteriores, None puede dar lugar a una infracción de restricción. Por ejemplo, si se elimina una
entidad principal o secundaria pero no se hace ninguna acción para cambiar la clave externa de una entidad
dependiente o secundaria, es probable que la base de datos genere una excepción en SaveChanges debido a una
infracción de restricción externa.
En un nivel superior:
Si tiene entidades que no pueden existir sin una entidad primaria y quiere que EF se encargue de eliminar
automáticamente las entidades secundarias, use Cascade.
Habitualmente, las entidades que no pueden existir sin una entidad primaria usan las relaciones
obligatorias, en las que el valor predeterminado es Cascade.
Si tiene entidades que pueden tener o no una entidad primaria y quiere que EF se encargue de anular
automáticamente la clave externa, use ClientSetNull.
Habitualmente, las entidades que pueden existir sin una entidad primaria usan las relaciones
opcionales, en las que el valor predeterminado es ClientSetNull.
Si quiere que la base de datos también intente propagar los valores NULL a las claves externas
secundarias incluso si no se cargó la entidad secundaria, use SetNull. Sin embargo, tenga en cuenta que
la base de datos debe admitir esta opción y que configurar de este modo la base de datos puede dar
lugar a otras restricciones, por lo que, en la práctica, a menudo esta opción no es factible. Es por este
motivo que SetNull no es el valor predeterminado.
Si no quiere que EF Core elimine una entidad o anule la clave externa automáticamente, use Restrict. Tenga en
cuenta que esta acción requiere que el código mantenga las entidades secundarias y sus valores de clave
externa sincronizados de manera manual. Si no es así, se generarán excepciones de restricción.
NOTE
En EF Core, a diferencia de lo que ocurre en EF6, los efectos en cascada no se producen de inmediato, sino que solo cuando
se llama a SaveChanges.
NOTE
Cambios en EF Core 2.0: en versiones anteriores, Restrict haría que las propiedades de claves externas opcionales de las
entidades dependientes con seguimiento se establecieran en NULL y que este fuera el comportamiento de eliminación
predeterminado de las relaciones opcionales. En EF Core 2.0, se introdujo ClientSetNull para representar ese
comportamiento y se transformó en el valor predeterminado de las relaciones opcionales. El comportamiento de Restrict se
ajustó para que nunca haya efectos secundarios en las entidades dependientes.
context.Remove(blog);
try
{
Console.WriteLine();
Console.WriteLine(" Saving changes:");
context.SaveChanges();
DumpSql();
Console.WriteLine();
Console.WriteLine($" SaveChanges threw {e.GetType().Name}: {(e is DbUpdateException ?
e.InnerException.Message : e.Message)}");
}
Saving changes:
DELETE FROM [Posts] WHERE [PostId] = 1
DELETE FROM [Posts] WHERE [PostId] = 2
DELETE FROM [Blogs] WHERE [BlogId] = 1
After SaveChanges:
Blog '1' is in state Detached with 2 posts referenced.
Post '1' is in state Detached with FK '1' and no reference to a blog.
Post '2' is in state Detached with FK '1' and no reference to a blog.
Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1
SaveChanges threw DbUpdateException: Cannot insert the value NULL into column 'BlogId', table
'EFSaving.CascadeDelete.dbo.Posts'; column does not allow nulls. UPDATE fails. The statement has been
terminated.
Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 2
DELETE FROM [Blogs] WHERE [BlogId] = 1
After SaveChanges:
Blog '1' is in state Detached with 2 posts referenced.
Post '1' is in state Unchanged with FK 'null' and no reference to a blog.
Post '2' is in state Unchanged with FK 'null' and no reference to a blog.
Saving changes:
SaveChanges threw InvalidOperationException: The association between entity types 'Blog' and 'Post' has
been severed but the foreign key for this relationship cannot be set to null. If the dependent entity should
be deleted, then setup the relationship to use cascade deletes.
blog.Posts.Clear();
try
{
Console.WriteLine();
Console.WriteLine(" Saving changes:");
context.SaveChanges();
DumpSql();
Console.WriteLine();
Console.WriteLine($" SaveChanges threw {e.GetType().Name}: {(e is DbUpdateException ?
e.InnerException.Message : e.Message)}");
}
Saving changes:
DELETE FROM [Posts] WHERE [PostId] = 1
DELETE FROM [Posts] WHERE [PostId] = 2
After SaveChanges:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Detached with FK '1' and no reference to a blog.
Post '2' is in state Detached with FK '1' and no reference to a blog.
Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
SaveChanges envía eliminaciones para las entidades dependientes o secundarias (entradas)
Después de guardar, las entidades dependientes o secundarias (entradas) se desasocian porque se eliminaron
de la base de datos
DeleteBehavior.ClientSetNull o DeleteBehavior.SetNull con relación obligatoria
Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1
SaveChanges threw DbUpdateException: Cannot insert the value NULL into column 'BlogId', table
'EFSaving.CascadeDelete.dbo.Posts'; column does not allow nulls. UPDATE fails. The statement has been
terminated.
Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
SaveChanges intenta establecer la clave externa de la entrada en NULL, pero se produce un error porque la
clave externa no admite un valor NULL
DeleteBehavior.ClientSetNull o DeleteBehavior.SetNull con relación opcional
Saving changes:
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 1
UPDATE [Posts] SET [BlogId] = NULL WHERE [PostId] = 2
After SaveChanges:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK 'null' and no reference to a blog.
Post '2' is in state Unchanged with FK 'null' and no reference to a blog.
Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
SaveChanges establece la clave externa de las entidades dependientes o secundarias (entradas) en NULL
Después de guardar, las entidades dependientes o secundarias (entradas) ahora tienen valores NULL de clave
externa y se quitó la referencia a la entidad principal o primaria (blog) que se eliminó
DeleteBehavior.Restrict con relación obligatoria u opcional
After loading entities:
Blog '1' is in state Unchanged with 2 posts referenced.
Post '1' is in state Unchanged with FK '1' and reference to blog '1'.
Post '2' is in state Unchanged with FK '1' and reference to blog '1'.
Saving changes:
SaveChanges threw InvalidOperationException: The association between entity types 'Blog' and 'Post' has
been severed but the foreign key for this relationship cannot be set to null. If the dependent entity should
be deleted, then setup the relationship to use cascade deletes.
Las entradas se marcan como modificadas porque la interrupción de la relación hizo que la clave externa se
marcara como NULL
Si la clave externa no admite un valor NULL, el valor actual no se modificará incluso si se marca como
NULL
Como Restrict le indica a EF que no establezca automáticamente la clave externa en NULL, no se anula y
SaveChanges genera una excepción sin guardar
Si solo se carga la entidad principal (por ejemplo, cuando se hace una solicitud para un blog sin
Include(b => b.Posts) para que también incluya las entradas), SaveChanges solo generará código SQL para
eliminar la entidad principal o primaria:
Las entidades dependientes o secundarias (entradas) solo se eliminarán si la base de datos tiene configurado un
comportamiento en cascada correspondiente. Si usa EF para crear la base de datos, este comportamiento en
cascada se configurará automáticamente.
Administrar los conflictos de simultaneidad
08/04/2020 • 7 minutes to read • Edit Online
NOTE
En esta página se documenta cómo funciona la simultaneidad en EF Core y cómo administrar los conflictos de simultaneidad
en la aplicación. Consulte Concurrency Tokens (Tokens de simultaneidad) para detalles sobre cómo configurar los tokens de
simultaneidad en el modelo.
TIP
Puede ver un ejemplo de este artículo en GitHub.
La simultaneidad de base de datos se refiere a las situaciones en las que varios procesos o usuarios acceden o
cambian los mismos datos de una base de datos al mismo tiempo. El control de simultaneidad se refiere a los
mecanismos específicos que se usan para garantizar la coherencia de los datos en presencia de cambios
simultáneos.
EF Core implementa el control de simultaneidad optimista, lo que significa que permitirá que varios procesos o
usuarios hagan cambios de manera independiente sin la sobrecarga que implica la sincronización o el bloqueo. En
la situación ideal, estos cambios no interferirán entre sí y, por tanto, se realizarán correctamente. En el peor
escenario, dos o más procesos intentarán hacer cambios conflictivos y solo uno de ellos se completará
correctamente.
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;
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.
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 DbContext.OnConfiguring para configurar el contexto y crear externamente DbContextOptions y pasarlas al
constructor del contexto.
TIP
DbContextOptionsBuilder es la API que usó en DbContext.OnConfiguring para configurar el contexto y ahora va a usarla
para crear externamente DbContextOptions .
Una alternativa es seguir usando DbContext.OnConfiguring , pero aceptar una DbConnection que se guarda y luego
se usa en DbContext.OnConfiguring .
public class BloggingContext : DbContext
{
private DbConnection _connection;
Utilizar System.Transactions
NOTE
Esta característica es nueva en EF Core 2.1.
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();
try
{
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
Limitaciones de System.Transactions
1. EF Core se basa en los proveedores de base de datos para implementar la compatibilidad con
System.Transactions. Si bien la compatibilidad es bastante común entre los proveedores de ADO.NET para
.NET Framework, la API solo se agregó recientemente a .NET Core y, por tanto, la compatibilidad no siempre
está tan extendida. Si un proveedor no implementa la compatibilidad con System.Transactions, es posible
que las llamadas a estas API se omitan completamente. SqlClient para .NET no lo admite desde la versión 2.1
en adelante. SqlClient para .NET Core 2.0 generará una excepción si intent usar la característica.
IMPORTANT
Se recomienda probar que la API se comporte correctamente con el proveedor antes de usarla para administrar las
transacciones. Si no es así, recomendamos que se ponga en contacto con el mantenedor del proveedor de base de
datos.
El guardado asincrónico evita bloquear un subproceso mientras se escriben los cambios en la base de datos. Esto
puede resultar útil para evitar la inmovilización de la interfaz de usuario de una aplicación cliente pesada. Las
operaciones asincrónicas también pueden aumentar el rendimiento de una aplicación web, donde se puede liberar
el subproceso para atender otras solicitudes mientras se completa la operación de la base de datos. Para más
información, consulte Programación asincrónica en C#.
WARNING
EF Core no admite que varias operaciones en paralelo se ejecuten en la misma instancia de contexto. Siempre debe esperar
que se complete una operación antes de iniciar la siguiente. Habitualmente, para esto se usa la palabra clave await en cada
una de las operaciones asincrónicas.
Entity Framework Core proporciona DbContext.SaveChangesAsync() como una alternativa asincrónica para
DbContext.SaveChanges() .
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.
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)
=> !context.Entry(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.
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.
Sin embargo, si la entidad usa valores de clave generados automáticamente, el método Update se puede usar para
ambos casos:
Habitualmente, el método Update marca la entidad para actualización y no para inserción. Sin embargo, si la
entidad tiene una clave generada automáticamente y no se estableció ningún valor de clave, la entidad se marca
automáticamente para inserción.
TIP
Este comportamiento se introdujo en EF Core 2.0. En las versiones anteriores siempre es necesario elegir explícitamente si
agregar o actualizar.
Si la entidad no usa claves generadas automáticamente, la aplicación debe decidir si la entidad se debe inserta ro
actualizar. Por ejemplo:
context.SaveChanges();
}
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).
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 = context.Blogs
.Include(b => b.Posts)
.FirstOrDefault(b => b.BlogId == blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId);
if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
}
}
context.SaveChanges();
}
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 = context.Blogs
.Include(b => b.Posts)
.FirstOrDefault(b => b.BlogId == blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
context.Entry(existingBlog).CurrentValues.SetValues(blog);
foreach (var post in blog.Posts)
{
var existingPost = existingBlog.Posts
.FirstOrDefault(p => p.PostId == post.PostId);
if (existingPost == null)
{
existingBlog.Posts.Add(post);
}
else
{
context.Entry(existingPost).CurrentValues.SetValues(post);
}
}
context.SaveChanges();
}
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)
{
context.ChangeTracker.TrackGraph(
rootEntity,
n =>
{
var entity = (EntityBase)n.Entry.Entity;
n.Entry.State = entity.IsNew
? EntityState.Added
: entity.IsChanged
? EntityState.Modified
: entity.IsDeleted
? EntityState.Deleted
: EntityState.Unchanged;
});
context.SaveChanges();
}
Las marcas solo se muestran como parte de la entidad para simplificar el ejemplo. Habitualmente, las marcas
serían parte de una DTO o alguno otro estado incluido en la solicitud.
Establecimiento de valores explícitos para
propiedades generadas
08/04/2020 • 6 minutes to read • Edit Online
Una propiedad generada es una propiedad cuyo valor se genera (ya sea por medio de EF o de la base de datos)
cuando la entidad se agrega o actualiza. Consulte Generated Properties (Propiedades generadas) para más
información.
Puede haber situaciones en las que desea establecer un valor explícito para una propiedad generada en lugar de
hacer que se genere uno.
TIP
Puede ver un ejemplo de este artículo en GitHub.
El modelo
El modelo que se usa en este artículo contiene una entidad Employee única.
modelBuilder.Entity<Employee>()
.Property(b => b.EmploymentStarted)
.HasDefaultValueSql("CONVERT(date, GETDATE())");
La salida muestra que la base de datos generó un valor para el primer empleado y que el valor explícito se usó
para el segundo.
NOTE
Tenemos una solicitud de característica en el trabajo pendiente para hacer esto de manera automática dentro del proveedor
de SQL Server.
context.Database.OpenConnection();
try
{
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Employees ON");
context.SaveChanges();
context.Database.ExecuteSqlRaw("SET IDENTITY_INSERT dbo.Employees OFF");
}
finally
{
context.Database.CloseConnection();
}
modelBuilder.Entity<Employee>()
.Property(b => b.LastPayRaise)
.ValueGeneratedOnAddOrUpdate();
modelBuilder.Entity<Employee>()
.Property(b => b.LastPayRaise)
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore);
NOTE
De manera predeterminada, EF Core generará una excepción si se intenta guardar un valor explícito para una propiedad
que se haya configurado para generarse durante la actualización. Para evitar esto, deberá descender a la API de metadatos
de nivel inferior y establecer AfterSaveBehavior (como se mostró anteriormente).
NOTE
Cambios en EF Core 2.0: en las versiones anteriores, el comportamiento posterior a la acción de guardar se controlaba
mediante la marca IsReadOnlyAfterSave . Esta marca ahora está obsoleta y la reemplaza AfterSaveBehavior .
También hay un desencadenador en la base de datos para generar valores para la columna LastPayRaise durante
las operaciones UPDATE .
CREATE TRIGGER [dbo].[Employees_UPDATE] ON [dbo].[Employees]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
context.SaveChanges();
La salida muestra que la base de datos generó un valor para el primer empleado y que el valor explícito se usó
para el segundo.
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
Mono 5.4
Xamarin.iOS(2) 10.14
Xamarin.Android(2) 8.0
UWP(3) 10.0.16299
Unity(4) 2018.1
.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 packages.config de NuGet a PackageReference. Agregue la
propiedad siguiente al archivo del proyecto:
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
Problemas de informes
En el caso de cualquier combinación que no funcione según lo esperado, se recomienda crear incidencias en el
rastreador de problemas de EF Core. En el caso de problemas relacionados con Xamarin, use el seguimiento de
problemas de Xamarin.Android o de Xamarin.iOS.
Proveedores de bases de datos
08/04/2020 • 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
PA Q UET E B A SE DE DATO S M A N T EN EDO R N OTA S O C REA DO PA RA VÍN C ULO S
N UGET C O M PAT IB L ES O P RO VEEDO R REQ UISITO S L A VERSIÓ N ÚT IL ES
FileContextCore Almacena datos Morris Janatzek Con fines de 3.0 Archivo Léame
en archivos. desarrollo.
Pomelo.EntityFra Servidor MyCAT Proyecto Pomelo Solo versión 1.1 Archivo Léame
meworkCore.My Foundation preliminar
Cat
optionsBuilder.UseSqlServer(
"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 Microsoft.EntityFrameworkCore.Relational , 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
Microsoft.EntityFrameworkCore.Relational . 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 Microsoft.EntityFrameworkCore.Relational como dependencia directa de la
aplicación.
Proveedor de base de datos de Microsoft SQL Server
para EF Core
08/04/2020 • 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 Microsoft.EntityFrameworkCore.SqlServer.
CLI de .NET Core
Visual Studio
NOTE
A partir de la versión 3.0.0, el proveedor hace referencia a Microsoft.Data.SqlClient (las versiones anteriores dependían de
System.Data.SqlClient). Si su proyecto depende directamente de SqlClient, asegúrese de que haga referencia al paquete
Microsoft.Data.SqlClient.
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.
NOTE
Esta API es nueva 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:
modelBuilder.HasServiceTier("BusinessCritical");
modelBuilder.HasDatabaseMaxSize("2 GB");
modelBuilder.HasPerformanceLevel("BC_Gen4_1");
Use HasPerformanceLevelSql para configurar el grupo elástico, ya que el valor no es un literal de cadena:
TIP
Puede encontrar todos los valores admitidos en la documentación de ALTER DATABASE.
Proveedor de base de datos SQLite para EF Core
08/04/2020 • 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 Microsoft.EntityFrameworkCore.Sqlite.
CLI de .NET Core
Visual Studio
Limitaciones
Vea Limitaciones de SQLite para conocer algunas limitaciones importantes del proveedor de SQLite.
Limitaciones del proveedor de bases de datos de
SQLite EF Core
11/03/2020 • 4 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 de 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
Columnas calculadas
modelBuilder.Entity<MyEntity>()
.Property(e => e.DecimalProperty)
.HasConversion<double>();
AddColumn ✔ 1.0
AddForeignKey ✗
AddPrimaryKey ✗
AddUniqueConstraint ✗
AlterColumn ✗
CreateIndex ✔ 1.0
CreateTable ✔ 1.0
DropColumn ✗
DropForeignKey ✗
DropIndex ✔ 1.0
DropPrimaryKey ✗
DropTable ✔ 1.0
DropUniqueConstraint ✗
RenameColumn ✔ 2.2.2
RenameIndex ✔ 2.1
RenameTable ✔ 1.0
Insertar ✔ 2.0
Actualizar ✔ 2.0
Eliminar ✔ 2.0
Vea realizar otros tipos de cambios de esquema de tabla en la documentación de SQLite para obtener más detalles.
En el futuro, EF puede admitir algunas de estas operaciones mediante el enfoque de regeneración de tablas en
segundo plano. Puede realizar un seguimiento de esta característica en nuestro proyecto de github.
Proveedor de Azure Cosmos DB para EF Core
08/04/2020 • 9 minutes to read
NOTE
Este proveedor es nuevo 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 Microsoft.EntityFrameworkCore.Cosmos.
CLI de .NET Core
Visual Studio
Introducción
TIP
Puede ver en GitHub un ejemplo de este artículo.
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; }
}
context.Add(new Order
{
Id = 1,
ShippingAddress = new StreetAddress { City = "London", Street = "221 B Baker St" },
PartitionKey = "1"
});
await context.SaveChangesAsync();
}
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.
modelBuilder.HasDefaultContainer("Store");
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:
modelBuilder.Entity<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:
modelBuilder.Entity<Order>()
.HasPartitionKey(o => o.PartitionKey);
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. Al emitir
una consulta, se puede agregar una condición para que sea una partición única.
await context.SaveChangesAsync();
}
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:
modelBuilder.Entity<Order>().OwnsOne(
o => o.ShippingAddress,
sa =>
{
sa.ToJsonProperty("Address");
sa.Property(p => p.Street).ToJsonProperty("ShipsToStreet");
sa.Property(p => p.City).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 :
No es necesario que las entidades en propiedad proporcionen valores de clave explícitos que se deban almacenar:
await context.SaveChangesAsync();
}
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:
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.
distributor.ShippingCenters.Remove(distributor.ShippingCenters.Last());
await context.SaveChangesAsync();
}
{
"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
}
Trabajar con datos no estructurados en EF Core
proveedor de Azure Cosmos DB
11/03/2020 • 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.
context.Add(order);
await context.SaveChangesAsync();
}
orderEntry.State = EntityState.Modified;
await context.SaveChangesAsync();
}
WARNING
La propiedad "__jObject" 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 versiones futuras.
NOTE
Los cambios en la entidad invalidarán los valores almacenados en "__jObject" durante SaveChanges .
Usar CosmosClient
Para desacoplar completamente de EF Core obtener el objeto CosmosClient que forma parte del SDK de Azure
Cosmos dB de DbContext :
order.Remove("TrackingNumber");
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 c.Id ASC, c. Discriminator
ASC ' devolvería elementos a los que les falta la propiedad "TrackingNumber" .
Limitaciones del proveedor de Azure Cosmos DB de
EF Core
11/03/2020 • 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.
Limitaciones temporales
Incluso si solo hay un tipo de entidad sin herencia asignada a un contenedor, todavía tiene una propiedad
discriminadora.
Los tipos de entidad con claves de partición no funcionan correctamente en algunos escenarios
no se admiten llamadas Include
no se admiten llamadas Join
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 llamando a .Wait() en el Task devuelto. 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
Este proveedor de base de datos permite usar Entity Framework Core con una base de datos en memoria. Puede
resultar útil en el caso de 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. Este proveedor se mantiene como parte
del proyecto Entity Framework Core.
Instalar
Instale el paquete NuGet Microsoft.EntityFrameworkCore.InMemory.
CLI de .NET Core
Visual Studio
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
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
Estos envíos no se han actualizado desde EF Core 1,1 y ha habido cambios significativos desde que se produjo el error 681 al
realizar un 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 https://github.com/aspnet/EntityFrameworkCore.
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.
Por ejemplo:
Microsoft.EntityFrameworkCore.SqlServer
Npgsql.EntityFrameworkCore.PostgreSQL
EntityFrameworkCore.SqlServerCompact40
Cambios que afectan al proveedor
11/03/2020 • 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 etiquetas providers-beware
y providers-fyi en nuestros problemas y solicitudes de incorporación de cambios.
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.
Sitio web
Devart Entity Developer
Entity Developer es un potente diseñador ORM para ADO.NET 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.
Sitio web
nHydrate ORM para Entity Framework
ORM 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 ORM 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.
Sitio 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: 2, 3.
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 ORM
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.
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
Microsoft.EntityFrameworkCore.AutoHistory
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.
Repositorio de GitHub
EFSecondLevelCache.Core
Extensión que permite almacenar los resultados de las consultas EF Core en una caché de segundo nivel. De este
modo, al volver a ejecutar las mismas cadenas, se pueden recuperar los datos directamente de la caché sin acceder
a la base de datos. Para EF Core: 2.
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
EntityFrameworkCore.Scaffolding.Handlebars
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.
Repositorio de GitHub
NeinLinq.EntityFrameworkCore
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.
Repositorio de GitHub
Microsoft.EntityFrameworkCore.UnitOfWork
Extensión de Microsoft.EntityFrameworkCore para admitir los repositorios, los patrones de unidades de trabajo y
varias bases de datos compatibles con las transacciones distribuidas. Para EF Core: 2.
Repositorio de GitHub
EFCore.BulkExtensions
Extensiones de EF Core para operaciones masivas (Insert, Update y Delete). Para EF Core: 2, 3.
Repositorio de GitHub
Bricelam.EntityFrameworkCore.Pluralizer
Agrega pluralización en tiempo de diseño. Para EF Core: 2.
Repositorio de GitHub
Toolbelt.EntityFrameworkCore.IndexAttribute
Recuperación del atributo [Index] (con extensión para la creación de modelos). Para EF Core: 2, 3.
Repositorio de GitHub
EfCore.InMemoryHelpers
Proporciona un contenedor alrededor del proveedor de la base de datos en memoria de EF Core. Así, hace que
actúe de una forma más similar a la de un proveedor relacional. Para EF Core: 2.
Repositorio de GitHub
EFCore.TemporalSupport
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
EFCore.TimeTraveler
Permita consultas completas de Entity Framework Core en el historial temporal de SQL Server mediante el código
de EF Core, las entidades y las asignaciones que ya ha definido. Para viajar a través del tiempo, encapsule el código
en using (TemporalQuery.AsOf(targetDateTime)) {...} . Para EF Core: 3.
Repositorio de GitHub
EntityFrameworkCore.TemporalTables
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.
Repositorio de GitHub
EntityFrameworkCore.Cacheable
Caché de consulta de segundo nivel y alto rendimiento. Para EF Core: 2.
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.
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.
Sitio web
Expressionify
Agregue compatibilidad para llamar a métodos de extensión en expresiones lambda LINQ. Para EF Core: 3.1
Repositorio de GitHub
XLinq
Tecnología Language Integrated Query (LINQ) para bases de datos relacionales. Permite usar C# para escribir
consultas fuertemente tipadas. Para EF Core: 3.1
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. XLinq declara instrucciones SQL (como SELECT , FROM , WHERE ) como métodos de
C# de primera clase, combinando 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
Referencia sobre las herramientas de Entity
Framework Core
08/04/2020 • 2 minutes to read
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.
Las herramientas de la Consola del Administrador de paquetes de EF Core se ejecutan en la Consola del
Administrador de paquetes de Visual Studio.
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="Microsoft.NET.Sdk" o similar en el archivo de proyecto).
Ambas herramientas exponen la misma funcionalidad. Si está desarrollando en Visual Studio, se recomienda usar
las herramientas de la Consola del Administrador de paquetes , ya que proporcionan una experiencia más
integrada.
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
11/03/2020 • 17 minutes to read
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 la CLI son multiplataforma y se ejecutan en un símbolo del sistema.
Por lo tanto, no tiene que hacer nada para instalar las herramientas, pero tiene que:
Restaure los paquetes antes de usar las herramientas en un nuevo proyecto.
Instale un paquete para actualizar las herramientas a una versión más reciente.
Para asegurarse de que está obteniendo la versión más reciente de las herramientas, se recomienda que realice
también el siguiente paso:
Edite el archivo . csproj y agregue una línea que especifique la versión más reciente del paquete
Microsoft. EntityFrameworkCore. Tools . Por ejemplo, el archivo . csproj podría incluir un ItemGroup que
tiene este aspecto:
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.3" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.1" />
</ItemGroup>
Actualice las herramientas cuando reciba un mensaje similar al del ejemplo siguiente:
La versión de herramientas de EF Core ' 2.1.1-RTM-30846 ' es anterior a la del runtime ' 2.1.3-RTM-32065 '.
Actualice las herramientas para las últimas características y correcciones de errores.
Install-Package Microsoft.EntityFrameworkCore.Tools
Actualice las herramientas ejecutando el siguiente comando en la consola del administrador de paquetes .
Update-Package Microsoft.EntityFrameworkCore.Tools
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.
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
-Context <cadena > 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.
Para mostrar información de ayuda sobre un comando, use el comando Get-Help de PowerShell.
TIP
Los parámetros context, Project y proyecto admiten la expansión de pestañas.
Agregar-migración
Agrega una nueva migración.
Parámetros:
PA RÁ M ET RO DESC RIP C IÓ N
Drop-Database
Quita la base de datos.
Parámetros:
PA RÁ M ET RO DESC RIP C IÓ N
Get-DbContext
Obtiene información sobre un tipo de DbContext .
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
Scaffold-DbContext
Genera código para un DbContext y tipos de entidad para una base de datos. Para que Scaffold-DbContext
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
-OutputDir <cadena > Directorio en el que se colocarán los archivos. Las rutas de
acceso son relativas al directorio del proyecto.
-Schemas <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.
-Tables <String [] > Tablas para las que se van a generar tipos de entidad. Si se
omite este parámetro, se incluyen todas las tablas.
Ejemplo:
Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;"
Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
Ejemplo que scaffolding solo selecciona tablas y crea el contexto en una carpeta independiente con un nombre
especificado:
Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;"
Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables "Blog","Post" -ContextDir Context -
Context BlogContext
Script: migración
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 <cadena > 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.
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 con el nombre de la migración.
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.
PA RÁ M ET RO DESC RIP C IÓ N
Update-Database -Migration 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:
Recursos adicionales
Migraciones
Ingeniería inversa
Referencia de herramientas de Entity Framework
Core: CLI de .NET
11/03/2020 • 19 minutes to read
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.
Si usa Visual Studio, se recomienda usar en su lugar las herramientas de la consola del administrador de
paquetes :
Funcionan automáticamente con el proyecto actual seleccionado en la consola del administrador de
paquetes sin necesidad de cambiar manualmente los directorios.
Abren automáticamente los archivos generados por un comando una vez completado el comando.
También puede usar dotnet ef como herramienta local. Para usarlo como una herramienta local,
restaure las dependencias de un proyecto que la declara como una dependencia de herramientas
mediante un archivo de manifiestode la herramienta.
Instale el SDK de .NET Core.
Instale el paquete de Microsoft.EntityFrameworkCore.Design más reciente.
EF Core 1. x
Instale la versión SDK de .NET Core 2.1.200. Las versiones posteriores no son compatibles con las
herramientas de la CLI para EF Core 1,0 y 1,1.
Configure la aplicación para que use la versión del SDK de 2.1.200 modificando su archivo global. JSON .
Este archivo se incluye normalmente en el directorio de la solución (uno encima del proyecto).
Edite el archivo de proyecto y agregue Microsoft.EntityFrameworkCore.Tools.DotNet como un elemento
de DotNetCliToolReference . Especifique la versión más reciente de 1. x, por ejemplo: 1.1.6. Vea el ejemplo
de archivo de proyecto al final de esta sección.
Instale la versión 1. x más reciente del paquete de Microsoft.EntityFrameworkCore.Design , por ejemplo:
Con las referencias de paquete agregadas, el archivo de proyecto tiene un aspecto similar al siguiente:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design"
Version="1.1.6"
PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet"
Version="1.1.6" />
</ItemGroup>
</Project>
Una referencia de paquete con PrivateAssets="All" no se expone a los proyectos que hacen referencia
a este proyecto. Esta restricción es especialmente útil para los paquetes que normalmente se usan solo
durante el desarrollo.
Comprobar la instalación
Ejecute los siguientes comandos para comprobar que las herramientas de la CLI de EF Core están instaladas
correctamente:
dotnet restore
dotnet ef
Opciones comunes
O P C IÓ N DESC RIP C IÓ N
-f --force No confirme.
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:
O P C IÓ N DESC RIP C IÓ N
En el ejemplo siguiente se scaffoldingan todos los esquemas y las tablas y se colocan los nuevos archivos en la
carpeta Models .
En el ejemplo siguiente se scaffolding solo las tablas seleccionadas y se crea el contexto en una carpeta
independiente con un nombre especificado:
dotnet ef dbcontext scaffold "Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;"
Microsoft.EntityFrameworkCore.SqlServer -o Models -t Blog -t Post --context-dir Context -c BlogContext
Opciones:
O P C IÓ N DESC RIP C IÓ N
O P C IÓ N DESC RIP C IÓ N
O P C IÓ N DESC RIP C IÓ N
En el ejemplo siguiente se crea un script para todas las migraciones después de la migración de InitialCreate.
Recursos adicionales
Migraciones
Ingeniería inversa
Creación de DbContext en tiempo de diseño
11/03/2020 • 4 minutes to read
Algunos de los comandos de herramientas de EF Core (por ejemplo, los comandos de migración ) requieren que se
cree una instancia de DbContext 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 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 el DbContext :
De servicios de aplicación
Si el proyecto de inicio usa el host de Web ASP.net 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
Program.CreateHostBuilder() , llamando a Build() y, a continuación, accediendo a la propiedad Services .
NOTE
Cuando se crea una nueva aplicación de ASP.NET Core, este enlace se incluye de forma predeterminada.
El propio DbContext 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 el DbContext que toma una
instancia de DbContextOptions<TContext> como argumento y mediante el método AddDbContext<TContext> .
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace MyProject
{
public class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
public BloggingContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("Data Source=blog.db");
NOTE
El parámetro args no se usa actualmente. Existe un problema al realizar el seguimiento de la capacidad de especificar
argumentos en tiempo de diseño desde las herramientas.
Un generador en tiempo de diseño puede ser especialmente útil si necesita configurar DbContext de forma
diferente en tiempo de diseño que en tiempo de ejecución, si el constructor DbContext 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
método BuildWebHost en la clase Main de la aplicación ASP.NET Core.
Servicios en tiempo de diseño
11/03/2020 • 2 minutes to read
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.
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 ASP.NET, y mediante enlace de datos, con
WPF y WinForms.
Conectividad de base de datos basada en ADO.NET y varios proveedores disponibles para conectarse a
SQL Server, Oracle, MySQL, SQLite, PostgreSQL, DB2, etc.
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.
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 ef6.exe para habilitar, agregar, generar scripts y aplicar migraciones desde los
ensamblados. Reemplaza a migrate.exe .
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\Entities.edmx" Link="Model\Entities.edmx" />
<Compile Include="..\EdmxDesignHost\Entities.Context.cs" Link="Model\Entities.Context.cs" />
<Compile Include="..\EdmxDesignHost\Thing.cs" Link="Model\Thing.cs" />
<Compile Include="..\EdmxDesignHost\Person.cs" Link="Model\Person.cs" />
</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
16/03/2020 • 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 NuGet.org.
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.
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
DbFunctions.Like() para habilitar la escritura de consultas LINQ que se traducen en LIKE en SQL #241
Migrate.exe 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 DbQuery.ToString() 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/web.
Configuration.
<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 ADO.net 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 atributo de [Index] 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 App/web. config 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 acoplable & ser vicio de singularidad .
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 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.
El método DbModelBuilder. Configurations. 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 VB.NET
Migraciones de Code First
Migraciones de Code First automática
Migrate. exe
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
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 de migraciones basadas en código EF 4,3
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
11/03/2020 • 8 minutes to read
En versiones anteriores de EF, el código se dividió entre las bibliotecas principales (principalmente System. Data.
Entity. dll) que se distribuyeron como parte de las bibliotecas de .NET Framework y fuera de banda (OOB)
(principalmente EntityFramework. dll) 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.
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
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>. Context.tt 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 VB.NET, 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.
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.
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 System. Data. dll, 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
11/03/2020 • 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:
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 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
11/03/2020 • 4 minutes to read
Entity Framework está formado por EF Tools para Visual Studio y EF runtime.
EF runtime
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.
Instalar EF NuGet package
Para instalar el paquete EntityFramework, haga clic con el botón derecho en la carpeta referencias del proyecto
y seleccione administrar paquetes NuGet.. .
Install-Package EntityFramework
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 EF runtimes anteriores a 4.1 formaban parte de .NET Framework y no se puede instalar por separado.
Instalación de la versión preliminar más reciente
Los métodos anteriores le proporcionarán la versión más reciente totalmente compatible con la versión de Entity
Framework. A menudo hay versiones preliminares disponibles de Entity Framework que nos encantaría que
probara y de las que puede 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 ninguna versión preliminar disponible
automáticamente obtendrá la última versión compatible de Entity Framework.
Como alternativa, puede ejecutar el siguiente comando en la consola del administrador de paquetes.
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 System.Data.Entity.DbContext
(a menudo se conoce como la clase de contexto). Puede usar un DbContext asociado a un modelo para:
Escribir y ejecutar consultas
Materializar resultados de una consulta como objetos entidad
Realizar un seguimiento de los cambios realizados en esos objetos
Almacenar los cambios del objeto 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.
Una vez que tenga un contexto, debe consultar, agregar (mediante métodos Add o Attach ) o quitar (mediante
Remove ) entidades en el contexto mediante estas propiedades. El acceso a una propiedad de DbSet en un objeto
de contexto representa una consulta de inicio que devuelve todas las entidades del tipo especificado. Tenga en
cuenta acceder a la propiedad no 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 recopilació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 método de extensión Load , DbEntityEntry.Reload ,
Database.ExecuteSqlCommand y DbSet<T>.Find , si no se encuentra una entidad con la clave especificada en el
contexto.
Vigencia
La duración del contexto comienza cuando se crea y finaliza cuando la instancia se elimina o es recogida por el
recolector de basura. 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 se crea un contenedor de inyección de dependencia, normalmente es
responsabilidad del contenedor el eliminar 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 tanto no debe compartirse entre varios subprocesos
que realizan el trabajo en él al mismo tiempo.
Si una excepción provoca que el contexto entre en un estado irrecuperable, puede finalizar toda la
aplicación.
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
11/03/2020 • 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 se puede gobernar por una restricción referencial, que
describe qué extremo de la relación es un rol principal y que 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.
course.DepartmentID = newCourse.DepartmentID;
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.
course.DepartmentID = 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, el course también se agrega a
la colección department.Courses y la propiedad de clave externa correspondiente en el objeto course se
establece en el valor de la propiedad clave del Departamento.
course.Department = 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:
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.
Eliminando o agregando un objeto en una colección de entidades. Por ejemplo, puede Agregar un objeto de
tipo Course a la colección department.Courses . Esta operación crea una relación entre un curso
determinado y un department determinado. Si los objetos están asociados al contexto, la referencia de
departamento y la propiedad de clave externa del objeto Course se establecerán en el department
adecuado.
department.Courses.Add(newCourse);
Mediante el método ChangeRelationshipState 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 utilizar 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 método
ChangeRelationshipState y pasar el parámetro EntityState.Added , permite al SchoolContext saber que se
ha agregado una relación entre los dos objetos:
((IObjectContextAdapter)context).ObjectContext.
ObjectStateManager.
ChangeRelationshipState(course, instructor, c => c.Instructor, EntityState.Added);
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 => c.Instructor, EntityState.Deleted);
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 EF Designer para crear un modelo,
establezca el atributo ConcurrencyMode en fixed para especificar que se debe comprobar la simultaneidad de la
propiedad. Al utilizar Code First para definir un modelo, use la anotación ConcurrencyCheck en las propiedades
cuya simultaneidad desea comprobar. Al trabajar con Code First también puede usar la anotación TimeStamp 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.
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.
using System.Collections.Generic;
using System.Data.Entity;
namespace AsyncDemo
{
public class BloggingContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
namespace AsyncDemo
{
class Program
{
static void Main(string[] args)
{
PerformDatabaseOperations();
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
Este código llama al método PerformDatabaseOperations , 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 completado
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 Program.cs
1. Línea 2: la instrucción using del espacio de nombres System. Data. Entity nos permite acceder a los métodos
de extensión de EF Async.
2. Línea 4: la instrucción using del espacio de nombres System. Threading. Tasks nos permite usar el tipo de
tarea .
3. Línea 12 & 18: estamos capturando 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 que todo el trabajo para el programa se haya realizado (línea 18).
4. Línea 25: hemos actualizado PerformSomeDatabaseOperations para que se marque como Async y
devuelva una tarea .
5. Línea 35: ahora se llama a la versión asincrónica de SaveChanges y se espera su finalización.
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 espacio de nombres System. Data.
Entity, consulte la clase QueryableExtensions. También deberá agregar "Using System. Data. Entity" a las
instrucciones using.
using System;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
namespace AsyncDemo
{
class Program
{
static void Main(string[] args)
{
var task = PerformDatabaseOperations();
task.Wait();
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
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 método PerformDatabaseOperations 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
llamada de espera hasta que se completa la operación de base de datos. Una vez que se completa, se ejecutará
el resto de PerformDatabaseOperations .
3. SaveChanges completado
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 sencilla, 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 provocar que un gran número de subprocesos aumentar la superficie de memoria.
Configuración basada en código
11/03/2020 • 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 (App.
config/Web. config) 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.
Usar DbConfiguration
La configuración basada en código en EF6 y versiones posteriores se consigue mediante la creación de una
subclase de System. Data. Entity. config. DbConfiguration. Se deben seguir las siguientes directrices para crear
subclases de DbConfiguration:
Cree solo una clase DbConfiguration para la aplicación. Esta clase especifica la configuración de todo el
dominio de aplicación.
Coloque la clase DbConfiguration en el mismo ensamblado que la clase DbContext. (Consulte la sección
mover DbConfiguration si desea cambiar esto).
Asigne a la clase DbConfiguration un constructor sin parámetros público.
Establezca las opciones de configuración llamando a métodos DbConfiguration 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 System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.SqlServer;
namespace MyNamespace
{
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => 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.
Mover DbConfiguration
Hay casos en los que no es posible colocar la clase DbConfiguration en el mismo ensamblado que la clase
DbContext. Por ejemplo, puede tener dos clases DbContext cada una en ensamblados distintos. Hay dos opciones
para controlar esto.
La primera opción es usar el archivo de configuración para especificar la instancia de DbConfiguration que se va
a usar. Para ello, establezca el atributo codeConfigurationType de la sección entityFramework. Por ejemplo:
El valor de codeConfigurationType debe ser el ensamblado y el nombre completo del espacio de nombres de la
clase DbConfiguration.
La segunda opción consiste en 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 tipo DbConfiguration, como se muestra arriba, o el ensamblado y el
nombre de espacio de nombres de tipo completo String. Por ejemplo:
[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")]
public class MyContextContext : DbContext
{
}
Invalidar DbConfiguration
Hay algunas situaciones en las que es necesario invalidar el conjunto de configuración en DbConfiguration.
Normalmente, no lo hacen los desarrolladores de aplicaciones, sino los proveedores y complementos de terceros
que no pueden usar una clase derivada de DbConfiguration.
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, debe registrar un controlador similar
al siguiente:
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.
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 ADO.NET normales. Por ejemplo:
<connectionStrings>
<add name="BlogContext"
providerName="System.Data.SqlClient"
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://*/BloggingModel.csdl|
res://*/BloggingModel.ssdl|
res://*/BloggingModel.msl;
provider=System.Data.SqlClient;
provider connection string=
"data source=(localdb)\mssqllocaldb;
initial catalog=Blogging;
integrated security=True;
multipleactiveresultsets=True;""
providerName="System.Data.EntityClient" />
</connectionStrings>
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="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices,
EntityFramework.SqlServer" />
</providers>
<interceptors>
<interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework"/>
</interceptors>
<interceptors>
<interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
<parameters>
<parameter value="C:\Temp\LogOutput.txt"/>
</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="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
<parameters>
<parameter value="C:\Temp\LogOutput.txt"/>
<parameter value="true" type="System.Boolean"/>
</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.
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>
<defaultConnectionFactory type="MyNamespace.MyCustomFactory, 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="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
<parameters>
<parameter value="System.Data.SqlServerCe.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="System.Data.Entity.Infrastructure.SqlConnectionFactory, 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.
<contexts>
<context type=" Blogging.BlogContext, MyAssembly" disableDatabaseInitialization="true" />
</contexts>
<contexts>
<context type=" Blogging.BlogContext, MyAssembly">
<databaseInitializer type="Blogging.MyCustomBlogInitializer, MyAssembly" />
</context>
</contexts>
Los parámetros de constructor utilizan la misma sintaxis que los generadores de conexiones predeterminados.
<contexts>
<context type=" Blogging.BlogContext, MyAssembly">
<databaseInitializer type="Blogging.MyCustomBlogInitializer, 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 inicializador de
MigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration> .
<contexts>
<context type="Blogging.BlogContext, MyAssembly">
<databaseInitializer type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[Blogging.BlogContext,
MyAssembly], [Blogging.Migrations.Configuration, MyAssembly]], EntityFramework" />
</context>
</contexts>
Cadenas de conexión y modelos
11/03/2020 • 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.
namespace Demo.EF
{
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.
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.
<configuration>
<connectionStrings>
<add name="BloggingCompactDatabase"
providerName="System.Data.SqlServerCe.4.0"
connectionString="Data Source=Blogging.sdf"/>
</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:
Como alternativa, puede usar el formato "Name =<nombre de cadena de conexión>" para la cadena que se pasa
al constructor DbContext. Por ejemplo:
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
app. config/Web. config
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 de EF al archivo app. config o Web. config. 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://*/Northwind.csdl|
res://*/Northwind.ssdl|
res://*/Northwind.msl;
provider=System.Data.SqlClient;
provider connection string=
"Data Source=.\sqlexpress;
Initial Catalog=Northwind;
Integrated Security=True;
MultipleActiveResultSets=True""
providerName="System.Data.EntityClient"/>
</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:
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.
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:
NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .
NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .
NOTE
Para obtener más información sobre los servicios relacionados con el proveedor en EF6, consulte la sección modelo de
proveedor EF6 .
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 tabla
__MigrationHistory utilizada 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 .
NOTE
Normalmente, este servicio no se cambia directamente, ya que la implementación predeterminada usa el registro del
proveedor ADO.NET normal. Para obtener más información sobre los servicios relacionados con el proveedor en EF6,
consulte la sección modelo de proveedor EF6 .
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. DbContext, Action < cadena>, 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.
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 () .
namespace ConnectionManagementExamples
{
class ConnectionManagementExampleEF5
{
public static void TwoDbContextsOneConnection()
{
using (var context1 = new BloggingContext())
{
var conn =
((EntityConnection)
((IObjectContextAdapter)context1).ObjectContext.Connection)
.StoreConnection;
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 System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
namespace ConnectionManagementExamples
{
class ConnectionManagementExample
{
public static void PassingAnOpenConnection()
{
using (var conn = new SqlConnection("{connectionString}"))
{
conn.Open();
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.
((IObjectContextAdapter)context).ObjectContext.Connection.State
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 el almacén subyacente la conexión se cerrará. 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 System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.EntityClient;
namespace ConnectionManagementExamples
{
public class DatabaseOpenConnectionBehaviorEF5
{
public static void DatabaseOpenConnectionBehavior()
{
using (var context = new BloggingContext())
{
// At this point the underlying store connection is closed
context.Database.Connection.Open();
context.SaveChanges();
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 System.Data;
using System.Data.Entity;
using System.Data.Entity.Core.EntityClient;
using System.Data.Entity.Infrastructure;
namespace ConnectionManagementExamples
{
internal class DatabaseOpenConnectionBehaviorEF6
{
public static void DatabaseOpenConnectionBehavior()
{
using (var context = new BloggingContext())
{
// At this point the underlying store connection is closed
context.Database.Connection.Open();
// The underlying store connection remains open for the next operation
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.
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, por lo que los resultados no tienen un identificador único). , etc.).
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.
trn.Commit();
}
}
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();
executionStrategy.Execute(
() =>
{
using (var db = new BloggingContext())
{
using (var trn = db.Database.BeginTransaction())
{
db.Blogs.Add(new Blog { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
db.SaveChanges();
trn.Commit();
}
}
});
Control de errores de confirmación de transacción
11/03/2020 • 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 System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.SqlServer;
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: Category (principal\Master) y Product
(Dependent\Detail). 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
Abra Visual Studio.
Archivo-> nuevo proyecto de>....
Seleccione Windows en el panel izquierdo y Windows FormsApplication en el panel derecho.
Escriba WinFormswithEFSample como nombre
Seleccione Aceptar .
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.
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Data.Entity;
namespace WinFormswithEFSample
{
public class ObservableListSource<T> : ObservableCollection<T>, IListSource
where T : class
{
private IBindingList _bindingList;
IList IListSource.GetList()
{
return _bindingList ?? (_bindingList = this.ToBindingList());
}
}
}
Definir 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 System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinFormswithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WinFormswithEFSample
{
public class Category
{
private readonly ObservableListSource<Product> _products =
new ObservableListSource<Product>();
Además de definir entidades, debe definir una clase que derive de DbContext y exponga las propiedades de
DbSet<de la carpa> . 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 el rellenado de 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 System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Text;
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.
Explorador de ser vidores de> de vista
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])
)
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 app. config 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 simples de ICollection<T>. 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 ProductModel.TT que se anidará en el archivo ProductModel. edmx.
Haga doble clic en el archivo ProductModel.tt 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 ProductModel.tt. 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 ProductModel.tt y
elija "ejecutar herramienta personalizada".
Si ahora abre el archivo Category.cs (que está anidado en ProductModel.tt), debería ver que la colección Products
tiene el tipo Obser vableListSource<>de producto .
Compile el proyecto.
Carga diferida
La propiedad Products de la clase categoría y la propiedad categoría 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 tiene acceso 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
En Explorador de soluciones, haga doble clic en el archivo Form1.CS 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 .
Presione 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 categor yBindingNavigatorSaveItem_click se agregará en la sección siguiente.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.Entity;
namespace WinFormswithEFSample
{
public partial class Form1 : Form
{
ProductContext _context;
public Form1()
{
InitializeComponent();
}
// 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.Categories.Load();
IMPORTANT
Este documento es válido para WPF en .NET Framework
En este documento se describe el enlace de datos para WPF en .NET Framework. Para los nuevos proyectos de .NET Core, se
recomienda usar EF Core en lugar de Entity Framework 6. Se realiza un seguimiento de la documentación para el enlace de
datos en EF Core en el #778.
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, vea Instalar NuGet.
Crear la aplicación
Abra Visual Studio.
Archivo> -> Nuevo - Proyecto....
Seleccione Windows en el panel izquierdo y WPFApplication en el panel derecho
Escriba WPFwithEFSample como nombre
Seleccione Aceptar
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 aplicaciones de
Entity Framework 6.
Definir un modelo
En este tutorial puede optar por implementar un modelo mediante Code First o EF Designer. Complete una de las
dos secciones siguientes.
Opción 1: Definir un modelo utilizando 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
siguiente sección (Opción 2: definir un modelo con Database First) si prefiere usar Database First para
realizar ingeniería inversa del modelo desde una base de datos mediante el diseñador de EF
Cuando se usa el desarrollo de Code First, normalmente se empieza escribiendo clases de .NET Framework que
definen el modelo conceptual (dominio).
Agregue una nueva clase a la WPFwithEFSample:
Haga clic con el botón derecho en el nombre del proyecto
Seleccione Agregar y, a continuación, Nuevo elemento
Seleccione Clase e introduzca Producto para el nombre de clase
Reemplace la definición de clase Product por el código siguiente:
namespace WPFwithEFSample
{
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
using System.Collections.ObjectModel;
namespace WPFwithEFSample
{
public class Category
{
public Category()
{
this.Products = new ObservableCollection<Product>();
}
El Products propiedad en el Categor y clase y Categor y propiedad en el Product clase 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 expone las propiedades de DbSet
TEntity.> Las propiedades<DbSet TEntity> permiten al contexto saber qué tipos desea incluir en el modelo.
Una instancia del tipo derivado DbContext administra los objetos de entidad durante el tiempo de ejecución, lo
que incluye rellenar objetos con datos de una base de datos, realizar el seguimiento de cambios y conservar los
datos en la base de datos.
Agregue una nueva clase ProductContext al proyecto con la siguiente definición:
using System.Data.Entity;
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 utilizando Database First
En esta sección se muestra cómo usar Database First para realizar ingeniería inversa del 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
mediante Code First), omita esta sección y vaya directamente a la sección Carga diferida.
Crear una base de datos existente
Normalmente, cuando se dirige a una base de datos existente, ya se creará, pero para este tutorial necesitamos
crear una base de datos para tener acceso.
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 LocalDB.
Vamos a seguir adelante y generar la base de datos.
Ver> - 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 el Explorador de servidores antes de que deba seleccionar
Microsoft SQL Server como origen de datos
Conéctese a LocalDB o SQL Express, dependiendo del que haya instalado, e introduzca 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 el Explorador de servidores, haga clic con el botón derecho en
ella y seleccione Nueva consulta
Copie el siguiente 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])
)
Seleccione la conexión a la base de datos que creó 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 de verificación situada junto a 'Tablas' para importar todas las tablas y haga clic en
'Finalizar'
Una vez completado el proceso de ingeniería inversa, el nuevo modelo se agrega al proyecto y se abre para que lo
vea en Entity Framework Designer. También se ha agregado un archivo App.config 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, deberá actualizar el diseñador de EF para usar la generación de código
EF6.
Haga clic con el botón derecho en un punto vacío del modelo en EF Designer 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 EF 6.x DbContext#Generator para C , escriba ProductsModel como nombre y haga clic en
Agregar
Actualización de la generación de código para el enlace de datos
EF genera código a partir del modelo mediante plantillas T4. Las plantillas incluidas con Visual Studio o
descargadas de la galería de Visual Studio están diseñadas 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 WPFWPF es deseable usar Obser vableCollection para las propiedades de colección
para que WPFWPF pueda realizar un seguimiento de los cambios realizados en las colecciones. Para ello vamos a
modificar las plantillas para utilizar ObservableCollection.
Abra el Explorador de soluciones y busque el archivo ProductModel.edmx
Busque el archivo ProductModel.tt que se anidará en el archivo ProductModel.edmx
Haga doble clic en el archivo ProductModel.tt para abrirlo en el editor de Visual Studio
Busque y reemplace las dos apariciones de "ICollection" por "Obser vableCollection ". Estos se
encuentran aproximadamente en las líneas 296 y 484.
Busque y reemplace la primera aparición de "HashSet " por "Obser vableCollection ". Esta ocurrencia 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 ". Se encuentra aproximadamente en la línea 424.
Guarde el archivo ProductModel.tt. Esto debería hacer que se regenere el código de las entidades. Si el
código no se regenera automáticamente, haga clic con el botón derecho en ProductModel.tt y elija "Ejecutar
herramienta personalizada".
Si ahora abre el archivo de Category.cs (que está anidado en ProductModel.tt), debería ver que la colección
Products tiene el tipo Obser vableCollection<Product> .
Compile el proyecto.
Carga diferida
El Products propiedad en el Categor y clase y Categor y propiedad en el Product clase 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 le ofrece la opción de cargar entidades relacionadas desde la base de datos automáticamente la primera vez
que tenga acceso a la propiedad de navegación. Con este tipo de carga (denominada carga diferida), tenga en
cuenta que la primera vez que acceda 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 logra la carga diferida mediante la creación de instancias de tipos de
proxy derivados durante el tiempo de ejecución y, a continuación, invalidar las propiedades virtuales en las clases
para agregar el enlace de carga. Para obtener la carga diferida de objetos relacionados, debe declarar los
captadores de propiedades de navegación como públicos y vir tuales (Overridable en Visual Basic) y la clase no
debe estar sellada (NotOverridable en Visual Basic). Cuando se utilizan las propiedades de navegación de
Database First se hacen virtuales automáticamente para habilitar la carga diferida. En la sección Code First
elegimos hacer que las propiedades de navegación sean virtuales por la misma razón
using System.Data.Entity;
using System.Linq;
using System.Windows;
namespace WPFwithEFSample
{
public partial class MainWindow : Window
{
private ProductContext _context = new ProductContext();
public MainWindow()
{
InitializeComponent();
}
_context.SaveChanges();
// Refresh the grids so the database generated values show up.
this.categoryDataGrid.Items.Refresh();
this.productsDataGrid.Items.Refresh();
}
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.
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
08/04/2020 • 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 <nombreDelModelo>.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 <nombreDelModelo>.Context.tt 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).
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.
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 ASP.NET 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).
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 generador de entidades de seguimiento propio.
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 el modelo
En primer lugar, necesitamos un proyecto en el que colocar el modelo.
Archivo-> nuevo proyecto de>...
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, ADO.NET 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 las entidades de
seguimiento propio.
Si usa Visual Studio 2012
Expanda BloggingModel. edmx en Explorador de soluciones y elimine BloggingModel.TT y
BloggingModel.Context.TT esto 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 C# plantilla, escriba STETemplate como nombre y haga clic en Agregar .
Los archivos STETemplate.TT y STETemplate.Context.TT 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, ADO.net generador de entidades de
seguimiento propio .
Escriba STETemplate como nombre y haga clic en Agregar .
Los archivos STETemplate.TT y STETemplate.Context.TT se agregan directamente al proyecto
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-> proyecto de complemento de>...
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 app. config para el proyecto **STESample **y copie el elemento connectionStrings .
Pegue el elemento connectionStrings como un elemento secundario del elemento de configuración del
archivo Web. config en el proyecto STESample. Ser vice.
Ahora es el momento de implementar el servicio real.
Abra ISer vice1.CS y reemplace el contenido por el código siguiente.
using System.Collections.Generic;
using System.ServiceModel;
namespace STESample.Service
{
[ServiceContract]
public interface IService1
{
[OperationContract]
List<Blog> GetBlogs();
[OperationContract]
void UpdateBlog(Blog blog);
}
}
namespace STESample.Service
{
public class Service1 : IService1
{
/// <summary>
/// Gets all the Blogs and related Posts.
/// </summary>
public List<Blog> GetBlogs()
{
using (BloggingContext context = new BloggingContext())
{
return context.Blogs.Include("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.
}
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.");
}
}
}
}
}
using STESample.ConsoleTest.BloggingService;
using System;
using System.Linq;
namespace STESample.ConsoleTest
{
class Program
{
static void Main(string[] args)
{
// Print out the data before we change anything
Console.WriteLine("Initial Data:");
DisplayBlogsAndPosts();
Console.WriteLine();
Console.WriteLine();
}
Initial Data:
ADO.NET Blog
- Intro to EF
- What is New
After Adding:
ADO.NET Blog
- Intro to EF
- What is New
The New Blog
- Welcome to the new blog
- What's new on the new blog
After Update:
ADO.NET 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:
ADO.NET Blog
- Intro to EF
- What is New
<Window.Resources>
<CollectionViewSource
x:Key="blogViewSource"
d:DesignSource="{d:DesignInstance {x:Type STESample:Blog}, CreateList=True}"/>
<CollectionViewSource
x:Key="blogPostsViewSource"
Source="{Binding Posts, Source={StaticResource blogViewSource}}"/>
</Window.Resources>
namespace STESample.WPFTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
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
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:
context.SaveChangesAsync().Wait();
}
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
(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 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:
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 ADO.NET subyacente cuando se intenta cancelar. Si esto
no ocurre y la tarea se cancela correctamente, la salida tendrá un aspecto similar al siguiente:
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:
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()'
DbInterception.Add(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 = LogManager.GetCurrentClassLogger();
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
11/03/2020 • 139 minutes to read
1. Introducción
Los marcos de trabajo de asignación relacional de objetos son una manera 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 de investigación y mejorar el
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.
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 ADO.NET tiene una publicación que describe cómo usar una plantilla T4 para la generación
de vistas (<http://blogs.msdn.com/b/adonet/archive/2008/06/20/how-to-use-a-t4-template-for-view-
generation.aspx>). 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:
VB.NET: <http://visualstudiogallery.msdn.microsoft.com/118b44f2-1b91-4de2-a584-7a680418941d>
C#: <http://visualstudiogallery.msdn.microsoft.com/ae7730ce-ddab-470f-8456-1b313cd2c44d>
Si usa Entity Framework 6, puede obtener las plantillas T4 de la generación de vistas de la galería de Visual Studio
en <http://visualstudiogallery.msdn.microsoft.com/18a7db90-6705-4d19-9dd1-0a6c23d0751f>.
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 <http://blog.oneunicorn.com/2011/12/11/whats-
the-deal-with-mapping-foreign-keys-using-the-entity-framework/>.
A L USA R H A GA ESTO
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.
context.Configuration.AutoDetectChangesEnabled = false;
var product = context.Products.Find(productId);
context.Configuration.AutoDetectChangesEnabled = true;
...
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 É
this.productsGrid.Visible = 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:
this.productsGrid.DataSource = context.GetProductsForCategory(selectedCategory);
if (this.orderCountFilterList.SelectedItem.Value != defaultFilterText)
{
int orderCount = int.Parse(orderCountFilterList.SelectedValue);
myCustomers = myCustomers.Where(c => c.Orders.Count > orderCount);
}
if (this.countryFilterList.SelectedItem.Value != defaultFilterText)
{
myCustomers = myCustomers.Where(c => c.Address.Country == countryFilterList.SelectedValue);
}
this.customersGrid.DataSource = myCustomers;
this.customersGrid.DataBind();
}
Para evitar esta recompilación, puede volver a escribir CompiledQuery para tener en cuenta los posibles filtros:
this.customersGrid.DataSource = myCustomers;
this.customersGrid.DataBind();
}
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)
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>. Contiene<>(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 usar 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á:
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 en que se>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 = context.MyEntities.Select(entity => entity.Id).Contains(id);
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:
Una versión más rápida de este mismo código implicaría llamar a SKIP con una expresión lambda:
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 = context.Customers.OrderBy(c => c.LastName).Skip(() => i);
for (; i < count; ++i)
{
var currentCustomer = skippyCustomers.FirstOrDefault();
ProcessCustomer(currentCustomer);
}
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:
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 context.MyEntities
where ids.Contains(entity.Id)
select entity;
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é.
((ObjectQuery)productsForCategory).MergeOption = MergeOption.NoTracking;
5.1.3 deshabilitar el seguimiento de cambios para un conjunto de entidades completo mediante ObjectContext
context.Products.MergeOption = MergeOption.NoTracking;
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:
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.
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:
context.Products.MergeOption = MergeOption.NoTracking;
var q = context.Products.Where(p => p.Category.CategoryName == "Beverages");
var q = context.Products.AsNoTracking()
.Where(p => p.Category.CategoryName == "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:
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
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
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:
SqlQuery en DbSet:
ExecyteStoreQuery:
var beverages = context.ExecuteStoreQuery<Product>(
@" SELECT P.ProductID, P.ProductName, P.SupplierID, P.CategoryID, P.QuantityPerUnit, P.UnitPrice,
P.UnitsInStock, P.UnitsOnOrder, P.ReorderLevel, P.Discontinued, P.DiscontinuedDate
FROM Products AS P INNER JOIN Categories AS C ON P.CategoryID = C.CategoryID
WHERE (C.CategoryName = '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
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 deben realizar 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. manzanas a manzanas, excepto
el costo de crear un nuevo contexto.
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 más rápido MetadataCollection<T> búsquedas.
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: <http://blogs.msdn.com/b/adonet/archive/2008/11/25/working-with-large-models-in-entity-framework-
part-2.aspx>.
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í: http://msdn.microsoft.com/library/dd468057.aspx.
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.
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
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:
¿Necesita acceder a muchas propiedades de navegación desde No : las dos opciones probablemente sí lo hagan. Sin
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.
¿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á.
¿El código se ejecuta lejos de la base de datos? (mayor latencia No : cuando la latencia de red no es un problema, el uso de la
de red) 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.
orders.Load();
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: <http://blogs.microsoft.co.il/blogs/gilf/archive/2009/10/13/table-splitting-in-entity-
framework.aspx>.
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:
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
{
context.Configuration.AutoDetectChangesEnabled = false;
var product = context.Products.Find(productId);
...
}
finally
{
context.Configuration.AutoDetectChangesEnabled = 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
<http://blog.oneunicorn.com/2012/03/12/secrets-of-detectchanges-part-3-switching-off-automatic-
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 r = q.ToList();
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 de C# null coherente entre distintos 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 C# null, ya que ahora Entity Framework generará un
SQL más sencillo que exponga el modo 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:
context.Configuration.UseDatabaseNullSemantics = 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 Tim adicionales e a la finalización de su
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, visite http://msdn.microsoft.com/library/hh191443.aspx. 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 el túnel. 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 utiliza 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). genere las vistas y, a continuación, valide las
vistas y tenga 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.
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 acción<cadena> delegado.
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 Web. config o app. config de la aplicación.
<interceptors>
<interceptor type="System.Data.Entity.Infrastructure.Interception.DatabaseLogger, EntityFramework">
<parameters>
<parameter value="C:\Path\To\My\LogOutput.txt"/>
</parameters>
</interceptor>
</interceptors>
Para obtener más información sobre cómo agregar registro sin volver a compilar, vaya a
<http://blog.oneunicorn.com/2014/02/09/ef-6-1-turning-on-logging-without-recompiling/>.
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
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 System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.Entity.Infrastructure;
using System.Data.EntityClient;
using System.Data.Objects;
using System.Linq;
namespace QueryComparison
{
public partial class NorthwindEntities : ObjectContext
{
private static readonly Func<NorthwindEntities, string, IQueryable<Product>> productsForCategoryCQ =
CompiledQuery.Compile(
(NorthwindEntities context, string categoryName) =>
context.Products.Where(p => p.Category.CategoryName == categoryName)
);
// '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 = record.FieldCount;
// Treat all products as Product, even if they are the subtype DiscontinuedProduct.
Product product = new Product();
product.ProductID = record.GetInt32(0);
product.ProductName = record.GetString(1);
product.SupplierID = record.GetInt32(2);
product.CategoryID = record.GetInt32(3);
product.QuantityPerUnit = record.GetString(4);
product.UnitPrice = record.GetDecimal(5);
product.UnitsInStock = record.GetInt16(6);
product.UnitsOnOrder = record.GetInt16(7);
product.ReorderLevel = record.GetInt16(8);
product.ReorderLevel = record.GetInt16(8);
product.Discontinued = record.GetBoolean(9);
productsList.Add(product);
}
}
}
}
<Query complexity="Lookup">
<CommandText>Select value distinct top(4) e.Idle_Time From NavisionFKContext.Session 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>
1 1 .3 .1 .3 A g g r e g a t i n g Su b t o t a l s
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 NGen. exe 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.
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:
NGen. exe 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 de Ngen.
exe.
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.
cd <Solution directory>\packages\EntityFramework.6.0.2\lib\net45
%WINDIR%\Microsoft.NET\Framework\v4.0.30319\ngen install EntityFramework.SqlServer.dll
%WINDIR%\Microsoft.NET\Framework64\v4.0.30319\ngen install EntityFramework.SqlServer.dll
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 NGen. exe puede detectar que EntityFramework. dll es una dependencia directa del ensamblado
EntityFramework. SqlServer. dll que se encuentra en el mismo directorio.
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).
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.
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:
Una vez que tenga el StorageMappingItemCollection, puede obtener acceso a los métodos GenerateViews y
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:
if (extentName == "BlogContext.Blogs")
{
return GetView2();
}
if (extentName == "BlogContext.Posts")
{
return GetView3();
}
return null;
}
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
08/04/2020 • 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.
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 app.config o web.config tiene el formato siguiente:
<entityFramework>
<providers>
<provider invariantName="My.Invariant.Name" type="MyProvider.MyProviderServices, 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 ADO.NET. 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 "System.Data.SqlClient"
para SQL Server y "System.Data.SqlServerCe.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 "System.Data.Entity.Core.Common.DbProviderServices". Por ejemplo, la cadena que se va a usar para SQL
Compact es "System.Data.Entity.SqlServerCompact.SqlCeProviderServices, EntityFramework.SqlServerCompact".
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 System.Data.Entity.DbConfiguration 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:
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.
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
tabla de __MigrationHistory utilizada 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 (App. config o Web. config) 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 ADO.NET 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="My.Invariant.Name" type="MyProvider.MyProviderServices, 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:
private SqlProviderServices()
{
AddDependencyResolver(new SingletonDependencyResolver<IDbConnectionFactory>(
new SqlConnectionFactory()));
AddDependencyResolver(new ExecutionStrategyResolver<DefaultSqlExecutionStrategy>(
"System.data.SqlClient", null, () => new DefaultSqlExecutionStrategy()));
AddDependencyResolver(new SingletonDependencyResolver<Func<MigrationSqlGenerator>>(
() => new SqlServerMigrationSqlGenerator(), "System.data.SqlClient"));
AddDependencyResolver(new SingletonDependencyResolver<DbSpatialServices>(
SqlSpatialServices.Instance,
k =>
{
var asSpatialKey = k as DbProviderInfo;
return asSpatialKey == null
|| asSpatialKey.ProviderInvariantName == ProviderInvariantName;
}));
}
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.
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, 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.
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)
Encapsular proveedores
Un proveedor de ajuste es un proveedor EF o ADO.NET 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:
DbConfiguration.OnLockingConfiguration +=
(_, a) =>
{
a.ReplaceService<DbProviderServices>(
(s, k) => new MyWrappedProviderServices(s));
a.ReplaceService<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.
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
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.
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.
La versión genérica de Create se puede usar si desea crear una instancia de un tipo de entidad derivada. Por
ejemplo:
Tenga en cuenta que el método Create no agrega ni adjunta la entidad creada al contexto.
Tenga en cuenta que el método Create 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.
Tenga en cuenta que si el tipo pasado 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
11/03/2020 • 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.
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 System.Collections.Generic;
using System.Data.Entity;
namespace TestingDemo
{
public class BloggingContext : DbContext
{
public virtual DbSet<Blog> Blogs { get; set; }
public virtual DbSet<Post> Posts { get; set; }
}
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
namespace TestingDemo
{
public class BlogService
{
private BloggingContext _context;
return blog;
}
return query.ToList();
}
namespace TestingDemo
{
[TestClass]
public class NonQueryTests
{
[TestMethod]
public void CreateBlog_saves_a_blog_via_context()
{
var mockSet = new Mock<DbSet<Blog>>();
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();
Assert.AreEqual(3, blogs.Count);
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("ZZZ", blogs[2].Name);
}
}
}
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 System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace TestingDemo
{
internal class TestDbAsyncQueryProvider<TEntity> : IDbAsyncQueryProvider
{
private readonly IQueryProvider _inner;
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}
public T Current
{
get { return _inner.Current; }
}
object IDbAsyncEnumerator.Current
{
get { return Current; }
}
}
}
Ahora que tenemos un proveedor de consultas asincrónicas, podemos escribir una prueba unitaria para el nuevo
método GetAllBlogsAsync.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Threading.Tasks;
namespace TestingDemo
{
[TestClass]
public class AsyncQueryTests
{
[TestMethod]
public async Task GetAllBlogsAsync_orders_by_name()
{
mockSet.As<IQueryable<Blog>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<Blog>(data.Provider));
Assert.AreEqual(3, blogs.Count);
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("ZZZ", blogs[2].Name);
}
}
}
Pruebas con sus propias dobles de pruebas
11/03/2020 • 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.
using System.Data.Entity;
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 System.Collections.Generic;
using System.Data.Entity;
namespace TestingDemo
{
public class BloggingContext : DbContext, IBloggingContext
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
}
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
namespace TestingDemo
{
public class BlogService
{
private IBloggingContext _context;
return blog;
}
return query.ToList();
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace TestingDemo
{
public class TestContext : IBloggingContext
{
public TestContext()
{
this.Blogs = new TestDbSet<Blog>();
this.Posts = new TestDbSet<Post>();
}
public TestDbSet()
{
_data = new ObservableCollection<TEntity>();
_query = _data.AsQueryable();
}
Type IQueryable.ElementType
{
get { return _query.ElementType; }
}
Expression IQueryable.Expression
{
get { return _query.Expression; }
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<TEntity>(_query.Provider); }
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
IEnumerator<TEntity> IEnumerable<TEntity>.GetEnumerator()
{
return _data.GetEnumerator();
}
IDbAsyncEnumerator<TEntity> IDbAsyncEnumerable<TEntity>.GetAsyncEnumerator()
{
return new TestDbAsyncEnumerator<TEntity>(_data.GetEnumerator());
}
}
IDbAsyncEnumerator IDbAsyncEnumerable.GetAsyncEnumerator()
{
return GetAsyncEnumerator();
}
IQueryProvider IQueryable.Provider
{
get { return new TestDbAsyncQueryProvider<T>(this); }
}
}
public T Current
{
get { return _inner.Current; }
}
object IDbAsyncEnumerator.Current
{
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 System.Linq;
namespace TestingDemo
{
class TestBlogDbSet : TestDbSet<Blog>
{
public override Blog Find(params object[] keyValues)
{
var id = (int)keyValues.Single();
return this.SingleOrDefault(b => b.BlogId == id);
}
}
}
namespace TestingDemo
{
[TestClass]
public class NonQueryTests
{
[TestMethod]
public void CreateBlog_saves_a_blog_via_context()
{
var context = new TestContext();
Assert.AreEqual(1, context.Blogs.Count());
Assert.AreEqual("ADO.NET Blog", context.Blogs.Single().Name);
Assert.AreEqual("http://blogs.msdn.com/adonet", context.Blogs.Single().Url);
Assert.AreEqual(1, context.SaveChangesCount);
}
}
}
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 Microsoft.VisualStudio.TestTools.UnitTesting;
namespace TestingDemo
{
[TestClass]
public class QueryTests
{
[TestMethod]
public void GetAllBlogs_orders_by_name()
{
var context = new TestContext();
context.Blogs.Add(new Blog { Name = "BBB" });
context.Blogs.Add(new Blog { Name = "ZZZ" });
context.Blogs.Add(new Blog { Name = "AAA" });
Assert.AreEqual(3, blogs.Count);
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("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 Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace TestingDemo
{
[TestClass]
public class AsyncQueryTests
{
[TestMethod]
public async Task GetAllBlogsAsync_orders_by_name()
{
var context = new TestContext();
context.Blogs.Add(new Blog { Name = "BBB" });
context.Blogs.Add(new Blog { Name = "ZZZ" });
context.Blogs.Add(new Blog { Name = "AAA" });
Assert.AreEqual(3, blogs.Count);
Assert.AreEqual("AAA", blogs[0].Name);
Assert.AreEqual("BBB", blogs[1].Name);
Assert.AreEqual("ZZZ", blogs[2].Name);
}
}
}
Capacidad de prueba y Entity Framework 4,0
11/03/2020 • 85 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 ADO.NET
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 ADO.NET, 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.
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.
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.
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.
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 => e.Name == "Scott")
.OrderBy(e => e.HireDate);
var firstEmployee = repository.FindById(1);
var newEmployee = new Employee() {/*... */};
repository.Add(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 ADO.NET, ya estará familiarizado con el patrón
de unidad de trabajo. Los conjuntos de datos de ADO.NET 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:
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.
¿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 que
se suele denominar N + 1 problema de consulta), 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.
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 ASP.NET. La acción recupera un objeto de
empleado y devuelve un resultado para mostrar una vista detallada del empleado.
¿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 la interfaz de
los repositorios, se necesita una abstracción de unidad de trabajo para enlazar repositorios juntos.
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.
Poner 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 generar
un ObjectSet concreto<T>. 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.
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<falsa IObjectSet> para almacenar los objetos de la tarjeta de la Se puede proporcionar
una implementación única mediante genéricos.
public class InMemoryObjectSet<T> : IObjectSet<T> where T : class
public InMemoryObjectSet()
: this(Enumerable.Empty<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 IEnumerable.GetEnumerator() {
return GetEnumerator();
}
Esta prueba Double delega la mayor parte de su trabajo en un objeto de> 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
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 funcionalidad. Por 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 = EmployeeObjectMother.CreateEmployees()
.ToList();
_repository = new InMemoryObjectSet<Employee>(_employeeData);
_unitOfWork = new InMemoryUnitOfWork();
_unitOfWork.Employees = _repository;
_controller = new EmployeeController(_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.
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 otro accesorio se centrará en la acción de creación usada en una solicitud HTTP
POST (para tomar la información enviada por el usuario 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.
Ilustración 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);
Assert.IsTrue(_repository.Contains(_newEmployee));
}
[TestMethod]
public void ShouldCommitUnitOfWork() {
_controller.Create(_newEmployee);
Assert.IsTrue(_unitOfWork.Committed);
}
// ... more tests
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 = result.ViewData.Model
as IEnumerable<Employee>;
Assert.IsTrue(model.Count() == _employeeData.Count);
}
[TestMethod]
public void ShouldOrderModelByHiredateAscending() {
var result = _controller.Index();
var model = result.ViewData.Model
as IEnumerable<Employee>;
Assert.IsTrue(model.SequenceEqual(
_employeeData.OrderBy(e => e.HireDate)));
}
// ...
}
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);
Assert.IsTrue(_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.
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.
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 = result.ViewData.Model as EmployeeSummaryViewModel;
Assert.IsTrue(model.TotalTimeCards == 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 C# compilador 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 las 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 => e.HireDate.Year > 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.
Observe que este operador include está definido como 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 EF4 genuina<T>, no se produce ningún daño y el operador include es una operación no operativa. Si la
secuencia subyacente es un> de ObjectQuery<t (o derivado 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.
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 lograr. En 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.
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.
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 => e.Id == id);
foreach (var card in employee.TimeCards) {
// ...
}
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.
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 ninguna><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 personalizada de IRepository<T>.
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 repositorio. La definición de IRepository<T> tendrá un aspecto similar
al siguiente.
Observe que volveremos a usar una interfaz IQueryable<T> para exponer colecciones de entidades.
IQueryable<T> permite a los árboles de expresión LINQ fluir en el proveedor EF4 y proporcionar 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 solo devuelva IEnumerable<T>
resultados 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 EF4.
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.
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 de unidad de trabajo concreta debe administrar la creación de instancias.
public class SqlUnitOfWork : IUnitOfWork {
public SqlUnitOfWork() {
var connectionString =
ConfigurationManager
.ConnectionStrings[ConnectionStringName]
.ConnectionString;
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.
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> compilando clases concretas respaldadas por
HashSet<Employee>, al igual que lo hicimos 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 trabajo. Estos 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 = mock.Object;
repository.Add(new Employee());
var employee = repository.FindById(1);
Solicitamos a MOQ una implementación de IRepository<empleado> y crea una dinámicamente. Podemos obtener
el objeto que implementa IRepository<empleado> accediendo 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>>();
mock.Setup(m => m.FindById(5))
.Returns(new Employee {Id = 5});
IRepository<Employee> repository = mock.Object;
var employee = repository.FindById(5);
Assert.IsTrue(employee.Id == 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 en la<falsas de IRepository>.
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.
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 => r.FindAll())
.Returns(_employeeData);
}
// .. tests
[TestMethod]
public void ShouldBuildModelWithAllEmployees() {
var result = _controller.Index();
var model = result.ViewData.Model
as IEnumerable<Employee>;
Assert.IsTrue(model.Count() == _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 => r.FindById(_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 => u.Commit());
}
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
ADO.NET 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 ADO.NET 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 OdeToCode.com. 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 ASP.NET altamente escalables. Puede ponerse en contacto con Scott en su blog en
OdeToCode o en Twitter en https://twitter.com/OdeToCode.
Creación de un modelo
08/04/2020 • 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:
Voy a crear una nueva base de Use Code First para definir el modelo Use Model First para definir el modelo
datos en el código y luego generar una base mediante cuadros y líneas y luego
de datos. 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
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 a una nueva base de datos
11/03/2020 • 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 clases de C# o VB.Net. Opcionalmente, se puede realizar una configuración adicional
mediante atributos en las clases y propiedades o mediante una API fluida.
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.
Abra Visual Studio.
Archivo-> nuevo proyecto de>...
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 Program.cs,
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 Program.cs, agregue las dos clases siguientes.
public class Blog
{
public int BlogId { get; set; }
public string Name { 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 un DbSet con tipo de> de la<de la carpa 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
Haga clic en Instalar .
Agregue una instrucción using para System. Data. Entity en la parte superior de Program.cs.
using System.Data.Entity;
namespace CodeFirstNewDatabaseSample
{
class Program
{
static void Main(string[] args)
{
}
}
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.
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.
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 CodeFirstNewDatabaseSample.Migrations
{
using System;
using System.Data.Entity.Migrations;
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
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 Program.cs
using System.ComponentModel.DataAnnotations;
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:
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_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; }
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 para mostrar_nombre:
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
11/03/2020 • 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 clases de C# o VB.Net.
Opcionalmente, se puede realizar una configuración adicional mediante atributos en las clases y propiedades o
mediante una API fluida.
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.
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)
);
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:
Abra Visual Studio.
Archivo-> nuevo proyecto de>...
Seleccionar ventanas en el menú izquierdo y en la aplicación de consola
Escriba CodeFirstExistingDatabaseSample como nombre
Seleccione Aceptar .
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 app. config 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="System.Data.SqlClient" />
</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 un >de<de 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")
{
}
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 blog.Name 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).
[StringLength(200)]
public string Name { get; set; }
[StringLength(200)]
public string Url { get; set; }
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.
Code First de las anotaciones de datos
11/03/2020 • 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
(<http://thedatafarm.com>).
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 ASP.NET 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.
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.
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.
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.
[ForeignKey("Passport")]
[Column(Order = 1)]
public int PassportNumber { get; set; }
[ForeignKey("Passport")]
[Column(Order = 2)]
public string IssuingCountry { get; set; }
Obligatorio
La anotación required 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 atributos MaxLength y MinLength permiten especificar validaciones de propiedades adicionales, tal y como
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 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; }
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 Title.Substring(0, 1) + ":" + BloggerName.Substring(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.
[MaxLength(250)]
public string Description { get; set; }
}
Observe que BlogDetails no tiene ningún tipo de propiedad de clave. En el diseño controlado por dominios,
BlogDetails se conoce como un objeto de valor. Entity Framework hace referencia a los objetos de valor como
tipos complejos. No se puede realizar un seguimiento de los tipos complejos por su cuenta.
Sin embargo, como una propiedad de la clase de blog, BlogDetails se realizará un seguimiento como parte de un
objeto de blog. Para que Code First lo reconozca, debe marcar la clase BlogDetails 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 clase de blog para representar el BlogDetails de ese blog.
En la base de datos, la tabla de blog contendrá todas las propiedades del blog, incluidas las propiedades
contenidas en su propiedad BlogDetail. De forma predeterminada, cada uno está precedido por el nombre del
tipo complejo, BlogDetail.
ConcurrencyCheck
La anotación ConcurrencyCheck permite marcar una o varias propiedades que se usarán para la comprobación
de simultaneidad en la base de datos cuando un usuario modifica o elimina una entidad. Si ha trabajado con el
diseñador de EF, esto se alinea con el establecimiento de la propiedad ConcurrencyMode en Fixed.
Vamos a ver cómo funciona ConcurrencyCheck agregándolo a la propiedad BloggerName.
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 anotación ConcurrencyCheck, puede utilizar la anotación de marca de tiempo más específica
siempre que el tipo de la propiedad sea una matriz de bytes. Code First tratará las propiedades timestamp igual
que las propiedades ConcurrencyCheck, 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 atributo de tabla. Aquí, por ejemplo, la
anotación está especificando que el nombre de la tabla es InternalBlogs.
[Table("InternalBlogs")]
public class Blog
La anotación de columna es un complemento 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 atributo de columna.
[Column("BlogDescription", TypeName="ntext")]
public String Description {get;set;}
No confunda el atributo TypeName de la columna con la DataAnnotations 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 la columna Descripción
del tipo complejo ahora es 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 anotación DatabaseGenerated para marcar esas propiedades en la clase
junto con la enumeración calculada. Otras enumeraciones son none e Identity.
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
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 DatabaseGeneratedOption.
Identity. Si no desea que sea una clave de identidad, puede establecer el valor en DatabaseGeneratedOption.
None.
Índice
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.
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 columna rating de la tabla posts en la
base de datos.
[Index("PostRatingIndex")]
public int Rating { get; set; }
De forma predeterminada, los índices no son únicos, pero puede usar el parámetro con nombre de IsUnique
para especificar que un índice debe ser único. En el ejemplo siguiente se presenta un índice único en el nombre
de inicio de sesión de un usuario .
[Index(IsUnique = true)]
[StringLength(200)]
public string Username { get; set; }
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 clase de blog creó un problema con su relación para publicar.
Al generar la base de datos, Code First ve la propiedad BlogId en la clase post y la reconoce, por la Convención
de que coincide con un nombre de clase más "ID", como una clave externa a la clase de blog. Pero no hay
ninguna propiedad BlogId en la clase de blog. La solución para esto es crear una propiedad de navegación en la
publicación y utilizar la anotación de la información externa para que el código de ayuda entienda primero cómo
crear la relación entre las dos clases (mediante la propiedad post. BlogId), así como la forma de especificar
restricciones en el base.
La restricción en la base de datos muestra una relación entre InternalBlogs. PrimaryTrackingKey y posts. BlogId.
También necesitará agregar en la clase Person a la que hacen referencia estas propiedades. La clase Person tiene
propiedades de navegación de vuelta a la publicación, una para todas las publicaciones escritas por la persona y
otra para todas las entradas actualizadas por esa persona.
Code First no puede hacer coincidir las propiedades de las dos clases por sí mismas. La tabla de base de datos
para las publicaciones debe tener una clave externa para la persona de CreatedBy y otra para la persona
UpdatedBy, pero Code First creará cuatro propiedades de clave externa: person_ID, Person_Id1, CreatedBy_ID y
UpdatedBy_ID.
Para corregir estos problemas, puede usar la anotación InverseProperty 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 que la propiedad PostsWritten de Person sabe que esto hace referencia al tipo de envío, generará la
relación con post. CreatedBy. Del mismo modo, PostsUpdated se conectará a post. UpdatedBy. 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
11/03/2020 • 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.
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.
Este contexto funciona exactamente de la misma manera que el contexto que usa la clase DbSet para sus
propiedades set.
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
11/03/2020 • 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 .
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 .
using System.Data.Entity;
context.SaveChanges();
Console.WriteLine(
"DepartmentID: {0} Name: {1}",
department.DepartmentID,
department.Name);
}
Resumen
En este tutorial, hemos visto cómo usar los tipos de enumeración con Entity Framework Code First.
Code First espacial
11/03/2020 • 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).
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 .
using System.Data.Spatial;
using System.Data.Entity;
context.SaveChanges();
Console.WriteLine(
"The closest University to you is: {0}.",
university.Name);
}
Code First le permite describir un modelo mediante C# o Visual Basic clases .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
VB.net.
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; }
}
// Navigation property
public virtual ICollection<Course> Courses { get; set; }
}
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
}
Si desea excluir un tipo del modelo, use el atributo NotMapped o la API fluida DbModelBuilder. ignore .
modelBuilder.Ignore<Department>();
. . .
}
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: '<nombre de propiedad de navegación><nombre de propiedad de clave principal de la entidad de
seguridad>', '<nombre de la clase principal><el nombre de la propiedad de clave principal ', o '><> 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.
// Navigation property
public virtual ICollection<Course> Courses { 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.
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 .
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
11/03/2020 • 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.
Nuestro modelo
Comencemos por definir un modelo simple que podamos usar con nuestras convenciones. Agregue las clases
siguientes al proyecto.
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
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:
modelBuilder.Properties<int>()
.Where(p => p.Name == "Key")
.Configure(p => p.IsKey());
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:
modelBuilder.Properties<int>()
.Where(x => x.Name == "Key")
.Configure(x => x.IsKey().HasColumnOrder(1));
modelBuilder.Properties()
.Where(x => x.Name == "Name")
.Configure(x => x.IsKey().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:
modelBuilder.Properties<DateTime>()
.Configure(c => c.HasColumnType("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()
{
this.Properties<DateTime>()
.Configure(c => c.HasColumnType("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:
modelBuilder.Conventions.Add(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.
Ahora, vamos a crear una Convención para aplicar este atributo a nuestro modelo:
modelBuilder.Properties()
.Where(x => x.GetCustomAttributes(false).OfType<NonUnicode>().Any())
.Configure(c => c.IsUnicode(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:
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:
modelBuilder.Properties()
.Where(x => x.GetCustomAttributes(false).OfType<IsUnicode>().Any())
.Configure(c => c.IsUnicode(c.ClrPropertyInfo.GetCustomAttribute<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 el
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:
modelBuilder.Properties()
.Having(x =>x.GetCustomAttributes(false).OfType<IsUnicode>().FirstOrDefault())
.Configure((config, att) => config.IsUnicode(att.Unicode));
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 = Regex.Replace(type.Name, ".[A-Z]", m => m.Value[0] + "_" + m.Value[1]);
return result.ToLower();
}
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_categoría en lugar de a ProductCategories.
Una vez que tenemos ese método, podemos llamarlo en una Convención similar a la siguiente:
modelBuilder.Types()
.Configure(c => c.ToTable(GetTableName(c.ClrType)));
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_categoría del producto en lugar
de las categorías Product_. 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.
return result.ToLower();
}
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; }
}
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.
modelBuilder.Types()
.Configure(c=>c.ToTable(c.ClrType.Name));
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.
modelBuilder.Properties<string>()
.Configure(c => c.HasMaxLength(500));
modelBuilder.Properties<string>()
.Where(x => x.Name == "Name")
.Configure(c => c.HasMaxLength(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.
modelBuilder.Conventions.AddBefore<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.
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.
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
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.
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
// Provides a convention for fixing the independent association (IA) foreign key column names.
public class ForeignKeyNamingConvention : IStoreModelConvention<AssociationType>
{
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
if (!matches.Any())
{
matches = primitiveProperties
.Where(p => (entityType.Name + Id).Equals(p.Name, StringComparison.OrdinalIgnoreCase));
}
// If the number of matches is more than one, then multiple properties matched differing only by
// case--for example, "Key" and "key".
if (matches.Count() > 1)
{
throw new InvalidOperationException("Multiple properties match the key convention");
}
return matches;
}
}
A continuación, 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) para
que, por ejemplo, una Convención anterior actualizara un nombre de columna para que coincida con algo de le
interesa su convención personalizada (cuando antes de que el nombre no sea de interés) se aplique a esa columna.
public class BlogContext : DbContext
{
public DbSet<Post> Posts { get; set; }
public DbSet<Comment> Comments { get; set; }
Notas
Una lista de las convenciones aplicadas actualmente por Entity Framework está disponible en la documentación de
MSDN aquí: http://msdn.microsoft.com/library/system.data.entity.modelconfiguration.conventions.aspx. Esta lista
se extrae directamente del código fuente. El código fuente de Entity Framework 6 está disponible en GitHub y
muchas de las convenciones utilizadas por Entity Framework son buenos puntos de partida para las convenciones
basadas en modelos personalizados.
API fluidas: relaciones
11/03/2020 • 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.
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.WithRequiredPrincipal(t => t.Instructor);
modelBuilder.Entity<Course>()
.HasMany(t => t.Instructors)
.WithMany(t => t.Courses)
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.
modelBuilder.Entity<Course>()
.HasMany(t => t.Instructors)
.WithMany(t => t.Courses)
.Map(m =>
{
m.ToTable("CourseInstructor");
m.MapLeftKey("CourseID");
m.MapRightKey("InstructorID");
});
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.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.
modelBuilder.Entity<Course>()
.HasRequired(t => t.Department)
.WithMany(t => t.Courses)
.HasForeignKey(d => d.DepartmentID)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Course>()
.HasRequired(c => c.Department)
.WithMany(t => t.Courses)
.Map(m => m.MapKey("ChangedDepartmentID"));
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;
// Navigation property
public virtual ICollection<Course> Courses { get; private set; }
}
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; private set; }
}
// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public System.DateTime HireDate { get; set; }
// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}
// Navigation property
public virtual Instructor Instructor { get; set; }
}
API fluida: configuración y asignación de
propiedades y tipos
11/03/2020 • 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.
modelBuilder.HasDefaultSchema("sales");
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.
modelBuilder.Entity<OfficeAssignment>().HasKey(t => t.InstructorID);
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.
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 => t.Name)
.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 => t.Name)
.HasColumnAnnotation(
"Index",
new IndexAnnotation(new[]
{
new IndexAttribute("Index1"),
new IndexAttribute("Index2") { IsUnique = true }
})));
modelBuilder.Entity<Department>()
.Property(t => t.Name)
.HasColumnName("DepartmentName");
modelBuilder.Entity<Course>()
.HasRequired(c => c.Department)
.WithMany(t => t.Courses)
.Map(m => m.MapKey("ChangedDepartmentID"));
modelBuilder.Entity<Department>()
.Property(t => t.Name)
.IsUnicode(false);
modelBuilder.Entity<Department>()
.Property(p => p.Name)
.HasColumnType("varchar");
modelBuilder.ComplexType<Details>()
.Property(t => t.Location)
.HasMaxLength(20);
También puede usar la notación de puntos para tener acceso a una propiedad de un tipo complejo.
modelBuilder.Entity<OnsiteCourse>()
.Property(t => t.Details.Location)
.HasMaxLength(20);
modelBuilder.Entity<OfficeAssignment>()
.Property(t => t.Timestamp)
.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.
modelBuilder.Entity<OfficeAssignment>()
.Property(t => t.Timestamp)
.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.
modelBuilder.ComplexType<Details>();
modelBuilder.Ignore<OnlineCourse>();
modelBuilder.Entity<Department>()
.ToTable("t_Department");
modelBuilder.Entity<Department>()
.ToTable("t_Department", "school");
modelBuilder.Entity<Course>()
.Map<Course>(m => m.Requires("Type").HasValue("Course"))
.Map<OnsiteCourse>(m => m.Requires("Type").HasValue("OnsiteCourse"));
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<OnsiteCourse>().ToTable("OnsiteCourse");
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.
modelBuilder.Entity<Course>()
.Property(c => c.CourseID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<OnsiteCourse>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("OnsiteCourse");
});
modelBuilder.Entity<OnlineCourse>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("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.
modelBuilder.Entity<Department>()
.Map(m =>
{
m.Properties(t => new { t.DepartmentID, t.Name });
m.ToTable("Department");
})
.Map(m =>
{
m.Properties(t => new { t.DepartmentID, t.Administrator, t.StartDate, t.Budget });
m.ToTable("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.
modelBuilder.Entity<OfficeAssignment>()
.HasKey(t => t.InstructorID);
modelBuilder.Entity<Instructor>()
.HasRequired(t => t.OfficeAssignment)
.WithRequiredPrincipal(t => t.Instructor);
modelBuilder.Entity<Instructor>().ToTable("Instructor");
modelBuilder.Entity<OfficeAssignment>().ToTable("Instructor");
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
// add a reference to System.ComponentModel.DataAnnotations DLL
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;
using System;
// Navigation property
public virtual ICollection<Course> Courses { get; private set; }
}
// Foreign key
public int DepartmentID { get; set; }
// Navigation properties
public virtual Department Department { get; set; }
public virtual ICollection<Instructor> Instructors { get; private set; }
}
// Primary key
public int InstructorID { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public System.DateTime HireDate { get; set; }
// Navigation properties
public virtual ICollection<Course> Courses { get; private set; }
}
// Navigation property
public virtual Instructor Instructor { get; set; }
}
}
API fluida con VB.NET
11/03/2020 • 11 minutes to read
Code First permite definir el modelo mediante clases de C# o VB.NET. 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 VB.NET.
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.
Abra Visual Studio.
Archivo-> nuevo proyecto de>...
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 VB.NET 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.
' 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
NOTE
Si no tiene la Administración de paquetes de NuGet.. . opción debe instalar la versión más reciente de NuGet
Imports System.ComponentModel.DataAnnotations
Imports System.ComponentModel.DataAnnotations.Schema
Imports System.Data.Entity
Imports System.Data.Entity.Infrastructure
Imports System.Data.Entity.ModelConfiguration.Conventions
' 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.
modelBuilder.Entity(Of Course)().ToTable("Course")
modelBuilder.Entity(Of OnsiteCourse)().ToTable("OnsiteCourse")
modelBuilder.Entity(Of OnlineCourse)().ToTable("OnlineCourse")
' 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.
modelBuilder.Entity(Of Course)().
HasRequired(Function(t) t.Department).
WithMany(Function(t) t.Courses).
HasForeignKey(Function(t) t.DepartmentID_FK)
' You can also remove the cascade delete conventions by using:
' modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
' and modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>().
modelBuilder.Entity(Of Course)().
HasRequired(Function(t) t.Department).
WithMany(Function(t) t.Courses).
HasForeignKey(Function(d) d.DepartmentID_FK).
WillCascadeOnDelete(False)
Module Module1
Sub Main()
End Using
End Sub
End Module
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.
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>_Inser t , < 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:
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
s.Update(u => u.HasName("modify_blog")));
modelBuilder
.Entity<Blog>()
.MapToStoredProcedures(s =>
s.Update(u => u.HasName("modify_blog"))
.Delete(d => d.HasName("delete_blog"))
.Insert(i => i.HasName("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 =>
{
s.Update(u => u.HasName("modify_blog"));
s.Delete(d => d.HasName("delete_blog"));
s.Insert(i => i.HasName("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 =>
s.Update(u => u.Parameter(b => b.BlogId, "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 =>
s.Update(u => u.HasName("modify_blog")
.Parameter(b => b.BlogId, "blog_id")
.Parameter(b => b.Name, "blog_name")
.Parameter(b => b.Url, "blog_url"))
.Delete(d => d.HasName("delete_blog")
.Parameter(b => b.BlogId, "blog_id"))
.Insert(i => i.HasName("insert_blog")
.Parameter(b => b.Name, "blog_name")
.Parameter(b => b.Url, "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 =>
s.Insert(i => i.Result(b => b.BlogId, "generated_blog_identity")));
modelBuilder
.Entity<Post>()
.MapToStoredProcedures(s =>
s.Insert(i => i.Parameter(p => p.Blog.BlogId, "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 =>
s.Insert(i => i.Navigation<Blog>(
b => b.Posts,
c => c.Parameter(b => b.BlogId, "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 <proper ty_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.
A continuación se muestra una clase de ejemplo y un procedimiento almacenado de actualización con un token de
simultaneidad no calculado.
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 =>
s.Update(u => u.Parameter(b => b.Timestamp, "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 => s.Update(u => u.Parameter(b => b.Url, "blog_url", "blog_original_url")));
Las relaciones de varios a varios se pueden asignar a procedimientos almacenados con la siguiente sintaxis.
modelBuilder
.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(t => t.Posts)
.MapToStoredProcedures();
modelBuilder
.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(t => t.Posts)
.MapToStoredProcedures(s =>
s.Insert(i => i.HasName("add_post_tag")
.LeftKeyParameter(p => p.PostId, "post_id")
.RightKeyParameter(t => t.TagId, "tag_id"))
.Delete(d => d.HasName("remove_post_tag")
.LeftKeyParameter(p => p.PostId, "post_id")
.RightKeyParameter(t => t.TagId, "tag_id")));
Migraciones de Code First
08/04/2020 • 20 minutes to read
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
Ahora que tenemos un modelo, es hora de usarlo para el acceso a los datos. Actualice el archivo Program.cs
con el código que se muestra a continuación.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
Habilitación de migraciones
Es hora de realizar más cambios en el modelo.
Vamos a introducir una propiedad Url en la clase Blog.
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 ( http://go.microsoft.com/fwlink/?
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.
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 MigrationsDemo.BlogContext 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
Además agregaremos una colección Posts a la clase Blog para formar el otro extremo de la relación entre
Blog y Post
namespace MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
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.
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 MigrationsDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
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.
namespace MigrationsDemo
{
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<BlogContext, Configuration>());
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
11/03/2020 • 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.
using System.Data.Entity;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity.Infrastructure;
namespace MigrationsAutomaticDemo
{
public class BlogContext : DbContext
{
public DbSet<Blog> Blogs { get; set; }
}
Ahora que tenemos un modelo, es hora de usarlo para el acceso a los datos. Actualice el archivo Program.cs
con el código que se muestra a continuación.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MigrationsAutomaticDemo
{
class Program
{
static void Main(string[] args)
{
using (var db = new BlogContext())
{
db.Blogs.Add(new Blog { Name = "Another Blog " });
db.SaveChanges();
Habilitación de migraciones
Es hora de realizar más cambios en el modelo.
Vamos a introducir una propiedad Url en la clase Blog.
Si tuviera que ejecutar la aplicación de nuevo, obtendría una excepción InvalidOperationException que indicaba
que el modelo de respaldo del 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 ( http://go.microsoft.com/fwlink/?
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.
Además agregaremos una colección Posts a la clase Blog para formar el otro extremo de la relación entre
Blog y Post
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.
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 MigrationsAutomaticDemo.Migrations
{
using System;
using System.Data.Entity.Migrations;
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.
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
11/03/2020 • 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.
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.
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.
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.
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, los DBA le gustaría cambiar el nombre de la columna MigrationId al
identificador de_de migración. Para ello, cree la clase siguiente derivada de HistoryContext:
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Migrations.History;
namespace CustomizableMigrationsHistoryTableSample
{
public class MyHistoryContext : HistoryContext
{
public MyHistoryContext(DbConnection dbConnection, string defaultSchema)
: base(dbConnection, defaultSchema)
{
}
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 System.Data.Entity;
namespace CustomizableMigrationsHistoryTableSample
{
public class ModelConfiguration : DbConfiguration
{
public ModelConfiguration()
{
this.SetHistoryContext("System.Data.SqlClient",
(connection, defaultSchema) => new MyHistoryContext(connection, defaultSchema));
}
}
}
Eso es prácticamente todo. Ahora puede ir a la consola del administrador de paquetes, habilitar-migraciones,
agregar-migrar y, finalmente, actualizar-base de datos. 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.
Uso de Migrate. exe
11/03/2020 • 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 Migrate. exe. Esta página proporcionará
información general rápida sobre cómo usar Migrate. exe 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.
. N ET 4. 5 . N ET 4. 0
NOTE
Migrate. exe no admite ensamblados x64.
Una vez que haya trasladado Migrate. exe 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
Migrate.exe /?
Lo anterior mostrará la página de ayuda asociada a esta utilidad. tenga en cuenta que deberá tener el archivo
EntityFramework. dll en la misma ubicación en la que está ejecutando Migrate. exe para que funcione.
Al ejecutar Migrate. exe, 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.
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.
Si el ensamblado tiene dependencias o lee archivos en relación con el directorio de trabajo, tendrá que establecer
startupDirectory.
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.
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 se Normalmente, esto significa que se ejecuta una aplicación .NET
pudo cargar el archivo o ensamblado ' EntityFramework, 4 sin el archivo Redirect. config. Debe copiar el archivo
version = 5.0.0.0, Culture = neutral, PublicKeyToken = Redirect. config en la misma ubicación que Migrate. exe y
b77a5c561934e089 ' o una de sus dependencias. La cambiarle el nombre a Migrate. exe. config.
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 se Esta excepción significa que está ejecutando una aplicación
pudo cargar el archivo o ensamblado ' EntityFramework, .NET 4,5 con el archivo Redirect. config copiado en la ubicación
version = 4.4.0.0, Culture = neutral, PublicKeyToken = Migrate. exe. 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 Migrate. exe. config.
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 pendientes creado una migración para hacer frente a los cambios
y la migración automática está deshabilitada. Escriba los realizados en el modelo y la base de datos no coincide con el
cambios de modelo pendientes en una migración basada en modelo. La adición de una propiedad a una clase de modelo y,
código o habilite la migración automática. Establezca a continuación, la ejecución de Migrate. exe sin crear una
DbMigrationsConfiguration. AutomaticMigrationsEnabled en migración para actualizar la base de datos es un ejemplo de
true para habilitar la migración automática. esto.
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 Migrate. exe.
UpdateRunner, EntityFramework, version = 5.0.0.0, 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 ejemplo,
objeto. si se especifica una cadena de conexión sin especificar el
en System. Data. Entity. Migrations. Console. Program. Main nombre del proveedor.
(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 Migrate. exe 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.
Migraciones de Code First en entornos de equipo
11/03/2020 • 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.
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.
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.
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 Developer #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.
El día de la suerte para Developer #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, el desarrollador #2 puede ejecutar Update-Database , que detectará la nueva migración de
AddRating (que no se ha aplicado a la base de datos de Developer #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 que
hemos estado siguiendo el desarrollador #2 está tomando en consideración la combinación y se han producido
para generar la última migración. Pero se pueden usar los mismos pasos si Developer #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 <pick_un nombre de_> – 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. 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_última_migración> (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 la __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 Add-migration <nombre de_completo_incluido_marca de tiempo___> de migración del
último (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
11/03/2020 • 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.
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:
Abra Visual Studio.
Archivo-> nuevo proyecto de>...
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, ADO.NET 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 cambie 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 derecho 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 Esto 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.
class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
Console.Write("Enter a name for a new Blog: ");
var name = Console.ReadLine();
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
11/03/2020 • 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.
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 .
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)
);
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:
Abra Visual Studio.
Archivo-> nuevo proyecto de>...
Seleccionar ventanas en el menú izquierdo y en la aplicación de consola
Escriba DatabaseFirstSample como nombre
Seleccione Aceptar .
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 app. config al proyecto con
los detalles de conexión de la base de datos.
class Program
{
static void Main(string[] args)
{
using (var db = new BloggingContext())
{
// Create and save a new Blog
Console.Write("Enter a name for a new Blog: ");
var name = Console.ReadLine();
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.
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
11/03/2020 • 13 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.
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.
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 .
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.
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.
context.SaveChanges();
Console.WriteLine(
"DepartmentID: {0} and Name: {1}",
department.DepartmentID,
department.Name);
}
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
11/03/2020 • 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).
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 .
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.
context.Universities.Add(new University()
{
Name = "School of Fine Art",
Location = DbGeography.FromText("POINT(-122.335197 47.646711)"),
});
context.SaveChanges();
Console.WriteLine(
"The closest University to you is: {0}.",
university.Name);
}
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
11/03/2020 • 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 a la asignación de un tipo de entidad a dos tablas se amplían fácilmente para
asignar 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.
Prerequisites
Visual Studio 2012 o Visual Studio 2010, Ultimate, Premium, Professional o Web Express.
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 la persona tipo de entidad y haga clic en <agregar una tabla o vista> en la ventana detalles de
la asignación .
Seleccione **PersonInfo ** de la lista desplegable. La ventana detalles de la asignación se actualiza con las
asignaciones de columnas predeterminadas, estas son correctas para nuestro escenario.
El tipo de entidad persona está asignado ahora 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@example.com",
Phone = "555-555-5555"
};
context.People.Add(person);
context.SaveChanges();
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
11/03/2020 • 7 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 objetos. Puede 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.
Prerequisites
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 .
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 nuevo de entidades .
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 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 de Asociación . El nombre de PersonHireInfo se proporciona de forma
predeterminada.
Especifique Multiplicity 1 (uno) en ambos extremos de la relación.
Presione 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 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> campo . 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 .
Presione Aceptar .
Usar el modelo
Pegue el código siguiente en el método Main.
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
11/03/2020 • 10 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.
Prerequisites
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 ADO.NET 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 para
el 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.
Repita estos pasos para el 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 es null o no es null comparación.
Ahora se implementa la herencia de tabla por jerarquía.
Usar el modelo
Abra el archivo Program.CS 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 .
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 (se 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.
Prerequisites
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 ADO.NET 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 para
el 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.
Usar el modelo
Abra el archivo Program.CS 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 context.Departments)
{
Console.WriteLine("The {0} department has the following courses:",
department.Name);
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:
Prerequisites
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 ADO.NET 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 para el 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 de tablas para seleccionar todas
las tablas.
Además, seleccione los siguientes procedimientos almacenados en el nodo procedimientos
almacenados y funciones : GetStudentGrades y GetDepar tmentName .
Usar el modelo
Abra el archivo Program.CS 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).
// Call GetDepartmentName.
// Declare the name variable that will contain the value returned by the output parameter.
ObjectParameter name = new ObjectParameter("Name", typeof(String));
context.GetDepartmentName(1, name);
Console.WriteLine("The department name is {0}", name.Value);
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
11/03/2020 • 10 minutes to read
Este tutorial paso a paso muestra cómo asignar las operaciones de creación\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:
Prerequisites
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 ADO.NET 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 para el 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.
if (deletedInstructor == null)
Console.WriteLine("A person with PersonID {0} was deleted.",
newInstructor.PersonID);
}
NOTE
El servidor genera automáticamente PersonID, por lo que probablemente verá un número diferente *
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.
Para obtener información sobre los elementos CSDL (ReferentialConstraint , Association , etc.), vea la
especificación de CSDL.
NOTE
En esta sección se supone que ya ha agregado las entidades con las que desea crear una asociación entre el modelo.
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.
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.
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.
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
11/03/2020 • 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 de>-> 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 de>-> consola del
administrador de paquetes
2. Ejecute Install-Package EntityFramework-version 4.3.0
Plantillas de generación de código del diseñador
08/04/2020 • 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 si se usan
versiones recientes de Visual Studio (de Visual Studio 2013 en adelante): Al crear un modelo, se usa esta plantilla
de forma predeterminada y los archivos T4 (.tt) se anidados bajo el archivo .edmx.
Versiones anteriores de Visual Studio
Visual Studio 2012: para obtener las plantillas DbContextGenerator de EF 6.x , tiene que instalar la versión
más reciente de Entity Framework Tools para Visual Studio : vea la página Obtener Entity Framework para
más información.
Visual Studio 2010: las plantillas DbContextGenerator de EF 6.x 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 VB.NET
Generador de DbContext para EF 5.x para sitios web de VB.NET
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 VB.NET
Generador de DbContext para EF 4.x para sitios web de VB.NET
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 VB.NET
Generador de EntityObject para EF 6.x para sitios web de VB.NET
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 VB.NET
Generador de EntityObject para EF 5.x para sitios web de VB.NET
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.
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.
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>.context.tt .
Si usa Visual Studio 2010, los archivos tt se agregan directamente al proyecto.
Revertir a ObjectContext en Entity Framework
Designer
11/03/2020 • 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.
Si usa VB.NET, debe seleccionar el botón Mostrar todos los archivos para ver los archivos anidados.
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 es accesible mediante
métodos en el 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.
CSDL v1 https://schemas.microsoft.com/ado/2006/04/edm
CSDL V2 https://schemas.microsoft.com/ado/2008/09/edm
CSDL V3 https://schemas.microsoft.com/ado/2009/11/edm
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="ExampleModel.Customer" Role="Customer" Multiplicity="1" >
<OnDelete Action="Cascade" />
</End>
<End Type="ExampleModel.Order" 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="ExampleModel.Customer" Role="Customer" Multiplicity="1" >
<OnDelete Action="Cascade" />
</End>
<End Type="ExampleModel.Order" 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):
Documentación (cero o un elemento permitido)
End (exactamente dos elementos necesarios)
Elementos Annotation (cero o más elementos permitidos)
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 .
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="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>
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 .
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="Edm.String"/>
<ReturnType>
<CollectionType ElementType="SchoolModel.Person"/>
</ReturnType>
<DefiningExpression>
SELECT VALUE p
FROM SchoolEntities.People AS p
WHERE p.LastName >= 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="Edm.String" />
<ReturnType>
<CollectionType>
<RowType>
<Property Name="FirstName" Type="Edm.String" Nullable="false" />
<Property Name="LastName" Type="Edm.String" Nullable="false" />
</RowType>
</CollectionType>
</ReturnType>
<DefiningExpression>
SELECT VALUE ROW(p.FirstName, p.LastName)
FROM SchoolEntities.People AS p
WHERE p.LastName >= 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="SchoolModel.Department"/>
</CollectionType>
</Parameter>
<ReturnType Type="Collection(Edm.Decimal)"/>
<DefiningExpression>
SELECT VALUE AVG(d.Budget) FROM Departments AS d
</DefiningExpression>
</Function>
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 .
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="BooksModel.Address" Name="Address" Nullable="false" />
<NavigationProperty Name="Books" Relationship="BooksModel.PublishedBy"
FromRole="Publisher" ToRole="Book" />
</EntityType>
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.
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="BooksModel.Book" Role="Book" Multiplicity="*" >
</End>
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>
<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>
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="ExampleModel.Customer" Role="Customer" Multiplicity="1" />
<End Type="ExampleModel.Order" Role="Order" Multiplicity="*">
<OnDelete Action="Cascade" />
</End>
</Association>
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 .
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="BooksModel.Book" />
<EntitySet Name="Publishers" EntityType="BooksModel.Publisher" />
<EntitySet Name="Authors" EntityType="BooksModel.Author" />
<AssociationSet Name="PublishedBy" Association="BooksModel.PublishedBy">
<End Role="Book" EntitySet="Books" />
<End Role="Publisher" EntitySet="Publishers" />
</AssociationSet>
<AssociationSet Name="WrittenBy" Association="BooksModel.WrittenBy">
<End Role="Book" EntitySet="Books" />
<End Role="Author" EntitySet="Authors" />
</AssociationSet>
</EntityContainer>
Ejemplo
En el ejemplo siguiente se muestra un elemento EntityContainer que define tres conjuntos de entidades y dos
conjuntos de asociaciones.
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 un elemento permitidos)
Elementos Annotation (cero o más elementos permitidos)
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
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 :
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 :
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="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>
Ejemplo
En el ejemplo siguiente se muestra un elemento enumType con tres elementos member :
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.
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(SchoolModel.StudentGrade)">
<Parameter Name="StudentID" Mode="In" Type="Int32" />
</FunctionImport>
<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="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
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="BooksModel.WrittenBy"
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.
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>
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="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>
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 .
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="ExampleModel.Customer" Role="Customer" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Type="ExampleModel.Order" Role="Order" Multiplicity="*" />
</Association>
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(SchoolModel.StudentGrade)">
<Parameter Name="StudentID" Mode="In" Type="Int32" />
</FunctionImport>
NOTE
Solo uno de los elementos CollectionType , referenceType o RowType puede ser un elemento secundario de un elemento
Proper ty .
Atributos aplicables
En la tabla siguiente se describen los atributos que se pueden aplicar al elemento Parameter .
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.
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="BooksModel.Book" Role="Book" Multiplicity="*" >
</End>
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>
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
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="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
FromRole="Book" ToRole="Author" />
</EntityType>
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 .
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="Edm.String" />
<ReturnType>
<CollectionType>
<RowType>
<Property Name="FirstName" Type="Edm.String" Nullable="false" />
<Property Name="LastName" Type="Edm.String" Nullable="false" />
</RowType>
</CollectionType>
</ReturnType>
<DefiningExpression>
SELECT VALUE ROW(p.FirstName, p.LastName)
FROM SchoolEntities.People AS p
WHERE p.LastName >= somestring
</DefiningExpression>
</Function>
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 .
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="BooksModel.PublishedBy"
FromRole="Book" ToRole="Publisher" />
<NavigationProperty Name="Authors" Relationship="BooksModel.WrittenBy"
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="BooksModel.Book" Role="Book" Multiplicity="*" >
</End>
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>
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 :
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="SchoolModel.Person" />
<ReturnType>
<ReferenceType Type="SchoolModel.Person" />
</ReturnType>
<DefiningExpression>
REF(p)
</DefiningExpression>
</Function>
<Association Name="PublishedBy">
<End Type="BooksModel.Book" Role="Book" Multiplicity="*" >
</End>
<End Type="BooksModel.Publisher" Role="Publisher" Multiplicity="1" />
<ReferentialConstraint>
<Principal Role="Publisher">
<PropertyRef Name="Id" />
</Principal>
<Dependent Role="Book">
<PropertyRef Name="PublisherId" />
</Dependent>
</ReferentialConstraint>
</Association>
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).
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="BooksModel.Book" />
<DefiningExpression>
Year(CurrentDateTime()) - Year(cast(book.PublishedDate as DateTime))
</DefiningExpression>
</Function>
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(BooksModel.Book )" EntitySet=”Books”>
<ReturnType Type=="Collection(BooksModel.Publisher)" EntitySet=”Publishers”>
</FunctionImport>
<Function Name="LastNamesAfter">
<Parameter Name="someString" Type="Edm.String" />
<ReturnType>
<CollectionType>
<RowType>
<Property Name="FirstName" Type="Edm.String" Nullable="false" />
<Property Name="LastName" Type="Edm.String" Nullable="false" />
</RowType>
</CollectionType>
</ReturnType>
<DefiningExpression>
SELECT VALUE ROW(p.FirstName, p.LastName)
FROM SchoolEntities.People AS p
WHERE p.LastName >= somestring
</DefiningExpression>
</Function>
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 con el formato
https://schemas.microsoft.com/ado/YYYY/MM/edm (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 .
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="https://schemas.microsoft.com/ado/2009/11/edm"
xmlns:cg="https://schemas.microsoft.com/ado/2009/11/codegeneration"
xmlns:store="https://schemas.microsoft.com/ado/2009/11/edm/EntityStoreSchemaGenerator"
Namespace="ExampleModel" Alias="Self">
<EntityContainer Name="ExampleModelContainer">
<EntitySet Name="Customers"
EntityType="ExampleModel.Customer" />
<EntitySet Name="Orders" EntityType="ExampleModel.Order" />
<AssociationSet
Name="CustomerOrder"
Association="ExampleModel.CustomerOrders">
<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="ExampleModel.CustomerOrders"
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="ExampleModel.CustomerOrders"
FromRole="Order" ToRole="Customer" />
<Property Type="Int32" Name="CustomerId" Nullable="false" />
</EntityType>
<Association Name="CustomerOrders">
<End Type="ExampleModel.Customer"
Role="Customer" Multiplicity="1" />
<End Type="ExampleModel.Order"
Role="Order" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customer">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Order">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
</Schema>
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="SchoolModel.Department"/>
</CollectionType>
</Parameter>
<ReturnType Type="Collection(Edm.Decimal)"/>
<DefiningExpression>
SELECT VALUE AVG(d.Budget) FROM Departments AS d
</DefiningExpression>
</Function>
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.
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 que se muestra es
BooksModel . La propiedad Address en el Publisher EntityType es un tipo complejo que se define en el espacio de
nombres ExtendedBooksModel (importado con el elemento using ).
<Schema xmlns="https://schemas.microsoft.com/ado/2009/11/edm"
xmlns:cg="https://schemas.microsoft.com/ado/2009/11/codegeneration"
xmlns:store="https://schemas.microsoft.com/ado/2009/11/edm/EntityStoreSchemaGenerator"
Namespace="BooksModel" Alias="Self">
<EntityType Name="Publisher">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" />
<Property Type="String" Name="Name" Nullable="false" />
<Property Type="BMExt.Address" Name="Address" Nullable="false" />
</EntityType>
</Schema>
El siguiente código recupera los metadatos del atributo Annotation y los escribe en la consola:
El código anterior supone que el archivo School.csdl está en el directorio de resultados del proyecto y que se han
agregado las siguientes instrucciones Imports y Using al proyecto:
using System.Data.Metadata.Edm;
El siguiente código recupera los metadatos del elemento de anotación y los escribe en la consola:
EdmItemCollection collection = new EdmItemCollection("School.csdl");
MetadataWorkspace workspace = new MetadataWorkspace();
workspace.RegisterItemCollection(collection);
EdmType contentType;
workspace.TryGetType("Person", "SchoolModel", DataSpace.CSpace, out contentType);
if (contentType.MetadataProperties.Contains("http://CustomNamespace.com:CustomElement"))
{
MetadataProperty annotationProperty =
contentType.MetadataProperties["http://CustomNamespace.com:CustomElement"];
object annotationValue = annotationProperty.Value;
Console.WriteLine(annotationValue.ToString());
}
El código anterior supone que el archivo School.csdl está en el directorio de resultados del proyecto y que se han
agregado las siguientes instrucciones Imports y Using al proyecto:
using System.Data.Metadata.Edm;
EDM. Byte Contiene un valor entero de 8 bits sin Precision, Nullable, Default
signo.
EDM. decimal Contiene un valor numérico con una Precision, Nullable, Default
precisión y escala fijas.
EDM. Int16 Contiene un valor entero de 16 bits con Precision, Nullable, Default
signo.
EDM. SByte Contiene un valor entero de 8 bits con Precision, Nullable, Default
signo.
EDM. Time Contiene una hora del día. Precision, Nullable, Default
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.
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:
https://schemas.microsoft.com/ado/2009/02/edm/annotation. 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="https://schemas.microsoft.com/ado/2009/02/edm/annotation" />
<Property Type="String"
Name="ProductName"
Nullable="false"
MaxLength="50" />
<Property Type="String"
Name="Location"
Nullable="true"
MaxLength="25" />
</EntityType>
Especificación MSL
11/03/2020 • 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.
MSL V2 https://schemas.microsoft.com/ado/2008/09/mapping/cs
MSL V3 https://schemas.microsoft.com/ado/2009/11/mapping/cs
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="https://schemas.microsoft.com/ado/2009/11/mapping/cs">
<Alias Key="c" Value="SchoolModel"/>
<EntityContainerMapping StorageEntityContainer="SchoolModelStoreContainer"
CdmEntityContainer="SchoolModelEntities">
<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="c.Course">
<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="c.Department">
<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>
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>
Para asignar la función de actualización de la entidad Course 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 FK_Course_Depar tment al procedimiento almacenado UpdateCourse (al
que se asigna la función de actualización del tipo de entidad Course ):
<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="SchoolModel.Course">
<MappingFragment StoreEntitySet="Course">
<ScalarProperty Name="Credits" ColumnName="Credits" />
<ScalarProperty Name="Title" ColumnName="Title" />
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="SchoolModel.Course">
<ModificationFunctionMapping>
<UpdateFunction FunctionName="SchoolModel.Store.UpdateCourse">
<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>
Ejemplo
En el ejemplo siguiente se muestra un elemento AssociationSetMapping en el que el curso de FK__ Asociación
de Departamento establecida 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>
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="SchoolModel.FullName" 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="SchoolModel.Person">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="HireDate" ColumnName="HireDate" />
<ScalarProperty Name="EnrollmentDate" ColumnName="EnrollmentDate" />
<ComplexProperty Name="Name" TypeName="SchoolModel.FullName">
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
</ComplexProperty>
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
Ejemplo
Observe el siguiente procedimiento almacenado:
<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="SchoolModel.Store.GetGrades" >
<ResultMapping>
<ComplexTypeMapping TypeName="SchoolModel.GradeInfo">
<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>
NOTE
Cuando el elemento Condition se usa dentro de un elemento FunctionImportMapping, solo el atributo Name no es
aplicable.
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(SchoolModel.Person)">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(SchoolModel.Instructor)">
<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(SchoolModel.Student)">
<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>
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.
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="SchoolModel.Person">
<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="SchoolModel.Person">
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertPerson">
<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="SchoolModel.Store.UpdatePerson">
<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="SchoolModel.Store.DeletePerson">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
</EntitySetMapping>
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="SchoolModel.CourseInstructor"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertCourseInstructor" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="SchoolModel.Store.DeleteCourseInstructor">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>
Ejemplo
En el ejemplo siguiente se muestra un elemento AssociationSetMapping en el que la Asociación de
FK_Course_Depar tment del modelo conceptual está asignada 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="SchoolModel.CourseInstructor"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertCourseInstructor" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="SchoolModel.Store.DeleteCourseInstructor">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>
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="c.Course">
<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="c.Department">
<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>
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(SchoolModel1.Course)">
<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(SchoolModel1.OnlineCourse)">
<MappingFragment StoreEntitySet="OnlineCourse">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
<ScalarProperty Name="URL" ColumnName="URL" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(SchoolModel1.OnsiteCourse)">
<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>
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 .
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="SchoolModel.Person">
<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="SchoolModel.Person">
<ModificationFunctionMapping>
<UpdateFunction FunctionName="SchoolModel.Store.UpdatePerson">
<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 sintaxis de IsOfType para los atributos TypeName .
<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="IsTypeOf(SchoolModel.Person)">
<MappingFragment StoreEntitySet="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
<ScalarProperty Name="FirstName" ColumnName="FirstName" />
<ScalarProperty Name="LastName" ColumnName="LastName" />
</MappingFragment>
</EntityTypeMapping>
<EntityTypeMapping TypeName="IsTypeOf(SchoolModel.Instructor)">
<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(SchoolModel.Student)">
<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>
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.
Ejemplo
El siguiente ejemplo se basa en el modelo School. Considere la siguiente función en el modelo de almacenamiento:
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="SchoolModel.Store.GetStudentGrades" />
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 .
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="SchoolModel.Person">
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertPerson">
<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="SchoolModel.Store.UpdatePerson">
<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="SchoolModel.Store.DeletePerson">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
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="SchoolModel.CourseInstructor"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertCourseInstructor" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="SchoolModel.Store.DeleteCourseInstructor">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>
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="https://schemas.microsoft.com/ado/2009/11/mapping/cs">
<Alias Key="c" Value="SchoolModel"/>
<EntityContainerMapping StorageEntityContainer="SchoolModelStoreContainer"
CdmEntityContainer="SchoolModelEntities">
<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="c.Course">
<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="c.Department">
<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>
<EntitySetMapping Name="Courses">
<EntityTypeMapping TypeName="SchoolModel.Course">
<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.
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.
<EntitySetMapping Name="People">
<EntityTypeMapping TypeName="SchoolModel.Person">
<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="SchoolModel.Person">
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertPerson">
<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="SchoolModel.Store.UpdatePerson">
<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="SchoolModel.Store.DeletePerson">
<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="SchoolModel.CourseInstructor"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertCourseInstructor" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="SchoolModel.Store.DeleteCourseInstructor">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>
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).
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.
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 Asociación de FK_Course_Department
en el modelo School.
<EntityContainerMapping StorageEntityContainer="SchoolModelStoreContainer"
CdmEntityContainer="SchoolEntities">
<EntitySetMapping Name="Courses" >
<QueryView>
SELECT VALUE SchoolModel.Course(c.CourseID,
c.Title,
c.Credits)
FROM SchoolModelStoreContainer.Course AS c
</QueryView>
</EntitySetMapping>
<EntitySetMapping Name="Departments" >
<QueryView>
SELECT VALUE SchoolModel.Department(d.DepartmentID,
d.Name,
d.Budget,
d.StartDate)
FROM SchoolModelStoreContainer.Department AS d
WHERE d.Budget > 150000
</QueryView>
</EntitySetMapping>
<AssociationSetMapping Name="FK_Course_Department" >
<QueryView>
SELECT VALUE SchoolModel.FK_Course_Department(
CREATEREF(SchoolEntities.Departments, row(c.DepartmentID), SchoolModel.Department),
CREATEREF(SchoolEntities.Courses, row(c.CourseID)) )
FROM SchoolModelStoreContainer.Course 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 .
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="SchoolModel.Person">
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertPerson">
<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="SchoolModel.Store.UpdatePerson">
<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="SchoolModel.Store.DeletePerson">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
<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="SchoolModel.Store.GetGrades" >
<ResultMapping>
<EntityTypeMapping TypeName="SchoolModel.StudentGrade">
<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>
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:
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="SchoolModel.Person">
<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="SchoolModel.Person">
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertPerson">
<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="SchoolModel.Store.UpdatePerson">
<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="SchoolModel.Store.DeletePerson">
<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="SchoolModel.CourseInstructor"
StoreEntitySet="CourseInstructor">
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ColumnName="PersonID" />
</EndProperty>
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ColumnName="CourseID" />
</EndProperty>
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertCourseInstructor" >
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</InsertFunction>
<DeleteFunction FunctionName="SchoolModel.Store.DeleteCourseInstructor">
<EndProperty Name="Course">
<ScalarProperty Name="CourseID" ParameterName="courseId"/>
</EndProperty>
<EndProperty Name="Person">
<ScalarProperty Name="PersonID" ParameterName="instructorId"/>
</EndProperty>
</DeleteFunction>
</ModificationFunctionMapping>
</AssociationSetMapping>
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.
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="SchoolModel.Person">
<ModificationFunctionMapping>
<InsertFunction FunctionName="SchoolModel.Store.InsertPerson">
<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="SchoolModel.Store.UpdatePerson">
<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="SchoolModel.Store.DeletePerson">
<ScalarProperty Name="PersonID" ParameterName="PersonID" />
</DeleteFunction>
</ModificationFunctionMapping>
</EntityTypeMapping>
Especificación SSDL
11/03/2020 • 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 el 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.
SSDL v1 https://schemas.microsoft.com/ado/2006/04/edm/ssdl
SSDL V2 https://schemas.microsoft.com/ado/2009/02/edm/ssdl
SSDL V3 https://schemas.microsoft.com/ado/2009/11/edm/ssdl
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 :
<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="ExampleModel.Store.Customers" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="ExampleModel.Store.Orders" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
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 restricción de clave externa
FK_CustomerOrders en la base de datos subyacente:
<AssociationSet Name="FK_CustomerOrders"
Association="ExampleModel.Store.FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
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.
<Schema>
<EntitySet Name="Tables" EntityType="Self.STable">
<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 vistas. Puede 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.
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="ExampleModel.Store.Customers" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="ExampleModel.Store.Orders" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
<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>
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="ExampleModel.Store.Customers" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="ExampleModel.Store.Orders" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
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="ExampleModel.Store.Customers"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="ExampleModel.Store.Orders"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="ExampleModel.Store.FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>
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="ExampleModel.Store.Customers"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="ExampleModel.Store.Orders"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="ExampleModel.Store.FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>
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.
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="ExampleModel.Store.Customers"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="ExampleModel.Store.Orders"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="ExampleModel.Store.FK_CustomerOrders">
<End Role="Customers" EntitySet="Customers" />
<End Role="Orders" EntitySet="Orders" />
</AssociationSet>
</EntityContainer>
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>
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.
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>
<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>
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="ExampleModel.Store.Customers" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="ExampleModel.Store.Orders" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
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>
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="ExampleModel.Store.Customers" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="ExampleModel.Store.Orders" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
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>
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>
<Association Name="FK_CustomerOrders">
<End Role="Customers"
Type="ExampleModel.Store.Customers" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="ExampleModel.Store.Orders" Multiplicity="*" />
<ReferentialConstraint>
<Principal Role="Customers">
<PropertyRef Name="CustomerId" />
</Principal>
<Dependent Role="Orders">
<PropertyRef Name="CustomerId" />
</Dependent>
</ReferentialConstraint>
</Association>
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>
Ejemplo
En el ejemplo siguiente se muestra un elemento Schema que contiene un elemento EntityContainer , dos
elementos EntityType y un elemento Association .
<Schema Namespace="ExampleModel.Store"
Alias="Self" Provider="System.Data.SqlClient"
ProviderManifestToken="2008"
xmlns="https://schemas.microsoft.com/ado/2009/11/edm/ssdl">
<EntityContainer Name="ExampleModelStoreContainer">
<EntitySet Name="Customers"
EntityType="ExampleModel.Store.Customers"
Schema="dbo" />
<EntitySet Name="Orders"
EntityType="ExampleModel.Store.Orders"
Schema="dbo" />
<AssociationSet Name="FK_CustomerOrders"
Association="ExampleModel.Store.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="http://CustomNamespace">
<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="ExampleModel.Store.Customers" Multiplicity="1">
<OnDelete Action="Cascade" />
</End>
<End Role="Orders"
Type="ExampleModel.Store.Orders" 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">
<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 .
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:
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
Prerequisites
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 .
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.
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 nuevo ->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 nuevo -> 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.
<EntityType Name="GradeReport">
. . .
</EntityType>
Como resultado, el elemento EntitySetMapping se agrega a la sección de asignación del archivo. edmx.
<EntitySetMapping Name="GradeReports">
<EntityTypeMapping TypeName="IsTypeOf(SchoolModel.GradeReport)">
<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>
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 pantalla. Antes 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:
try
{
db.Database.Connection.Open();
// Run the sproc
var reader = cmd.ExecuteReader();
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.
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>
...
...
<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>
<FunctionImport Name="GetAllBlogsAndPosts">
<ReturnType EntitySet="Blogs" Type="Collection(BlogModel.Blog)" />
<ReturnType EntitySet="Posts" Type="Collection(BlogModel.Post)" />
</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:
...
<FunctionImportMapping FunctionImportName="GetAllBlogsAndPosts"
FunctionName="BlogModel.Store.GetAllBlogsAndPosts">
<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 ="BlogModel.Blog">
<ScalarProperty Name="BlogId" ColumnName="BlogId" />
<ScalarProperty Name="Name" ColumnName="Name" />
<ScalarProperty Name="Description" ColumnName="Description" />
</EntityTypeMapping>
</ResultMapping>
<ResultMapping>
<EntityTypeMapping TypeName="BlogModel.Post">
<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:
Console.ReadLine();
}
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 con valores de tabla (TVF)
11/03/2020 • 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.
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. Abra 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 .
(@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 ADO.NET 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 para el 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 los procedimientos
almacenados y las 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
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
11/03/2020 • 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.
Alt + w Cambiar el foco al panel de selección de Permite especificar los objetos de base
objetos de base de datos de datos a los que se va a aplicar
ingeniería inversa.
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 y No está disponible para todas las
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.
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.
Finalizar Ú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.
Tabulación 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.
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.
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.
M ÉTO DO A B REVIA DO A C C IÓ N N OTA S
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 a
Control + Mayús + MouseWheel centrar el diagrama cuando aleja el
hacia atrás centro del diagrama actual.
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
Alt + flecha abajo Abrir lista Desplegar una lista si se selecciona una
celda que tenga una lista desplegable.
M ÉTO DO A B REVIA DO
OtherContextMenus.MicrosoftDataEntityDesignContext.AddCodeGenerationItem
OtherContextMenus.MicrosoftDataEntityDesignContext.AddFunctionImpor t
OtherContextMenus.MicrosoftDataEntityDesignContext.AddNewDiagram
OtherContextMenus.MicrosoftDataEntityDesignContext.AddtoDiagram
OtherContextMenus.MicrosoftDataEntityDesignContext.Conver ttoEnum
OtherContextMenus.MicrosoftDataEntityDesignContext.GenerateDatabasefromModel
OtherContextMenus.MicrosoftDataEntityDesignContext.GoToDefinition
OtherContextMenus.MicrosoftDataEntityDesignContext.IncludeRelated
OtherContextMenus.MicrosoftDataEntityDesignContext.MappingDetails
OtherContextMenus.MicrosoftDataEntityDesignContext.MoveDiagramstoSeparateFile
OtherContextMenus.MicrosoftDataEntityDesignContext.MoveProper ties.Down5
OtherContextMenus.MicrosoftDataEntityDesignContext.MoveProper ties.ToBottom
OtherContextMenus.MicrosoftDataEntityDesignContext.MoveProper ties.ToTop
M ÉTO DO A B REVIA DO
OtherContextMenus.MicrosoftDataEntityDesignContext.MoveProper ties.Up5
OtherContextMenus.MicrosoftDataEntityDesignContext.MovetonewDiagram
OtherContextMenus.MicrosoftDataEntityDesignContext.RemovefromDiagram
OtherContextMenus.MicrosoftDataEntityDesignContext.ScalarProper tyFormat.DisplayNameandType
OtherContextMenus.MicrosoftDataEntityDesignContext.SelectAssociation
OtherContextMenus.MicrosoftDataEntityDesignContext.ShowinDiagram
OtherContextMenus.MicrosoftDataEntityDesignContext.ShowinModelBrowser
OtherContextMenus.MicrosoftDataEntityDesignContext.StoredProcedureMapping
OtherContextMenus.MicrosoftDataEntityDesignContext.UpdateModelfromDatabase
Ver. EntityDataModelBrowser
Ver. EntityDataModelMappingDetails
Consulta y búsqueda de entidades
08/04/2020 • 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.
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, DbEntityEntry.Reload y
Database.ExecuteSqlCommand.
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 .
// Will find the new blog even though it does not exist in the database
var newBlog = context.Blogs.Find(-1);
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
11/03/2020 • 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:
_context.Categories.Load();
categoryBindingSource.DataSource = _context.Categories.Local.ToBindingList();
}
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:
// Load the posts with the 'entity-framework' tag related to a given blog
context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Where(p => p.Tags.Contains("entity-framework"))
.Load();
}
Datos locales
11/03/2020 • 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.
Si teníamos dos blogs en la base de datos: ' ADO.NET 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: ADO.NET Blog with state Deleted
Found 2: The Visual Studio Blog with state Unchanged
Suponiendo que tenemos algunas entradas etiquetadas con "Entity-Framework" y "asp.net", la salida puede tener
un aspecto similar al siguiente:
return base.SaveChanges();
}
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.
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; }
}
Tracked blogs:
Found Blog 1: The New ADO.NET Blog with original Name ADO.NET 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 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:
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.
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:
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.
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
11/03/2020 • 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.
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.
// Load all blogs, all related posts, and all related comments
// using a string to specify the relationships.
var blogs2 = context.Blogs
.Include("Posts.Comments")
.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:
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:
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:
// Load the posts with the 'entity-framework' tag related to a given blog.
context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Where(p => p.Tags.Contains("entity-framework"))
.Load();
// Load the posts with the 'entity-framework' tag related to a given blog
// using a string to specify the relationship.
context.Entry(blog)
.Collection("Posts")
.Query()
.Where(p => p.Tags.Contains("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.
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
11/03/2020 • 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:
DbSet.Find
DbSet. local
DbSet. Add
DbSet. AddRange
DbSet. Remove
DbSet. RemoveRange
DbSet. Attach
DbContext.SaveChanges
DbContext. GetValidationErrors
DbContext.Entry
DbChangeTracker. entradas
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
11/03/2020 • 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.
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 = "ADO.NET Blog" };
context.Entry(blog).State = EntityState.Added;
context.SaveChanges();
}
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:
context.SaveChanges();
}
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.
context.SaveChanges();
}
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 = "ADO.NET Blog" };
context.SaveChanges();
}
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.
context.SaveChanges();
}
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.
context.SaveChanges();
}
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.
context.SaveChanges();
}
}
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
11/03/2020 • 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.
// Read the current value of the Name property using a string for the property name
object currentName2 = context.Entry(blog).Property("Name").CurrentValue;
// Set the Name property to a new value using a string for the property name
context.Entry(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.
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.
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 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.
Console.WriteLine("\nOriginal values:");
PrintValues(context.Entry(blog).OriginalValues);
Console.WriteLine("\nDatabase values:");
PrintValues(context.Entry(blog).GetDatabaseValues());
}
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.
// Change the current and original values by copying the values from other objects
var entry = context.Entry(blog);
entry.CurrentValues.SetValues(coolBlog);
entry.OriginalValues.SetValues(boringBlog);
Console.WriteLine("\nOriginal values:");
PrintValues(entry.OriginalValues);
}
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.
PrintValues(currentValues);
}
Use la propiedad OriginalValues en lugar de la propiedad CurrentValues para establecer los valores originales.
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.
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.
// Get the nested State complex object using a single lambda expression
var state2 = context.Entry(user)
.Property(u => u.Location.State)
.CurrentValue;
// Get the value of the Name property on the nested State complex object using chained calls
var name1 = context.Entry(user)
.ComplexProperty(u => u.Location)
.ComplexProperty(l => l.State)
.Property(s => s.Name)
.CurrentValue;
// Get the value of the Name property on the nested State complex object using a single lambda expression
var name2 = context.Entry(user)
.Property(u => u.Location.State.Name)
.CurrentValue;
// Get the value of the Name property on the nested State complex object using a dotted string
var name3 = context.Entry(user)
.Property("Location.State.Name")
.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.
Para imprimir todos los valores de propiedad actuales, se llamaría al método de la siguiente manera:
WritePropertyValues("", context.Entry(user).CurrentValues);
}
Administrar los conflictos de simultaneidad
11/03/2020 • 8 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.
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update the values of the entity that failed to save from the store
ex.Entries.Single().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 Management Studio. También puede insertar una línea antes de SaveChanges para
actualizar la base de datos directamente mediante SqlCommand. Por ejemplo:
context.Database.SqlCommand(
"UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
} while (saveFailed);
}
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
var entry = ex.Entries.Single();
var currentValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
// as instances of the entity type
var entry = ex.Entries.Single();
var databaseValues = entry.GetDatabaseValues();
var databaseValuesAsBlog = (Blog)databaseValues.ToObject();
} while (saveFailed);
}
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.
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 System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
namespace TransactionsExamples
{
class TransactionsExample
{
static void StartOwnTransactionWithinContext()
{
using (var context = new BloggingContext())
{
using (var dbContextTransaction = context.Database.BeginTransaction())
{
context.Database.ExecuteSqlCommand(
@"UPDATE Blogs SET Rating = 5" +
" WHERE Name LIKE '%Entity Framework%'"
);
context.SaveChanges();
dbContextTransaction.Commit();
}
}
}
}
}
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 ().
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):
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 System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
namespace TransactionsExamples
{
class TransactionsExample
{
static void UsingExternalTransaction()
{
using (var conn = new SqlConnection("..."))
{
conn.Open();
sqlTxn.Commit();
}
}
}
}
}
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 la 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 System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
namespace TransactionsExamples
{
class TransactionsExample
{
static void UsingTransactionScope()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required))
{
using (var conn = new SqlConnection("..."))
{
conn.Open();
scope.Complete();
}
}
}
}
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 System.Collections.Generic;
using System.Data.Entity;
using System.Data.SqlClient;
using System.Linq;
using System.Transactions;
namespace TransactionsExamples
{
class TransactionsExample
{
public static void AsyncTransactionScope()
{
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
using (var conn = new SqlConnection("..."))
{
await conn.OpenAsync();
await context.SaveChangesAsync();
}
}
}
}
}
}
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
(https://thedatafarm.com).
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.
Anotaciones de datos
Code First usa anotaciones del ensamblado de System.ComponentModel.DataAnnotations 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,
ASP.NET 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 anotación Required 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 Web. config 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 anotación de Required (antes que los
dos para crear un comando de INSERT 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; }
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
{
db.Entry(blog).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
catch (DbEntityValidationException ex)
{
var error = ex.EntityValidationErrors.First().ValidationErrors.First();
this.ModelState.AddModelError(error.PropertyName, error.ErrorMessage);
return View();
}
}
La validación no se vuelve a pasar automáticamente a la vista, que es el motivo por el que se usa el código
adicional que usa ModelState.AddModelError . Esto garantiza que los detalles del error lo convierten en la vista que
utilizará el ValidationMessageFor HtmlHelper para mostrar el error.
IValidatableObject
IValidatableObject es una interfaz que reside en System.ComponentModel.DataAnnotations . 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 método de Validate que Entity Framework llamará
durante SaveChanges o se puede llamar a sí mismo en cualquier momento en el que desee validar las clases.
Las configuraciones como Required y MaxLength realizan la validación en un único campo. En el método Validate
puede tener una lógica aún más compleja, por ejemplo, comparando dos campos.
En el ejemplo siguiente, se ha extendido la clase Blog para implementar IValidatableObject y, a continuación,
proporcionar una regla que el Title y el BloggerName no coincidan.
public class Blog : IValidatableObject
{
public int Id { get; set; }
[Required]
public string Title { get; set; }
El constructor ValidationResult toma un string que representa el mensaje de error y una matriz de string s que
representan los nombres de miembro que están asociados a la validación. Dado que esta validación comprueba el
Title y el BloggerName , se devuelven ambos nombres de propiedad.
A diferencia de la validación proporcionada por la API fluida, el resultado de la validación lo reconocerá en la vista y
el controlador de excepción que se usó anteriormente para agregar el error a ModelState no es necesario. Dado
que he establecido ambos nombres de propiedad en el ValidationResult , MVC HtmlHelpers muestra el mensaje
de error de 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 de 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 método
Blog.Validate agregado en la sección anterior.
A continuación se muestra un ejemplo de una invalidación ValidateEntity que valida las nuevas Post 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, se crea una instancia de DbEntityValidationResult y, a continuación, se agregan los
errores detectados a su colección de ValidationErrors .
if (result.ValidationErrors.Count > 0)
{
return result;
}
else
{
return base.ValidateEntity(entityEntry, items);
}
}
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 ha visto una serie de puntos de extensibilidad para personalizar el comportamiento si usa la interfaz
IValidatableObject o si va al método DbContext.ValidateEntity . Y estos dos últimos métodos de validación están
disponibles a través del DbContext , independientemente de que use el flujo de trabajo Code First, Model First o
Database First para describir el modelo conceptual.
Entity Framework blogs
11/03/2020 • 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:
Bloggers de la comunidad EF
Julia Lerman
Shawn Wildermuth
Casos prácticos de Microsoft para Entity Framework
11/03/2020 • 8 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 orientada a servicios (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 ADO.NET 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
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 de tamaño mediano, el equipo de desarrollo de silverware se ha establecido para mejorar
su producto con características de nivel empresarial más grandes con el fin de atraer más cadenas de restaurante.
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
11/03/2020 • 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 a leer la Guía de contribución en
nuestro repositorio de documentación.
Obtener ayuda con Entity Framework
11/03/2020 • 2 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.
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.
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 para el objeto CLR antiguo. 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.
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
11/03/2020 • 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.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
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 herramientas DBML/EDMX
Entity Developer