Iniciar sesión
Registrarse
He olvidado mi contraseña
17 AGO 2011
Vistas de la Entrada 34 Vistas de la Entrada 16
Sin categoría
Patrón UoW Parte 2

Introducción

En vista de que parece que el artículo que publique ayer sobre UoW, parecía que era demasiado simple…, y a pesar de que Lucas Ontivero ha publicado un ejemplo (Gracias), he decidido publicar un ejemplo un poco más amplio para ver su utilización.

Usando el Patrón

Partiendo de una clase base:

   1: public abstract class cBase : INotifyPropertyChanged

   2:     {

   3:         public event PropertyChangedEventHandler PropertyChanged;

   4:         public void CambiarPropiedad(string Propiedad)

   5:         {

   6:             if (PropertyChanged != null)

   7:             {

   8:                 PropertyChanged(this, new PropertyChangedEventArgs(Propiedad));

   9:             }

  10:         }

  11:         public abstract SqlCommand Insertar();

  12:         public abstract SqlCommand Modificar();

  13:         public abstract SqlCommand Eliminar();

  14:     }

La clase base va a servir como definición de nuestras entidades, va a incluir el evento que va a permitir tener un control sobre los cambios, y va a declarar como abstractos las funciones de Insertar, Modificar y Eliminar que utilizaremos un poco más adelante.

Para el ejemplo he creado una pequeña Base de Datos en SQL Server que presenta una tabla muy sencilla:

 

Para “mapear” los datos de está tabla voy a hacer uso de la clase Persona:

 

   1: public class Persona : cBase

   2:     {

   3:         private int _ID = 0;

   4:         public int ID

   5:         {

   6:             get 

   7:             { 

   8:                 return _ID; 

   9:             }

  10:             set 

  11:             {

  12:                 if (!_ID.Equals(value))

  13:                 {

  14:                     _ID = value;

  15:                     CambiarPropiedad("ID");

  16:                 }

  17:             }

  18:         }

  19:         private string _Nombre = string.Empty;

  20:         public string Nombre 

  21:         {

  22:             get

  23:             {

  24:                 return _Nombre; 

  25:             }

  26:             set

  27:             {

  28:                 if (!_Nombre.Equals(value))

  29:                 {

  30:                     _Nombre = value;

  31:                     CambiarPropiedad("Nombre");

  32:                 }

  33:             }

  34:         }

  35:         private DateTime _Fecha = DateTime.Now;

  36:         public DateTime Fecha

  37:         {

  38:             get

  39:             {

  40:                 return _Fecha;

  41:             }

  42:             set

  43:             {

  44:                 if (!_Fecha.Equals(value))

  45:                 {

  46:                     _Fecha = value;

  47:                     CambiarPropiedad("Fecha");

  48:                 }

  49:             }

  50:         }

  51:         public override SqlCommand Insertar()

  52:         {

  53:             SqlCommand command = new SqlCommand("INSERT INTO PERSONA (NOMBRE,FECHA) VALUES(@NOMBRE,@FECHA)");

  54:             command.Parameters.AddWithValue("Nombre", Nombre);

  55:             command.Parameters.AddWithValue("Fecha", Fecha);

  56:             return command;

  57:         }

  58:         public override SqlCommand Modificar()

  59:         {

  60:             SqlCommand command = new SqlCommand("UPDATE PERSONA SET NOMBRE=@NOMBRE, FECHA=@FECHA WHERE ID=@ID");

  61:             command.Parameters.AddWithValue("ID", ID);

  62:             command.Parameters.AddWithValue("Nombre", Nombre);

  63:             command.Parameters.AddWithValue("Fecha", Fecha);

  64:             return command;

  65:         }

  66:         public override SqlCommand Eliminar()

  67:         {

  68:             SqlCommand command = new SqlCommand("DELETE FROM PERSONA WHERE ID=@ID");

  69:  

  70:             command.Parameters.AddWithValue("ID", ID);

  71:             return command;

  72:         }

  73:         public static List<Persona> Obtener()

  74:         {

  75:             List<Persona> listado = new List<Persona>();

  76:             DataTable datos = new DataTable();

  77:             using (SqlConnection con = new SqlConnection(@"Data Source=.SQLEXPRESS;Initial Catalog=Concept;Integrated Security=True;Pooling=False"))

  78:             {

  79:                 using (SqlDataAdapter da = new SqlDataAdapter("SELECT ID,NOMBRE,FECHA FROM PERSONA", con))

  80:                 {

  81:                     da.Fill(datos);

  82:                 }

  83:             }

  84:             foreach (DataRow row in datos.Rows)

  85:             {

  86:                 listado.Add(new Persona () { ID=Convert.ToInt32(row["ID"]),

  87:                                              Nombre = Convert.ToString(row["Nombre"]),

  88:                                              Fecha = Convert.ToDateTime(row["Fecha"])

  89:                 });

  90:             }

  91:             return listado;

  92:         }

  93:     }

