4.26.2009

El Entity Framework en una Arquitectura n-layer – Parte 2

En el post anterior, iniciamos el desarrollo de una aplicacion n-layer utilizando en Entity Framework. En el post mencionado definimos y construimos la capa de acceso a datos la cual esta compuesta principalmente por el Entity Framework – aunque como veremos en futuros post, esta capa se va a extender con clases que le agrega funcionalidad el Entity Framework. En este post vamos a construir la capa de lógica de negocios para la aplicación que estamos desarrollando.

La capa de negocios es la capa en la cual vamos a crear los métodos unitarios que llevan a cabo las tareas correspondiente de cada una de las entidades. Estas tareas incluyen desde las operaciones CRUD – create, retrieve, update, delete – hasta procesos de negocios más elaborados.

Para crear esta capa de negocios, vamos a crear una librería utilizando los mismos pasos que hicimos en el post anterior, pero vamos a llamar la libreria como LogicaDeNegocios.

image Al igual que hicimos en la librería de la capa de acceso a datos, vamos a eliminar la clase que es creada por defecto cuando creamos el componente. La solución ahora debería lucir así:

image Seguidamente vamos a crear las clases de lógica de negocios para poder llevar a cabo operaciones que se requieran para cada entidad. En este caso, vamos a crear la clase de lógica de negocios para la entidad Usuario. Para crear esta clase, le damos click derecho sobre el proyecto de lógica de negocios y seleccionamos agregar nuevo ítem –> Agregar Clase. En el diálogo de agregar clase escribimos UsuarioBL como nombre de la clase, para poder identificar la clase de lógica de negocio para el usuario en las capas de presentación que vayamos a crear o incluso en la capa de servicios si deseamos crear una para permitir que nuestra funcionalidad de negocios se pueda acceder a través de servicios.

image El siguiente paso es agregar una referencia a la capa de acceso a datos para así poder tener visibilidad de las operaciones existentes dentro del modelo de entidades creado en esa librería. Para lograr esto, le damos click derecho al proyecto de lógica de negocios y seleccionamos la opción agregar referencia del menu contextual. En el dialogo de agregar referencias seleccionamos la cejilla de proyectos y escojemos el proyecto para capa de acceso a datos.

image La referencia debe poder verse en la carpeta de referencias del proyecto como se muestra a continuación.

imageUna vez agregada la referencia, procedemos a agregar el uso de la librería en la clase recién creada. Esto se logra a través de la instrucción using, seguidamente creamos una propiedad automática para tener acceso al modelo de entitdades que creamos anteriormente en la capa de acceso a datos.

imageComo se puede ver en la figura anterior, es requerido agregar la librería System.Data.Entities para poder hacer uso de las entidades presentes en la capa de acceso a datos. En post posteriores vamos a analizar con un poco más de profundidad el entity framework y estos detalles. Para agregar esta librería seguimos el mismo proceso que llevamos a cabo anteriormente para agregar la referencia a la capa de acceso a datos, solo que esta vez vamos a buscar la librería en la cejilla de .NET.

image Una vez agregada esta referencia, la clase actual para manejar la lógica de de negocios del usuario ya compila.

image

Un aspecto a destacar, es que agregamos una propiedad dinámica para tener acceso al modelo de entidades que acabamos de agregar desde la capa de acceso a datos, y además instanciamos el modelo de entidades generado desde el constructor de la clase. Este comportamiento puede no ser el deseado ya que puede producirnos trabajo extra a la hora de agregar relaciones que se obtienen desde otras clases de lógica de negocios por que cada clase va a venir de diferentes instancias del modelo, es decir desde diferentes contextos. En el post de como extender y mejorar el EF en una arquitectura n-layer vamos a ver cuales pueden ser las posibles soluciones para manejar estos casos.

Seguidamente procedemos a crear los métodos principales de CRUD en la lógica de negocios. Primeramente vamos a crear el método para agregar un usuario.Este método recibe una entidad del tipo Usuario, y la agrega al contexto instanciado utilizando el método generado AddToUsuario. Finalmente, procedemos a persistir los cambios en la base de datos utilizando el metodo SaveChanges.

