Sunteți pe pagina 1din 104

ENTITY FRAMEWORK (I): CREACIÓN

DE UN ENTITY MODEL
Entity Framework es la evolución natural de ADO.NET 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.

Añadiendo un Entity Data Model

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 en el diálogo de selección, navegamos hasta la sección Data y seleccionamos


el elemento ADO.NET Entity Data Model, al que le daremos un nombre identificativo.

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.

Como podemos ver, los objetos se componen de propiedades (Properties) y de


propiedades de navegación (Navigation Properties), que simbolizarán elementos o
conjuntos de elementos con los que la entidad esté relacionada (por ejemplo, Cliente
posee una propiedad de navegación Pedido que simbolizará el conjunto de
entidades Pedido con los que un cliente está relacionado).

En este punto, hay que destacar un par de detalles:


 Es obligatorio que cada entidad posea al menos una clave de entidad. En caso
contrario, Entity Framework no podrá utilizarse con ese elemento. La clave de
entidad se importa automáticamente de la base de datos (clave primaria).
 Las propiedades de navegación son de dos tipos, dependiendo de la relación:
 Si la relación es 1:*, la parte “1” tendrá un listado compuesto por uno o más
elementos del lado “n”
 Por ejemplo, Cliente posee una propiedad Pedido, que es un listado de sus
pedidos asociados.
 Si la relación es 1:*, la parte “n” tendrá una referencia a un único objeto del
elemento del lado “1”
 Por ejemplo, Pedido posee una propiedad Cliente, que se corresponde con el
Cliente al cual están asociados los pedidos.
 Es buena práctica cambiar el nombre de los listados a plural y mantener las
referencias a objetos individuales en singular. De este modo, la nomenclatura
ayudará a distinguir cuándo se trata de un listado o de un único elemento.

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.

<!-- SSDL content -->


1
<EntityType Name="Cliente">
2
<Key>
3
<PropertyRef Name="IdCliente" />
4
</Key>
5 <Property Name="IdCliente" Type="int" Nullable="false" StoreGeneratedPatt

6 <Property Name="Nombre" Type="nvarchar" Nullable="false" MaxLength="50" />

<Property Name="FechaNacimiento" Type="date" Nullable="false" />


7
</EntityType>
8

9
<!-- CSDL content -->
10
<EntityType Name="Cliente">
11
<Key>
12 <PropertyRef Name="IdCliente" />
13 </Key>

14 <Property Name="IdCliente" Type="Int32" Nullable="false" p1:StoreGenerate

15 <Property Name="Nombre" Type="String" Nullable="false" MaxLength="50" Unic

16 <Property Name="FechaNacimiento" Type="DateTime" Nullable="false" Precisi

<NavigationProperty Name="Pedido" Relationship="GestionPedidos.FK


17
ToRole="Pedido" />
18 </EntityType>
19

20

Si desplegamos el fichero .edmx, vemos que, además, contiene un conjunto de


ficheros, divididos en Context y Model. La parte del contexto se encargará de realizar
las operaciones comunes sobre las entidades, que se alojarán en la parte del modelo.

El modelo de una entidad será, por lo tanto, un POCO cuyas propiedades se


corresponderán con las mapeadas a partir del modelo relacional. Por ejemplo, el
siguiente POCO simboliza una entidad de tipo Cliente:

public partial class Cliente


1
{
2 public Cliente()

3 {

this.Pedido = new HashSet();


4
}
5

6
public int IdCliente { get; set; }
7
public string Nombre { get; set; }
8
public System.DateTime FechaNacimiento { get; set; }
9

10 public virtual ICollection Pedido { get; set; }


11 }

12

13

Investigando el modelo

La mejor forma de navegar a través de nuestro modelo es con el explorador de modelo


o Model Browser. Por defecto no se muestra, por lo que para acceder a él, habrá que
pulsar click derecho sobre el modelo y seleccionar la opción Model Browser.

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…

Acto seguido, seleccionamos los objetos que queremos mapear a objetos.


Finalmente aparecerán en el modelo, colocados en su carpeta correspondiente.
Desde el explorador podemos acceder a las propiedades de los modelos, y realizar
operaciones como pluralizar las colecciones, tal y como sugerimos antes.

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

Acceder al modelo es similar a lo que ya vimos con LINQ to SQL. Comenzaremos


instanciando un DbContext (estará definido en el fichero
<nombreModelo>.Context.cs), al que podemos pasar un parámetro de configuración
como una cadena de conexión o bien dejarlo en blanco para que utilice la conexión
por defecto.

A continuación lanzaremos una consulta mediante LINQ y recorreremos los elementos,


tal y como vimos que se hacía en LINQ to SQL.

1 // Instanciamos el contexto

2 var contexto = new testdbEntities();


3

4 // Lanzamos una consulta

5 var clientes = from cliente in contexto.Clientes

select cliente;
6

7
// Recorremos los clientes
8
foreach (Cliente cliente in clientes)
9
{
10
Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
11 cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));
12

13 // Recorremos los pedidos

14 foreach (Pedido pedido in cliente.Pedidos)

15 {

16 Console.WriteLine(string.Format("\tPEDIDO: {0}\tFECHA: {1}",

pedido.IdPedido, pedido.FechaPedido));
17

18
// Recorremos las líneas de pedido
19
foreach (LineaPedido linea in pedido.LineasPedido)
20
{
21
Console.WriteLine(string.Format("\t\tPRODUCTO: {0}\tCANTIDAD: {1}\tTO
22 linea.Producto.Descripcion, linea.Cantidad,
(linea.Producto.Precio*linea.Cantidad)));
23
}
24
}
25
Console.WriteLine(" -----------------------------------------\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 ADO.NET 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 ADO.NET 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.

Por lo tanto, si queremos acceder al ObjectContext del DbContext que el diseñador


habrá generado por nosotros, será necesario realizar un casting explícito
a IObjectContextAdapter y acceder a su propiedad ObjectContext, tal y como
mostramos en el siguiente ejemplo, en el que se ejecuta una sentencia Entity SQL en la
que se recuperan todos los clientes de la tabla Clientes.

// 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 Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC:
13 {2}",
14 cliente.IdCliente, cliente.Nombre,
15 cliente.FechaNacimiento.Year));
}

El nombre de la tabla Clientes está en plural porque es el nombre que recibe la


propiedad en la clase de contexto generada por el editor, no porque sea el nombre de
la tabla de la base de datos. Recordemos que, Entity SQL realiza consultas sobre
entidades, es decir, objetos, no sobre la base de datos.

Nuevamente, comprobamos que la consulta recupera los datos correctamente.


ENTITY FRAMEWORK (III): SELECT,
INSERT, UPDATE, DELETE

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.

Select. Carga diferida explícita.

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

var contexto = new testdbEntities();


2

3
// Lanzamos una consulta
4
var clientes = from cliente in contexto.Clientes
5
select cliente;
6

7
// Recorremos los clientes
8
foreach (Cliente cliente in clientes)
9 {
10 Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
11 cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));

12

13 // Recorremos los pedidos

foreach (Pedido pedido in cliente.Pedidos)