En estás clase hago la implementación de los métodos abstractos que había definido en la clase Base y que van a construir objetos de tipo SqlCommand para que posteriormente sean tratados.

 

A continuación voy a exponer una “aproximación” más elaborada que la que expuse ayer, pero que tampoco es la que realmente utilizo cuando hago uso de este patrón.

 

   1: public class UnidadDeTrabajo

   2:     {

   3:        

   4:         List<cBase> objetosNuevos;

   5:         List<cBase> objetosModificados;

   6:         List<cBase> objetosEliminados;

   7:  

   8:         public UnidadDeTrabajo()

   9:         {

  10:             objetosNuevos = new List<cBase>();

  11:             objetosModificados = new List<cBase>();

  12:             objetosEliminados = new List<cBase>();

  13:        }

  14:  

  15:         public void Añadir(cBase objNuevo)

  16:         {

  17:             if (!objetosNuevos.Contains(objNuevo))

  18:             {

  19:                 objetosNuevos.Add(objNuevo);

  20:             }

  21:         }

  22:  

  23:         public void Eliminar(cBase objEliminado)

  24:         {

  25:             if (!objetosEliminados.Contains(objEliminado))

  26:             {

  27:                 objetosEliminados.Add(objEliminado);

  28:             }

  29:         }

  30:  

  31:         public void Modificar(cBase objModificado)

  32:         {

  33:             if (!objetosModificados.Contains(objModificado))

  34:             {

  35:                 objetosModificados.Add(objModificado);

  36:             }

  37:         }

  38:         public void Confirmar()

  39:         {

  40:             SqlCommand cmd;

  41:             using (TransactionScope scope = new TransactionScope())

  42:             {

  43:                 using (SqlConnection con = new SqlConnection(@"Data Source=.SQLEXPRESS;Initial Catalog=Concept;Integrated Security=True;Pooling=False"))

  44:                 {

  45:                     con.Open();

  46:                     foreach (cBase obj in objetosNuevos)

  47:                     {

  48:                         cmd = obj.Insertar();

  49:                         cmd.Connection = con;

  50:                         cmd.ExecuteNonQuery();

  51:                     }

  52:                     foreach (cBase obj in objetosModificados)

  53:                     {

  54:                         cmd = obj.Modificar();

  55:                         cmd.Connection = con;

  56:                         cmd.ExecuteNonQuery();

  57:                     }

  58:                     foreach (cBase obj in objetosEliminados)

  59:                     {

  60:                         cmd = obj.Eliminar();

  61:                         cmd.Connection = con;

  62:                         cmd.ExecuteNonQuery();

  63:                     }

  64:                     

  65:                 }

  66:                 scope.Complete();

  67:                 Limpiar();

  68:             }

  69:         }

  70:  

  71:         private void Limpiar()

  72:         {

  73:             objetosNuevos.Clear();

  74:             objetosModificados.Clear();

  75:             objetosEliminados.Clear();

  76:         }

  77:     }

La función que realmente interesa de la clase UnidadDeTrabajo es Confirmar. Como podéis observar “solo” se declara una conexión, y “solo” se abre una vez, para ejecutar todos los comandos existentes en las listas de objetos nuevos, objetos modificados y objetos eliminados.

Para ver un pequeño ejemplo de su uso:

   1: var personas = Persona.Obtener();

   2:            

   3:            UnidadDeTrabajo unit = new UnidadDeTrabajo();

   4:            unit.Eliminar(personas.Where(n=>n.Nombre.ToUpper().Equals("PRUEBA")).FirstOrDefault());

   5:            personas.Where(n => n.Nombre == "Javier").FirstOrDefault().Nombre = "JTP";

   6:            unit.Modificar(personas.Where(n => n.Nombre.ToUpper().Equals("JTP")).FirstOrDefault());

   7:            unit.Añadir(new Persona() { Nombre = "Test", Fecha = DateTime.Now });

   8:            unit.Confirmar();

 

En respuesta a los comentarios del anterior post

Creo que este ejemplo deja claro, el tener un conjunto de datos que se van a tratar de una “tacada” a la hora de ser enviados a la base de datos.

Con respecto a las relaciones, independientemente que usemos EF o no, tendremos que tener los métodos adecuados para tratar dichas relaciones si es que se realizan desde la aplicación, quizás existen triggers para manejar dichos aspectos (aunque no sea una cosa que me gusten en exceso, puede resultar fácil).

 