image En muchas ocasiones surge la pregunta, ¿por qué recibir un usuario y no los campos para agregar a la entidad? La respuesta esta relacionada con la adaptabilidad al cambio de parte de la aplicación. Puede ser que en un futuro se requiera quitar o agregar campos a la entidad usuario; si estamos utilizando lo campos en lugar de una entidad, vamos a tener que modificar todos los métodos donde interactuamos con la entidad para modificar, actualizar, seleccionar, etc. Sin embargo, si utilizamos una entidad, los puntos que varían son la entidad, el UI en donde construimos la entidad y la capa de acceso a datos donde agregamos los campos al repositorios – si estamos usando el EF, no se requieren cambios en la capa de acceso a datos ya que el EF actualiza los métodos desde el modelo – con lo que nuestra aplicación es más fácil para modificar.

Seguidamente procedemos a crear los métodos necesarios para obtener todos los usuarios que existen en la base de datos, y el método que me retorna un usuario en específico.

Inicialmente agregamos la referencia a la librería System.Linq, esto para tener acceso a los métodos de extensión

using System.Collections.Generic;
using CapaDeAccesoADatos;
using System.Linq;


Seguidamente creamos los métodos mencionados anteriormente. En este caso vamos a utilizar la sintaxis del EF para acceder a los datos. En post posteriores vamos a utilizar LinqToEntities para llevar a cabo las mismas tareas.



public List<Usuario> ObtenerUsuarios( )
{
var usuarios = ModeloEntidades.Usuario;
return usuarios.ToList( );
}

public Usuario ObtenerUsuario( int id )
{
var usuario = ModeloEntidades.Usuario.Where(u => u.Id == id).FirstOrDefault( );
return usuario;
}



Con estos métodos ya tenemos un conjunto de funcionalidad suficiente para poder trabajar con nuestra capa de UI. Digo suficiente por que por supuesto faltan los métodos para actualizar y borrar usuarios, además los correspondientes métodos para asociar los usuarios a las entidades correspondientes. Estos métodos los vamos a agregar en futuros post.



La solución de la aplicación debe de lucir de la siguiente manera:



image



En el próximo post vamos a crear la capa de UI utilizando WPF.



Technorati Tags: ,,

12 comentarios:

Alicia dijo...

Buenas Tardes,

estoy empezado a trabajar con ADO .NET Entity Framework y Web Services. Se me esta presentando la siguiente situacion y queria saber si tenias algun conocimiento del tema:

Al serializar una de las clases generadas por EF me doy cuenta que serializa la EntityKey y no me interesa que esto pase pues quiero el objeto plano.

TIenes idea de como evitar la serializacion de la clave?

Muchisimas Gracias por tus post me han ayudado bastante!

Anónimo dijo...

Como quedaria el update en la capa de negocios? como le hago le para actualizar solo un campo y los demas queden igual. Podrias poner como se haria este metodo?
Gracias.

Luis D. Rojas dijo...

En post próximos voy a tomar en cuenta tu sugerencia... Gracias por tomarte el tiempo para leer mi blog

Saludos

De aqui y de allá dijo...

Hola, muy bueno tu blog, me esta sirviendo de mucho. Me gustaría saber como implementar el ejemplo que pones en la capa de negocio para que admita concurrencia optimista, y llendo más allá poder también realizar transacciones.
Gracias

Anónimo dijo...

Muy buen blog... Estamos iniciando con Entity Framework y nos es de mucha utilidad..Gracias por el aporte!

Luis D. Rojas dijo...

Gracias por los comentarios. En post posteriores voy a tocar temas del Entity Framework 4.0

Mauro dijo...

