Creación de un Entity Model en EF
Creación de un Entity Model en EF
DE UN ENTITY MODEL
Entity Framework es la evolución natural de [Link] hacia el tratamiento de los datos
almacenados en una base de datos relacional a través del paradigma objetual. Por lo
tanto, se trata, al igual que LINQ to SQL, de un mapper objeto-relacional que es capaz
de abstraer las tablas y columnas de una base de datos y tratarlas como objetos
u entidades y relaciones.
Una entidad, por tanto, no es más que un objeto persistente, que cumple las
condiciones de unicidad e identidad.
Lo primero que deberemos hacer una vez que hayamos creado nuestro proyecto (por
ejemplo, de consola) será añadir un nuevo Entity Data Model. Para ello haremos click
derecho sobre nuestro proyecto y seleccionaremos la opción Add > New Item…
Una vez añadido, se nos preguntará el modo de generación del modelo. Queremos
mapear automáticamente nuestra base de datos, por lo que seleccionaremos la
primera opción (Generate from database).
A continuación rellenaremos los datos de conexión para acceder a la base de datos.
El siguiente paso será seleccionar los elementos que queremos importar. Elegimos
aquellos que nos interese modelar, le asignamos un nombre al namespace y pulsamos
el botón Finish.
La opción Pluralize or singularize generated object names teóricamente se encarga de
asignar un nombre en plural a las colecciones y hacerlo singular en las referencias. Sin
embargo, según mi experiencia previa con la pluralización automática, es más
aconsejable realizarlo a mano (salvo que no tengamos tiempo y se trata de una base
de datos enorme).
El resultado será el siguiente: un modelo con sus relativas relaciones que mantiene
gran similitud con el modelo relacional.
Si abrimos el documento XML perteneciente al fichero .edmx, vemos que por un lado
generará información sobre la parte relacional y por otro, sobre la parte objetual,
realizando el mapeo objeto-relacional.
9
<!-- CSDL content -->
10
<EntityType Name="Cliente">
11
<Key>
12 <PropertyRef Name="IdCliente" />
13 </Key>
20
3 {
6
public int IdCliente { get; set; }
7
public string Nombre { get; set; }
8
public [Link] FechaNacimiento { get; set; }
9
12
13
Investigando el modelo
En este explorador se nos mostrará que por un lado tenemos el modelo objetual y por
otro, el relacional.
Siempre podemos añadir nuevos objetos desde la base de datos. Por ejemplo, si
queremos añadir una vista y varios procedimientos almacenados, haremos click
derecho sobre la parte relacional y seleccionaremos la opción Update Model from
Database…
En caso de que no se cumpla, recordemos que toda entidad debe tener una clave de
entidad. Sin ella, Entity Framework no será efectivo. El explorador del modelo también
nos permitirá realizar esta operación.
Renombramos a plural también los elementos del Contexto, para dejar claro que se
trata de colecciones
Si queremos accede a los detalles del mapeado, también es posible hacer click derecho
sobre la entidad a consultar y pulsar click derecho sobre ella, seleccionando a
continuación “Table Mapping”
Esto mostrará la información del mapeo, comparando columna y propiedad. Desde
esta ventana podemos realizar operaciones como cambiar el mapeo o añadir ciertas
condiciones.
Accediendo al modelo
1 // Instanciamos el contexto
select cliente;
6
7
// Recorremos los clientes
8
foreach (Cliente cliente in clientes)
9
{
10
[Link]([Link]("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
11 [Link], [Link], [Link]));
12
15 {
[Link], [Link]));
17
18
// Recorremos las líneas de pedido
19
foreach (LineaPedido linea in [Link])
20
{
21
[Link]([Link]("\t\tPRODUCTO: {0}\tCANTIDAD: {1}\tTO
22 [Link], [Link],
([Link]*[Link])));
23
}
24
}
25
[Link](" -----------------------------------------\n");
26
}
27
28
Hecho esto, vemos que el acceso es correcto y que nuestro modelo funciona
correctamente.
ENTITY FRAMEWORK (II):
OBJECTCONTEXT Y ENTITY SQL
Una de las mayores ventajas de Entity Framework es que es una tecnología agnóstica
respecto a la base de datos que tiene por debajo. Con [Link] era necesario utilizar
clases específicas para cada base de datos (SqlCommand para SQLServer,
OracleCommand para Oracle, etc.). Sin embargo, Entity Framework no se casa con
nadie: hace uso de elementos genéricos (EntityConnection, EntityCommand, etc.) que
genera instrucciones en un lenguaje intermedio denominado Entity SQL, que es muy
similar al lenguaje SQL estándar.
En última instancia, el [Link] de toda la vida estará trabajando por debajo, pero
Entity Framework establece una capa de abstracción superior que evita la necesidad de
atarnos a una fuente de datos concreta. No obstante, es posible descender un poco en
el nivel de abstracción y, en lugar de hacer uso de LINQ to Entities tal y como hicimos
en el artículo anterior, lanzar directamente consultas sobre el ObjectContext usando
para ello Entity SQL.
Dependiendo de la versión de Entity Framework que se esté usando, esto se realizará
de un modo u otro, ya que la versión 4.1 introdujo como novedad la utilización
del DbContext por defecto, envolviendo el ObjectContext dentro de él.
// Instanciamos el DbContext
1 var dbContext = new testdbEntities();
2
3 // Extraemos el ObjectContext del DbContext (a partir de Entity
4 Framework 4.1)
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
5
6 // Ejecutamos una sentencia de Entity SQL que recupere todos los
7 clientes
8 string sqlQuery = "SELECT VALUE c FROM Clientes AS c";
9 var clientes = new ObjectQuery<Cliente>(sqlQuery, objectContext);
10
11 foreach (Cliente cliente in clientes)
{
12 [Link]([Link]("ID: {0}\tNOMBRE: {1}\tAÑO NAC:
13 {2}",
14 [Link], [Link],
15 [Link]));
}
Pese a que LINQ to Entities es bastante parecido a LINQ to SQL, a la hora de trabajar
con ambas tecnologías es necesario conocer las diferencias más importantes a nivel
práctico (dejaremos los fundamentos teóricos a un lado). Ambas tecnologías tienden a
la convergencia, puesto que Microsoft está intentando coger lo mejor de cada una de
ellas y adaptarlo a ambos mundos. Sin embargo, siguen existiendo diferencias
importantes, especialmente en las primeras versiones de LINQ to Entities.
Las primeras versiones de Entity Framework contenían ciertas carencias que, con
posteriores actualizaciones, han sido solventadas y corregidas. Una de las carencias
más importantes se correspondería con la imposibilidad de las versiones anteriores a
4.1 de realizar una carga automática de los elementos referenciados por un objeto. Es
decir, si tenemos el siguiente código:
1 // Instanciamos el contexto
3
// Lanzamos una consulta
4
var clientes = from cliente in [Link]
5
select cliente;
6
7
// Recorremos los clientes
8
foreach (Cliente cliente in clientes)
9 {
10 [Link]([Link]("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
11 [Link], [Link], [Link]));
12
18
// Recorremos las líneas de pedido
19 foreach (LineaPedido linea in [Link])
20 {
22 [Link], [Link],
([Link]*[Link])));
23
}
24
}
25 [Link](" -----------------------------------------\n");
26 }
27
28
Las versiones anteriores a la 4.1 eran incapaces de iterar sobre los pedidos de un
cliente, ya que era necesario realizar una carga implícita de estos elementos antes de
utilizarlos. Esto se hacía mediante el método Load(), que debía ser invocado antes de
hacer uso del listado:
7 [Link]();
8
// Recorremos los pedidos
9
10 foreach (Pedido pedido in [Link])
11 {
// ...
Insert
1 // Instanciamos el DbContext
11 };
12
14 {
18
Cliente guillermoSanabria = new Cliente()
19
{
20 Nombre = "Guillermo Sanabria San Juan",
21 FechaNacimiento = new DateTime(1983, 11, 1)
22 };
23
25 [Link](katiaRamos);
26
// MÉTODO 2: AddObject (genérico)
27
[Link]("Clientes", arturoSaavedra);
28
29
// MÉTODO 3: AddClientes (específico del contexto) – Versiones anteriores a
30 la 4.1
31 [Link](guillermoSanabria);
32
[Link]();
34
35
// Comprobamos si todo es correcto
36
foreach (Cliente cliente in [Link])
37
{
38
[Link]([Link]("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
39 [Link], [Link], [Link]));
40 }
41
42
Hecho esto, comprobamos que todo sea correcto:
1 // Realizamos la consulta
3
// Creamos los registros asociados:
4
Pedido p = new Pedido()
5
{
6
Cliente = cliente,
7
FechaPedido = [Link],
8
};
9
13 Cantidad = 7
};
14
15
// Insertamos registros la línea de pedido, que ya tiene asociado el pedido.
16
// LINQ to Entities se encargará de realizar la inserción previa del pedido por
17 nosotros.
18 [Link](lp);
19
20 // Guardamos los cambios
21 [Link]();
22
Update
1
// Instanciamos el DbContext
2
var dbContext = new testdbEntities();
3
4 // Realizamos la consulta
5 var clientes = [Link](cliente =>
[Link]("Katia"));
6
7
// Modificamos los objetos que consideremos oportunos
8
foreach (var cliente in clientes)
9
[Link] = [Link]("Katia", "Katerina");
10
16 {
El resultado:
Delete
1 // Instanciamos el DbContext
6
// Realizamos la consulta
7
var clienteEliminar = [Link](cliente => [Link] ==
8 8).First();
10 // Eliminamos el cliente
13
// Guardamos los cambios
14
[Link]();
15
16
// Comprobamos si todo es correcto
17
foreach (Cliente cliente in [Link])
18 {
19 [Link]([Link]("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
20 [Link], [Link], [Link]));
21 }
22
Sin embargo, ¿qué ocurrirá si eliminamos un cliente que tenga asociado algún pedido,
como por ejemplo el cliente 9? Veamos:
Por desgracia, en este caso deberemos limitarnos a decir “así no lo hagas“. Entity
Framework no es capaz de manejar bien los borrados en cascada, por lo que
deberemos delegar las operaciones de borrado a la base de datos, bien a través de una
restricción de borrado en cascada, bien mediante la creación de un procedimiento
almacenado que se encargue de realizar este proceso (que podremos invocar también
desde nuestro DbContext.
Un ejemplo para esto sería crear dos procedimientos almacenados: uno que borre los
pedidos y sus líneas de pedido y otro que, además de borrar pedidos y líneas, borre
también los clientes. El primero de ellos será algo como lo siguiente:
1
create procedure sp_Pedido_DeleteCascade
2 @IdPedido int
3 as
4 begin
1
create procedure sp_Cliente_DeleteCascade
2
@IdCliente int
3 as
4 begin
12
declare @IdPedido int;
13
14
15 -- Borramos todos los pedidos junto a sus lineas de pedido, recorriendo
los
16
-- pedidos devueltos por el cursor.
17
open cur;
18 fetch next from cur into @IdPedido
19 while @@FETCH_STATUS = 0
20 begin
25
delete from Cliente where IdCliente = @IdCliente;
26
end;
// Instanciamos el DbContext
1
var dbContext = new testdbEntities();
2
3
// Realizamos la consulta
4
var clienteEliminar = [Link](cliente => [Link] ==
5 9).First();
6
// Ejecutamos el procedimiento almacenado pasando el ID (9) como parámetro.
7
dbContext.sp_Cliente_DeleteCascade([Link]);
8
9
// Guardamos los cambios
10
[Link]();
11
{
14
[Link]([Link]("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
15
[Link], [Link], [Link]));
16
}
17
18
Es importante saber que Entity Framework tiene una política de todo o nada para hacer
uso de esta característica, es decir: si se decide utilizar un procedimiento almacenado
para realizar las eliminaciones, será obligatorio definir también los métodos de
inserción y actualización.
1
2 create procedure sp_Cliente_Insert
@Nombre nvarchar(256),
3
@FechaNacimiento date
4 as
5 begin
6 set nocount on;
7
8 -- Realizamos la inserción
9 insert into Cliente(Nombre, FechaNacimiento)
values(@Nombre, @FechaNacimiento);
10
11 -- Devolvemos el nuevo id insertado
12 select SCOPE_IDENTITY() as id;
13 end;
14
El procedimiento de actualización será similar: contará con todos los valores del
registro y realizará la comparación por el ID, tal y como se muestra a continuación:
Tras rellenar los tres elementos, se nos debería mostrar algo como lo siguiente,
realizando el mapeo entre los parámetros del procedimiento y las propiedades de
nuestros objetos:
Sólo nos queda un detalle: en la inserción queremos que el campo IdCliente se rellene
automáticamente en nuestro objeto tras la inserción. ¿Cómo lograr eso? Si recordamos,
el procedimiento ejecutaba la siguiente sentencia al finalizar la inserción:
// Instanciamos el DbContext
1 var dbContext = new testdbEntities();
2
3 // Realizamos la consulta
4 var clientes = [Link](cliente =>
5 [Link]("Kat"));
6
// Modificamos los objetos que consideremos oportunos
7 foreach (var cliente in clientes)
8 [Link] = [Link]("Katia", "Katerina");
9
10 // Guardamos los cambios
[Link]();
11
12 // Comprobamos si todo es correcto
13 foreach (Cliente cliente in [Link])
14 {
15 [Link]([Link]("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
[Link], [Link], [Link]));
16 }
17
18
19
Una vez que tenemos claro que un DbContext es el encargado de velar por la
integridad en el esquema objeto-relacional, ¿qué ocurriría si manejamos objetos que,
por alguna razón, escapan a su control? Nos referimos, por ejemplo, al envío de los
datos de un cliente a través de un formulario web y ponernos a la espera de que el
usuario los modifique en otra página distinta. Este escenario implicaría, con toda
seguridad, que el DbContext en el que se recuperó el objeto sea distinto al DbContext
que se encargará de modificarlo.
Volver a recuperar el objeto completo, copiar todos los datos del objeto
enviado por el usuario al objeto que acabamos de recuperar y guardar los
cambios.
Enlazar directamente el objeto al DbContext y decirle que actualice los
cambios.
Para la primera opción no es necesaria mucha explicación, puesto que se trata de una
modificación normal y corriente. Para la segunda opción habrá que trabajar un poco
más, pero se considera una opción más correcta. Además, el proceso dependerá de si
estamos haciendo uso de un ObjectContext (versión anterior a 4.1) o un DbContext
(versión posterior a ésta).
3 // Instanciamos el DbContext
{
5
// Recuperamos el cliente anterior
6
datosAntiguos = [Link](cliente => [Link] ==
7 3).First();
8 }
{
2
// Creamos un nuevo cliente con el MISMO ID que el anterior y
3
// cambiamos algunos datos
4
Cliente datosNuevos = new Cliente()
5
{
6 IdCliente = [Link],
7 Nombre = "Ana Maria Lopez Diaz",
8 FechaNacimiento = new DateTime(1947, 1, 15)
9 };
10
17 [Link]();
}
18
19
El método Attach indica a nuestro context que “empiece a preocuparse” por el destino
del objeto que le hemos pasado como parámetro. Por lo tanto, cualquier cambio
realizado sobre este objeto será ahora repercutido sobre la fuente de datos al invocar
el método SaveChanges().
A partir de esta versión, este proceso se simplifica bastante. Bastará con realizar lo
siguiente:
5 {
17 [Link](datosNuevos);
18
// Cambiamos el estado a "Modificado" para que Entity Framework sepa que
19
// deben actualizarse los datos
20
[Link](datosNuevos).State = [Link];
21
[Link]();
22
}
23
24
Si nos fijamos bien, en la versión 4.1 nos hemos limitado a enlazar el nuevo objeto al
listado en lugar de al contexto, y hemos cambiado manualmente el estado de la
entidad para forzar su actualización. Al ejecutar SaveChanges(), el contexto
comprobará los estados de todas sus entidades y aplicará la operación
correspondiente a cada una. En nuestro caso, actualizando sus datos.
El método opuesto a Attach() es Detach() y, como su propio nombre indica, hace que el
contexto deje de trazar los cambios realizados sobre ese objeto, ignorándolo cuando
se ejecute un SaveChanges().
Sabiendo esto, podemos jugar un poco con las propiedades y realizar operaciones de
forma “alternativa”, tal y como hemos hecho con la actualización. Así, una forma de
insertar un nuevo cliente podría ser la siguiente:
1
Cliente guillermoSanabria = new Cliente()
2
{
3 Nombre = "Guillermo Sanabria San Juan",
4 FechaNacimiento = new DateTime(1983, 11, 1)
5 };
[Link](guillermoSanabria).State = [Link];
7
8
// Guardamos los cambios
9
[Link]();
10
ENTITY FRAMEWORK
(VI): WEBSERVICES
Hasta ahora hemos visto el funcionamiento de LINQ y Entity Framework. La siguiente
serie de artículos estarán orientados hacia los servicios web, por lo que haremos una
pequeña introducción aplicando los conocimientos que hemos obtenido hasta el
momento.
Para crear un nuevo servicio web, crearemos un nuevo proyecto web de tipo [Link]
Empty Web Application y le asociaremos un nuevo nombre. Los servicios web reciben
ese nombre porque operan sobre el protocolo HTTP, así que este será nuestro punto
de partida.
A continuación volveremos la vista atrás y, tal y como vimos en los artículos dedicados
a Entity Framework, añadiremos un nuevo modelo de datos a nuestro proyecto web.
Una vez añadido, el asistente nos preguntará el origen de los datos. Le responderemos
que nuestro deseo es generarlo a partir de la base de datos y pulsaremos Next >
Tal y como hicimos en ocasiones anteriores, seleccionaremos los objetos a modelar:
tablas, vistas, procedimientos almacenados…
Si todo ha ido bien, nuestro modelo debería generarse con los elementos
seleccionados.
A partir del Framework 3.5, Microsoft encapsuló la gestión de los servicios web en un
conjunto de bibliotecas denominadas Windows Communication Foundation. Nuestra
intención es crear un servicio de datos (veremos los tipos de webservices en
posteriores artículos), por lo que haremos click derecho sobre nuestro proyecto web y
añadiremos un nuevo elemento Web > WCF Data Service.
Al finalizar la generación, veremos que se han creado dos elementos: un fichero con
extensión .svc, que representa el servicio web en sí y un fichero .[Link] que contendrá
el code behind o comportamiento, es decir, el código fuente que definirá las acciones
que realizará.
Si aún no hemos tocado nada, el cuerpo del fichero c# será similar al siguiente:
1 namespace EntityFrameworkService
{
2
public class GestionPedidosDataService : DataService< /* TODO: put your data s
3 */ >
4 {
{
7
// TODO: set rules to indicate which entity sets and service operatio
8 updatable, etc.
9 // Examples:
10 // [Link]("MyEntityset", [Link]
11 // [Link]("MyServiceOperation", Service
[Link] = DataServiceProtocolVe
12
}
13
}
14
}
15
Como podemos ver, existe una sección TODO dentro de los símbolos “<” y “>” que nos
indica que introduzcamos la clase de nuestra fuente de datos. Ésta no será otra que la
clase generada en el paso anterior y que dio lugar a nuestro Entity Data Model, es
decir, el DbContext. Por lo tanto, añadimos el nombre de la clase para que el servicio
sepa a qué conjunto de datos podrá tener acceso.
Configuración y permisos
Si, por el contrario (y que será con toda seguridad el escenario más común) deseamos
asignar más de un permiso a una entidad, los permisos se concatenarán con el
símbolo OR binario “|”. Por ejemplo, la siguiente configuración asigna permisos de
lectura a todas las entidades, y, además, permisos de inserción (WriteAppend) a las
entidades Pedido y LineaPedido:
4 {
[Link] = DataServiceProtocolVersio
5
6
[Link]("*", [Link]);
7
[Link]("Pedido", [Link] |
8 [Link]);
9 [Link]("LineaPedido", [Link] |
[Link]);
10
}
11
}
12
Esto abrirá una ventana que mostrará un fichero XML con un aspecto parecido al
siguiente:
Como podemos ver, además de la cabecera, nos encontramos con un nodo service que
posee un nodo workspace que contiene un conjunto de nodos collection. Estos
elementos, como podremos adivinar nada más verlos, se corresponden a los
elementos que nuestro DbContext se encarga de mapear.
Uno de los nodos tiene un atributo denominado href cuyo valor es Cliente. Probemos,
por lo tanto, a navegar a esta ruta relativa indicando la siguiente dirección en nuestro
navegador (cambiando el puerto por el que nos asigne el navegador).
[Link]
Es posible que realizar esta operación provoque que se muestre un mensaje como el
siguiente:
Probemos algo más. Sabemos que Entity Framework gestiona de forma interna una
clave primaria para cada entidad. También sabemos que el modelo objetual que
expone Entity Frameworkintegra colecciones de objetos con los que el objeto actual se
encuentra relacionado. Por lo tanto, probemos a comprobar los pedidos asociados al
cliente cuya clave es “3”. Basta con algo como lo siguiente:
[Link]
Consultas REST
En cuanto a los tipos de consultas que podemos realizar mediante REST, aquí se
muestran unos pocos ejemplos:
$value: recupera el valor solicitado, sin metadatos asociados (sin XML, en este
caso).
/Cliente(3)/Nombre/$value
$count: devuelve el total de registros de una entidad.
/Cliente/$count
$skip=n: ignora los primeros n elementos. En conjunción con top, sirve para
realizar paginaciones.
/Cliente?$top=1&$skip=1
Creando el cliente
Lo siguiente será añadir una referencia a nuestro servicio. WCF proporciona un servicio
de descubrimiento que, a partir de la dirección de un servicio web, extrae toda la
información disponible asociada a éste. Haremos click derecho sobre nuestro proyecto
cliente y seleccionaremos la opción Add Service Reference…
En la caja de dirección, insertaremos la URI del servicio web, incluyendo la extensión
svc. Una vez hecho esto, pulsaremos en Go, dejando que Visual Studio descubra qué
es lo que se encuentra al otro lado. Finalmente, le daremos un nombre al namespace,
que servirá para identificar los objetos que se encuentran al otro lado.
Con algo tan sencillo como esto habremos configurado nuestra aplicación para
comunicarse con el servicio web. Lo siguiente que haremos será crear una referencia
al DbContext remoto, para lo cual le tendremos que pasar la URI del servicio web.
Realizar una consulta será similar a lo que hacemos en local: realizamos una
consulta LINQ to Entities buscando el objeto a recuperar, teniendo en cuenta que
nuestro DbContext es limitado. Es importante darse cuenta de que no disponemos de
todas las operaciones que podemos realizar sobre un contexto local: nos tendremos
que limitar a operaciones más simples como condiciones, ordenaciones y
paginaciones. Deberemos olvidarnos de joins, funciones de agregación o similares.
Eso no significa que no podamos realizar esas operaciones, sino que tendrán un mayor
coste. Siempre es posible obtener la totalidad de registros de una tabla, transformarlos
en una lista y realizar las operaciones complejas en entorno local. Sin embargo, si este
tipo de operaciones son necesarias, será aconsejable hacer uso de otros recursos
como procedimientos almacenados para que nuestro rendimiento y escalabilidad no se
vean comprometidos.
Nuestro primer paso será, por lo tanto, crear una consulta y generar cuatro nuevos
objetos: un cliente, un pedido y dos líneas de pedido asociadas a dos productos
distintos que, previamente, existirán en la base de datos. Por ejemplo, una línea de
pedido contendrá tres lapiceros y la otra, siete bolígrafos.
5
6 // Creamos un nuevo cliente
{
8
Nombre = "Pedro Gonzalez Arnau",
9
FechaNacimiento = new DateTime(1988, 2, 2)
10
};
11
12
// Creamos un nuevo pedido y dos nuevas lineas de pedido
13 Pedido pedido = new Pedido()
14 {
15 FechaPedido = [Link]
16 };
17
{
19
IdProducto = [Link],
20
Cantidad = 3
21
};
22
23
LineaPedido lineaPedidoBoligrafos = new LineaPedido()
24 {
25 IdProducto = [Link],
26 Cantidad = 7
27 };
28
Inserción
2 [Link](nuevoCliente);
6
// Añadimos las relaciones entre pedido y lineas de pedido al contexto
7
[Link](pedido, "LineasPedido",
8 lineaPedidoBoligrafos);
El siguiente paso será invocar el método SaveChanges para comprometer los cambios.
Además, echaremos un vistazo dentro del valor que el servicio devuelve como
respuesta para comprobar qué es lo que ha ocurrido en el otro lado de la conexión.
3
// Mostramos los cambios
4
foreach (ChangeOperationResponse cambio in respuesta)
5
{
6
EntityDescriptor descriptor = (EntityDescriptor)[Link];
7 if (descriptor != null)
8 {
9 if ([Link]().IsAssignableFrom(typeof(Cliente)))
10 {
[Link]([Link]("Cliente: {0}\t{1}\t{2}",
11
((Cliente)[Link]).IdCliente, ((Cliente)[Link]
12
}
13
else if ([Link]().IsAssignableFrom(typeof(Pedido)))
14
{
15 [Link]([Link]("\tPedido: {0}\t{1}\t{2}",
16 ((Pedido)[Link]).IdPedido, ((Pedido)[Link])
[Link]()));
17
}
18
else if ([Link]().IsAssignableFrom(typeof(LineaPedido))
19
{
20
[Link]([Link]("\t\tLinea: {0}\t{1}\t{2}",
21 ((LineaPedido)[Link]).IdLineaPedido, ((LineaPedido)des
[Link]()));
22
}
23
24
}
25
}
26
27
Esto nos devolverá la siguiente información. Como podemos observar, los campos de
los identificadores, por ejemplo, ya habrán sido rellenados por Entity Framework.
A continuación mostraremos, mediante foreach anidados, una consulta de todos los
clientes con ID = 61 (el que acabamos de insertar) junto a sus pedidos y líneas de
pedido.
1
var clientes = [Link](cliente => [Link] == 61);
2
[Link]("\n\nTRAS LA INSERCION:");
3
foreach (var cliente in clientes)
4 {
5 [Link]([Link]("\tID: {0}\tNOMBRE: {1}",
6 [Link], [Link]));
8 {
[Link], [Link]));
14
}
15
}
16
}
17
Si ejecutamos este código veremos, asombrados, que únicamente se habrá recuperado
el ID y el Nombre del cliente, pero no se mostrará nada de información acerca de
pedidos o líneas de pedido. Esto se debe a que, por defecto, la petición de consulta
es lazy, y habrá que indicar específicamente que se quiere recuperar la información de
los listados asociados.
Para realizar esta operación, basta con indicar con el método LoadProperty el listado
del objeto que se quiere recuperar, siendo el primer parámetro el objeto cuyo listado
se quiere expandir y el segundo, una cadena de texto con el nombre del listado. Así,
nuestro código tendría el siguiente aspecto:
1 [Link]("\n\nTRAS LA INSERCION:");
3 {
4 [Link](cliente, "Pedidos");
12 {
13 [Link](l, "Productos");
[Link]([Link]("\t\t\tPRODUCTO: {0}\tCANTIDAD:
14 {1}",
15 [Link], [Link]));
16 }
17 }
18 }
19
Modificación
1
// MODIFICACION
2
3
dbContext = new [Link](serviceRoot);
4 clientes = [Link](cliente => [Link] == 61);
5
10 [Link] = 59;
11
[Link](clienteModificar);
12
[Link](lineaPedidoModificar);
13
[Link]();
14
Por último, el proceso de eliminación, que será similar al de modificación, salvo que
invocaremos el método DeleteObject en lugar de UpdateObject. El proceso de
eliminación en cascada es parecido al visto en la sección correspondiente de Entity
Framework, por lo que nuevamente, suele aconsejarse utilizar procedimientos
almacenados para realizar este proceso.
1 // ELIMINACION
5
var lineaPedidoEliminar = (from linea in [Link]
6
where [Link] ==
7 [Link]
8 select linea).Single();
10 [Link](lineaPedidoEliminar);
11 [Link]();
Tabla Carrera.
EF 5 Code First
[Link]: Proyecto
Web [Link] MVC 4 (Razor).
[Link]: Proyecto de
tipo Biblioteca de Clases. Contendrá el contexto a la
base de datos (BBDD).
[Link]:
Proyecto de tipo Biblioteca de Clases. Contendrá las entidades, que darán lugar posteriormente
a las tablas de la BBDD.
2. El siguiente paso será agregar Entity Framework (EF), para ello utilizaremos
el Administrador de Paquetes NuGet. EF será agregado a dos de los tres proyectos:
[Link]
[Link]
5. Crear la primera entidad POCO (código primero), las entidades las creáremos en el proyecto
de entidades ([Link])
namespace [Link]
namespace [Link]
}
7. Ahora sólo queda hacer uso del contexto y las entidades, para ello escribiremos en
el Load de alguna página o en el Controller en el caso de usar MVC.
namespace [Link]
[Link] = [Link]();
return View();
EF Code First Migrations, no es otra cosa que habilitar la capacidad de actualizar la base de
datos con los cambios realizados en nuestras entidades de código (POCO)… pero veamos un
ejemplo.
Sigamos con el mismo ejemplo del articulo anterior “EF 5 Code First (Entity Framework Code
First)”, en el que teníamos una entidad Persona:
namespace [Link]
Imaginemos que deseamos agregar una nueva propiedad, por ejemplo …. Numero de
Documento, quedando así la nueva clase:
namespace [Link]
Entity Framework te brinda además la posibilidad de que las migraciones se realicen de forma
automática, así nos ahorraríamos el tener que ir a la Consola del Administrador de paquetes y
escribir estos 2 comandos:
Add-Migration
Update-Database
public Configuration()
AutomaticMigrationsEnabled = true;
}
Después debemos registrar el inicializador MigrateDatabaseToLatestVersion. Para ello
escribimos el siguiente código en el [Link], método Application_Start (o cualquier otro
punto de entrada de nuestra aplicación);
[Link](new MigrateDatabaseToLatestVe
rsion<AppEjemploEfCodeFirstDbContext, Configuration>());
Y con esto todo listo, ya puedes hacer cualquier modificación en el código (clases POCO) y
después de ejecutar la aplicación, los cambios serán migrados a la base de datos.
En estos casos tendrás (al menos hasta la versión actual) que forzar la migración de forma
manual, para ello escribirás el siguiente comando en la consola:
Existen además algunos casos en los que no es suficiente, pongamos un ejemplo: Imagina que
tienes una entidad con una propiedad (public long Id { get; set; }) y decides cambiar el
tipo a entero (public int Id { get; set; }).
En el caso anterior no basta con forzar la actualización. En este caso, mi consejo es que quites
la entidad del modelo (o la propiedad) y fuerces la actualización (Update-Database -Force -
Verbose), con esto se eliminará la tabla de datos, vuelves a incluir la entidad y nuevamente
fuerzas la actualización para que cree la tabla con el nuevo tipo deseado.
Model First,
le da la posibilidad de diseñar toda la lógica del negocio, le permite
crear un modelo nuevo mediante Entity Framework Designer y después genera
un esquema de la base de datos a partir del modelo.
En esta ocasión voy a usar DataBase First pero antes se debe crear una base
de datos de prueba.
1
CREATE DATABASE PruebaEF
2
GO
3
USE PruebaEF
4
GO
5 CREATE TABLE [dbo].[Personal](
6 [Id] [varchar](6) NOT NULL,
) ON [PRIMARY]
15
16
GO
17
18
SET ANSI_PADDING OFF
19
GO
20
Para este ejemplo estoy usando Visual Studio 2013 y SQL 2014.
Una vez teniendo la base de datos creada pasamos a crear una solución
distribuida en una arquitectura de 3 capas, Presentación(Proyecto Windows
Forms), Dominio(Proyecto Class Library), Persistencia Datos(Proyecto Class
Library), se hace las referencia entre capas Persistencia en Dominio,
Persistencia y Dominio en Presentación, referencio Persistencia en Presentación
por que el mapeo a la infraestructura de la DB esta en esa capa solo por eso,
pero la lógica de la aplicación va en el Dominio.
Obtenemos el modelo.
1 //------------------------------------------------------------------------------
2 // <auto-generated>
//
4
// Los cambios manuales en este archivo pueden causar un comportamiento inesperad
5 aplicación.
6 // Los cambios manuales en este archivo se sobrescribirán si se regenera el códig
7 // </auto-generated>
8 //------------------------------------------------------------------------------
9
10 namespace [Link]
11 {
using System;
12
using [Link];
13
14
public partial class Personal
15
{
16
public string Id { get; set; }
17 public string Nombre { get; set; }
18 public string Direccion { get; set; }
21 }
}
22
23
1 using [Link];
2 using System;
3 using [Link];
4 using [Link];
using [Link];
5
6 using [Link];
7 using [Link];
using [Link];
8
9
namespace [Link]
10
{
11
public class PersonalRepository
12
{
13
17 {
25
27 {
{
29
int codigo;
30
var ultimoId = Convert.ToInt32([Link](x => [Link])) + [Link]
31
codigo = ultimoId;
32
return [Link]("{0:000000}", codigo);
33 }
34 }
35
{
39
int resultado = [Link](x => [Link] == codigo).Count();
40
if (resultado == 0)
41
return false;
42
else
43 return true;
44 }
45 }
47 {
54 }
55
57 {
try
58
{
59
using(var context = new PruebaEFEntities())
60
{
61
[Link](model);
62 [Link]();
63 }
64 }
66 {
69 }
70
public void Actualizar(Personal model)
71
{
72
try
73
{
74
using(var context = new PruebaEFEntities())
75 {
76 [Link](model).State = [Link];
77 [Link]();
78 }
79 }
88 {
95 }
96
97 }
98 }
99
100
101
102
103
104
105
106
107
1 using System;
using [Link];
2
using [Link];
3
using [Link];
4
using [Link];
5
using [Link];
6 using [Link];
7
8 namespace [Link]
9 {
11 {
14
PersonalRepository personal = new PersonalRepository();
15
16
public List<[Link]> GetPersonal()
17
{
18 return [Link]();
19 }
20
22 {
return [Link](codigo);
23
}
24
25
public void Guardar([Link] model)
26
{
27
[Link]();
28 if ([Link]([Link])) [Link]("Debe ingresar el
29
30 if([Link]() == 0)
31 {
32 if([Link]([Link]))
33 [Link] = [Link]();
34
try
35
{
36
if([Link]([Link]))
37
{
38
[Link](model);
39 MensajeLogica = "Registro actualizado";
40 }
41 else
42 {
43 [Link](model);
50
51 }
52 }
53
public void Eliminar(string codigo)
54
{
55
[Link](codigo);
56
}
57
58
}
59 }
60
61
62
63
64
65
using System;
1
using [Link];
2
using [Link];
3
using [Link];
4 using [Link];
5 using [Link];
6 using [Link];
7 using [Link];
8 using [Link];
using [Link];
9
using [Link];
10
11
namespace [Link]
12
{
13
public partial class frmPersonal : Form
14 {
16
[Link] personal = new [Link]();
17
18
public frmPersonal()
19
{
20
InitializeComponent();
21
}
22
25 LoadDGVPersonal();
26
27 }
28
{
30
[Link] model = new [Link]
31
[Link] = [Link];
32
[Link] = [Link];
33
[Link] = [Link];
34 [Link] = [Link];
35 [Link] = Convert.ToInt16([Link] ? "1" : "0");
36 [Link](model);
37 LoadDGVPersonal();
38 }
39
void LoadDGVPersonal()
40
{
41
List<[Link]> list = [Link]
42
[Link] = false;
43
[Link] = list;
44
45 }
46
47 void ObtenerId()
{
48
strCodigo = [Link]([Link][0].Value);
49
}
50
51
void Buscar()
52
{
53 if(strCodigo != [Link])
54 {
56 [Link] = [Link];
57 [Link] = [Link];
[Link] = [Link];
58
[Link] = [Link];
59
[Link] = [Link]([Link]);
60
}
61
}
62
63 void Eliminar()
64 {
65 ObtenerId();
67 [Link],
70
if ([Link](msg, "Personal", [Link], [Link]
71 [Link])
72 {
73 [Link](strCodigo);
LoadDGVPersonal();
74
}
75
76 }
77
{
79
ObtenerId();
80
Buscar();
81
[Link] = tabPage2;
82
}
83
86 if([Link] == Keys.F7)
87 {
88 Eliminar();
}
89
}
90
91
}
92
}
93
94
95
96
97
98
99
100
A partir de la versión 4.1, se utiliza el método Add del DbContext para realizar inserciones. En versiones anteriores, se debía usar AddObject en el ObjectContext. Finalmente, se usa SaveChanges() para confirmar los cambios en la base de datos .
Las primeras versiones de Entity Framework carecían de ciertas facilidades presentes en LINQ to SQL, como la carga automática de elementos referenciados. Mientras que en LINQ to SQL esta carga diferida es automática, en Entity Framework, hasta la versión 4.1, se debía realizar manualmente usando métodos como Load() o mediante cláusulas Include en las consultas .
Habilitar las migraciones es crucial en EF Code First porque asegura que la base de datos se mantenga actualizada con los cambios en las entidades de código. Esto se hace usando la consola del Administrador de paquetes de Visual Studio, ejecutando el comando 'Enable-Migrations', luego 'Add-Migration' seguido de un nombre para identificar la migración, y finalmente 'Update-Database' para aplicar los cambios .
DbContext es más ligero y fácil de usar en comparación con ObjectContext, proporcionando una API simplificada y mejoras en el manejo de conjuntos de entidades, lo que lo hace más adecuado para aplicaciones más recientes. Además, facilita la implementación de patrones como Code First y aligna con las mejores prácticas modernas de ORM .
Las actualizaciones en Entity Framework implican modificar las propiedades de los objetos recuperados a través de consultas y luego ejecutar SaveChanges() para aplicar los cambios a la base de datos. Es importante asegurar que los objetos estén correctamente anclados en el contexto para evitar conflictos o pérdida de datos .
Entity Framework maneja automáticamente las asociaciones entre entidades al insertar datos. Cuando se añade una línea de pedido que refiere a un pedido no existente, el framework gestionará la inserción previa del pedido al insertar la línea de pedido asociada .
Desde la versión 4.1 de Entity Framework, se introdujo el uso del DbContext por defecto, que envuelve el ObjectContext. Para acceder al ObjectContext a través del DbContext, se realiza un casting explícito a IObjectContextAdapter y se accede a su propiedad ObjectContext. Esto permite lanzar consultas directamente sobre el ObjectContext usando Entity SQL .
El uso de Entity SQL permite lanzar consultas directamente sobre ObjectContext, ofreciendo mayor control y flexibilidad en ciertas situaciones, especialmente cuando se necesita un mayor nivel de abstracción o cuando se integran características específicas no soportadas directamente por LINQ. Sin embargo, esto puede requerir un conocimiento más profundo del modelo de datos y puede reducir la legibilidad en comparación con las sintaxis más simples de LINQ .
Los cambios en las entidades POCO requieren que se actualice la base de datos correspondiente usando EF Code First Migrations. Primero, se habilitan las migraciones, luego se ejecuta 'Add-Migration' para generar el script necesario y por último, 'Update-Database' para aplicar los cambios a la base de datos. Esto asegura que las estructuras de la base de datos estén en sincronía con el modelo de código .
En una arquitectura de tres capas utilizando Entity Framework, la capa de presentación es responsable de la interfaz de usuario, la capa de dominio gestiona la lógica del negocio y la capa de persistencia se encarga del acceso a datos y conexión con la base de datos. Entity Framework suele estar en la capa de persistencia, administrando el mapping de la infraestructura de la base de datos .