Bueno espero que este Post os guste más que el anterior.

 

Saludos

 

Enviado por
Compartir : Compartir en Facebook Compartir en Twitter Compartir en Google+ Compartir en Linkedin
anonymous 17 de agosto del 2011 a las 09:35

seguro que el sr. Suplantador quedará satisfecho …o no? :-) salu2grz

Responder
anonymous 17 de agosto del 2011 a las 15:21

Excelente, articulos orientados al mundo real.

Responder
jirigoyen 18 de agosto del 2011 a las 00:57

Entiendo que el ejemplo, muestra el uso del patrón, sin embargo me gustaría comentar un par de factores que no me gustan de este ejemplo. Pienso que no es adecuado introducir en el código de la entidad las sentencias SqlCommand, esto crea dependencias con clases de acceso a datos de Sql, entiendo que estas normalmente deberían alojarse en un proyecto o clase diferente para eliminar estas dependencias, creo que la entidad no debe conocer cómo se va a actualizar, borrar o insertar en la base de datos, esto corresponde a las clases de datos y deberían tratarse de forma independiente, por otra parte escribir los nombres de los campos dentro de una cadena continua siendo un error muy habitual ya que si alteramos el nombre en la tabla de la base de datos no detectaremos el error, almacenar la conexión en una cadena también es un problema de seguridad que debemos evitar.

Un saludo.

Responder
jtorrecilla 18 de agosto del 2011 a las 08:24

@Ivan y @Manuel Gracias.
@Juan, cuando escribi el artículo sabia que lo de la cadena de conexion sabia que iba a traer cola, pero esto es un “mero” ejemplo no he querido entrar a un nivel maximo.
Respecto de los campos de los nombres, personalmente prefiero utilizar los nombres de los campos siempre, si hay un cambio en la BD, para mi siempre será necesario comprobar que la aplicación funciona…
Y con respecto a los SQLCommand, no queria complicar el ejemplo, haciendo referencia a otras DLL, y existe otras formas de hacerlo.
Saludos, y gracias por comentar!

Responder
anonymous 18 de agosto del 2011 a las 10:06

Hola Javier,

Primero, buen artículo ya que se ha entendido bastante bien de lo que consta el UnitOfWork. Una sugerencia/pregunta… se podría mejorar mediante genéricos la clase Unidad de trabajo?

Responder
jtorrecilla 18 de agosto del 2011 a las 10:26

@Fernando gracias por opinar!
Donde quieres aplicar Generics en la Unidad de Trabajo?

Responder
anonymous 18 de agosto del 2011 a las 10:30

public class UnidadDeTrabajo where t: cbase

Responder
jtorrecilla 18 de agosto del 2011 a las 10:37

@Fernando, si lo podrías aplicar:
public class UnidadDeTrabajo where T : cBase y la definición sería:
UnidadDeTrabajo unidad=new UnidadDeTrabajo()

Pero… tienes que evaluar el sentido que tendría. Yo en principio no se lo veo mucho a no ser que los datos tengan que ir a distintos destinos que vengan dados por alguna peculiriadad de T…

Responder
jirigoyen 18 de agosto del 2011 a las 12:32

@Javier, si cuando realizas un cambio en la base de datos tienes que probar de nuevo toda la aplicación perderás mucho tiempo, sobre todo si la base de datos es grande y sufre muchos cambios, esto se puede solucionar fácilmente utilizando un enumerador con los nombres de los campos en la entidad o utilizando técnicas de reflexión para generar las sentencias sql.

Por otra parte utilizar generics y reflexión puede tener mucho sentido si quieres generalizar algunas sentencias insert, update y delete evitando tener que escribir cada sentencia sql en base a la entidad que utilizas, no te digo nada si tienes que escribir esto con entidades que tengan muchos campos, y si encima quieres verificar si el contenido de los campos a sido alterado por otro usuario, no solo tenemos el coste de escribir el código, también debemos mantenerlo.

Un saludo.

Responder
jtorrecilla 18 de agosto del 2011 a las 12:47

@Juan Si haces un cambio en la aplicación, no ejecutas todo el conjunto de pruebas sobre la misma para comprobar que todo está como debe?

Con respecto a lo de generics y reflection, quiero recalcar que esto es un “ejemplo” y sencillo, evidentemente podría haber hecho el ejemplo usando Reflection para obtener los tipos de la BD, incluso, podría haber definido una T4 para auto generarme las clases…

Responder
jirigoyen 18 de agosto del 2011 a las 16:44

Javier, no es mi intención criticar o discutir sobre el ejemplo que has escrito, entiendo que lo has simplificado para mostrar el patrón, tan solo, quiero exponer algunas dudas al escribir código sobre todo para aquellos que apliquen este ejemplo sin más, algo muy habitual.