14
{
15
Console.WriteLine(string.Format("\tPEDIDO: {0}\tFECHA: {1}",
16
pedido.IdPedido, pedido.FechaPedido));
17

18
// Recorremos las líneas de pedido
19 foreach (LineaPedido linea in pedido.LineasPedido)
20 {

21 Console.WriteLine(string.Format("\t\tPRODUCTO: {0}\tCANTIDAD: {1}\tTO

22 linea.Producto.Descripcion, linea.Cantidad,
(linea.Producto.Precio*linea.Cantidad)));
23
}
24
}
25 Console.WriteLine(" -----------------------------------------\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:

1 foreach (Cliente cliente in clientes)


2 {

3 Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC:


{2}",
4
cliente.IdCliente, cliente.Nombre,
5 cliente.FechaNacimiento.Year));

7 cliente.Pedidos.Load();

8
// Recorremos los pedidos
9
10 foreach (Pedido pedido in cliente.Pedidos)

11 {

// ...

Otro modo de realizar lo mismo sería mediante la cláusula Include al realizar la


consulta, especificando mediante una cadena de texto el nombre de los elementos de
la relación que se quieren incluir:

1 var clientes = contexto.Clientes.Include("Pedidos");

Afortunadamente, la carga diferida automática propia de LINQ to SQL ha sido incluida


en Entity Framework 4.1, y ya no es necesario realizar esta invocación en las versiones
actuales. Sin embargo, si estás trabajando con versiones anteriores, es necesario
conocer esta carencia.

Insert

La inserción también variará dependiendo de nuestra versión del Entity Framework. A


partir de 4.1 podremos realizar inserciones mediante el método Add del listado
correspondiente contenido dentro del DbContext. En versiones anteriores, deberemos
usar los métodos AddObject(), que añadirá cualquier entidad siempre y cuando le
pasemos el nombre de ésta) o AddClientes() (se generará un método para cada entidad
dentro de nuestro ObjectContext).

Una vez realizadas las inserciones, se invocará el método SaveChanges() para


comprometer los cambios (recordemos que el método equivalente en LINQ to
SQL era SubmitChanges()).

1 // Instanciamos el DbContext

2 var dbContext = new testdbEntities();

4 // Extraemos el ObjectContext del DbContext (a partir de Entity Framework


4.1)
5
var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
6

7 Cliente katiaRamos = new Cliente()


8 {
9 Nombre = "Katia Ramos Oliveira",

10 FechaNacimiento = new DateTime(1991, 12, 4)

11 };
12

13 Cliente arturoSaavedra = new Cliente()

14 {

Nombre = "Arturo Saavedra Gonzalez",


15
FechaNacimiento = new DateTime(1987, 4, 14)
16
};
17

18
Cliente guillermoSanabria = new Cliente()
19
{
20 Nombre = "Guillermo Sanabria San Juan",
21 FechaNacimiento = new DateTime(1983, 11, 1)

22 };

23

24 // MÉTODO 1: Método Add – Versiones 4.1 y superiores

25 dbContext.Clientes.Add(katiaRamos);

26
// MÉTODO 2: AddObject (genérico)
27
objectContext.AddObject("Clientes", arturoSaavedra);
28

29
// MÉTODO 3: AddClientes (específico del contexto) – Versiones anteriores a
30 la 4.1

31 objectContext.AddClientes(guillermoSanabria);

32

33 // Guardamos los cambios

dbContext.SaveChanges();
34

35
// Comprobamos si todo es correcto
36
foreach (Cliente cliente in dbContext.Clientes)
37
{
38
Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
39 cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));
40 }
41

42
Hecho esto, comprobamos que todo sea correcto:

Si lo que queremos es insertar elementos asociados, Entity Framework se encargará de


realizar las inserciones asociadas si los elementos no existían previamente. Así, si le
asociamos un pedido al cliente 9 con una línea de pedido asociada a un producto,
bastaría con añadir la línea de pedido si ésta ha incluido previamente la referencia al
pedido (aunque aún no esté insertado en la base de datos).

1 // Realizamos la consulta

2 Cliente cliente = dbContext.Clientes.Where(c => c.IdCliente == 9).First();

3
// Creamos los registros asociados:
4
Pedido p = new Pedido()
5
{
6
Cliente = cliente,
7
FechaPedido = DateTime.Now,
8
};
9

10 LineaPedido lp = new LineaPedido() {


11 Pedido = p,

12 Producto = dbContext.Productos.Where(prod => prod.IdProducto == 4).First(),

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 dbContext.LineasPedido.Add(lp);

19
20 // Guardamos los cambios

21 dbContext.SaveChanges();

22

Update

El proceso de actualización sera similar al que vimos en LINQ to SQL: se realizará la


modificación sobre el objeto y se guardarán los cambios mediante el
método SaveChanges().

1
// Instanciamos el DbContext
2
var dbContext = new testdbEntities();
3

4 // Realizamos la consulta
5 var clientes = dbContext.Clientes.Where(cliente =>
cliente.Nombre.StartsWith("Katia"));
6

7
// Modificamos los objetos que consideremos oportunos
8
foreach (var cliente in clientes)
9
cliente.Nombre = cliente.Nombre.Replace("Katia", "Katerina");
10

11 // Guardamos los cambios


12 dbContext.SaveChanges();
13

14 // Comprobamos si todo es correcto

15 foreach (Cliente cliente in dbContext.Clientes)

16 {

Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",


17
cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));
18
}
19

El resultado:
Delete

El proceso de borrado será el inverso al de inserción, mediante la utilización del


método Remove():

1 // Instanciamos el DbContext

2 var dbContext = new testdbEntities();

4 // Extraemos el ObjectContext del DbContext (a partir de Entity Framework 4.1)

var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;


5

6
// Realizamos la consulta
7
var clienteEliminar = dbContext.Clientes.Where(cliente => cliente.IdCliente ==
8 8).First();

10 // Eliminamos el cliente

11 objectContext.DeleteObject(clienteEliminar); // Para el Framework 4.0 o inferi

dbContext.Clientes.Remove(clienteEliminar); // Para el Framework 4.1 o superi


12

13
// Guardamos los cambios
14
dbContext.SaveChanges();
15

16
// Comprobamos si todo es correcto
17
foreach (Cliente cliente in dbContext.Clientes)
18 {
19 Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
20 cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));

21 }
22

Comprobamos si el cliente 8 ha sido eliminado. Como podemos observar, así ha sido.

Sin embargo, ¿qué ocurrirá si eliminamos un cliente que tenga asociado algún pedido,
como por ejemplo el cliente 9? Veamos:

Según observamos, no es posible borrar el cliente si no eliminamos antes sus objetos


asociados. Sin embargo, tal y como vimos en LINQ to SQL, no podemos hacer uso del
método Remove() para realizar eliminaciones en cascada, ya que este método se limita
a eliminar las referencias entre los distintos objetos, no los objetos en sí. Por tanto,
¿cuál será la solución?

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

5 set nocount on;

7 delete from LineaPedido where IdPedido = @IdPedido;

delete from Pedido where IdPedido = @IdPedido;


8
end;
9

El segundo procedimiento utilizará un cursor que recorrerá los identificadores de los


pedidos e invocará por cada uno el procedimiento anterior, borrando, además de los
clientes, sus pedidos y líneas de pedido asociadas.

1
create procedure sp_Cliente_DeleteCascade
2
@IdCliente int
3 as
4 begin

5 set nocount on;

7 -- Declaramos un cursor que obtenga todos los Ids de los pedidos


asociados
8
-- al cliente que queremos borrar
9 declare cur cursor for
10 select distinct IdPedido from Pedido where IdCliente =
@IdCliente;
11

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

21 exec sp_Pedido_DeleteCascade @IdPedido;

fetch next from cursorPedidos into @IdPedido


