Hola! en este post aplicaremos patrones de diseño para nuestra aplicación, en concreto dos patrones , Abstract Factory y Data Access Object (DAO).
Primero un poco de teoría , a una pequeña definición de:
Patrones de Diseño:
Segun Wikipedia:
“Los patrones de diseño (design patterns) son la base para la búsqueda de soluciones a problemas comunes en el desarrollo de software y otros ámbitos referentes al diseño de interacción o interfaces.”
Entonces como recordarán en el post anterior hicimos un helper, denominado NHelper que usaba el concepto de Generics y Templates, en este post y con su respectivo ejemplo extenderemos un poco más esto haciendo uso de estos patrones:
Abstract Factory, un ejemplo más concreto lo hemos visto usando el Bloque de Acceso de datos de Enterprise Library atravez de DbFactory.
Según Wikipedia:
(Fábrica Abstracta) es un patrón de diseño para el desarrollo de software.
El problema que intenta solucionar este patrón es el de crear diferentes familias de objetos.
Data Access Object (DAO), es un patrón de uso de común en nuestras aplicaciones , generalmente lo identificamos de manera equivocada con nuestra Capa de Datos, A cuyos objetos de “acceso a datos”, le implementados los metodos caracteristicos de mantenimento o tambíen denominados CRUD.
Según Wikipedia:
En software de computadores, un Data Access Object (DAO, Objeto de Acceso a Datos) es un componente de software que suministra una interfaz común entre la aplicación y uno o más dispositivos de almacenamiento de datos, tales como una Base de datos o un archivo.
Diseñando nuestra Arquitectura:
En la denominada Capa 3 (Capa de Datos) implementaremos el patrón DAO y Abstract Factory.
En la denominada Capa 2 ( Capa de Logica de Negocios) implementaremos solo el patrón Abstrac Factory.
Problema:
Sucede que erróneamente se suele implementar un Objeto DAO para entidad y luego implementar un objeto de negocio que hace una llamada directa al objeto DAO, siguiendo esta logica , habrian tantas llamadas desde la capa de negocios hasta la capa de datos como entidades en nuestro modelo, lo cual debe tratar de evitarse. pues sino la aplicación quedaria asi:
Una solución a este problema , es aplicando patrones de diseño, para nuestro caso Abstract factory y DAO , implementandolo en ambas capas podemos redudir el numero de nuestras llamadas,
Como lo hacemos?
GenericDAO , GenericBussiness , implementando estas fabricas abstractas a y haciendo uso de ellas en las llamadas de nuestros objetos de negocios hacia nuestros objetos de datos, entonces cambiaria de esta manera:
Nuestro Diseño de Soluciones y proyectos:
Extendiento nuesto NHelper: De NHelper a NHibernateHelper
Implementando: GenericNHibernateDAO , IGenericDAO, NHibbernateHelper.
Instanciemos graficamente para poder entenderlo rápido:
Primero definimos nuestra interface IGenericDAO:
Esta interfaz tendra cada uno de los metodos que utilizarán nuestras entidades, tales como:
- FindAll(): Devuelve todos los elementos.
- FindAllOrdered() : Devuelve todos elementos ,bajo un criterio de orden
- FindByExample() : Devuelve todos los elementos que coincidan con el objeto de ejemplo enviado como parametro
- FindbyId(): devuelve un ojeto a partir de su identificador , sea este un primitivo(string, int, etc.) o un objeto , para el caso de nuestras claves compuestas.
- CRUD: Save(), Update(),MakeTransient() (Delete), etc.
01.using System.Collections; 02.using System.Collections.Generic; 03.namespace NHDataAccess 04.{ 05. public interface IGenericDao<T,ID> 06. { 07. T FindByid(Id id); 08. 09. IList<T> FindAll(); 10. 11. IList<T> FindAllOrdered(IList<STRING> propertyNames); 12. 13. IList<T> FindByExample(T entity); 14. 15. IList<T> FindBydExampleOrdered(T entity, IList<STRING> propertyNames); 16. 17. IList<T> FindLikeExample(T entity); 18. 19. IList<T> FindLikeExampleOrdered(T entity, IList<STRING> propertyNames); 20. 21. IList<T> FindLikeExampleIgnoreCase(T entity); 22. 23. IList<T> FindLikeExampleIgnoreCaseOrdered(T entity, IList<STRING> propertyNames); 24. 25. IList<T> FindByQuery(string query); 26. 27. IList<T> FindByQuery(string query, string[] parameters, object[] values); 28. 29. IList<T> FindByNamedQuery(string query); 30. 31. IList<T> FindByNamedQuery(string query, string[] parameters, object[] values); 32. 33. IList FindObjectsByQuery(string query); 34. 35. IList FindObjectsByNamedQuery(string query); 36. 37. IList FindObjectsByNamedQuery(string query, string[] parameteres, object[] values); 38. 39. IList FindObjectsBySQLQuery(string query, string[] parameters, object[] values); 40. 41. T Save(T entity); 42. 43. T Update(T entity); 44. 45. void MakeTransient(T entity); 46. 47. Id GetMax(string propertyName); 48. 49. } 50.} Porque en unos casos usar IList<T> y en otros IList:
Para el primer caso cuando utilizamos IList<T> hacemos una consulta sobre una entidad en particular, los metodos que utilizen IList<T> solo devolverán un tipo en particular.
Para el segundo caso , se da cuando deseamos recuperar “Propiedades cruzadas” que quiere decir esto , imaginemos un join entre entidades , osea por ejm:
Class A –> propiedad A1 , propiedad A2
Clase B –> propiedad B1, propiedad B2
y quisieramos recuperar una lista que contenga la Propiedad A1 y propiedad B2, en este caso Nhibernate me devolvera una lista de arrays, en la cual yo podré iterar y recuperar lo que necesito, más adelante implementaremos un ejemplo de este caso.
GenericNHibernateDao:
Esta clase hereda la interfaz IGenericDAO e implementa sus métodos:
La implementación de estos metodos lo hacemos atreves del uso de criteria, si recuerdan en el post anterior para nuestra primera versión de NHelper , usabamos HQL a modo de emular “SQL”, en este Nuevo NHelper, GenericNHibernateDAO , NH genera las consultas SQL a partir de criteria.
(*) Para la presentación configuraremos Log4net para visualizar las consultas SQL generadas por NHibernate.
001.using System; 002.using System.Collections; 003.using System.Collections.Generic; 004.using System.Linq; 005.using System.Text; 006.using NHDataAccess; 007.using NHibernate; 008.using NHibernate.Criterion; 009.using NHibernate.Type; 010.namespace NHDataAccess 011.{ 012. public abstract class GenericNhibernateDao<T,ID> : IGenericDao<T,ID> 013. { 014. public T FindByid(Id id) 015. { 016. return NhibernateHelper.GetCurrentSession().Get<T>(id); 017. } 018. 019. public IList<T> FindAll() 020. { 021. return NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)).List<T>(); 022. } 023. 024. public IList<T> FindAllOrdered(IList<STRING> propertyNames) 025. { 026. var criteria = NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)); 027. foreach (var propertyName in propertyNames) 028. { 029. criteria.AddOrder(Order.Asc(propertyName)); 030. } 031. return criteria.List<T>(); 032. } 033. 034. public IList<T> FindByExample(T entity) 035. { 036. return NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)).Add(Example.Create(entity)).List<T>(); 037. } 038. 039. public IList<T> FindBydExampleOrdered(T entity, IList<STRING> propertyNames) 040. { 041. var criteria = NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)).Add(Example.Create(entity)); 042. foreach (var propertyName in propertyNames) 043. { 044. criteria.AddOrder(Order.Asc(propertyName)); 045. } 046. return criteria.List<T>(); 047. } 048. 049. public IList<T> FindLikeExample(T entity) 050. { 051. return NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)).Add(Example.Create(entity).EnableLike()).List<T>(); 052. } 053. 054. public IList<T> FindLikeExampleOrdered(T entity, IList<STRING> propertyNames) 055. { 056. var criteria = NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)).Add(Example.Create(entity)); 057. foreach (var propertyName in propertyNames) 058. { 059. criteria.AddOrder(Order.Asc(propertyName)); 060. } 061. return criteria.List<T>(); 062. } 063. 064. public IList<T> FindLikeExampleIgnoreCase(T entity) 065. { 066. return067. NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)).Add( 068. Example.Create(entity).EnableLike().IgnoreCase()).List<T>(); 069. } 070. 071. public IList<T> FindLikeExampleIgnoreCaseOrdered(T entity, IList<STRING> propertyNames) 072. { 073. var criteria = 074. NhibernateHelper.GetCurrentSession().CreateCriteria(typeof(T)).Add( 075. Example.Create(entity).EnableLike().IgnoreCase()); 076. foreach (var propertyName in propertyNames) 077. { 078. criteria.AddOrder(Order.Asc(propertyName)); 079. } 080. return criteria.List<T>(); 081. } 082. 083. public IList<T> FindByQuery(string query) 084. { 085. return NhibernateHelper.GetCurrentSession().CreateQuery(query).List<T>(); 086. } 087. 088. public IList<T> FindByQuery(string myquery, string[] parameters, object[] values) 089. { 090. var query = NhibernateHelper.GetCurrentSession().CreateQuery(myquery); 091. for (var i = 0; i < parameters.Length-1; i++) 092. { 093. query.SetParameter(parameters[i], values[i]); 094. } 095. return query.List<T>(); 096. } 097. 098. public IList<T> FindByNamedQuery(string query) 099. { 100. return NhibernateHelper.GetCurrentSession().GetNamedQuery(query).List<T>(); 101. } 102. 103. public IList<T> FindByNamedQuery(string myquery, string[] parameters, object[] values) 104. { 105. var query = NhibernateHelper.GetCurrentSession().GetNamedQuery(myquery); 106. for (var i = 0; i < parameters.Length-1; i++) 107. { 108. query.SetParameter(parameters[i], values[i]); 109. } 110. return query.List<T>(); 111. } 112. 113. public System.Collections.IList FindObjectsByQuery(string query) 114. { 115. return NhibernateHelper.GetCurrentSession().CreateQuery(query).List(); 116. } 117. 118. public System.Collections.IList FindObjectsByNamedQuery(string query) 119. { 120. return NhibernateHelper.GetCurrentSession().GetNamedQuery(query).List(); 121. } 122. 123. public System.Collections.IList FindObjectsByNamedQuery(string myquery, string[] parameteres, object[] values) 124. { 125. var query = NhibernateHelper.GetCurrentSession().GetNamedQuery(myquery); 126. for (var i = 0; i < parameteres.Length-1; i++) 127. { 128. query.SetParameter(parameteres[i], values[i]); 129. } 130. return query.List(); 131. } 132. 133. public IList FindObjectsBySQLQuery(string query) 134. { 135. return NhibernateHelper.GetCurrentSession().CreateSQLQuery(query).List(); 136. } 137. 138. public IList FindObjectsBySQLQuery(string myquery, string[] parameters, object[] values) 139. { 140. var query = NhibernateHelper.GetCurrentSession().CreateSQLQuery(myquery); 141. for (int i = 0; i < parameters.Length-1; i++) 142. { 143. query.SetParameter(parameters[i], values[i]); 144. } 145. return query.List(); 146. } 147. 148. public T Save(T entity) 149. { 150. NhibernateHelper.GetCurrentSession().Save(entity); 151. return entity; 152. } 153. 154. public T Update(T entity) 155. { 156. NhibernateHelper.GetCurrentSession().Update(entity); 157. return entity; 158. } 159. 160. public void MakeTransient(T entity) 161. { 162. NhibernateHelper.GetCurrentSession().Delete(entity); 163. } 164. 165. public Id GetMax(string propertyName) 166. { 167. return NhibernateHelper.GetCurrentSession().CreateCriteria(GetType()).SetProjection(Projections.ProjectionList().Add(Projections.Max(propertyName))).UniqueResult<ID>(); 168. } 169. protected void DeleteObjectByQuery(string query, object[] values, IType[] types) 170. { 171. NhibernateHelper.GetCurrentSession().Delete(query, values, types); 172. } 173. } 174.}NhibernateHelper:
Esta clase administra las sessiones de Nhibernate, con esto quiero decir que crea , borra , inicializa transacciones , cierra , commitea y hacer rollback, etc.
Los métodos de esta Clase son estaticos, una vez inicializada la session de NH , la guardan en el session Current del objeto Session de ASP.NET, para poder así mantener la session activa.
01.using System; 02.using System.IO; 03.using NHibernate; 04.using Configuration=NHibernate.Cfg.Configuration; 05. 06.namespace NHDataAccess 07.{ 08. public class NhibernateHelper 09. { 10. private const string CurrentSessionKey = "nhibernate.current_session"; 11. public static ISessionFactory SessionFactory; 12. static NhibernateHelper() 13. { 14. try15. { 16. log4net.Config.XmlConfigurator.Configure(); 17. var cfg = new Configuration(); 18. var configurationSection = NhibernateConfigurationSection.CurrentConfiguration; 19. cfg.Configure(configurationSection.HibernateFile); 20. var path = new DirectoryInfo(configurationSection.MappingPath); 21. cfg.AddDirectory(path); 22. SessionFactory = cfg.BuildSessionFactory(); 23. } 24. catch (Exception ex) 25. { 26. throw new Exception("Falló Inicialización de Nhibernate",ex); 27. } 28. } 29. 30. public static ISession OpenSession() 31. { 32. return SessionFactory.OpenSession(); 33. } 34. 35. public static ISession GetCurrentSession() 36. { 37. var context = System.Web.HttpContext.Current; 38. var currentSession = (ISession) (context.Items[CurrentSessionKey]); 39. if(currentSession == null) 40. { 41. currentSession = SessionFactory.OpenSession(); 42. context.Items[CurrentSessionKey] = currentSession; 43. } 44. return currentSession; 45. } 46. 47. public static void CloseSession() 48. { 49. var context = System.Web.HttpContext.Current; 50. var currentSession = (ISession) (context.Items[CurrentSessionKey]); 51. if(currentSession == null) 52. { 53. return; 54. } 55. currentSession.Close(); 56. context.Items.Remove(CurrentSessionKey); 57. } 58. public static void CloseSessionFactory() 59. { 60. if(!(SessionFactory == null)) 61. { 62. SessionFactory.Close(); 63. } 64. } 65. 66. public static void BeginTransaction() 67. { 68. if(GetCurrentSession().Transaction.IsActive) 69. { 70. GetCurrentSession().BeginTransaction(); 71. } 72. } 73. public static void EndTransaction() 74. { 75. GetCurrentSession().Transaction.Commit(); 76. } 77. public static void RollBackTransaction() 78. { 79. GetCurrentSession().Transaction.Rollback(); 80. } 81. } 82.}Nuestras Entidades:
Igual que en nuestro ejemplo anterior utilizaremos solo 3 entidades , Usuario, Rol , y la entidad intermedia , UsuarioRol
La diferencia es en el uso de claves compuestas para el caso de “UsersRoles” esto con el efecto de utilizarla en la demostración
01.using System; 02.using System.Collections; 03.namespace NHEntity 04.{ 05. [Serializable()] 06. public class RoleEntity : IEquatable<RoleEntity> 07. { 08. public virtual int IdRole{ get; set;} 09. 10. public virtual string RoleName{ get; set;} 11. 12. public virtual IList UserRoleList { get; set; } 13. 14. #region Miembros de IEquatable<ROLE> 15. 16. public virtual bool Equals(RoleEntity other) 17. { 18. return other.IdRole.Equals(IdRole ); 19. } 20. #endregion 21. } 22.}01.using System; 02.namespace NHEntity 03.{ 04. [Serializable] 05. public class UserRoleEntity : IEquatable<UserRoleEntity> 06. { 07. private PKUserRole id = new PKUserRole(); 08. public virtual PKUserRole IdUserRole 09. { 10. set11. { 12. id = value; 13. } 14. get15. { 16. return id; 17. } 18. } 19. 20. public virtual bool Equals(UserRoleEntity other) 21. { 22. return other.IdUserRole.Equals(this.IdUserRole); 23. } 24. } 25.}01.using System; 02.using System.Collections; 03.using System.Collections.Generic; 04.namespace NHEntity 05.{ 06. [Serializable] 07. public class UserEntity : IEquatable<UserEntity> 08. { 09. private IList userRoleList; 10. public UserEntity() 11. { 12. userRoleList = new List<UserRoleEntity>(); 13. } 14. 15. public virtual int IdUser 16. { 17. set; 18. get; 19. } 20. 21. public virtual string Login 22. { 23. set; 24. get; 25. } 26. 27. public virtual string Password 28. { 29. set; get; 30. } 31. 32. public virtual IList UserRoleList 33. { 34. get35. { 36. return userRoleList; 37. } 38. set39. { 40. userRoleList = value; 41. } 42. } 43. 44. public virtual bool Equals(UserEntity other) 45. { 46. return other.IdUser.Equals(IdUser); 47. } 48. } 49.}Objetos PK
Para el uso de las claves compuestas, se necesita implementar una clase que denominaremos Objetos PK
01.using System; 02.using System.Collections; 03. 04.namespace NHEntity.PK 05.{ 06. [Serializable] 07. public class PKUserRole : IEquatable<PKUSERROLE> 08. { 09. private UserEntity idUser = new UserEntity(); 10. private RoleEntity idRole = new RoleEntity(); 11. readonly CaseInsensitiveComparer objComparer = new CaseInsensitiveComparer(); 12. public virtual UserEntity IdUser 13. { 14. set15. { 16. idUser = value; 17. } 18. get19. { 20. return idUser; 21. } 22. } 23. public virtual RoleEntity IdRole 24. { 25. set26. { 27. idRole = value; 28. } 29. get30. { 31. return idRole; 32. } 33. } 34. public bool Equals(PKUserRole obj) 35. { 36. return objComparer.Compare(this, obj) == 0; 37. } 38. public override bool Equals(object obj) 39. { 40. if(obj.Equals(this)) 41. { 42. return true; 43. } 44. return false; 45. } 46. public override int GetHashCode() 47. { 48. return base.ToString().ToLowerInvariant().GetHashCode() + 1; 49. } 50. } 51.}Objetos DAO
- UserDao
- RoleDao
- UserRoleDao
01.using NHDao.Interfaces; 02.using NHDataAccess; 03.using NHEntity; 04. 05.namespace NHDao.Implementations 06.{ 07. public class UserDao : GenericNhibernateDao<UserEntity, int>, IUserDao 08. { 09. } 10.}01.using NHDao.Interfaces; 02.using NHDataAccess; 03.using NHEntity; 04. 05.namespace NHDao.Implementations 06.{ 07. public class RoleDao : GenericNhibernateDao<RoleEntity,INT>,IRoleDao 08. { 09. } 10.}01.using NHDao.Interfaces; 02.using NHDataAccess; 03.using NHEntity; 04.using NHEntity.PK; 05. 06.namespace NHDao.Implementations 07.{ 08. public class UserRoleDao : GenericNhibernateDao<UserRoleEntity, PKUserRole>, IUserRoleDao 09. { 10. } 11.}1.using NHDataAccess; 2.using NHEntity; 3.namespace NHDao.Interfaces 4.{ 5. public interface IUserDao : IGenericDao<UserEntity, int> 6. { 7. } 8.}1.using NHEntity; 2.using NHDataAccess; 3.namespace NHDao.Interfaces 4.{ 5. public interface IRoleDao : IGenericDao<RoleEntity,INT> 6. { 7. } 8.}01.using NHDataAccess; 02.using NHEntity; 03.using NHEntity.PK; 04. 05.namespace NHDao.Interfaces 06.{ 07. public interface IUserRoleDao : IGenericDao<UserRoleEntity, PKUserRole> 08. { 09. } 10.}DAOFactory
Como la clase DAOFactory es una clase abstracta(al ser abstracta no podemos instanciarla)
para poder hacer esto implementamos un metodo estático “Instance()” que me creara una
instancia de un tipo, en este caso de la clase abstracta DAOFactory
01.using System; 02.using NHDao.Interfaces; 03.namespace NHDao 04.{ 05. public abstract class DaoFactory 06. { 07. public static Type DAO_FACTORY = typeof (FrameworkDaoFactory); 08. public static DaoFactory Instance(Type factory) 09. { 10. try11. { 12. return (DaoFactory) Activator.CreateInstance(factory); 13. 14. }catch(Exception ex) 15. { 16. throw new Exception("No se crear Factory de DAO:"+factory); 17. } 18. } 19. public abstract IRoleDAO GetRoleDAO(); 20. public abstract IUserDAO GetUserDAO(); 21. public abstract IUserRoleDAO GetUserRoleDAO(); 22. } 23.}FrameworkDaoFactory
01.using NHDao.Implementations; 02.using NHDao.Interfaces; 03.namespace NHDao 04.{ 05. public class FrameworkDaoFactory : DaoFactory 06. { 07. public override IRoleDAO GetRoleDAO() 08. { 09. return new RoleDAO(); 10. } 11. public override IUserDAO GetUserDAO() 12. { 13. return new UserDAO(); 14. } 15. public override IUserRoleDAO GetUserRoleDAO() 16. { 17. return new UserRoleDAO(); 18. } 19. } 20.}Hasta Aquí su primera parte , en unos dias publícare la segunda parte , y despues la tercera parte.
Espero hasta aportado , si tienen dudas ,no duden en escribirme y trataré de responder en lo más rápido que me sea posible.
(*) Actualización:
Bueno

0 post:
Publicar un comentario en la entrada