Por otra parte, he comentado el uso de generics y reflexión en base a la pregunta de Fernando Urkijo y no para criticar el ejemplo.

Cuando haces un cambio en la aplicación, lo lógico es que ejecutes las pruebas unitarias, siempre que las tengas, la mayoría de los desarrollos que veo, no utilizan pruebas de base de datos, ni siquiera en las entidades, con lo que se hace difícil detectar errores por cambios en la estructura de las tablas. Con EF esto no se hace tan imprescindible pues los cambios en las entidades se reflejan cuando actualizas el modelo y estos errores se detectan en tiempo de compilación.

Un saludo.

Responder
omarvr 19 de agosto del 2011 a las 08:20

Hoña Javier, excelente articulo.

Una duda, cuando usas EF, Linq2sql o cualquier otro ORM también te creas una clase para la unidad de trabajo? Lo pregunto porque el contexto casi siempre es una implementación del patrón UoW.

Yo por mi parte, siempre heredo del contexto y la nueva clase seria la Unidad de trabajo, pero me gustaría saber como lo desarrolla el resto…

Gracias

Responder
jtorrecilla 19 de agosto del 2011 a las 08:30

@Omar, lo primero gracias por contestar.
Exacto, en principio el contexto es la Unidad de Trabajo en el caso de los ORM, por lo que bajo mi punto de vista no sería necesario crear una nueva clase, a no ser que quieras realizar comprobaciones más exhautivas de los datos que se tienen que enviar o no( y aun así no sería 100% necesaria una nueva clase que fuera la UoW)

Saludos!

Responder
omarvr 19 de agosto del 2011 a las 13:50

Si Javier, yo lo hago por un tema de IoC y por mantener una uniformidad en mis repositorios respecto a la unidad de trabajo.

Yo tengo definida una interfaz para mi UoW, y todos mis proyectos trabajan de la misma manera con la unidad de trabajo sin tener en cuenta si es EF, Linq2sql, NH u otra cosa pq trabajan con la interfaz q tengo….

La clase q me creo hereda del contexto e implementa la interfaz…

De todas maneras estoy de acuerdo en q la mayoría de los casos el propio contexto vale…

Gracias por la aclaración 😉

Responder
anonymous 30 de enero del 2012 a las 11:03

Introducción Hace unos días publique ORAPOCO , un ORM ligero para trabajar con Bases de Datos Oracle

Responder
anonymous 30 de enero del 2012 a las 11:04

Introducción Hace unos días publique ORAPOCO , un ORM ligero para trabajar con Bases de

Responder
Dejar un comentario
Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Dejar una respuesta

Sin categoría

Tip Rapido: Método Extensor

Bueno aquí va un mini-post. Cuantas veces os habéis encontrado con: if (a>=5 and a<=10) … Yo al menos recuerdo unas pocas. Bueno aquí va el mini truco. Creamos una clase estática con un método extensor: Code Snippet public static class ExtensionMethod  {    public  static bool Between(this int original, int menor, int mayor)      {          return (original […]

Enviado por
Sin categoría

Patrón UoW Parte 2

Introducción En vista de que parece que el artículo que publique ayer sobre UoW, parecía que era demasiado simple…, y a pesar de que Lucas Ontivero ha publicado un ejemplo (Gracias), he decidido publicar un ejemplo un poco más amplio para ver su utilización. Usando el Patrón Partiendo de una clase base: 1: public abstract […]

Enviado por
Sin categoría

[Opinión]Los Nuevos Programadores y VB o C#

Llevo relativamente poco tiempo en el mundo de la programación ya que mis orígenes, los verdaderos, serán del 2001 con C, aunque anteriormente ya había hecho cosas con Visual Basic 6. Por si no lo he dicho nunca, o no me has leído decirlo me encanta programar. Después de la primera introducción voy al hilo […]

Enviado por
Sin categoría

[Personal] Microsoft Community Contributor 2011

En mi revisión matutina del correo, me he encontrado una más que Grata Sorpresa: Dear Javier, Congratulations! We’re pleased to inform you that your contributions to Microsoft online technical communities have been recognized with the Microsoft Community Contributor Award. The Microsoft Community Contributor Award is reserved for participants who have made notable contributions in Microsoft […]

Enviado por
Sin categoría

[Personal] MVP Visual C# 2011

Buenas tardes a todos, Este post es como dice el titulo para anunciar que he sido galordando por el premio MVP de Microsoft en la categoria de C#. Desde aquí quiero darles las gracias a los chicos de Microsoft empezando por Cristina Gonzalez, Jose Bonnin, David Salgado, Boris Armenta… También mi gratitud a la gente de […]

Enviado por