22
end;
23
close cur;
24

25
delete from Cliente where IdCliente = @IdCliente;
26
end;

A continuación iremos a nuestro Model Browser e importaremos los dos


procedimientos almacenados que acabamos de crear. Haremos click derecho sobre
nuestro modelo y seleccionaremos Update Model from Database…

Seleccionaremos los procedimientos almacenados que acabamos de crear (o como


mínimo, sp_Cliente_DeleteCascade, que es el que vamos a utilizar)
Codificamos lo siguiente en nuestro método de borrado:

// Instanciamos el DbContext
1
var dbContext = new testdbEntities();
2

3
// Realizamos la consulta
4
var clienteEliminar = dbContext.Clientes.Where(cliente => cliente.IdCliente ==
5 9).First();

6
// Ejecutamos el procedimiento almacenado pasando el ID (9) como parámetro.
7
dbContext.sp_Cliente_DeleteCascade(clienteEliminar.IdCliente);
8

9
// Guardamos los cambios
10
dbContext.SaveChanges();
11

12 // Comprobamos si todo es correcto mostrando los clientes por pantalla

13 foreach (Cliente cliente in dbContext.Clientes)

{
14
Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
15
cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));
16
}
17

18

Con esto, el borrado en cascada estaría completado.


ENTITY FRAMEWORK (IV): MAPEO
MEDIANTE
PROCEDIMIENTOS ALMACENADOS
En el anterior artículo aprendimos cómo mapear un procedimiento almacenado en
nuestro Entity Model y hacer uso de él mediante una llamada explícita para eliminar en
cascada todos los elementos asociados tanto a los clientes como a los pedidos.

Entity Framework proporciona la opción de personalizar los métodos de inserción,


eliminación y actualización a través de procedimientos almacenados, haciendo que
éstos se realicen mediante la sintaxis natural de Entity Framework pero
permitiéndonos a nosotros indicar, a través de procedimientos almacenados, la forma
de hacerlo.

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.

Ya definimos antes un procedimiento para las eliminaciones,


llamado sp_Cliente_DeleteCascade. Codificaremos ahora un procedimiento almacenado
para realizar las inserciones y otro para las actualizaciones.

El procedimiento de inserción recibirá como parámetros los componentes del registro


salvo el identificador, que será generado automáticamente. Además, devolverá como
valor de retorno la llamada al método SCOPE_IDENTITY(), que almacenará el valor del
IdCliente generado para el registro insertado. A este valor le daremos un nombre para
poder identificarlo en nuestro mapeo, por ejemplo, “id”.

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:

1 create procedure sp_Cliente_Update


2 @IdCliente int,
3 @Nombre nvarchar(256),
4 @FechaNacimiento date
as
5 begin
6 set nocount on;
7
8 -- Realizamos la actualización
9 update Cliente set Nombre = @Nombre, FechaNacimiento =
10 @FechaNacimiento
11 where IdCliente = @IdCliente;
12
end;
13

Una vez compilados nuestros procedimientos, nos dirigiremos al explorador de


modelo y haremos click derechos sobre la parte correspondiente a la base de datos,
seleccionando la opción Update Model from Database…

Hecho esto, seleccionaremos los procedimientos que acabamos de crear y


esperaremos a que termine la actualización de nuestro modelo, tras lo cual
seleccionaremos la entidad que queremos modificar (en este caso, Cliente) y haremos
click derecho seleccionando Stored Procedure Mapping para proceder con el mapeo de
los procedimientos almacenados.
Se mostrarán ahora tres opciones: inserción, actualización y eliminación.
Desplegaremos cada uno de los elementos y seleccionaremos el procedimiento
adecuado para cada funció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:

1 -- Devolvemos el nuevo id insertado


2 select SCOPE_IDENTITY() as id;

Por lo tanto, devolveremos en un parámetro denominado ‘id’ el valor del identificador


del nuevo cliente. Sabiendo esto, añadiremos ‘id’ debajo de Result Column
Bindings bajo el procedimiento de inserción, y le asignamos la
propiedad IdCliente como objetivo. De este modo, el campo IdCliente adquirirá este
valor una vez finalizada la inserción.

Para finalizar, ejecutaremos una actualización o una inserción de la forma habitual. A


partir de ahora, serán nuestros procedimientos almacenados los que se encarguen, en
silencio, de realizar estas tareas por nosotros.

// Instanciamos el DbContext
1 var dbContext = new testdbEntities();
2
3 // Realizamos la consulta
4 var clientes = dbContext.Clientes.Where(cliente =>
5 cliente.Nombre.StartsWith("Kat"));
6
// Modificamos los objetos que consideremos oportunos
7 foreach (var cliente in clientes)
8 cliente.Nombre = cliente.Nombre.Replace("Katia", "Katerina");
9
10 // Guardamos los cambios
dbContext.SaveChanges();
11
12 // Comprobamos si todo es correcto
13 foreach (Cliente cliente in dbContext.Clientes)
14 {
15 Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));
16 }
17
18
19

ENTITY FRAMEWORK (V):


ENLAZADO Y DESENLAZADO DE
ENTIDADES. ESTADOS

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.

Por lo tanto, tendremos dos opciones a la hora de tratar este problema:

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

Enlazado en Entity Framework 4.0

Empezaremos explicando cómo se realizaría esta operación con un ObjectContext.


1 Cliente datosAntiguos = null;

3 // Instanciamos el DbContext

4 using(var objectContext = new testdbObjectContext())

{
5
// Recuperamos el cliente anterior
6
datosAntiguos = objectContext.Clientes.Where(cliente => cliente.IdCliente ==
7 3).First();

8 }

Tenemos los datos de un cliente almacenados en datosAntiguos. Como podemos


comprobar, al finalizar el using nuestro contexto habrá muerto, por lo que el
objeto datosAntiguos estará libre. Esos serán los datos que se le remitirán al cliente.

Ahora imaginemos que el cliente recibe esos datos y modifica los


campos Nombre y FechaNacimiento, y los recibimos en un objeto
llamado datosNuevos. Simularemos esto creando nosotros el registro y cambiándole
manualmente los datos. Recordemos que el identificador debe ser el mismo que tenía
previamente.

1 using (var objectContext = new testdbObjectContext ())

{
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 = datosAntiguos.IdCliente,
7 Nombre = "Ana Maria Lopez Diaz",
8 FechaNacimiento = new DateTime(1947, 1, 15)

9 };

10

11 // Añadimos el cliente anterior al contexto para informarle que


queremos
12
// enlazar el objeto.
13 objectContext.Attach(datosAntiguos);
14

15 // Actualizamos el cliente mediante el método ApplyPropertyChanges


16 objectContext.ApplyPropertyChanges("Cliente", datosNuevos);

17 objectContext.SaveChanges();

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

Al invocar ApplyPropertyChanges y pasarle el objeto con los nuevos datos, Entity


Framework ya sabrá, gracias a que el identificador de ambos elementos coincide, que
los datos de ese registro deben ser actualizados.

Una vez invocado SaveChanges(), los cambios son aplicados.

Enlazado en Entity Framework 4.1+

A partir de esta versión, este proceso se simplifica bastante. Bastará con realizar lo
siguiente:

1 // Instanciamos un nuevo DbContext. El objeto clienteAnterior ya no tiene ninguna


relación
2
// con el contexto, ya que éste ha muerto en el bloque anterior.
3 // Cualquier cambio sobre este objeto no será ya reflejado en la base de datos
4 using (var dbContext = new testdbEntities())