Hola Diego, estos post me ayudaron mucho a dar mis primeros pasos en Entity Framework en capas.
Pero me surgió una duda:
Usted devuelve la lista completa de la tabla, pero yo quiero devolver solo algunos campos de la tabla, por lo que realizo la consulta de linq así:
"var Localidades = from l in Modelo.localidad select new { l.activo, l.nombre, l.provincia };" y al hacer el Localidades.ToList() genera una lista de tipos anónimos, por lo al intentar hacer el return, da el error de que no se puede convertir un tipo anónimo en Localidad.
¿Existe alguna forma de hacer que la funcion ("public List CargarGrilla()") en lugar de devolver List devuelva List<"tipoAnonimo">? O ¿Cuál es la forma correcta de hacerlo?

Gracias y saludos.

Luis D. Rojas dijo...

Hola Mauro, gracias por leer el blog. Mira, lo que pasa es que cuando generas un select con menos campos o con campos de otras tablas - por ejemplo cuando haces un join - se genera un tipo anónimo porque linq no puede encontrar un tipo para devolver. Aqui hay varias soluciones, la primera utilizar genercis, para que la lista a retornar sea List y pasa T por parametro a la hora de invocar el método. O tambien puedes devolver todos los datos, pero extiendes la entidad para que dependiendo de algun valor que le indiques no permite acceso a las propiedades que no deseas que se vean y por último devolerlo todo y a nivel del UI poner invisible los campos.
Saludos

Mauro dijo...

Gracias Diego, al final implemente la solución de devolver toda la lista y que en la UI se seleccionen los campos a mostrar, muchas gracias!!

Ahora tengo otro problema jeje el cual es que cuando modifico un registro, y se actualiza el DataSource de la tabla que los muestra, aparece sin la modificación, pero si cierro y abro la ventana, aparece modificado, ahora te pego el código que tengo para ver si ahí está el error:

Este es el código del botón modificar (que abre otra ventana con los detalles para modificar):

private void btModificar_Click(object sender, EventArgs e)
{
int idLocalidad = (int)dgLocalidades.CurrentRow.Cells[0].Value;

Formularios.frmLocalidad form = new Formularios.frmLocalidad(idLocalidad);

if (form.ShowDialog() == DialogResult.OK)
{
cargarGrilla();
}
}

El frmLocalidad toma como parámetro el idLocalidad para determinar si se está haciendo un nuevo registro o se está modificando uno existente.

El botón de aceptar de frmLocalidad primero valida los datos, luego carga el objeto con los datos y luego ejecuta este código:

if (obLocalidad.idLocalidad == 0)
{
obLocalidadLN.AgregarLocalidad(obLocalidad);
}
else
{
obLocalidadLN.ModificarLocalidad(obLocalidad);
}
this.DialogResult = DialogResult.OK;

Si idLocalidad es igual a 0, es un registro nuevo y lo agrega, sino es una modificación y llama a ModificarLocalidad, el código de ese método es el siguiente:

public int ModificarLocalidad(localidad pLocalidad)
{
var Localidad = Modelo.localidad.FirstOrDefault(u => u.idLocalidad == pLocalidad.idLocalidad);

Localidad.nombre = pLocalidad.nombre;
Localidad.codigoPostal = pLocalidad.codigoPostal;
Localidad.provincia = pLocalidad.provincia;
Localidad.estado = pLocalidad.estado;

return Modelo.SaveChanges();
}

Los cambios se guardan en la base de datos, pero supongo que por algo de cache, sigue mostrando los datos anteriores a la modificación, pero espero su consejo ya que usted es el que sabe de esto!

Gracias otra vez!

lucas dijo...

Saludos Diego, Me puede explicar como se puede escribir esta instrucción en vb
ModeloEntidades.Usuario.Where(u => u.Id == id).FirstOrDefault( );

Luis D. Rojas dijo...

hola Lucas, claro puedes ver este post acerca de expresiones lambda en VB.net http://icomparable.blogspot.com/2009/10/expresiones-lambda-en-vbnet-90.html

Anónimo dijo...

tengo un problema de error en el momento de guardar la tupla system.data.updateException en la parte de return modeloentidades.savechanges() como puedo resolverlo