Nhibernate y WCF – Parte 1
Buenas con todos,
En esta oportunidad compartiré una ultima experiencia personal y profesional al usar estas tecnologías en un mismo proyecto,
Previo: Para empezar con la demo , es necesario asumir varias cosas, dado que así se dio el escenario donde surgió el proyecto y se partirá de ese supuesto.
Sucede que imaginemos que tenemos una app web, en 3 capas bien definidas tales como: Capa de Datos , Capa de Negocios y Capa de Presentación, asumiendo que la app ya esta finalizada y que usa Nhibernate para su persistencia así como SQL Server como Gestor de Base de Datos y tiene una Capa de negocios "hecha en casa", Este ejemplo esta basado en este post y otro este post.
Entonces surge un nuevo requerimiento, exponer como servicios WCF los Métodos de Recuperación y ABM(CRUD) de mis entidades y también tomar como un requerimiento NO FUNCIONAL considerar un entorno de Transacciones Distribuidas, esto debido a que la misma base de datos y los mismos objetos dentro de ella (tablas, vistas, etc) pueden ser accedidas por otros sistemas.
y Empezamos!
Mi Escenario Actual:
IDE: Visual Studio 2008 Profesional
Persistencia: Nhibernate 2.1
Base de Datos: SQL Server 2005 Express Edition
Servicios: Windows Comunication Foundation
Test: Nunit Framework
La Idea en sí es reutilizar los métodos en la capa de negocios, es decir Los servicios WCF solo expondrán como servicios los métodos de mis objetos de negocios
Creando mis proyectos:
Creamos 2 Proyectos para los servicios WCF, el primero NHServices de Tipo Librería de Clases tendrá el código referente al servicio WCF , el Segundo NHServices.Host de tipo WCF Service Application tendrá referencia al Primero y nos servirá para la publicación del servicio, lo separo en 2 partes esto para poder hacer test unitarios.
En la imagen se ve la estructura de los proyectos, se ha separado en carpetas a las clases en función a su Responsabilidad.
Core: En esta carpeta se crearon las sgtes clases y/o interfaces
– IGenericService
– GenericService
– ServiceFactory
– FrameworkServiceFactory
A continuación describiré brevemente cada una de ellas
IGenericService:
01.[ServiceContract] 02. public interface IGenericService<T,ID> 03. { 04. [OperationContract] 05. T FindByid(Id id); 06. 07. [UseNetDataContractSerializer] 08. [OperationContract] 09. IList<T> FindAll(); 10. 11. [UseNetDataContractSerializer] 12. [OperationContract] 13. IList<T> FindAllOrdered(IList<STRING> propertyNames); 14. 15. [UseNetDataContractSerializer] 16. [OperationContract] 17. IList<T> FindByExample(T entity); 18. 19. [UseNetDataContractSerializer] 20. [OperationContract] 21. IList<T> FindBydExampleOrdered(T entity, IList<STRING> propertyNames); 22. 23. [UseNetDataContractSerializer] 24. [OperationContract] 25. IList<T> FindLikeExample(T entity); 26. 27. [UseNetDataContractSerializer] 28. [OperationContract] 29. IList<T> FindLikeExampleOrdered(T entity, IList<STRING> propertyNames); 30. 31. [UseNetDataContractSerializer] 32. [OperationContract] 33. IList<T> FindLikeExampleIgnoreCase(T entity); 34. 35. [UseNetDataContractSerializer] 36. [OperationContract] 37. IList<T> FindLikeExampleIgnoreCaseOrdered(T entity, IList<STRING> propertyNames); 38. 39. [UseNetDataContractSerializer] 40. [OperationContract] 41. IList<T> FindByQuery(string query); 42. 43. [UseNetDataContractSerializer] 44. [OperationContract] 45. IList<T> FindByQueryWithParameters(string query, string[] parameters, object[] values); 46. 47. [UseNetDataContractSerializer] 48. [OperationContract] 49. IList<T> FindByNamedQuery(string query); 50. 51. [UseNetDataContractSerializer] 52. [OperationContract] 53. IList<T> FindByNamedQueryWithParameters(string query, string[] parameters, object[] values); 54. 55. [UseNetDataContractSerializer] 56. [OperationContract] 57. IList FindObjectsByQuery(string query); 58. 59. [UseNetDataContractSerializer] 60. [OperationContract] 61. IList FindObjectsByNamedQuery(string query); 62. 63. [UseNetDataContractSerializer] 64. [OperationContract] 65. IList FindObjectsByNamedQueryWithParameters(string query, string[] parameteres, object[] values); 66. 67. [UseNetDataContractSerializer] 68. [OperationContract] 69. IList FindObjectsBySQLQuery(string query, string[] parameters, object[] values); 70. 71. [UseNetDataContractSerializer] 72. [OperationContract] 73. [TransactionFlow(TransactionFlowOption.Allowed)] 74. T Save(T entity); 75. 76. [UseNetDataContractSerializer] 77. [OperationContract] 78. [TransactionFlow(TransactionFlowOption.Allowed)] 79. T Update(T entity); 80. 81. [UseNetDataContractSerializer] 82. [OperationContract] 83. [TransactionFlow(TransactionFlowOption.Allowed)] 84. int DeleteById(Id id); 85. 86. [UseNetDataContractSerializer] 87. [OperationContract] 88. [TransactionFlow(TransactionFlowOption.Allowed)] 89. int Delete(T entity); 90. 91. [UseNetDataContractSerializer] 92. [OperationContract] 93. Id GetMax(string propertyName); 94. }IGenericService:
Esta interfaz Genérica será implementada por todos los servicios que la requieran, es la interfaz de Servicio[ServiceContract] principal, expone los métodos de obtención , filtrado, búsqueda y ABM del servicio que lo implementa.
Para tener presente: Aunque al compilar me permite sobrecargar un método de "Operación de Servicio" (así los llamó yo) ó Contratos de Operación "[OperationContract]" WCF no te permite la sobrecarga de estos métodos, en cambio si me permite la sobre-escritura.
Hasta aquí todo normal, excepto por el atributo: [UseNetDataContractSerializer]
Para que me sirve [UseNetDataContractSerializer] ?
La implementación de [UseNetDataContractSerializer] es de Davy Brion’s tome su ejemplo y lo modifique de acuerdo a mis necesidades
, como sabemos al usar WCF los objetos ó tipos que enviamos ó recibamos por un servicio tienen que tener el atributo [DataContract] para las clases y [DataMember] para sus propiedades, esto vuelve a la cada clase y/o propiedades serializable para WCF , nuestras entidades no tienen dicho atributo por lo tanto las propiedades no son serializadas para WCF, agregando dicho atributo en cada de Método de Operación nos aseguramos de serializar las entidades de cada método que recibe ó devuelve nuestras entidades.
GenericService:
Esta clase Genérica implementa la interfaz IGenericService, si recordamos lo mismo se hizo este post y otro este post. implementando el patrón Abstract Factory , así logramos un Bajo Acoplamiento entre capas de nuestra aplicación.
También podemos observar los Atributos “Transaccionales” del espacio de nombres “System.Transaccions” en los metodos de ABM, esto para asegurar la transaccionabilidad de nuestros servicios.
001.public abstract class GenericService<T,ID> : IGenericService<T,ID> 002. { 003. private BussinesFactory bussinesFactory = BussinesFactory.Instance(BussinesFactory.BUSSINESS_FACTORY); 004. 005. public BussinesFactory BussinesFactory 006. { 007. get { return bussinesFactory; } 008. set { bussinesFactory = value; } 009. } 010. 011. public IGenericController <T,> genericService { set; get; } 012. 013. 014. public T FindByid(Id id) 015. { 016. return genericService.FindByid(id); 017. } 018. 019. public IList<T> FindAll() 020. { 021. return genericService.FindAll(); 022. } 023. 024. public IList<T> FindAllOrdered(IList<STRING> propertyNames) 025. { 026. return genericService.FindAllOrdered(propertyNames); 027. } 028. 029. public IList<T> FindByExample(T entity) 030. { 031. return genericService.FindByExample(entity); 032. } 033. 034. public IList<T> FindBydExampleOrdered(T entity, IList<STRING> propertyNames) 035. { 036. return genericService.FindBydExampleOrdered(entity, propertyNames); 037. } 038. 039. public IList<T> FindLikeExample(T entity) 040. { 041. return genericService.FindLikeExample(entity); 042. } 043. 044. public IList<T> FindLikeExampleOrdered(T entity, IList<STRING> propertyNames) 045. { 046. return genericService.FindLikeExampleOrdered(entity, propertyNames); 047. } 048. 049. public IList<T> FindLikeExampleIgnoreCase(T entity) 050. { 051. return genericService.FindLikeExampleIgnoreCase(entity); 052. } 053. 054. public IList<T> FindLikeExampleIgnoreCaseOrdered(T entity, IList<STRING> propertyNames) 055. { 056. return genericService.FindLikeExampleIgnoreCaseOrdered(entity, propertyNames); 057. } 058. 059. public IList<T> FindByQuery(string query) 060. { 061. return genericService.FindByQuery(query); 062. } 063. 064. public IList<T> FindByQueryWithParameters(string query, string[] parameters, object[] values) 065. { 066. return genericService.FindByQuery(query, parameters, values); 067. } 068. 069. public IList<T> FindByNamedQuery(string query) 070. { 071. return genericService.FindByNamedQuery(query); 072. } 073. 074. public IList<T> FindByNamedQueryWithParameters(string query, string[] parameters, object[] values) 075. { 076. return genericService.FindByNamedQuery(query, parameters, values); 077. } 078. 079. public IList FindObjectsByQuery(string query) 080. { 081. return genericService.FindObjectsByQuery(query); 082. } 083. 084. public IList FindObjectsByNamedQuery(string query) 085. { 086. return genericService.FindObjectsByNamedQuery(query); 087. } 088. 089. public IList FindObjectsByNamedQueryWithParameters(string query, string[] parameteres, object[] values) 090. { 091. return genericService.FindObjectsByNamedQuery(query, parameteres, values); 092. } 093. 094. public IList FindObjectsBySQLQuery(string query, string[] parameters, object[] values) 095. { 096. return genericService.FindObjectsBySQLQuery(query, parameters, values); 097. } 098. 099. [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] 100. public T Save(T entity) 101. { 102. return genericService.Save(entity); 103. } 104. 105. [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] 106. public T Update(T entity) 107. { 108. return genericService.Update(entity); 109. } 110. 111. [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] 112. public int DeleteById(Id id) 113. { 114. try115. { 116. genericService.DeleteById(id); 117. return 1; 118. } 119. catch(Exception exception) 120. { 121. return 0; 122. } 123. 124. } 125. 126. [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)] 127. public int Delete(T entity) 128. { 129. try130. { 131. genericService.Delete(entity); 132. return 1; 133. } 134. catch (Exception) 135. { 136. return 0; 137. } 138. } 139. 140. public Id GetMax(string propertyName) 141. { 142. return genericService.GetMax(propertyName); 143. } 144. 145. } ServiceFactory: Clase Abstracta que expone métodos abstractos que serán implementado por la Clase “FramewokServiceFactory” similar al DaoFactory y el BussinessFactory , son estas clases las que nos permiten mantener un bajo acoplamiento entre nuestras capas.
01.public abstract class ServiceFactory 02. { 03. public static Type SERVICE_FACTORY = typeof (FrameworkServiceFactory); 04. public static ServiceFactory Instance(Type type) 05. { 06. try07. { 08. return (ServiceFactory) Activator.CreateInstance(type); 09. } 10. catch (Exception) 11. { 12. throw new Exception("No se puede crear Servicio: " + type); 13. } 14. } 15. 16. public abstract IUserService GetUserService(); 17. public abstract IRoleService GetRoleService(); 18. public abstract IUserRoleService GetUserRoleService(); 19. }FrameworkServiceFactory: Esta Clase hereda de la Clase abstracta ServiceFactory y sobre-escribe sus métodos devolviendo una instancia de un objeto en cada según Servicio.
01.public class FrameworkServiceFactory : ServiceFactory 02. { 03. public override IUserService GetUserService() 04. { 05. return new UserService(); 06. } 07. public override IRoleService GetRoleService() 08. { 09. return new RoleService(); 10. } 11. public override IUserRoleService GetUserRoleService() 12. { 13. return new UserRoleService(); 14. } 15. }Implementando nuestro Primer Servicio:
Contrato:
1.[ServiceContract] 2. [NhibernateContext] 3. public interface IUserService : IGenericService<USERENTITY,INT> 4. { 5. }Aquí como se observa el atributo “NhibernateContext” no pertenece a los atributos propios de WCF , la explicación de este y su implementación estará pendiente para la Segunda Parte.
Servicio:
1.[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,TransactionIsolationLevel = System.Transactions.IsolationLevel.Serializable)] 2.public class UserService : GenericService<USERENTITY,INT>, IUserService 3.{ 4. public UserService() 5. { 6. genericService = BussinesFactory.GetUserController(); 7. } 8.}Exponiendo nuestro Primer Servicio: En nuestro proyecto NhServices.Host, creando un nuevo archivo de Servicio WCF con el nombre de UserService, le eliminamos el code behind “svc.cs” y el Contrato (Interfaz) “.cs”
1.<%@ ServiceHost Language="C#" Debug="true" Service="NHServices.Services.UserService" %>Configuración de los Servicios: la realizamos en el Web.Config del Proyecto WCF NHServices.Host
El Servicio Expuesto y corriendo:
Consumiendo nuestro Servicio WCF:
Como sabemos existen varias formas que un cliente Consuma nuestro servicio WCF ,particularmente yo defino 3: La Clásica forma con el Wizard de VS –>Agregar Referencia a Servicio ; usando la herramienta svcutil.exe que es un generador de código que nos provee una Clase Cliente y el archivo de configuración del cliente ó Creando un Proxy del Servicio atreves de la Clase ChannelFactory del Espacio de Nombre “System.ServiceModel”, yo elegí la tercera por ser la más sencilla y práctica.
App.Config: La Configuración de nuestro cliente , en nuesto caso Windows la hacemos en el Archivo de Configuración de la Aplicación Windows “App.Config”
Creando nuestro proxy y obteniendo datos:
1.var factory = new ChannelFactory<IUSERSERVICE>("UserEndPoint"); 2.var service = factory.CreateChannel(); 3.var users = service.FindAll(); 4.dataGridView1.DataSource = users;
Hay aún muchas cosas que se quedan pendientes y espero publicarlos en una 2da y 3era parte, El Código fuentes para su descarga lo encuentran en CodePlex
Un saludo desde Lima , Perú.
About this entry
You’re currently reading “Nhibernate y WCF – Parte 1,” an entry on Jose Fabricio Rojas – Desarrollo ALTer.NETivo
- Published:
- 15 Octubre 2009 / 1:44 AM
- Category:
- .NET, ALT.NET, ASP.NET, NHibernate, SQL Server 2005
- Tags:
- .NET, ALT.NET, ASP.NET, NHibernate, SQL Server 2005
No comments yet
Jump to comment form | comments rss [?] | trackback uri [?]