5 {

7 // Creamos un nuevo cliente con el MISMO ID que el anterior y

8 // cambiamos algunos datos

Cliente datosNuevos = new Cliente()


9
{
10
IdCliente = 3,
11
Nombre = "Ana Maria Lopez Diaz",
12
FechaNacimiento = new DateTime(1947, 1, 15)
13 };
14

15 // Añadimos el cliente anterior al contexto para informarle que queremos


16 // enlazar el objeto.

17 dbContext.Clientes.Attach(datosNuevos);

18
// Cambiamos el estado a "Modificado" para que Entity Framework sepa que
19
// deben actualizarse los datos
20
dbContext.Entry(datosNuevos).State = System.Data.EntityState.Modified;
21
dbContext.SaveChanges();
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().

Estados de una entidad

Para finalizar, hablaremos de los posibles estados de una entidad. Según


el Intellisense, podemos observar los siguientes estados:

Estos estados son:

 Added: la entidad se añadirá a la lista. Se ejecutará una sentencia insert sobre


la base de datos.
 Deleted: la entidad se eliminará. Se ejecutará una sentencia delete sobre la base
de datos.
 Detached: la entidad no será tenida en cuenta a la hora de aplicar cambios.
 Modified: la entidad ha modificado alguno de sus valores. Se ejecutará una
sentencia update sobre la base de datos.
 Unchanged: la entidad no ha sufrido cambios desde que se recuperó de la
fuente de datos.

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

6 // Cambiamos el estado de la entidad a Added

dbContext.Entry(guillermoSanabria).State = EntityState.Added;
7

8
// Guardamos los cambios
9
dbContext.SaveChanges();
10

Esto provocará que la entidad se inserte en la base de datos.

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.

Lo primero que deberemos aclarar es el propio concepto de servicio web. Ya vimos en


artículos anteriores de qué se tratan, cómo se crean y cómo se consumen estas
pequeñas aplicaciones cuyo objetivo es el intercambio de información entre distintas
plataformas y lenguajes de modo estándar.

Para crear un nuevo servicio web, crearemos un nuevo proyecto web de tipo ASP.NET
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.

Creando el Data Service

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

5 // This method is called only once to initialize service-wide policies.

6 public static void InitializeService(DataServiceConfiguration config)

{
7
// TODO: set rules to indicate which entity sets and service operatio
8 updatable, etc.
9 // Examples:
10 // config.SetEntitySetAccessRule("MyEntityset", EntitySetRights.AllRe

11 // config.SetServiceOperationAccessRule("MyServiceOperation", Service

config.DataServiceBehavior.MaxProtocolVersion = 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.

1 public class GestionPedidosDataService : DataService<TestDbContext>

Configuración y permisos

A continuación es posible asignar permisos a las diferentes entidades presentes en


nuestro modelo de datos. Si quisiéramos proporcionar todos los permisos (lectura,
escritura, eliminación), seleccionaríamos All. Si se desea un permiso en concreto, se
seleccionará a título individual a partir de la enumeración EntitySetRights.

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:

public class GestionPedidosDataService : DataService<TestDbContext>


1
{
2
// This method is called only once to initialize service-wide policies.
3 public static void InitializeService(DataServiceConfiguration config)

4 {

config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersio
5

6
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
7
config.SetEntitySetAccessRule("Pedido", EntitySetRights.AllRead |
8 EntitySetRights.WriteAppend);

9 config.SetEntitySetAccessRule("LineaPedido", EntitySetRights.AllRead |
EntitySetRights.WriteAppend);
10
}
11
}
12

Finalizada la configuración inicial, podemos iniciar nuestro servicio web y echar un


vistazo a lo que está ocurriendo dentro. Para ello, haremos click derecho sobre el
fichero .svc y seleccionaremos la opción View in Browser.

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

http://localhost:6040/GestionPedidosDataService.svc/Cliente

Es posible que realizar esta operación provoque que se muestre un mensaje como el
siguiente:

Si es nuestro caso significará que el navegador tiene activado por defecto la


configuración de feeds. Esto se debe a que un gran número de aplicaciones web
permiten realizar una suscripción a través de la cual, mediante un servicio web en este
formato, se pueden recuperar los titulares y cabeceras de los últimos contenidos
(como en el recientemente fallecido Google Reader). Debido a esto, puede que el
navegador “transforme” esa respuesta XML en algo más “legible” para el ser humano,
pero que en nuestro caso concreto, nos está haciendo un flaco favor.

Desactivarlo en Internet Explorer no es difícil. Basta con acceder a las propiedades y


seleccionar la pestaña Contenido. Una vez allí, pulsaremos el botón Configuración.

A continuación, nos aseguraremos de que la casilla Activar la vista de lectura de


fuentes se encuentra desactivada y pulsaremos Aceptar.
Si recargamos la página, nuestra colección habrá cambiado significativamente,
mostrando algo similar a esto:

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:

http://localhost:6040/GestionPedidosDataService.svc/Cliente(3)/Pedidos

Consultas REST

Por lo tanto, ¿estamos insinuando que es posible realizar un conjunto de operaciones


de consulta sobre nuestro DbContext usando para ello un navegador web? Así es. En
realidad lo importante no es el navegador, sino el protocolo (HTTP), pero obviamente,
un navegador es capaz de realizar este tipo de peticiones. Acabamos de realizar
nuestra primera comunicación REST (REpresentational State Transfer). No
profundizaremos ahora en ello (lo haremos en posteriores artículos), pero baste decir
que normalmente nos encontraremos con dos tipos de servicios web: SOAP y RESTful.

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

 $filter=condición: permite realizar una consulta. Equivaldría a un where.


 /Cliente?$filter=IdCliente eq 3

 $orderby=criterio_de_ordenación: permite ordenar los resultados.


 /Cliente?$orderby=IdCliente desc
 $expand=listado_a_expandir: realiza la consulta de los objetos referenciados
que se le especifiquen como parámetro
 /Cliente?$expand=Pedidos

 entidadHija/$links/entidadPadre: proporciona la URL en la que se encuentra el


elemento referenciado.
 /Pedido(1)/$links/Clientes

 $top=n: recupera los primeros n elementos


 /Cliente?$top=1

 $skip=n: ignora los primeros n elementos. En conjunción con top, sirve para
realizar paginaciones.
 /Cliente?$top=1&$skip=1
Creando el cliente

Generar un servicios de datos conectado a un DbContext ha sido sencillo: Entity


Framework y WCF jugando mano a mano y haciéndonos más fácil el acceso a los datos.
Es hora de dar un paso más y, en lugar de trastear con el navegador, crear un pequeño
programa que sea capaz de realizar operaciones más concretas y complejas con los
datos obtenidos por el servicio web. Comenzaremos haciendo click derecho sobre
nuestra solución y seleccionando la opción de añadir un nuevo proyecto.
En mi caso crearé una pequeña aplicación de consola.

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.

Uri serviceRoot = new


1
Uri("http://localhost:6040/GestionPedidosDataService.svc");
2 var dbContext = new GestionPedidosDataService.TestDbContext(serviceRoot);
Consulta

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.

Por lo tanto, realizaremos dos consultas para recuperar los


productos Lapicero y Boligrafo e instanciaremos los objetos que queremos insertar.
Para las líneas de producto, indicaremos el identificador del producto en su
campo IdProducto.

1 // Obtenemos referencias a los productos que queremos referenciar

2 Producto lapicero = dbContext.Producto.Where(producto =>


producto.Descripcion.Equals("Lapicero")).First();
3
Producto boligrafo = dbContext.Producto.Where(producto =>
4 producto.Descripcion.Equals("Boligrafo")).First();

5
6 // Creamos un nuevo cliente

7 var nuevoCliente = new 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 = DateTime.Now

16 };

17

18 LineaPedido lineaPedidoLapiceros = new LineaPedido()

{
19
IdProducto = lapicero.IdProducto,
20
Cantidad = 3
21
};
22

23
LineaPedido lineaPedidoBoligrafos = new LineaPedido()
24 {
25 IdProducto = boligrafo.IdProducto,

26 Cantidad = 7

27 };

28

Inserción

La inserción resulta un poco más compleja que su operación equivalente en Entity


Frameworkcuando trabajamos en local. Para realizar la operación correctamente es
necesario indicar las relaciones de forma explícita (no servirá únicamente con añadir
los objetos y dejar que Entity Framework se encargue.

Por ello, añadiremos el cliente mediante el método AddToCliente y pasándole el


objeto clientecomo parámetro. La referencia al DbContext remoto incorporará por
defecto un conjunto de métodos AdToXXXXX que simplificará la tarea de añadir un
registro a una colección.
Sin embargo, en el caso de las entidades relacionadas no bastará con añadirlas a sus
tablas correspondientes: habrá que indicar al servicio web que los objetos están
relacionados. Para ello se utilizará el método AddRelatedObject, que recibirá tres
parámetros:

 Objeto padre (por ejemplo, nuevoCliente)


 Cadena de texto con el nombre de la colección del objeto padre en el que se
insertará el objeto relacionado (por ejemplo, Pedidos).
 Objeto relacionado (por ejemplo, pedido).

Realizaremos el mismo proceso con el pedido y sus respectivas líneas. No será


necesario hacerlo con las líneas de pedido y los productos, ya que los valores de sus
claves primarias fueron indicados explícitamente en el campo IdProducto de cada una
de las líneas de pedido, relacionando los objetos de forma interna.

1 // Insertamos el nuevo cliente

2 dbContext.AddToCliente(nuevoCliente);

4 // Añadimos la relación entre cliente y pedido al contexto

dbContext.AddRelatedObject(nuevoCliente, "Pedidos", pedido);


5

6
// Añadimos las relaciones entre pedido y lineas de pedido al contexto
7
dbContext.AddRelatedObject(pedido, "LineasPedido",
8 lineaPedidoBoligrafos);

9 dbContext.AddRelatedObject(pedido, "LineasPedido", lineaPedidoLapiceros);

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.

1 // Guardamos los cambios

DataServiceResponse respuesta = dbContext.SaveChanges(SaveChangesOptions.Batch);


2

3
// Mostramos los cambios
4
foreach (ChangeOperationResponse cambio in respuesta)
5
{
6
EntityDescriptor descriptor = (EntityDescriptor)cambio.Descriptor;
7 if (descriptor != null)
8 {
9 if (descriptor.Entity.GetType().IsAssignableFrom(typeof(Cliente)))

10 {

Console.WriteLine(string.Format("Cliente: {0}\t{1}\t{2}",
11
((Cliente)descriptor.Entity).IdCliente, ((Cliente)descriptor.Enti
12
}
13
else if (descriptor.Entity.GetType().IsAssignableFrom(typeof(Pedido)))
14
{
15 Console.WriteLine(string.Format("\tPedido: {0}\t{1}\t{2}",
16 ((Pedido)descriptor.Entity).IdPedido, ((Pedido)descriptor.Entity)
descriptor.State.ToString()));
17
}
18
else if (descriptor.Entity.GetType().IsAssignableFrom(typeof(LineaPedido))
19
{
20
Console.WriteLine(string.Format("\t\tLinea: {0}\t{1}\t{2}",
21 ((LineaPedido)descriptor.Entity).IdLineaPedido, ((LineaPedido)des
descriptor.State.ToString()));
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 = dbContext.Cliente.Where(cliente => cliente.IdCliente == 61);
2
Console.WriteLine("\n\nTRAS LA INSERCION:");
3
foreach (var cliente in clientes)
4 {
5 Console.WriteLine(string.Format("\tID: {0}\tNOMBRE: {1}",

6 cliente.IdCliente, cliente.Nombre));

7 foreach (var p in cliente.Pedidos)

8 {

Console.WriteLine(string.Format("\t\tID: {0}\tAÑO: {1}",


9
p.IdPedido, p.FechaPedido.Year));
10
foreach (var l in p.LineasPedido)
11
{
12
Console.WriteLine(string.Format("\t\t\tPRODUCTO: {0}\tCANTIDAD:
13 {1}",

l.Productos.Descripcion, l.Cantidad));
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 Console.WriteLine("\n\nTRAS LA INSERCION:");

2 foreach (var cliente in clientes)

3 {

4 dbContext.LoadProperty(cliente, "Pedidos");

Console.WriteLine(string.Format("\tID: {0}\tNOMBRE: {1}",


5
cliente.IdCliente, cliente.Nombre));
6
foreach (var p in cliente.Pedidos)
7
{
8
dbContext.LoadProperty(p, "LineasPedido");
9 Console.WriteLine(string.Format("\t\tID: {0}\tAÑO: {1}",
10 p.IdPedido, p.FechaPedido.Year));

11 foreach (var l in p.LineasPedido)

12 {

13 dbContext.LoadProperty(l, "Productos");

Console.WriteLine(string.Format("\t\t\tPRODUCTO: {0}\tCANTIDAD:
14 {1}",
15 l.Productos.Descripcion, l.Cantidad));
16 }

17 }

18 }
19

Esto sí provocará la carga de los listados asociados a cada entidad, mostrando la


información pertinente.

Modificación

La modificación es sencilla: basta recuperar el objeto deseado, invocar el


método UpdateObjectpasándole como parámetro la entidad que hemos modificado e
invocar el método SaveChangespara que éstos se reflejen en el lado del servidor.

1
// MODIFICACION
2

3
dbContext = new GestionPedidosDataService.TestDbContext(serviceRoot);
4 clientes = dbContext.Cliente.Where(cliente => cliente.IdCliente == 61);
5

6 var clienteModificar = clientes.First();;


7 clienteModificar.Nombre = "Pedro Javier Gonzalez Arnau";

9 var lineaPedidoModificar = dbContext.LineaPedido.ToList().Last();

10 lineaPedidoModificar.Cantidad = 59;

11
dbContext.UpdateObject(clienteModificar);
12
dbContext.UpdateObject(lineaPedidoModificar);
13
dbContext.SaveChanges();
14

Esta operación provocará el siguiente resultado:


Eliminación

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

3 dbContext = new GestionPedidosDataService.TestDbContext(serviceRoot);

4 clientes = dbContext.Cliente.Where(cliente => cliente.IdCliente == 61);

5
var lineaPedidoEliminar = (from linea in dbContext.LineaPedido
6
where linea.IdLineaPedido ==
7 lineaPedidoBoligrafos.IdLineaPedido
8 select linea).Single();

10 dbContext.DeleteObject(lineaPedidoEliminar);

11 dbContext.SaveChanges();

Tras eliminar los bolígrafos, esta sería la salida por pantalla.

Y con esto, finalizamos la sección dedicada a Entity Framework y abrimos la puerta


a Windows Communication Foundation. Mañana realizaré un pequeño recopilatorio con
todos los artículos relacionados con LINQ y Entity Framework para que, a modo de
tutorial, se aborden todos los artículos que sirven de introducción a estas tecnologías.
TUTORIAL: CRUD con MVC y Entity Framework

A continuación se explicará qué es el Modelo Vista Controlador (MVC), los


beneficios y la manera de desarrollar un CRUD con MVC y ADO.NET
Entity Framework paso a paso.

Introducción al Modelo Vista


Controlador
El Modelo Vista Controlador (MVC) es un patrón de arquitectura de
software que separa los datos y la lógica de negocio de una aplicación de
la interfaz de usuario y el módulo encargado de gestionar los eventos y las
comunicaciones.

Para ello MVC propone la construcción de tres componentes distintos que


son el modelo, la vista y el controlador, es decir, por un lado define
componentes para la representación de la información, y por otro lado para
la interacción del usuario. Este patrón de diseño se basa en las ideas de
reutilización de código y la separación de conceptos, características que
buscan facilitar la tarea de desarrollo de aplicaciones y su posterior
mantenimiento.
ADO.NET Entity Framework es un Framework ORM para la plataforma
.NET.

Ya que sabemos los conceptos básicos podremos ver los beneficios de


usar estas tecnologías.

Comenzando con la base de datos


Bien empecemos, lo primero será realizar una base de datos, para eso
podremos hacerla en SQL Server desde el mismo Visual Studio o cualquier
otro manejador, en este caso usaremos SQL.

Abrimos Visual Studio, hacemos clic en Ver -> Explorador de Servidores,


en la pestaña damos clic derecho en Crear nueva base de datos SQL
Server…
Procedemos a crear la base de datos local de Visual Studio; en este caso
se utilizó un ejemplo pequeño de una escuela, esta base de datos
contendrá 2 tablas una de alumnos y otra de carrera que se relacionarán.

Ya que se haya creado, hacemos clic derecho en Tablas y en la opción


Agregar nueva tabla.
La estructura de la base de datos será la siguiente.

Nombre del campo Tipo


Id_Alumno Int
Nombre Varchar
Apellido_Paterno Varchar
Apellido_Materno Varchar
Grado int
Id_Carrera int
Tabla Alumnos

Tabla Carrera.

Nombre del campo Tipo


Id_Carrera Int
Nombre Varchar

Entonces creamos nuestras tablas con sus Foreign Key.

Agregando el modelo con Entity


Framework
Ahora sí empieza lo bueno…

Comenzamos por crear un nuevo proyecto. Si usas Visual Studio 2013 es


en la plantilla Web -> Aplicación web ASP.NET.
Si usas Visual 2012 es en Aplicación web ASP.Net MVC 4
Seleccionamos la plantilla MVC si usas VS2013.
Y si usas VS2012, selecciona Aplicación de Internet.
Al crear nuestro proyecto podremos ver que nos crea la estructura y se
aprecian las carpetas de Vista (Views), Modelo (Models) y Controlador
(Controllers).
Ahora crearemos la capa de datos con Entity Framework, para esto
hacemos clic derecho en la solución opción Agregar y seleccionamos
Nuevo Proyecto.
Buscamos la plantilla Biblioteca de Clases, le asignamos un nombre y
pulsamos Aceptar.
Ya creada nuestra biblioteca, el paso siguiente es agregar nuestro modelo
con Entity Framework lo cual se hace de la siguiente manera:
Lo cual nos dará como resultado un modelo
Ya casi terminamos, el siguiente paso es agregar Entity a nuestro proyecto
de MVC; para eso seleccionamos el proyecto de MVC, pulsamos clic
derecho y seleccionamos Agregar y después Referencia.
El siguiente paso es copiar la línea connectioString de nuestra
App.Config y remplazar esa misma sección en Web.Config del proyecto
MVC.
Creando CRUD
Ahora sí, el momento de la verdad en que compilamos la solución. Es
entonces el momento de crear nuestro CRUD, para eso damos clic
derecho en la carpeta Controllers -> Agregar -> Agregar Controlador.
Al agregar nombramos nuestro controlador, seleccionamos la clase y el
contexto.

Listo, ya tenemos nuestro CRUD hecho, ahora si ejecutamos nuestra


aplicación no podremos acceder a ella porque no tenemos un link que nos
enlace a las vistas que creamos, para solucionar esto solo necesitamos
editar el _Layout.cshtm que es la plantilla padre, todas las páginas que
creemos tendrán esa estructura. Solo agregamos un link hacia la vista.
Ejecutamos nuestra aplicación.
¡Y listo! Ya tenemos nuestra aplicación CRUD con MVC y Entity
Framework, espero que este tutorial les haya servido, si tienen alguna
duda pueden contactarme en los comentarios o a mi correo.
EF 5 Code First (Entity Framework Code First)
En mis últimos desarrollos, estaba usando Entity Framework (EF) pero en su variante Base de
Datos Primero(Database First), pero recientemente mi equipo y yo hemos apostado por EF
Code First y estamos tan encantados con esta nueva maravilla del equipo de ADO.NET, que
he decidido compartir esta pequeña entrada sobre su uso.

Dividiremos la serie en al menos 2 artículos:

1. EF 5 Code First. Este post.


2. EF 5 Code First Migrations. Un segundo articulo que habla sobre las
migraciones en Entity Framework Code First.

EF 5 Code First

1. Crearemos una aplicación de ejemplo (AppEjemploEfCodeFirst) que contendrá 3


proyectos:

 AppEjemploEfCodeFirst.Web: Proyecto
Web ASP.NET MVC 4 (Razor).
 AppEjemploEfCodeFirst.Data: Proyecto de
tipo Biblioteca de Clases. Contendrá el contexto a la
base de datos (BBDD).
 AppEjemploEFCodeFirst.Data.Entities:
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:

 AppEjemploEfCodeFirst.Web
 AppEjemploEfCodeFirst.Data

3. Agregar las referencias necesarias:

 AppEjemploEfCodeFirst.Web debe hacer referencias a:


o AppEjemploEfCodeFirst.Data
o AppEjemploEfCodeFirst.Data.Entities
 AppEjemploEfCodeFirst.Data debe hacer referencias a:
o AppEjemploEfCodeFirst.Data.Entities

4. Agregar la cadena de conexión a la base de datos, aunque es suficiente con agregarla al


proyecto de datos AppEjemploEfCodeFirst.Data también es recomendable agregarlo al
proyecto Web AppEjemploEfCodeFirst.Web. El fichero de configuración (en mi caso) lucirá
así:

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


<configuration>
<connectionStrings>
<add name="CodeFirstDBContext"
connectionString="server=.\SQLEXPRESS; database=AppEjemploEfCodeFirst;
integrated security=true;"
providerName="System.Data.SqlClient" />
</connectionStrings>

5. Crear la primera entidad POCO (código primero), las entidades las creáremos en el proyecto
de entidades (AppEjemploEfCodeFirst.Data.Entities)

namespace AppEjemploEfCodeFirst.Data.Entities

public partial class Persona

public int PersonaId { get; set; }

public string Nombre { get; set; }

public string Apellidos { get; set; }

public DateTime FechaNacimiento { get; set; }

6. Ahora crearemos la clase de contexto (AppEjemploEfCodeFirstDbContext) en el proyecto


(AppEjemploEfCodeFirst.Data) esta clase debe heredar de DbContex.

namespace AppEjemploEfCodeFirst.Data

public partial class AppEjemploEfCodeFirstDBContext : DbContext

public DbSet<Persona> Footballers { get; set; }

}
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 AppEjemploEFCodeFist.Controllers

public class HomeController : Controller

public ActionResult Index()

var ctx = new AppEjemploEfCodeFirstDbContext();

ViewBag.LstPersonas = ctx.Personas.ToList();

return View();

8. Ya todo está listo, compilamos la aplicación y la


ejecutamos y en cuanto se haga referencia al contexto de
datos veremos que se crea la base de datos con la
estructuras de las clases o entidades (POCO) que creamos
por código.

Conclusiones: Hemos visto como mediante el uso de EF


5 podemos crear el código de nuestras entidades en C# o
VB.Net y posteriormente generar automáticamente la base
de datos.
Pero ¿qué pasa si agrego una nueva entidad o modifico una
existente? para resolver este problema debemos habilitar las
migraciones (EF Code First Migrations). Este tema lo
veremos en un post posterior.

EF Code First Migrations


Hace unos días escribí un articulo “EF 5 Code First (Entity Framework Code First)” que
deberías leer antes que este. En aquel articulo explicaba como usar EF 5 Code First, en esta
nueva entrada pretendo hablarles brevemente sobre:

1. EF Code First Migrations. Migraciones manuales.


2. EF Code First Migrations. Migraciones automáticas.
3. EF Code First Migrations. Otras consideraciones.
EF Code First Migrations. Migraciones manuales.

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 AppEjemploEfCodeFirst.Data.Entities

public partial class Persona

public int PersonaId { get; set; }

public string Nombre { get; set; }

public string Apellidos { get; set; }

public DateTime FechaNacimiento { get; set; }

Imaginemos que deseamos agregar una nueva propiedad, por ejemplo …. Numero de
Documento, quedando así la nueva clase:

namespace AppEjemploEfCodeFirst.Data.Entities

public partial class Persona

public int PersonaId { get; set; }

public string Nombre { get; set; }

public string Apellidos { get; set; }

public DateTime FechaNacimiento { get; set; }

public string DocumentoNo { get; set; }

Si ejecutamos ahora la aplicación, recibiríamos un error como el siguiente:


Este error se debe a que no tenemos activada las migraciones (EF Code First Migrations) que
por defecto viene desactivada, al menos de las versiones 4.3 a la 5.0 de EF. Pero no hay que
preocuparse, habilitarla es muy simple, veamos como:

Pasos para habilitar EF Code First Migrations:

1. Ir al menú Herramienta / Administrador de paquetes de biblioteca / Consola del


Administrador de paquetes.
2. Estando en la consola (importantísimo) escoger el proyecto al cual queremos
habilitarle la migración (AppEjemploEfCodeFirst.Data).
3. Escribir el siguiente comando en la consola (Enable-Migrations) y presionar
“enter”. Con esto hemos habilitado la migración manual, mas adelante veremos la
migración automática. Podrás observar que en el proyecto escogido para habilitarle la
migración (EF Code First Migrations) se ha creado una nueva carpeta (Migrations),
aunque no es necesario puedes ojear un poco en los archivos creados para llevarte
una idea de lo que esta sucediendo.
4. Escribir en la consola (Add-Migration Migracion1Prueba) la palabra
Migracion1Prueba es un nombre cualquiera para identificar la migración.
5. Y por ultimo escribimos otro comando en la consola (Update-Database). Ya
tendríamos el modelo de base de datos actualizado con los cambios realizados en las
entidades.
EF Code First Migrations. Migraciones automáticas.

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

Veamos como hacerlo:

Primero debemos ir a la clase (Configuration) y modificar la siguiente línea de código


del Constructor:

public Configuration()

AutomaticMigrationsEnabled = true;

}
Después debemos registrar el inicializador MigrateDatabaseToLatestVersion. Para ello
escribimos el siguiente código en el Global.asax, método Application_Start (o cualquier otro
punto de entrada de nuestra aplicación);

Database.SetInitializer(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.

Consideraciones a tener en cuenta:

 Puede que necesites cambiar el nivel de accesibilidad de la clase (Configuration)


 Puede que tengas que agregar algunos (Namespace) al Global.asax.

EF Code First Migrations. Otras consideraciones.

Puede que se te presenten ciertos escenarios donde no se apliquen las migraciones


automáticas. Por ejemplo cuando eliminas una columna o eliminas una entidad (tabla de
datos).

No se ha aplicado la migración automática porque podría ocasionar una pérdida 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:

Update-Database -Force -Verbose

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.

Entity Framework 6 operaciones CRUD


11 respuestas

Entity Framework es un potente ORM creado por Microsoft que facilita el


trabajo del mapeo a las entidades y facilita la implementación de la capa de
datos usando Linq to Entities. Entity Framework tiene varios enfoques como,
Code First, Model First, Database First.
Code First,es un enfoque mas de Entity Framework que permite crear una base
de datos a partir de código(C#, VB.NET), por defecto se utiliza el nombre de
nuestras clases y correspondientes propiedades para crear nombres de tablas y
campos.

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.

Database First, el modelo conceptual se crea a partir de una base de datos


existente, el cual se almacena en una archivo .edmx y este se podrá ver y
editar en un diseñador, para actualizar nuevos cambios que surjan en nuestra
base de datos.

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,

7 [Nombre] [varchar](80) NULL,

8 [Direccion] [varchar](100) NULL,

[Telefono] [varchar](9) NULL,


9
[lEstado] [smallint] NULL,
10
CONSTRAINT [PK_Personal] PRIMARY KEY CLUSTERED
11
(
12
[Id] ASC
13 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW
14 [PRIMARY]

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

Se instala en la capa de Persistencia y Presentación Entity Framework desde


Nuguet, hecho eso se agrega un nuevo elemento desde el proyecto de
Persistencia, ADO.NET Entity Data Model.

Ahora nos conectamos a la base de datos para obtener el modelo.

Seleccionamos las tablas que se requieren para el modelo.


Escogemos el Framework a trabajar en mi caso lo dejo como esta.

Obtenemos el modelo.

Y también tenemos el mapping de la infraestuctura de la DB.

1 //------------------------------------------------------------------------------

2 // <auto-generated>

3 // Este código se generó a partir de una plantilla.

//
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 Prueba.PersistenciaDatos.Modelo

11 {

using System;
12
using System.Collections.Generic;
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; }

19 public string Telefono { get; set; }

20 public Nullable<short> lEstado { get; set; }

21 }

}
22

23

Ya podemos comenzar a trabajar comenzando con la implementación vamos a ubicar el


context que es el que se conecta a la base de datos para poder persistir contra ella.

Esto se encuentra ubicado en el Model1.Context, voy a implementar la capa de persistencia


usando linq to entities, me olvidaba agregar la referencia System.Data.Entity

1 using Prueba.PersistenciaDatos.Modelo;
2 using System;

3 using System.Collections.Generic;

4 using System.Data.Entity;

using System.Linq;
5
6 using System.Linq.Expressions;

7 using System.Text;

using System.Threading.Tasks;
8

9
namespace Prueba.PersistenciaDatos
10
{
11
public class PersonalRepository
12
{
13

14 public List<ModeloMinimo.PersonalMinimalModel> GetPersonal()


15 {

16 using(var context = new PruebaEFEntities())

17 {

18 return (from p in context.Personal

select new ModeloMinimo.PersonalMinimalModel


19
{
20
Id = p.Id,
21
Nombre = p.Nombre
22
}).ToList();
23 }
24 }

25

26 public string GenerarCodigo()

27 {

28 using(var context = new PruebaEFEntities())

{
29
int codigo;
30
var ultimoId = Convert.ToInt32(context.Personal.Max(x => x.Id)) + Convert.ToInt
31
codigo = ultimoId;
32
return string.Format("{0:000000}", codigo);
33 }
34 }

35

36 public bool Existe(string codigo)


37 {

38 using(var context = new PruebaEFEntities())

{
39
int resultado = context.Personal.Where(x => x.Id == codigo).Count();
40
if (resultado == 0)
41
return false;
42
else
43 return true;
44 }
45 }

46 public Personal Buscar(string codigo)

47 {

using(var context = new PruebaEFEntities())


48
{
49
var strSQL = from p in context.Personal
50
where p.Id == codigo
51
select p;
52 return strSQL.First();
53 }

54 }

55

56 public void Guardar(Personal model)

57 {

try
58
{
59
using(var context = new PruebaEFEntities())
60
{
61
context.Personal.Add(model);
62 context.SaveChanges();
63 }

64 }

65 catch (Exception ex)

66 {

throw new Exception("No se puede guardar el registro", ex);


67
68 }

69 }

70
public void Actualizar(Personal model)
71
{
72
try
73
{
74
using(var context = new PruebaEFEntities())
75 {
76 context.Entry(model).State = EntityState.Modified;

77 context.SaveChanges();

78 }

79 }

catch (Exception ex)


80
{
81
throw new Exception("No se puede actualizar el registro", ex);
82
}
83
}
84

85 public void Eliminar(string codigo)


86 {

87 using(var context = new PruebaEFEntities())

88 {

89 var entities = (from p in context.Personal

where p.Id == codigo


90
select p).Single();
91
context.Personal.Remove(entities);
92
context.SaveChanges();
93
}
94

95 }
96

97 }

98 }
99

100

101

102

103

104

105

106

107

Implementando el dominio en el cual uso una técnica para desacoplar la capa de


Presentación con la capa de Dominio.

1 using System;

using System.Collections.Generic;
2
using System.Linq;
3
using System.Text;
4
using System.Threading.Tasks;
5
using Prueba.PersistenciaDatos;
6 using System.Linq.Expressions;
7

8 namespace Prueba.Dominio
9 {

10 public class Personal

11 {

public string MensajeLogica;


12
public string MensajeError;
13

14
PersonalRepository personal = new PersonalRepository();
15

16
public List<PersistenciaDatos.ModeloMinimo.PersonalMinimalModel> GetPersonal()
17
{
18 return personal.GetPersonal();
19 }
20

21 public PersistenciaDatos.Modelo.Personal Buscar(string codigo)

22 {

return personal.Buscar(codigo);
23
}
24

25
public void Guardar(PersistenciaDatos.Modelo.Personal model)
26
{
27
BusinessException.Clear();
28 if (string.IsNullOrEmpty(model.Nombre)) BusinessException.Add("Debe ingresar el
29

30 if(BusinessException.Count() == 0)

31 {

32 if(string.IsNullOrEmpty(model.Id))

33 model.Id = personal.GenerarCodigo();

34
try
35
{
36
if(personal.Existe(model.Id))
37
{
38
personal.Actualizar(model);
39 MensajeLogica = "Registro actualizado";
40 }

41 else

42 {

43 personal.Guardar(model);

MensajeLogica = "Registro guardado";


44
}
45
}
46
catch (Exception ex)
47
{
48 BusinessException.Add(ex.Message);
49 }

50
51 }

52 }

53
public void Eliminar(string codigo)
54
{
55
personal.Eliminar(codigo);
56
}
57

58
}
59 }
60

61

62

63

64

65

Implementando la capa de Presentación, se debe copiar el app.config de la capa de


Persistencia a la Presentación.

using System;
1
using System.Collections.Generic;
2
using System.ComponentModel;
3
using System.Data;
4 using System.Drawing;
5 using System.Linq;
6 using System.Text;

7 using System.Threading.Tasks;

8 using System.Windows.Forms;

using Prueba.PersistenciaDatos;
9
using Prueba.Dominio;
10

11
namespace Prueba.WindowsUI
12
{
13
public partial class frmPersonal : Form
14 {

15 private string strCodigo;

16
Dominio.Personal personal = new Dominio.Personal();
17

18
public frmPersonal()
19
{
20
InitializeComponent();
21
}
22

23 private void frmPersonal_Load(object sender, EventArgs e)


24 {

25 LoadDGVPersonal();

26

27 }

28

29 private void btnGuardar_Click(object sender, EventArgs e)

{
30
PersistenciaDatos.Modelo.Personal model = new PersistenciaDatos.Modelo.Personal
31
model.Id = lblCodigo.Text;
32
model.Nombre = txtNombre.Text;
33
model.Direccion = txtDireccion.Text;
34 model.Telefono = txtTelefono.Text;
35 model.lEstado = Convert.ToInt16(chkEstado.Checked ? "1" : "0");

36 personal.Guardar(model);

37 LoadDGVPersonal();

38 }

39
void LoadDGVPersonal()
40
{
41
List<PersistenciaDatos.ModeloMinimo.PersonalMinimalModel> list = personal.GetPe
42
dgvPersonal.AutoGenerateColumns = false;
43
dgvPersonal.DataSource = list;
44
45 }

46

47 void ObtenerId()

{
48
strCodigo = Convert.ToString(dgvPersonal.CurrentRow.Cells[0].Value);
49
}
50

51
void Buscar()
52
{
53 if(strCodigo != string.Empty)
54 {

55 PersistenciaDatos.Modelo.Personal model = personal.Buscar(strCodigo);

56 lblCodigo.Text = model.Id;

57 txtNombre.Text = model.Nombre;

txtDireccion.Text = model.Direccion;
58
txtTelefono.Text = model.Telefono;
59
chkEstado.Checked = Convert.ToBoolean(model.lEstado);
60
}
61
}
62

63 void Eliminar()
64 {

65 ObtenerId();

66 string msg = string.Format("Se va a ELIMINAR el registro: {0} {1} {0} Código: {

67 Environment.NewLine,

new string('-', 50), dgvPersonal.CurrentRow.Cells[0].Value,


68
dgvPersonal.CurrentRow.Cells[1].Value);
69

70
if (MessageBox.Show(msg, "Personal", MessageBoxButtons.YesNo, MessageBoxIcon.Qu
71 System.Windows.Forms.DialogResult.Yes)

72 {

73 personal.Eliminar(strCodigo);

LoadDGVPersonal();
74
}
75
76 }

77

78 private void dgvPersonal_CellDoubleClick(object sender, DataGridViewCellEventArg

{
79
ObtenerId();
80
Buscar();
81
tabControl1.SelectedTab = tabPage2;
82
}
83

84 private void frmPersonal_KeyDown(object sender, KeyEventArgs e)


85 {

86 if(e.KeyCode == Keys.F7)

87 {

88 Eliminar();

}
89
}
90

91
}
92
}
93

94

95

96

97

98

99

100

Al ejecutar la aplicación queda de esta manera.


Adjunto el proyecto para que lo puedan descargar, el próximo post será Inversión de
Control IoC con Ninject.

S-ar putea să vă placă și