Tecnocrata

Charla de Paralelismo con ASP.NET MVC

El Dia Jueves 26 de Enero tuve el placer de compartir con los miembros del CUTMS aqui en Cochabamba, una charla acerca de las ventajas de usar paralelismo con ASP.NET MVC.

Aqui les dejo algunas fotografias:

WP_000016 WP_000013
   

Aqui pueden descargarse el codigo de demostracion que utilice en esa presentacion:

https://skydrive.live.com/redir.aspx?cid=e46b27f9dbbd40a1&resid=E46B27F9DBBD40A1!429&parid=E46B27F9DBBD40A1!379&authkey=!ADcs8NahlPigLD0

Un abrazo.

Mejorando la experiencia en ASP.NET MVC (II)

Continuando con la serie de articulos, en esta ocacion les muestro como mejorar el codigo del anterior post, esta vez si aprovechando las caracteristicas de paralelismos y asincronismo de ASP.NET MVC.

En esta ocacion lo que voy a mostra es mismo codigo que accede a Rss Feeds, pero por alguna “extrana” razon voy a necesita consultar varios Rss, por lo tanto tenemos en nuestras manos un conjunto de procesos de alta duracion y que probablemente bloquearan tanto la interface de usuario como las peticiones al servidor web.

El codigo normal que escribiriamos es el siguiente:

1 public class Normal1Controller : Controller 2 { 3 public ActionResult Index2() 4 { 5 RssFeed feed = new RssFeed(); 6 IEnumerable<SyndicationItem> geeks = 7 feed.GetRssFeed("http://geeks.ms/blogs/MainFeed.aspx"); 8 9 IEnumerable<SyndicationItem> betters = 10 feed.GetRssFeed("http://feeds.feedburner.com/CodeBetter"); 11 12 IEnumerable<SyndicationItem> items = geeks.Concat(betters); 13 14 return View(items); 15 } 16 17 }

El codigo que mejora y aprovecha las caracteristicas asincronas del Framework 4.0, es el siguiente:

1 public class Parallel1Controller : AsyncController 2 { 3 public void Index2Async() 4 { 5 AsyncManager.OutstandingOperations.Increment(2); 6 7 RssFeed geekfeed = new RssFeed(); 8 geekfeed.GetRssFeedAsyncCompleted += (s, e) => 9 { 10 AsyncManager.Parameters["geeks"] = e.Items; 11 AsyncManager.OutstandingOperations.Decrement(); 12 }; 13 geekfeed.GetRssFeedAsync("http://geeks.ms/blogs/MainFeed.aspx"); 14 15 RssFeed betterfeed = new RssFeed(); 16 betterfeed.GetRssFeedAsyncCompleted += (s, e) => 17 { 18 AsyncManager.Parameters["betters"] = e.Items; 19 AsyncManager.OutstandingOperations.Decrement(); 20 }; 21 betterfeed.GetRssFeedAsync("http://feeds.feedburner.com/CodeBetter"); 22 } 23 24 public ActionResult Index2Completed(IEnumerable<SyndicationItem> geeks, IEnumerable<SyndicationItem> betters) 25 { 26 return View(geeks.Concat(betters)); 27 } 28 29 }

Es interesante observar como este codigo tan simple puede brindarnos mejores resultados de rendimiento.

Espero que les sea util, un abrazo.

Posted: 22/1/2012 21:30 por Enrique Ortuño | con 1 comment(s)
Archivado en: ,
Mejorando la experiencia en ASP.NET MVC (I)

Este es el primer articulo de una serie de 3 articulos, que pretenden mostrar algunos de mis experimentos y resultados con ASP.NET, tocando fundamentalmente la experiencia de respuesta al usuario. Como Uds saben no hay nada peor que un sitio/pagina que tarde demasiado en realizar una tarea y mientras mas rapido se complete la tarea mucho mejor y si la tarea involucra procesos que no podemos controlar pues hay algunas tecnicas que si podemos aplicar, una de ellas es motivo de este primer post de la serie.

Utilizando controladores asincronos (AsynController)

Vamos a pensar en una aplicacion de ejemplo irrisoriamente simple, lo unico que hara es recuperar de internet una lista de todos los post de un blog mediante RSS, para ello normalmente utilizariamos codigo como el siguiente:

1 public class RssFeed 2 { 3 public event EventHandler<RssEventArgs> GetRssFeedAsyncCompleted; 4 5 // Synchronous model methods 6 public IEnumerable<SyndicationItem> GetRssFeed(string uri) 7 { 8 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 9 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 10 11 using (XmlReader reader = XmlReader.Create(response.GetResponseStream())) 12 { 13 SyndicationFeed feed = SyndicationFeed.Load(reader); 14 return feed.Items; 15 } 16 } 17 18 // Asynchronous model methods 19 public void GetRssFeedAsync(string uri) 20 { 21 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); 22 request.BeginGetResponse(new AsyncCallback(OnGetRssFeedAsyncCompleted), request); 23 } 24 25 private void OnGetRssFeedAsyncCompleted(IAsyncResult result) 26 { 27 HttpWebRequest request = (HttpWebRequest)result.AsyncState; 28 HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result); 29 30 using (XmlReader reader = XmlReader.Create(response.GetResponseStream())) 31 { 32 SyndicationFeed feed = SyndicationFeed.Load(reader); 33 if (GetRssFeedAsyncCompleted != null) 34 GetRssFeedAsyncCompleted(this, new RssEventArgs { Items = feed.Items }); 35 } 36 } 37 }

 

1 public class NormalController : Controller 2 { 3 public ActionResult Index() 4 { 5 Stopwatch clock = new Stopwatch(); 6 clock.Start(); 7 RssFeed feed = new RssFeed(); 8 IEnumerable<SyndicationItem> items = 9 feed.GetRssFeed("http://geeks.ms/blogs/MainFeed.aspx"); 10 clock.Stop(); 11 TimeMeasure result = new TimeMeasure(); 12 result.TimeElapsed = clock.ElapsedMilliseconds; 13 result.Data = items; 14 return View(result); 15 } 16 17 }

Por favor omitan el codigo de medicion del tiempo relacionado a la clase Stopwatch, en el codigo anterior la linea 9 es la que realiza la accion del cargado del Rss Feed. El resultado de ejecutar este controlador es un tiempo de carga aproximado de 14 segundos:

image

Ahora utilizando un Controlador Asincrono, se necesita el siguiente codigo:

1 public class ParallelController : AsyncController 2 { 3 public void IndexAsync() 4 { 5 Stopwatch clock = new Stopwatch(); 6 clock.Start(); 7 8 AsyncManager.OutstandingOperations.Increment(); 9 10 RssFeed feed = new RssFeed(); 11 feed.GetRssFeedAsyncCompleted += (s, e) => 12 { 13 AsyncManager.Parameters["items"] = e.Items; 14 AsyncManager.Parameters["clock"] = clock; 15 AsyncManager.OutstandingOperations.Decrement(); 16 }; 17 feed.GetRssFeedAsync("http://geeks.ms/blogs/MainFeed.aspx"); 18 } 19 20 public ActionResult IndexCompleted(IEnumerable<SyndicationItem> items, Stopwatch clock) 21 { 22 //ViewData["SyndicationItems"] = items; 23 clock.Stop(); 24 TimeMeasure result = new TimeMeasure(); 25 result.TimeElapsed = clock.ElapsedMilliseconds; 26 result.Data = items; 27 return View(result); 28 } 29 30 }

Como pueden observar en el codigo anterior hay algunos elementos que debemos destacar:

  • El Controlador ahora hereda de AsyncController
  • Existen dos metodos necesarios <Name>Async y su contraparte <Name>Completed, en nuestro caso son IndexAsync e IndexCompleted respectivamente.
  • El paso de parametros se lo realiza mediante una clase especifica llamada AsyncManager.
  • La invocacion simultanea de varios usuarios es controlada, mediante el incremento/decremento de un contador manipulado tambien por AsyncManager.

Finalmente el resultado obtenido es la reduccion del tiempo en 4 segundos, tal como se muestra a continuacion:

image

La reduccion no es significativa a simple vista pero para los que desarrollamos y tenemos que pensar en cuestiones de performance 4 segundos es una diferencia notable Smile Pero los AsyncControllers no han sido pensados para reducir velocidad per-se, probablemente aqui es uno de esos efectos colaterales bien deseados. La utilizacion mas importante de los AsyncControllers es evitar el bloqueo del Servidor Web, en la atencion de multiples peticiones concurrentes, esto traera repito el efecto colateral de que un servidor mas libre puede atender mas usuarios y por ende se siente mucho mas rapido.

Espero que esto les sirva a todos Uds, un abrazo y feliz 2012!!!

Posted: 19/1/2012 0:25 por Enrique Ortuño | con 3 comment(s)
Archivado en: ,
La podredumbre del Software, soluciona el problema!

Cada cierto tiempo me gusta volver, una y otra vez, a leer este interesante articulo, que describe la decadencia en la que estan o podrian estar algunos proyectos de software. Tampoco voy a mentir u ocultar que algun proyecto que paso por mis manos (y cayo en otras) ha llegado a “podrirse” irremediablemente. Pero si analizamos el articulo a un nivel mas general, a lo que se refiere es a la capacidad y cualidad de mantenibilidad (no se si esta palabra exista siquiera) que tiene un artefacto de software. En otras palabras una pieza de software tendera a podrirse mas rapidamente mientras menor sea su capacidad de ser mantenible

1756863567_52b429104f

Entonces. el debate que debemos enfocar en cualquier caso (creo yo), NO ES si el software debe podrirse o no, sino cuan RAPIDO debe podrirse. Porque indudablemente en algun momento ese software que tanto nos costo disenar e implementar, terminara por derrumbarse (y aqui debo hacer uso de la mala analogia con las construcciones civiles,) al igual que un edificio terminara por sucumbir a su deterioro/desgaste natural. Y los arquitectos de software, disenadores, programadores debemos asegurarnos que nuestras edificaciones sean resistentes a esa podredumbre inevitable, que trae consigo, nuestro querido amigo “el cambio”.

   
CONSTRUCCION_edificio-alambre

Nuestro software puede y debe ser resitente a factores de cambio “obvios”, es decir a aquellos factores que podemos controlar, como los que son descritos en el articulo referenciado, tales como la viscosidad, rigidez, fragilidad e inmovilidad. Por otra parte, los factores que no podemos controlar, son aquellos que produciran el deterioro “natural” de un proyecto de software. Entre los factores que esta fuera de nuestro control, puedo enumerar: Los cambios en el liderazgo, cambios de vision del proyecto, cambios tecnologicos, cambios en los recursos humanos, etc.

Yo puedo aceptar que un producto de software, se deteriore “o se vaya pudriendo” por aquellos cambios sobre los que yo no tengo control, pero no aceptare nunca que el mismo producto se derrumbe por aquellos factores en los que si pude hacer algo para evitar su caida.

   

Desde mi humilde punto de vista y como aporte a los articulos referenciados puedo decir que, muchos problemas de mantenibilidad de un producto de software se deben a la gran diferencia, entre Resolver un Problema y Solucionar un Problema.

wpa1255l

Aunque a primera vista ambas frases podrian parecer lo mismo, el concepto detras de “Resolver un problema” va ligado a un parche temporal que se aplica para corregir un problema reportado, en cambio el concepto de “Solucionar un problema” esta vinculado a un proceso mas prolongado de razonamiento e implementacion, para corregir el mismo problema. Mientras el que resuelve el problema ve solo el arbol y se avoca a eliminar de la lista de sus tareas ese incomodo elemento llamado bug, lo mas rapido posible y aplicando una correccion inmediatista, que tarde o temprano provocara o iniciara otro punto de deterioro. En su lugar el que soluciona el problema ve el bosque, toma su tiempo para analizar la implicancia de su correccion y elige la alternativa que brinde un balance entre la urgencia por solucionar el problema y la batalla interna por sostener una buena estructura futura que impida el inicio de un punto de deterioro.

   

Es por esto que en los equipos de desarrollo que he tenido el gusto de dirigir, mi sugerencia implicita o explicita en otros casos fue: En desarrollo de software, cuando encuentras un problema, por favor NO resuelvas el problema, SOLUCIONA el problema!

La podredumbre del software, se puede retrasar aplicando soluciones a los problemas que vayan apareciendo y aunque estoy consciente que en algunos escenarios no es posible tomarse mucho tiempo para razonar una solucion, siempre es posible volver hacia atras y remover ese horrendo parche que introdujimos al resolver un problema. Smile

Saludos.

Posted: 26/11/2011 9:20 por Enrique Ortuño | con no comments
Archivado en:
Pasando objetos JSON a los Action Methods en MVC3

Hace un tiempo atras escribi un post relacionado a como evitar los postbacks haciendo uso de ajax y obviamente jquery (Articulo referenciado). De ese momento hace practicamente un anio y hoy con algo mas de experiencia vuelvo a analizar un tema similar.

Como pasar un objeto JSON a un Action Method?

Escenario del problema

En el controlador existe el siguiente metodo (Action Method):

image

 

 

 

 

La clase Person, ridiculamente simple, es como sigue:

image

Las partes mas importantes del codigo html son los 3 botones locos para las pruebas y los manejadores del evento click para cada boton:

image

La idea principal es que cuando se presione uno de los botones HTML, automaticamente se pasa desde javascript hacia el Action Method los objetos que esta esperando dicho metodo y uno de esos parametros es la clase Person que, vendra desde un objeto JSON

Soluciones posibles

Pareceria una tarea trivial, pero en MVC2 experimente dos soluciones:

  1. 1. El JsonBinder que propuse en el articulo mencionado.
  2. 2. Eduar Tomas critico correctamente el uso de un model binder y propuso usar un Value Provider, solucion perfecta para mis necesidades

El codigo para usar el model Binder o el Value Provider es:

image

image

Solucion definitiva

Por la necesidad de migrar mi aplicacion hacia MVC3 me vi en la obligacion de volver a analizar esta solucion y vi que MVC3 ya traer un value provider por lo que la solucion es bastante simple y es la siguiente:

image

image

Lo mas importante a destacar de la solucion es la utilizacion del atributo contentType y de que todos los parametros se colocan en un unico objeto JSON y luego son sometidos al JSON.stringify.

Espero que les resulte util, tambien les dejo adjunta la solucion que utilice para que puedan hacer sus propias comprobaciones.

Abrazos

Posted: 10/11/2011 0:07 por Enrique Ortuño | con no comments
Archivado en: ,,
Internet Explorer o Chrome cambiamos de navegador?

Con todas esas nuevas y excitantes noticias sobre Windows 8, VS 11 e IE10 y otras tantas cosas que trajo el BUILD, yo me pregunto Microsoft esta vez nos escuchara a los desarrolladores y a los usuarios en general, sobre las deficiencias que observamos aun en Internet Explorer. Mis amigos al leer este post tambien se sorprenderan y quien sabe quiza esto es una secuela de los 39 grados de temperatura que tuve ayer, unidos a un episodio de delirios concientes Smile, pero no!!! no solo es eso Smile es ya varios anios de reiterar los puntos que voy a mencionar y que no han sido escuchados por Microsoft, entonces que mejor momento que ahora antes que salga el tan esperado IE10.

Internet Explorer no alcanza muchos sitios y Chrome si.

Esto por muy sorprendente que les parezca a algunos lectores de paises desarrollados es totalmente valido en un pais tercer mundista como el mio donde dificilmente internet es comun denominador y donde las velocidades comercializadas no sobrepasan el 1 Mbps (para los simples mortales) Si uno utiliza IE y lamento decirlo, en muchas ocaciones obtiene

image

Mientras que con Chrome por mas velocidad lenta que uno tenga siempre y repito SIEMPRE obtiene la pagina, tal como aqui:

image

Podriamos echarle la culpa a mil motivos, provedor local, proveedor de la pagina, velocidad en el momento, hora pico, etc etc etc, los que se imaginen, pero bajo toda circunstancia y durante bastante tiempo que vengo probando Chrome (tenia que hacerlo jajaja) y cada vez veo el mismo comportamiento en diferentes parte IE falla, Chrome no. Por que?

Parece que el motor de renderizacion de Chrome sigue y sigue intentando recuperar la pagina, cuando el de IE se agota a los primeros intentos.

Ahora yo me imagino que pasa en paises con altas velocidades de internet? obviamente a todas luces Chrome mantendra mas aun ea ventaja.

Hacer addins o plugins para IE es frustrante y no hay muchos.

Esta paso a ser mi segunda mayor critica, por que hasta ahora es tan complicado hacer un addin para IE, en comparacion con los miles que hay para Firefox y el creciente numero (asombroso) de Chrome?

Que tan complicado es generar una buena documentacion, clara, precisa y que guie paso a paso en esta tarea?

Que tan complicado puede ser crear una API de programacion en C# (perdonen si estoy equivocado, porque hasta ahora no la vi) o bueno minimamente en Javascript?

 

Las caracteristicas de IE respecto a aprovechamiento de tarjetas graficas, renderizacion, inclusion de HTML5 y CSS3 y otras tantas cosas que se vienen en IE10 adicionalmente, pueden quedar opacadas para usuarios frustrados con la eficacia del navegador y claro luego algunos se preguntan sorprendidos el porque del crecimiento de Chrome.

No me malinterpreten, no he venido a hacerle propaganda gratuita a Chrome o Firefox, aun soy un fiel usuario de IE y Bing jajaja Pero, si Microsoft sigue sin escuchar a estas voces pronto Internet Explorer podria ser historia, por muy HTML5 o Metro que tenga. IMHO

La respuesta a la pregunta del titulo de este post es: No, todavia, aun tengo fe que Microsoft escuchara.

Alo? hay alguien ahi?….. si por favor le pasas la voz a Microsoft? Gracias.

Saludos.

Posted: 18/9/2011 1:01 por Enrique Ortuño | con 2 comment(s) |
Archivado en: ,,
Explorando NHibernate 3.0 (II)

Practicamente 6 meses despues del primer post de esta serie, me animo a escribir una nueva entrada y es que NHibernate despierta mis “amores” por lo simple de configurar que es Smile, mas alla del sarcaso, empecemos entonces: Como esta es una serie de entradas relacionadas a NHibernate, aun continuo trabajando sobre el mismo proyecto que tenia anteriormente, con las 2 entidades anteriores y una adicional “Course” tal cual se muestra en el diagrama siguiente:

image

En esta ocacion planeo explorar las diferentes desafios opciones de borrado en cascada que tiene. La idea general es que yo deseo borrar un estudiante y por lo tanto al borrar el estudiante debo borrar sus inscripciones a los diferentes cursos que hay, esto en otras palabras significa eliminar un registro de la tabla Students y muchos registros de la tabla StudentCourse, como es “logico” no debo borrar en cascada ningun registro de la tabla Courses.

Save-Update

La primera aproximacion que tome es tener los archivos de mapeo de la siguiente manera:

1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="Student" table="Students"> 4 <id name="StudentId" column="StudentId"> 5 <generator class="guid" /> 6 </id> 7 <property name="StudentName" column="StudentName" /> 8 <bag name="StudentCourses" cascade="save-update" > 9 <key column="StudentId" not-null="false" /> 10 <one-to-many class="StudentCourse" /> 11 </bag> 12 </class> 13 </hibernate-mapping>
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="StudentCourse" table="StudentCourse"> 4 <id name="StudentCourseId" column="StudentCourseId"> 5 <generator class="guid" /> 6 </id> 7 <property name="RegistrationDate" column="RegistrationDate" type="DateTime" /> 8 <many-to-one name="Student" not-null="false" class="Student" cascade="save-update"> 9 <column name="StudentId" not-null="false" /> 10 </many-to-one> 11 <many-to-one name="Course" not-null="true" class="Course" cascade="save-update"> 12 <column name="CourseId" /> 13 </many-to-one> 14 </class> 15 </hibernate-mapping>
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="Course" table="Courses"> 4 <id name="CourseId" column="CourseId"> 5 <generator class="guid" /> 6 </id> 7 <property name="CourseName" column="CourseName" /> 8 <bag name="StudentCourses" inverse="true" cascade="save-update"> 9 <key column="CourseId" /> 10 <one-to-many class="StudentCourse" /> 11 </bag> 12 </class> 13 </hibernate-mapping>

Las configuraciones mas importantes en estos archivos de mapeo son:

1. La definicion de cascade=”save-update” que define que solamente se hara el control del save y de los updates, pero el borrado se lo deja al comportamiento por defecto que tiene NHibernate. Cual es ese comportamiento por defecto? Es colocar o tratar de colocar en NULL todas las llaves foraneas de la tabla StudentCourse, que pertenecen al registro de student que se esta borrando.

2. Otro elemento importante es que no se especifica inverse=”true” en la relacion one-to-many.

1 [TestMethod] 2 public void DeletingParentChildElements() 3 { 4 Execute((context, tx) => 5 { 6 var st = (from s in context.Students 7 select s).FirstOrDefault(); 8 Assert.IsNotNull(st); 9 Session.Delete(st); 10 tx.Commit(); 11 }); 12 }

Al ejecutar la prueba unitaria obtenemos el siguiente log de sentencias SQL que NHibernate ejecuto:

1 NHibernate: select TOP (@p0) student0_.StudentId as StudentId1_, student0_.StudentName as StudentN2_1_ from Students student0_;@p0 = 1 [Type: Int32 (0)] 2 NHibernate: UPDATE StudentCourse SET StudentId = null WHERE StudentId = @p0;@p0 = 812c5ef7-0f1a-4f84-a383-002586213d35 [Type: Guid (0)] 3 NHibernate: DELETE FROM Students WHERE StudentId = @p0;@p0 = 812c5ef7-0f1a-4f84-a383-002586213d35 [Type: Guid (0)]

Observen lo interesante de esta tecnica, es que se han generado dos sentencias para poder borrar el registro padre y sus dependientes, aunque en realidad no se borraron los registros de la tabla hija, sino que estos quedaron con el valor NULL en su llave foranea, en terminos de NHibernate, estos registros quedaron huerfanos. En la captura de pantalla siguiente se ve solo un registro huerfano, pero les aseguro que hay muchos mas.

image

all-delete-orphan

Como segunda opcion he analizado una forma de no dejar esos registros huerfanos en la base de datos. En los foros y documentacion la configuracion de mapeo recomendada es como sigue:

1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="Student" table="Students"> 4 <id name="StudentId" column="StudentId"> 5 <generator class="guid" /> 6 </id> 7 <property name="StudentName" column="StudentName" /> 8 <bag name="StudentCourses" inverse="true" cascade="all-delete-orphan" > 9 <key column="StudentId" not-null="false" /> 10 <one-to-many class="StudentCourse" /> 11 </bag> 12 </class> 13 </hibernate-mapping>
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="StudentCourse" table="StudentCourse"> 4 <id name="StudentCourseId" column="StudentCourseId"> 5 <generator class="guid" /> 6 </id> 7 <property name="RegistrationDate" column="RegistrationDate" type="DateTime" /> 8 <many-to-one name="Student" class="Student" cascade="save-update"> 9 <column name="StudentId" /> 10 </many-to-one> 11 <many-to-one name="Course" not-null="true" class="Course" cascade="save-update"> 12 <column name="CourseId" /> 13 </many-to-one> 14 </class> 15 </hibernate-mapping>

En el anterior archivo de mapeo los elementos a los que deben prestar atencion son: la definicion de cascade=”all-delete-orphan” y al elemento que define la llave foranea como not-null=”false”. El primer elemento borrara los registros huerfanos y el segundo sirve para especificar que la llave foranea permitira valores null.

Lo que se optiene como resultado luego de ejecutar la peticion de borrado, es sorprendentemente lo siguiente:

1 NHibernate: select TOP (@p0) student0_.StudentId as StudentId1_, student0_.StudentName as StudentN2_1_ from Students student0_;@p0 = 1 [Type: Int32 (0)] 2 NHibernate: SELECT studentcou0_.StudentId as StudentId1_, studentcou0_.StudentCourseId as StudentC1_1_, studentcou0_.StudentCourseId as StudentC1_2_0_, studentcou0_.RegistrationDate as Registra2_2_0_, studentcou0_.StudentId as StudentId2_0_, studentcou0_.CourseId as CourseId2_0_ FROM StudentCourse studentcou0_ WHERE studentcou0_.StudentId=@p0;@p0 = 62b4407e-1025-4ae2-bd60-0178fcefe0c2 [Type: Guid (0)] 3 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = afe0f28e-0f68-4799-849b-018eb3d61780 [Type: Guid (0)] 4 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 24a8d202-0fc0-4895-92f6-0279715c894b [Type: Guid (0)] 5 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 81b06673-2b99-439f-bc92-0954f1f6a72d [Type: Guid (0)] 6 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 3a8101c1-fb86-4569-b62f-1a4a5f61e076 [Type: Guid (0)] 7 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = e23ead18-d36d-48f0-91b5-1e0f218a9490 [Type: Guid (0)] 8 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 55b89be1-d753-4d13-9b73-2f98e65d94a4 [Type: Guid (0)] 9 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = ad56599b-afd5-42c1-94f2-36d82186e99c [Type: Guid (0)] 10 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 8b0010d5-a3ff-4951-b908-377ac3c8b228 [Type: Guid (0)] 11 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 50f3306e-a376-4662-85a0-3b4dd021313d [Type: Guid (0)] 12 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = c5c4ad94-d764-4fc5-868f-3e90bc849223 [Type: Guid (0)] 13 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = c2835509-49b2-47ae-93f6-3fa2690de749 [Type: Guid (0)] 14 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = d97f98e8-17e2-4cb0-9472-4cd1d18291d4 [Type: Guid (0)] 15 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 45c7abf5-fdfd-4085-862f-4e1221d53cc7 [Type: Guid (0)] 16 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 0cff1150-12cf-4e7e-8bbd-4ff363295506 [Type: Guid (0)] 17 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 617af7f4-0b4b-4fbc-8f73-53bc0d72c0a7 [Type: Guid (0)] 18 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 4fe5434d-6ccf-4850-9ee2-5769812893ee [Type: Guid (0)] 19 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 326791ac-ff46-4859-b875-5bb760597dd6 [Type: Guid (0)] 20 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = a1fb2063-b779-49d0-b520-5e8c1379a4bc [Type: Guid (0)] 21 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = fe9273dd-c0b7-4c5c-8e0e-6252bd24ec01 [Type: Guid (0)] 22 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = ce4e980a-d5e5-45f9-bf48-63b30b1cdb2a [Type: Guid (0)] 23 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = b50dc1dd-7280-479b-a0d0-63dd305d27f3 [Type: Guid (0)] 24 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = cb63acdd-999f-4f89-9de9-67d213cbcb91 [Type: Guid (0)] 25 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = c4c84f6b-a1df-4468-a5c0-71e54da6f32e [Type: Guid (0)] 26 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = bc9f7bbb-6197-4324-a6e1-76eaa044b0fd [Type: Guid (0)] 27 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 48ccb3c5-95f2-4efd-9907-85a551672dec [Type: Guid (0)] 28 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 254cd9ae-179d-4ab7-9147-8d2da2fe50de [Type: Guid (0)] 29 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 370a76db-01e2-4e63-8010-8fdf232066c0 [Type: Guid (0)] 30 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 539d2b31-ec71-474f-911b-915e174ad929 [Type: Guid (0)] 31 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 5bc0ff81-1517-4f28-91e5-91e482b6a8e1 [Type: Guid (0)] 32 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 037d8dfd-4f5a-437f-baf9-9fb852fe1af0 [Type: Guid (0)] 33 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 60f4a979-d5e8-4321-80ae-a02db446011b [Type: Guid (0)] 34 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 839d4d5d-8c37-4962-87e5-a25f048f4308 [Type: Guid (0)] 35 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 0f9f6b77-b181-403d-8c8f-a6f2d5da36d1 [Type: Guid (0)] 36 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 9d469ea0-b078-43d7-8377-b209c6755305 [Type: Guid (0)] 37 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 4ae8090a-d4b0-4022-bc69-b66c558c970a [Type: Guid (0)] 38 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = f066dc0b-12d8-41bf-aee9-be2e571bc80f [Type: Guid (0)] 39 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = a909893f-9395-4172-a327-bf3d3cf91465 [Type: Guid (0)] 40 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 592c002f-9ff2-4891-98e6-c221a6314884 [Type: Guid (0)] 41 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 7349388b-9443-4388-8591-c6bb6740fa8b [Type: Guid (0)] 42 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = d6e9da25-1353-4266-8a36-c6f40833fa5c [Type: Guid (0)] 43 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 877123a5-198b-4889-af38-c81adf0cb9c9 [Type: Guid (0)] 44 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = e188ff07-0022-43a5-a132-d03715ce52d3 [Type: Guid (0)] 45 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 315f4dfa-41be-47a5-8f1a-d9d08af64def [Type: Guid (0)] 46 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = b3c97332-3283-4662-8e76-da4c92a975c8 [Type: Guid (0)] 47 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 86e7cfdc-387e-4111-9f3b-dadbb2b72868 [Type: Guid (0)] 48 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = cf9618ab-6d57-4daf-be3a-dbe69a271853 [Type: Guid (0)] 49 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = d6494f0a-31ed-4a9c-a200-ec5883531d6c [Type: Guid (0)] 50 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 0f7c5562-0ce6-4e56-bdcb-f28effa44b89 [Type: Guid (0)] 51 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = ab76283b-d7c2-4ad6-a633-f369bf52a715 [Type: Guid (0)] 52 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 1248e956-3fc0-4cee-afc4-f861e8ac5bed [Type: Guid (0)] 53 NHibernate: DELETE FROM Students WHERE StudentId = @p0;@p0 = 62b4407e-1025-4ae2-bd60-0178fcefe0c2 [Type: Guid (0)]

Dije sorprendentemente lento, porque primero ha generado una sentencia select para recuperar todos los registros hijos (linea 2: imaginen si fuesen cientos de registros!!!) y luego itera sobre esa lista generando tambien multiples sentencias DELETE para los registros de esa tabla. Realmente ineficiente.

Las malas noticias del uso de all-delete-orphan no terminan ahi, si utilizamos una configuracion de mapeo como la siguiente:

1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="Student" table="Students"> 4 <id name="StudentId" column="StudentId"> 5 <generator class="guid" /> 6 </id> 7 <property name="StudentName" column="StudentName" /> 8 <bag name="StudentCourses" cascade="all-delete-orphan" > 9 <key column="StudentId" not-null="false" /> 10 <one-to-many class="StudentCourse" /> 11 </bag> 12 </class> 13 </hibernate-mapping>
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="StudentCourse" table="StudentCourse"> 4 <id name="StudentCourseId" column="StudentCourseId"> 5 <generator class="guid" /> 6 </id> 7 <property name="RegistrationDate" column="RegistrationDate" type="DateTime" /> 8 <many-to-one name="Student" class="Student" cascade="save-update"> 9 <column name="StudentId" /> 10 </many-to-one> 11 <many-to-one name="Course" not-null="true" class="Course" cascade="save-update"> 12 <column name="CourseId" /> 13 </many-to-one> 14 </class> 15 </hibernate-mapping>

El resultado es el siguiente:

1 NHibernate: select TOP (@p0) student0_.StudentId as StudentId1_, student0_.StudentName as StudentN2_1_ from Students student0_;@p0 = 1 [Type: Int32 (0)] 2 NHibernate: SELECT studentcou0_.StudentId as StudentId1_, studentcou0_.StudentCourseId as StudentC1_1_, studentcou0_.StudentCourseId as StudentC1_2_0_, studentcou0_.RegistrationDate as Registra2_2_0_, studentcou0_.StudentId as StudentId2_0_, studentcou0_.CourseId as CourseId2_0_ FROM StudentCourse studentcou0_ WHERE studentcou0_.StudentId=@p0;@p0 = 3df163e6-9e0c-4ea4-bbab-024d8a826478 [Type: Guid (0)] 3 NHibernate: UPDATE StudentCourse SET StudentId = null WHERE StudentId = @p0;@p0 = 3df163e6-9e0c-4ea4-bbab-024d8a826478 [Type: Guid (0)] 4 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = ed22628f-70fb-49da-8e9d-017cb8804153 [Type: Guid (0)] 5 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 823132a9-81dd-4d15-8bc5-0560002b616b [Type: Guid (0)] 6 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = df8a6f0d-e437-4033-adfe-1226b7e6ccc5 [Type: Guid (0)] 7 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = e4e217f2-6035-49b7-93ed-1625a33bebf2 [Type: Guid (0)] 8 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = ff4367c5-e113-408d-afe5-17cde78b098d [Type: Guid (0)] 9 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 76939286-24cb-446c-95a7-19c12c1cacc7 [Type: Guid (0)] 10 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 406e5eab-3ee3-4425-9d41-1c435916cab7 [Type: Guid (0)] 11 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 943ebddb-6ceb-4f53-a0a3-2df9514578ee [Type: Guid (0)] 12 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 48a95446-1b72-4e1a-aaca-311b74788f22 [Type: Guid (0)] 13 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = d5f4b24a-ac78-4538-aed1-3c48c41267ef [Type: Guid (0)] 14 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 4005baf4-fed6-4a6b-8970-3ec1cd8b6e6b [Type: Guid (0)] 15 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = bf5a3bec-4870-4765-a2c0-42f01ac649d9 [Type: Guid (0)] 16 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = c3a18b48-62bc-4aae-86fa-486f041271c2 [Type: Guid (0)] 17 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 87ebe587-f0bc-4ea4-84b0-48db75689bc9 [Type: Guid (0)] 18 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 90cf3e31-689e-45a8-a28e-4ba673dfd411 [Type: Guid (0)] 19 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 0e9ca64c-b451-4113-a445-4c6c060fe0f4 [Type: Guid (0)] 20 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 168b732b-de67-4a6c-9261-5318dae239ae [Type: Guid (0)] 21 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = dc2263c8-44d3-4aa0-9c7e-58d1e13f7764 [Type: Guid (0)] 22 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = b055aa2e-d8c2-4b9d-9c73-58f1aff63e3a [Type: Guid (0)] 23 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 20283918-7c19-48e4-acd1-5d7c680df30a [Type: Guid (0)] 24 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 4d62c9e8-e36e-4c6c-85ed-60dac8218b34 [Type: Guid (0)] 25 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = fec90de0-c22a-4cf1-b47c-63e55b0b34b4 [Type: Guid (0)] 26 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 59a946c9-6a99-4f82-bdc0-64202a9c2eb3 [Type: Guid (0)] 27 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 13c240f1-9808-47cf-ba10-653fb444991d [Type: Guid (0)] 28 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 34c13a9d-0394-4984-85e7-66e2ee9f8f55 [Type: Guid (0)] 29 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 59ac31ed-2636-49b4-a5e1-69f636f5dffa [Type: Guid (0)] 30 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = f44d99fb-3548-4bff-9df7-70c8dddccf9e [Type: Guid (0)] 31 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = b651ee73-b30b-4e0e-a86b-76d8158f408c [Type: Guid (0)] 32 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = c8ac848a-2a1a-4155-9b83-7ba18ed0ab40 [Type: Guid (0)] 33 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = a8a62220-e5d2-44f0-b7cd-7cbc54de1638 [Type: Guid (0)] 34 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 4604521f-484f-4bcc-bc75-80c27070e3e4 [Type: Guid (0)] 35 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 179b5622-66b1-4bf3-bbe6-820f682aad87 [Type: Guid (0)] 36 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = a76f36d2-9d5f-48f5-b9f4-8c1cdf05da7c [Type: Guid (0)] 37 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = db4600e1-3782-4986-8548-99e53fad2e7d [Type: Guid (0)] 38 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = e7e99834-5f65-4f85-9302-a4695ef45141 [Type: Guid (0)] 39 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 28b13fe5-1daf-448c-80f6-a6d8de81eedd [Type: Guid (0)] 40 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = ad44e417-af7c-4db8-9d65-a884b494e0c0 [Type: Guid (0)] 41 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 90815e58-8d09-43df-bee7-af0c484b4a9b [Type: Guid (0)] 42 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 03c92453-75f7-455e-8c7f-ba043c6e4960 [Type: Guid (0)] 43 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = db405dcc-33d6-457f-b534-c1128a6f1646 [Type: Guid (0)] 44 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = ebf07a1c-1de3-4b4c-a231-c2e8a59b0a7d [Type: Guid (0)] 45 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 19ade8a2-132e-42fa-a9e3-c513950ffd11 [Type: Guid (0)] 46 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = fb0eb4d3-aacd-457e-9c6c-cf09aadbf814 [Type: Guid (0)] 47 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = adcb700e-a3a9-4126-a2f2-d742559a1c4b [Type: Guid (0)] 48 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 57c21829-56bc-474a-80d5-e359d4f02a62 [Type: Guid (0)] 49 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = c1960cce-931e-4969-924e-ec295d1da75b [Type: Guid (0)] 50 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = b17205cf-f08f-4601-97fa-ee851e0d8ae9 [Type: Guid (0)] 51 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = eab5b833-a03d-4f72-a75e-eeb29fe7ae3c [Type: Guid (0)] 52 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = c362251d-ce69-4f2e-b716-f6c6a33f7ac0 [Type: Guid (0)] 53 NHibernate: DELETE FROM StudentCourse WHERE StudentCourseId = @p0;@p0 = 9e7e6b92-3115-4312-bbe1-fbac3b2c2574 [Type: Guid (0)] 54 NHibernate: DELETE FROM Students WHERE StudentId = @p0;@p0 = 3df163e6-9e0c-4ea4-bbab-024d8a826478 [Type: Guid (0)]

Lo unico diferente en estos mapeos es la definicion del inverse ="true", lo cual ha generado una sentencia UPDATE adicional que se observa en la linea 3. Ahora esto me hace pensar cuantas personas tendran un mapeo que funciona, borra, pero lo hace cada vez de manera ineficiente?. Con razon mi carino especial a NHibernate.

On Delete = “Cascade”

Finalmente la opcion que yo recomiendo utilizar, aunque cabe decir que en algunos foros no la aconsejan, es utilizar las caracteristicas de borrado en cascada de la base de datos. Esta opcion consiste en definir los archivos de mapeo como siguen:

1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="Student" table="Students"> 4 <id name="StudentId" column="StudentId"> 5 <generator class="guid" /> 6 </id> 7 <property name="StudentName" column="StudentName" /> 8 <bag name="StudentCourses" inverse="true" cascade="save-update" > 9 <key column="StudentId" not-null="false" on-delete="cascade" /> 10 <one-to-many class="StudentCourse" /> 11 </bag> 12 </class> 13 </hibernate-mapping>

En este archivo el punto mas importante es la definicion de on-delete=”cascade” y la especificacion de la relacion inversa con inverse=”true”, si esto ultimo no se coloca, al momento de utilizar la coleccion saldra una excepcion indicando que es necesario especificar este atributo.

A continuacion el resultado de la ejecucion de la peticion de borrado, en esta ocacion como se puede apreciar solo se tiene dos sentencias SQL generadas por NHibernate, una para recuperar el primer registro de la tabla estudiantes y la segunda sentencia para realizar el borrado fisico, no existe ninguna otra sentencia adicional y luego de que se produce el borrado no han quedado registros huerfanos.

1 NHibernate: select TOP (@p0) student0_.StudentId as StudentId1_, student0_.StudentName as StudentN2_1_ from Students student0_;@p0 = 1 [Type: Int32 (0)] 2 NHibernate: DELETE FROM Students WHERE StudentId = @p0;@p0 = 51aa39c1-3929-4970-8401-00e4cc1e20a6 [Type: Guid (0)]

Es necesario advertir que los temas de rendimiento (performance) al momento de borrar son tema de otro articulo, pero probablemente la opcion mas recomendable sigue siendo la tercera opcion, al menos por que no genera sentencias SQL explicitas y confia en la definicion de la base de datos.

Espero que estos “hallazgos” les sea de utilidad.

Saludos.

Archivos de mi presentacion de MVC3 Razor

Estimados amigos

Aqui comparto con Uds. las diapositivas y el codigo fuente de mi charla sobre ASP.NET Razor, que di el anterior Jueves 28 de Julio, en la Comunidad de Usuarios de Tecnologias Microsoft (CUTM)

Espero que le sea util.

Saludos

Como acelerar la construccion del NHibernate SessionFactory

Hola, voy a cubrirme un poco antes de que entren a leer completamente el articulo, lo que se logra con la tecnica que mostrare puede estar demas si utilizan algo como NHFluent o ConfORM.

Bueno, empecemos.

La idea de este articulo nace a raiz de un problema que tuve por los tiempos de carga de una aplicacion web construida con NHibernate 2.0, esta aplicacion tiene un conjunto considerable de entidades y fue disenada usando los clasicos archivos de mapeo (.hbm.xml). En este sentido antes de que la aplicacion entrase en su primer ciclo de produccion, percibimos que existian tiempos de carga demasiado elevados la primera vez que se navegaba en ella. Luego de examinar las posibles causas identifique que el mayor responsible era la construccion de SessionFactory y bueno era bastante comprensible por la cantidad de entidades que tenemos y leyendo los articulos que adjunto se puede ver que existe la misma preocupacion en varios lugares:

Me pregunto, sera esta una de las razones de la rapida adopcion de NHFluent? Creo que si.

En cualquier caso, continuando y resumiendo lo que encontraran en los articulos adjuntos, las soluciones que se plantean son:

  1. Crear un unico mega archivo de mapeo.
  2. Serializar y Deserializar la configuracion.
  3. Utilizar la configuracion de manera compilada, el caso de NHFluent o Loquacious o ConfORM.

El utilizar NHFluent no es desafiante a excepcion quiza de aprender una serie de convenciones para mapear por codigo, ahora hasta Entity Framework tiene cosas similares como Code-First. Pero la verdad aun no me nace ni las ganas ni el tiempo para convertir todos mis archivos .hbm.xml a codigo C#, prometo que lo hare y seguramente hare un post al menos para comentar que tal la construccion del SessionFactory, pero por ahora tengo que trabajar con lo que tengo.

He probado el tema de Serializar y Deserializar la configuracion pero mi mayor “pero” a esta tecnica es la escritura/lectura de disco en aplicaciones web, como entenderan en un entorno donde no se controla el hosting (comun en esta aplicacion) existen proveedores que no permiten la escritura directa a disco, entonces eso me desanimo practicamente de entrada.

La opcion que me quedaba y con la que me fue muy bien fue la de combinar (hacer un merge) de todos los archivos de mapeo. Pero se imaginaran que el hacerlo manualmente no suena optimo y mucho menos entretenido.

Aqui surge la solucion que se me ocurrio. Por que no hacer una plantilla T4 que haga la combinacion/merge por mi? y bueno aqui tienen el codigo de la misma.

1 <#@ template language="C#v3.5" debug="false" hostspecific="true" language="C#" #> 2 <#@ output extension=".hbm.xml" #> 3 <#@ assembly name="System.Core.dll" #> 4 <#@ import namespace="System" #> 5 <#@ import namespace="System.IO" #> 6 <#@ import namespace="System.Linq" #> 7 <#@ import namespace="System.Collections.Generic" #> 8 <#@ import namespace="System.Text.RegularExpressions" #> 9 <?xml version="1.0" encoding="utf-8" ?> 10 <# 11 var structure= GetMappingStructure(); 12 13 WriteLine(structure.StartTag); 14 foreach(var c in structure.MappingContent) 15 { 16 WriteLine(c.ClassContent); 17 } 18 foreach(var c in structure.MappingContent) 19 { 20 WriteLine(c.OqlContent); 21 } 22 foreach(var c in structure.MappingContent) 23 { 24 WriteLine(c.SqlContent); 25 } 26 WriteLine(structure.EndTag); 27 #> 28 29 <#+ 30 private MappingStructure GetMappingStructure() 31 { 32 var structure= new MappingStructure(); 33 string[] filePaths = Directory.GetFiles(Host.ResolvePath(""),"*.hbm.xml", SearchOption.AllDirectories); 34 var files = from fp in filePaths 35 where !fp.Contains("Unique.hbm.xml") 36 select fp; 37 var i=0; 38 structure.MappingContent= new List<MappingContent>(); 39 structure.EndTag="</hibernate-mapping>"; 40 foreach (var f in files) 41 { 42 var content=GetFileContent(f); 43 if (i==0) 44 structure.StartTag =GetMappingContent(content,"open"); 45 var inside=GetMappingContent(content,"cnt"); 46 var mapContent= new MappingContent(); 47 mapContent.ClassContent =GetClassContent(inside); 48 mapContent.OqlContent= GetOqlContent(inside); 49 mapContent.SqlContent=GetSqlContent(inside); 50 structure.MappingContent.Add(mapContent); 51 i++; 52 } 53 return structure; 54 } 55 56 public string GetFileContent(string fileName) 57 { 58 StreamReader streamReader = new StreamReader(fileName); 59 string text = streamReader.ReadToEnd(); 60 streamReader.Close(); 61 return text; 62 } 63 64 private string GetMappingContent(string fullContent, string groupName) 65 { 66 return GetContent(fullContent, groupName,"(?<open><hibernate-mapping.[^<>]*>)(?<cnt>.*)(?<close></hibernate-mapping>)"); 67 } 68 69 private string GetContent(string content, string groupName, string pattern) 70 { 71 string resultString = null; 72 try { 73 resultString = Regex.Match(content, pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Multiline).Groups[groupName].Value; 74 } catch (ArgumentException ex) { 75 // Bad practice but for this sample... 76 } 77 return resultString; 78 } 79 80 private string GetClassContent(string content) 81 { 82 return GetContent(content, "classes","(?<classes><class.*</class>)"); 83 } 84 85 private string GetOqlContent(string content) 86 { 87 return GetContent(content, "oqls","(?<oqls><query.*</query>)"); 88 } 89 90 private string GetSqlContent(string content) 91 { 92 return GetContent(content, "sqls","(?<sqls><sql-query.*</sql-query>)"); 93 } 94 95 private class MappingContent 96 { 97 public string ClassContent{get;set;} 98 public string OqlContent{get;set;} 99 public string SqlContent{get;set;} 100 } 101 102 private class MappingStructure 103 { 104 public string StartTag{get;set;} 105 public string EndTag{get;set;} 106 public List<MappingContent> MappingContent{get;set;} 107 } 108 #> 109

 

Las condiciones y/o consideraciones que deben seguir/tener presente para usar esta plantilla son:

  1. La plantilla combinara todos los archivos .hbm.xml que esten en la carpeta del proyecto, no importara si estos archivos NO estan en incluidos en el proyecto, asi que cuidado, que en algun caso se me olvido borrar un archivo antiguo y me toco generar un archivo de mapeo inconsistente con el modelo.
  2. Las entidades deben encontrarse bajo un unico y mismo namespace, uffa que huevada diran y bueno tendria que pagarse algo por esto o no? Como la plantilla T4 toma cabecera del primer archivo .hbm.xml que encuentre, se esta asumiendo que todos comparten la misma estructura, repito estan bajo el mismo namespace y el mismo ensamblado. En algun momento hare una plantilla para generar varios archivos dependiendo de la cabecera de los archivos de mapeo.
  3. Como sabemos, los archivos hbm.xml debe ser incluidos en el proyecto como recurso embebido, pero como en este caso tendremos un unico archivo, todos a excepcion del archivo Unique.hbm.xml deben ser removidos de esta caracteristica.

image

Finalmente el template se ejecutara a peticion (Run Custom Tool), aun no necesito que se ejecute antes del proceso de compilacion. Bueno la idea de esto es que no tenga que estar ejecutando algun comando para que el mega-archivo de mapeo se genere, lo ideal seria que este archivo se genere cada vez que se modifica alguno de los otros archivos de mapeo o al menos cuando se solicita compilar el proyecto. Como dije no lo averigue aun, pero si alguien gentilmente quiere pasarme la informacion, bienvenida.

image

Esperando que esto le sirva a alguno de uds, seguramente me servira a mi en mi futura charla sobre NHibernate Smile, me despido cordialmente.

Descargar el codigo:

Saludos.

Posted: 23/7/2011 0:41 por Enrique Ortuño | con no comments
Archivado en: ,
Pruebas con multiples hilos.

La necesidad de realizar pruebas de concurrencia sobre un componente/servicio de mi aplicación, me hicieron ver la necesidad de buscar una forma de lanzar múltiples hilos que realicen las peticiones que deseaba. Sé que varios o muchos de Uds. ya conocen esto, pero lo que escribo a continuación es más para aquellos que están aprendiendo.

El código básico que encontré en Internet es el siguiente:

 

1 foreach (Thread thread in threads) 2 { 3 thread = new Thread(DoSomeWork()); 4 } 5 foreach (Thread thread in threads) 6 { 7 thread.Start(); 8 } 9 foreach (Thread thread in threads) 10 { 11 thread.Join(); 12 }

 

Este código lo tome de una entrada de stackoverflow, y tiene dos cosas importantes: Una es la línea 11 que permite esperar a que todos los hilos terminen su operación y el otro punto es que tiene un error, la línea 3 dará un error porque se está tratando de modificar un objeto read-only, al menos eso es lo que yo obtuve. Ante todo esto me permito presentarles una versión ligeramente modificada, que actualmente estoy utilizando:

 

1 static void MultiThreadedTest(int count) 2 { 3 Thread[] threads = new Thread[count]; 4 for (int i = 0; i < threads.Count(); i++) 5 { 6 var j = i; 7 ThreadStart starter = delegate { DoSomeWork(j); }; 8 threads[i] = new Thread(starter); 9 } 10 foreach (Thread thread in threads) 11 { 12 thread.Start(); 13 } 14 foreach (Thread thread in threads) 15 { 16 thread.Join(); 17 } 18 }

He encerrado toda la funcionalidad en un método, he reemplazado el bloque foreach por un for simple, que me permite evitar el problema del read-only. En las líneas 7 y 8 estoy inicializando los hilos parametrizados con el valor numérico, hay varias formas de hacer esto pero la que les presento en el ejemplo es la que yo escogí. Luego hay una línea que parecería sin sentido, es la linea 6, esta línea evita el problema del acceso a un closure modificado (“Access to modified closure”), por cierto esta advertencia me la da Resharper. Que significa esto?, pues si no se coloca esa línea el valor que se pase al método DoSomeWork siempre será el ultimo valor del bucle. Ya tuve malas experiencias con las expresiones lambda y estas advertencias, así que para evitarme problemas… que me cuesta agregar una línea!!!?

Finalmente el método DoSomeWork, que es el que realizara el acceso a mi servicio en última instancia.

 

1 private static void DoSomeWork(int number) 2 { 3 //..... 4 }

 

Espero que le sea de utilidad a alguien que este realizando pruebas con multiples hilos.

Saludos.

Posted: 13/7/2011 1:48 por Enrique Ortuño | con no comments
Archivado en:
Gracias por el apoyo … he aqui mi premio.

Primeramente expresar mi mas sincera gratitud a todos y cada uno de los miembros de esta comunidad que me apoyaron viendo el video que publique en el concurso “Evidencias” de Microsoft, como lo mencione en mi cuenta de twitter fui el ganador de dicho concurso y queria compartir con Uds. mi alegria y algunas fotografias del premio que obtuve.

26062011925 26062011926
26062011928 26062011933

Si bien el premio fue fisico y no esta nada mal, tambien gane muchos amigos que de todas partes del globo se pusieron en contacto conmigo y tambien me abrio puertas “magicamente” para ser invitado como orador en algunos eventos online, en suma fue una experiencia unica. Para las personas que creen que los concursos de Microsoft y sus premios son una falacia o que quiza no tienen una oportunidad de ganar, animo!!!! si yo pude ganar Uds. tambien pueden.

Muchas gracias nuevamente.

Posted: 29/6/2011 1:29 por Enrique Ortuño | con 4 comment(s)
Archivado en:
Codigo fuente de mi presentacion

En esta ocacion deseo compartir con Uds. el codigo fuente de la presentacion con la que estoy participando en el concurso/iniciativa de Microsoft, llamado evidencias.

Aqui esta el video:

Aqui el codigo fuente:

Aqui el video en formato mp4:

Espero que les sea de utilidad y si les gusta el material, por favor difundanlon entre sus conocidos, se los agradeceria un monton.

Un abrazo.

Como llamar a un Action Method MVC desde Silverlight

Trabajando en un proyecto que integra Silverlight y ASP.NET MVC, tuve la necesidad de hacer algo “extrano”, bueno quiza extrano para algunos. Necesite recuperar informacion que esta disponible en el contexto de MVC desde Silverlight, lo pongamos simple: Necesite llamar a un metodo, en este caso un ActionController desde el codigo de Silverlight.

En internet pueden encontrar muchos ejemplos, pero la gran mayoria son para realizar llamdas GET y considerando que quiero darle un poquitin mas de seguridad, pue mi requerimiento es solo permitir invocaciones POST…. y que dijeron? pues no hay muchos ejemplos clarificadores, entonces aqui va el mio.

A continuacion el ActionMethod que quiero invocar dese Silverlight:

1 [AcceptVerbs(HttpVerbs.Post)] 2 public ActionResult GetContextInformation() 3 { 4 return new Json(new ServiceConfiguration() 5 { 6 FullName = "UnknowData" 7 }); 8 }

La clase ServiceConfiguration, puede ser tan simple como la que se muestra a continuacion o tan compleja como deseemos. Esta clase es serializada a JSON para poder enviarla hacia Silverlight.

1 public class ServiceConfiguration 2 { 3 public string FullName { get; set; } 4 }

He aqui el codigo de Silverlight, para la invocacion del anterior ActionMethod:

 

1 private void GetWithWebRequest(string url) 2 { 3 var r = HttpWebRequest.Create(url) as HttpWebRequest; 4 r.Method = "POST"; 5 r.Accept = "application/json"; 6 r.BeginGetResponse(ReturnContext, r); 7 } 8 9 private void ReturnContext(IAsyncResult ar) 10 { 11 var request = ar.AsyncState as HttpWebRequest; 12 if (request != null) 13 { 14 var response = request.EndGetResponse(ar); 15 var jsonSerializer = new DataContractJsonSerializer(typeof(ServiceConfiguration)); 16 using (var stream = response.GetResponseStream()) 17 { 18 ServiceConfiguration ContextConfiguration = jsonSerializer.ReadObject(stream) as ServiceConfiguration; 19 } 20 //this.Dispatcher.BeginInvoke(() => 21 // PostRetrieved(Post)); 22 } 23 //if (OnInitializeServices != null) 24 // OnInitializeServices(this, EventArgs.Empty); 25 }

Aunque Resharper me sugiere reemplazar la invocacion de HttpWebRequest por una clase mas especifica como WebRequest, pues esta vez no le hago caso. Tambien observaran unos trozos de codigo comentado en el AsyncCallBack,, bueno estos los dejo para una explicacion futura. En este ejemplo no estoy pasando parametros al ActionMethod, pero podran encontrar bastante informacion de como hacerlo en Internet. El codigo es perfectamente compatible y usable con Windows Phone 7.

 

Espero que les sea util.

Un abrazo


Posted: 14/4/2011 0:14 por Enrique Ortuño | con 1 comment(s)
Archivado en: ,,
CodeKata del Anagrama

En busqueda de la diversion y el aprendizaje me puse a realizar un CodeKata muy breve, que consiste en encontrar todos los anagramas de una palabra dada, los anagramas no son otra cosa que una permutacion de todos los caracteres de dicha palabra, por ejemplo si tenemos “abc”, los anagramas seran: abc, acb, bac, bca, cab, cba

A continuacion el codigo del metodo, es bastante simple, podria haberlo reducido un poco mas, pero en aras de la legibilidad y de que se pueda entender lo dejamos asi:

Como varios de los CodeKata tienen el proposito de incentivar la practica de TDD, aqui algunas pruebas unitarias que hice:

Para los interesados aqui el codigo fuente del proyecto y las pruebas, las pruebas contienen los casos que mas vinieron a mi mente, sabed disculparme si cometo algun error Smile

P.D. El enlace al video que les comente hace algunas semanas.

Posted: 10/4/2011 23:55 por Enrique Ortuño | con no comments
Archivado en: ,
Resultados sobre la Comida y Bebida favorita de un Desarrollador

Aqui les dejo los resultados a una encuesta que realice hace algunos dias, sobre la comida y bebida favorita de un desarrollador, luego de haber alcanzado 100 participantes. La encuesta sigue abierta y bueno seguramente podran hacer variar los resultados, pero si todo va bien a fin de anio mostrare el resultado hasta esa fecha y con la cantidad total de participantes hasta ese momento:

En la categoria de comida rapida hay un ganador, la majestuosa “Pizza” que se codea el primer lugar con la hamburguesa, personalmente prefiero la pizza.

image

Sobre la bebida no alcoholica la cosa esta un poco mas disputada entre las bebidas cola y el café, yo prefiero el jugo de frutas.

image

Donde si tenemos un ganador indiscutible es en la categoria de bebida alcoholica, la cerveza sres. Yo prefiero el vino Smile

image

Todo comentario y aporte a estos resultados son bienvenidos.

P.D.

Bueno adicionalmente y con las disculpas por el cambio abruto de tema, de ahora en adelante, en este y mis futuros post, hasta el mes de Mayo voy a permitirme divulgar un video que hice y con el que estoy participando en un concurso, el link es este. El video se relaciona al uso de las caracteristicas funcionales de C# y a LINQ.

Saludos.

Simple Proyecto WCF de Consola en VS2010

Este post probablemente puede ser criticado por su simplicidad pero de todas maneras ahi va. En esta occasion comparto con Uds. proyecto de consola que funge como un servicio WCF, la verdad es que no hay nada fuera de lo normal o extraordinario en este post, solo quiza le sirva a alguien para empezar a realizar algunas pruebas con WCF. A mi me sirve como recordatorio o punto inicial de algunas demos que siempre hago sobre WCF.

Contenido

Una libreria de clases que contiene la interface y la implementacion del servicio, repito nada de extraordinario, solo los metodos y las interfaces comunes que trae el template de Visual Studio.

image

El proyecto de consola, que hospeda el servicio. Algunos en estos momentos se preguntaran por que realizo este post? Pues es como casi todo es una auto referencia y unas miguitas que dejo para que yo mismo o alguien pueda retroceder hacia el pasado y no partir de cero.

image

Lo mejor que puedo aportar en este post es quiza la configuracion hecha en el App.config para habilitar el servicio. Luego de intentar algunas variaciones he encontrado que esta configuracion es la que me funciona mejor en varios escenarios iniciales, luego se va complicando el .config.

1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.serviceModel> 4 <services> 5 <service name="WcfServiceLibraryDemo.Service1" behaviorConfiguration="HotsingSamples.Service1Behavior"> 6 <!-- Service Endpoints --> 7 <endpoint address="http://localhost:9192/HelloService" binding="basicHttpBinding" contract="WcfServiceLibraryDemo.IService1"> 8 </endpoint> 9 <endpoint address="http://localhost:9192/HelloService/mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 10 <!--<host> 11 <baseAddresses> 12 <add baseAddress =""/> 13 </baseAddresses> 14 </host>--> 15 </service> 16 </services> 17 <behaviors> 18 <serviceBehaviors> 19 <behavior name="HotsingSamples.Service1Behavior"> 20 <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment --> 21 <serviceMetadata httpGetEnabled="false"/> 22 <!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information --> 23 <serviceDebug includeExceptionDetailInFaults="false"/> 24 </behavior> 25 </serviceBehaviors> 26 </behaviors> 27 </system.serviceModel> 28 29 </configuration>

Archivos

Finalmente les dejo el link hacia el codigo fuente de este proyecto:

Tambien les dejo el link hacia una valiosa utilidad llamada HttpNamespaceManager, esta utilidad les permitira registar la reservacion de Namespaces. No se entiende para que sirve? les aseguro que la buscaran cuando tengan errores similares a: HTTP could not register URL http://+:8080/

Saludos

Posted: 15/3/2011 23:48 por Enrique Ortuño | con 2 comment(s)
Archivado en: ,
Programacion Funcional & LINQ

Como parte del programa denominado Evidencias de Microsoft para Latinoamerica, he grabado un video mostrando las caracteristicas funcionales de C#, asi como la simplicidad del uso de LINQ. El video dura aproximadamente 11 minutos, espero que en tan breve tiempo les pueda mostrar algo interesante. Los videos estan disponibles para descarga en los siguientes enlaces, espero les guste.

Youtube: http://www.youtube.com/watch?v=M1mEW07m3B0

image

DailyMotion: http://www.dailymotion.com/video/xhg43x_evidencias_tech

image

Vimeo: http://vimeo.com/20817981

Skydrive: http://cid-e46b27f9dbbd40a1.office.live.com/self.aspx/Archivos%20Publicos/Evidencias-Video.rar

Rapidshare (uploading...)

.NET es libre? hay que pagar licencias?

Ayer un amigo, con bastante preocupación, me hizo la siguiente consulta:

"Mira yo estoy en la Universidad aún tengo que sacar un proyecto. Que opción elegirías tu para desarrollar un proyecto web? Excluyendo ASP.NET, por el tema del pago de licencias."

image

Yo que andaba concentrado en NHibernate, moví la cabeza, como sacudiéndome el letargo, que me traían horas de desarrollo previo y le dije:

"Un momento, dijiste licencias por usar ASP.NET?"

A lo que Ricardo, respondió:

"Si, sucede que yo estoy laburando desde hace varios meses con .NET, vi todas sus ventajas y como tengo que hacer ese proyecto para la Universidad, le dije a mi docente si podía hacerlo usando .NET y ella me respondió que no, porque a) No es libre y b) Tendría que pagar licencias para usar"

Preambulo

Arrrgggg, dije para mis adentros: "Otro docente (en mi país) totalmente desinformado que a su vez genera desinformación y confusión entre sus estudiantes.". No es la primera vez que algún estudiante de alguna universidad (especialmente de las Universidades del Gobierno o Estatales) vienen hacia mí con dudas como esa y no los culpo. Si aquel que se supone sabe más que tu (el docente), te dice lo primero que escucho de alguna banda de resentidos o fanáticos de "otras" plataformas y que no ha tenido la voluntad ni el deseo de informarse correctamente..... ¿Qué podemos esperar de sus estudiantes?.... pues ese tipo de preguntas y aseveraciones.

image Tampoco los culpo, porque el medio en el que hoy nos desenvolvemos, es un creciente caldo de cultivo de odios, miedos y resentimientos hacia Microsoft, caldo de cultivo que es aprovechado por cuan aprendiz desinformado de geek, que suelta la primera ideota que ha escuchado o leído incompletamente por ahí, alentados sin duda "malintencionadamente" por algunos otros geeks "seniors" que ven la oportunidad de hacer crecer su reputación con cuasi-seguidores-oveja que solo repetirán lo que ellos dicen, ocultando la verdad.

 

También existe un factor casi misterioso que alienta esta desinformación en mi país y sucede que desde hace varios años, las universidades públicas se han cerrado a enseñar únicamente J2EE como plataforma de desarrollo, con el mismo conjunto de argumentos que Ricardo vino a mí: "Si desarrollas en .NET te harán pagar licencias o tienes que pagar licencias, no es gratis, te cobraran…. Etc.", cosa totalmente equivocada, como lo explicare un poco más adelante. Si me preguntan quien metio esa idea en las universidades… la verdad no se.

Como podrán percibir, este es un tema que me molesta y repito, no por los estudiantes, sino por sus docentes y las universidades y su corta y mediocre visión. Es por esta razón que por varios años consecutivos tomo cartas en el asunto, visitando las Universidades en las ciudades que me toca vivir y dando charlas o conferencias gratuitamente sobre este tema y temas técnicos.

Este articulo/post servirá a la postre, como una referencia a preguntas y dudas similares, creo que es más que suficiente preámbulo.

Respondiendo…

Volvamos a la respuesta y los argumentos esgrimidos por la docente de nuestro amigo Ricardo:

1. NO ES LIBRE?

Ella aduce que .NET no es libre, como yo conozco bien estos argumentos puede que por LIBRE este entendiendo una de las siguientes opciones o en el peor de los casos una combinación absurda de todas ellas:

a) No es Gratis.

image

Contexto

.NET no es un producto, .NET es una plataforma, .NET es la suma de varios productos, servicios e incluso mejores prácticas; desde ese punto de vista no podría ponerse a .NET un precio. Tal vez está entendiendo por .NET a VisualStudio.NET, bueno aquí hay algo de cierto en la afirmación, VS.NET en sus versiones Profesional/Ultimate no son gratis, pero existen versiones gratuitas de VS, las versiones llamadas Express, que pueden ser encontradas aquí.

Si alguien usa estas versiones puede desarrollar aplicaciones .NET sin pagar la licencia de VS, incluso puedes desarrollar productos comerciales y venderlos y no pagaras nada por el uso de estas versiones Express. Aquí también hay una duda recurrente y es lo relacionado a SQLServer, efectivamente las versiones comerciales de SQLServer son sujetas a licenciamiento, pero también se dispone de una versión Express que pueden descargarla aquí, que también es totalmente gratuita, para hacer incluso proyectos comerciales.

Respuesta

Depende del producto que estes usando, algunos productos son gratuitos y de libre distribucion, otros estan sujetos a un pago por licenciamiento.

b) No es OpenSource.

image

Contexto

Entiéndase por OpenSource la posibilidad de disponer del código fuente de un producto de software, tal cual se define aquí; si nos vamos por ese camino y analizamos algunos de los muchos posibles pensamientos que la docente, de nuestro "caso de estudio", podría estar teniendo obtenemos lo siguiente:

Respuesta

Es VisualStudio.NET OpenSource? definitivamente no!, no tenemos en nuestras manos el código fuente de VS y yo me pregunto: Realmente lo necesitamos? Para aquellos que creen que por objetivos académicos y para aprender y para saber que pasa realmente por “detrás”, la respuesta sigue siendo no. Aunque se que Microsoft ha compartido el código de Windows con gobiernos y universidades bajo un programa/licencia conocido como Shared-Source, quizá exista una iniciativa similar para el código de VS. Me sigo encontrando personas que aun quieren aprender a hacer un entorno similar a VS y siguen preguntando más allá de la respuesta anterior. Para ellos quizá les sirve saber que existe un entorno "parecido" a VS que se llama SharpDevelop, este si es un proyecto OpenSource e incluso hay un libro que relata como construyeron su entorno, que es totalmente gratuito. Además de ser algo que he leído, probado y recomendado académicamente, es una muestra más que con .NET también podemos hacer proyectos OpenSource.

Aquí también es oportuno aclarar que pueden acceder al código de la libreria de clases del Framework.NET, el cual puede ser descargado mediante VisualStudio y a demanda y puede verse como han sido construidas las clases del FX y ni que decir de las toneladas de aprendizaje que esto trae.

c) No es libre.

image

Contexto

Quizá la docente está pensando, en la libertad en el sentido estricto o al menos en el sentido tal cual Richard Stallman, la propuso en su GPL. Para los que leen esto por primera vez seguramente es sorprendente ver que OpenSource != SoftwareLibre, son dos cosas totalmente distintas y a su vez parecidas. Estas semejanzas son las que son aprovechadas malintencionadamente como dije, por algunas personas, que con el ánimo de confundir no hacen la clara distinción entre ambas. En este vínculo he colocado un resumen de una de las charlas que di hace tiempo sobre este tema.

Respuesta

VS no está sujeta a la licencia GPL, pero nuevamente lo pregunto: Realmente lo necesitamos? Quiza desde el punto de vista de un purista de la libertad si resulte importante, pero desde mi punto de vista de la productividad sigue siendo poco relevante.

Como vimos el tema de que .NET es gratis/libre/opensource no es de lo más simple de explicar y tampoco hay una respuesta inmediata a una afirmación, que no tiene mayor información. A continuación respondemos la segunda afirmación realizada por la docente.

2. HAY QUE PAGAR LICENCIAS?

Como vimos en el punto anterior, dependiendo del caso y de las herramientas que estemos seleccionando, tendremos que pagar o no licencias. Si deseamos usar VS o SQL Server en una de sus versiones Standard/Profesional/Ultimate si tendremos que pagar licencias; si deseamos usar las versiones Express, NO, no tendremos que pagar ninguna licencia.

Pero la respuesta del licenciamiento en el ámbito académico va más allá de la anterior. Microsoft ha creado un programa denominado Microsoft Campus Enrollment (yo lo conocía por Campus Agreement), que desde el 11 de Marzo será reemplazado por Microsoft Enrollment for Education Solutions. Este programa permite que las Universidades licencien el uso de su software, por un precio anual (yo diría incluso nominal por su bajo costo) de tal manera que todos sus laboratorios, docentes, administrativos y estudiantes, pueden usar los productos Microsoft (hay que ver que algunos productos están restringidos). En el caso de VS si está permitido, así como muchas de las soluciones de la plataforma .NET, por no decir todas.

image

Entonces el tema de licenciamiento, para una universidad, no es un pretexto para no usar .NET, yo creo que pasa por un tema de información que deben recibir las autoridades de la universidad y también por una cuestión de voluntad; si las autoridades conocen los beneficios que traen estos programas o similares pero no tienen la voluntad de implementarlos es otro tema, solo están perjudicando a sus propios estudiantes.

Conclusiones

En todo caso no debería existir un docente que restrinja a un estudiante, la utilización de una herramienta o plataforma, eso demuestra solamente el miedo que puede que tengan a algo que no conocen o sus limitaciones o prejuicios personales o de sus superiores.

Así que ya saben estimados amigos estudiantes, no se dejen intimidar por sus docentes o por la universidad, el conocimiento es libre y muchas de las herramientas que ofrece Microsoft también, si alguno tiene un “caso” difícil, no dude en llamarme que gentilmente acudiré en su ayuda. :D.

Posted: 22/2/2011 23:09 por Enrique Ortuño | con 22 comment(s) |
Archivado en: ,,
Explorando NHibernate 3.0 (I)

Las personas que me conocen seguramente se sorprenderán por este y los posteriores post que escribiré con relación a NHibernate, bueno todo tiene una explicación y esa para este caso podrían ser varias: Quizá la primera y más importante el “bendito” Karma que me persigue para usar NHibernate desde hace varios proyectos y no me malinterpreten no es que esté en contra de NHibernate pero la verdad es que tiene una curva de aprendizaje bastante empinada y aunque hay mucho soporte por ahí (incluso comercial) pues me mandan a la batalla con prácticamente cero de esa armadura, otra razón puede ser que me he involucrado en proyectos en los que no quieren comprar un EF Provider para Oracle y tampoco quieren esperar a la próxima liberación de este provider “gratuito” por parte de Oracle; sea cual fuese la razón aquí me tienen escribiendo sobre NHibernate.

Como todos mis post, incluso aquellos que pongo en twitter, lo hago como una guía auto-recordatoria en un 70% y por esta razón también lanzo un ligero aviso antes de continuar leyendo. En este post, aquellos que ya conocen NHibernate no encontraran algo nuevo, quizá encuentren algunos errores míos o algo malo que esté haciendo, son libres de criticar y corregirlos, por favor.

También he decidido explorar las nuevas características de NHibernate 3.0 entre ellas probablemente la más esperada es la integración de LINQ to NHibernate (que según dicen, fue reescrito desde cero) veamos:

Entidades

Tengo inicialmente las siguientes entidades, debo mencionar que quizá la relación parezca algo extraña por que debería ser una relacion many-to-many , pero la entidad StudentCourse va a convertirse “pronto” en esa tabla intermedia, por lo que permítanme la libertad de una relación one-to-many

image

Mapeo

La configuración de los archivos de mapeo inicialmente es

 

1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="Student" table="Students"> 4 <id name="StudentId" column="StudentId"> 5 <generator class="guid" /> 6 </id> 7 <property name="StudentName" column="StudentName" /> 8 <bag name="StudentCourses" cascade="save-update"> 9 <key column="StudentId" /> 10 <one-to-many class="StudentCourse" /> 11 </bag> 12 </class> 13 </hibernate-mapping>
1 <?xml version="1.0" encoding="utf-8" ?> 2 <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="DemoNHibernate.Dal.Entities" namespace="DemoNHibernate.Dal.Entities"> 3 <class name="StudentCourse" table="StudentCourse"> 4 <id name="StudentCourseId" column="StudentCourseId"> 5 <generator class="guid" /> 6 </id> 7 <property name="RegistrationDate" column="RegistrationDate" type="DateTime" /> 8 <many-to-one name="Student" class="Student" cascade="all"> 9 <column name="StudentId" /> 10 </many-to-one> 11 </class> 12 </hibernate-mapping>

Pruebas

Como podrán ver el mapeo y las entidades no son la gran cosa, pero aquí veamos cómo se comportan algunas pruebas que quiero hacer, fundamentalmente para borrar entidades hijas a través del update del padre (quizá esté hablando en chino para algunos pero les pido paciencia, al leer el código del UpdatingChildEntities se entenderá un poco más.

Explicare algunos elementos que verán en el código:

  • RandomText, es una clase de Markov que genera aleatoriamente frases/oraciones /parrafos
  • Estoy usando algo métodos anónimos para encapsular la llamada a la inicialización del contexto y de la transacción, el método Execute.
  • Estoy usando el patron TestDataBuilder como lo explique en un anterior post.
  • El código está en un solo archivo por ahora, por simplicidad, en la versión final lo dividiré correctamente
1 namespace DemoNHibernate.Dal.Tests 2 { 3 [TestClass] 4 public class PersistenceTests 5 { 6 private RandomText generator; 7 private ISession session; 8 protected ISession Session 9 { 10 get { return session ?? (session = NHibernateHelper.OpenSession()); } 11 } 12 13 [TestInitialize] 14 public void GenerateData() 15 { 16 generator = new RandomText(); 17 18 Execute((context, tx) => 19 { 20 var st = new StudentTestDataBuilder() 21 { 22 StudentName = generator.Sentance(), 23 StudentCourses = new StudentCoursesTestBuilder().BuildList(3) 24 }.Build(); 25 26 Session.Save(st); 27 tx.Commit(); 28 }); 29 } 30 31 [TestMethod] 32 public void DeletingChildElement() 33 { 34 Execute((context, tx) => 35 { 36 var st = (from s in context.Students 37 select s).FirstOrDefault(); 38 Assert.IsNotNull(st); 39 Assert.IsNotNull(st.StudentCourses); 40 Assert.IsTrue(st.StudentCourses.Count > 0); 41 st.StudentName = "Deleted at "+ DateTime.Now; 42 st.StudentCourses.RemoveAt(0); 43 Session.Update(st); 44 tx.Commit(); 45 }); 46 } 47 48 public void Execute(Action<BaseModelContext, ITransaction> method) 49 { 50 using (var context = new BaseModelContext(Session)) 51 using (var trx = Session.BeginTransaction()) 52 { 53 method(context, trx); 54 } 55 } 56 } 57 58 public class StudentCoursesTestBuilder 59 { 60 public virtual Student Student { get; set; } 61 public virtual DateTime RegistrationDate { get; set; } 62 63 public IList<StudentCourse> BuildList(int quantity) 64 { 65 var result = new List<StudentCourse>(); 66 for (int i = 0; i < quantity; i++) 67 { 68 StudentCourse sc = new StudentCoursesTestBuilder() 69 { 70 RegistrationDate = DateTime.Now 71 }.Build(); 72 result.Add(sc); 73 } 74 return result; 75 } 76 77 public StudentCourse Build() 78 { 79 return new StudentCourse() 80 { 81 RegistrationDate = this.RegistrationDate, 82 Student = this.Student 83 }; 84 } 85 } 86 87 public class StudentTestDataBuilder 88 { 89 public virtual string StudentName { get; set; } 90 public virtual IList<StudentCourse> StudentCourses { get; set; } 91 public Student Build() 92 { 93 return new Student() 94 { 95 StudentName = this.StudentName, 96 StudentCourses = this.StudentCourses 97 }; 98 } 99 } 100 } 101

Resultados

La prueba que me trae aquí es la que se encuentra en la línea 42, donde verán que estoy eliminando un elemento de la colección de StudentCourses, mediante el RemoveAt y luego en la línea 43 hago un update de la entidad padre, esto ocasiona un comportamiento probablemente esperado por muchos de los que ya conocen NHibernate:

  1. La entidad padre se actualiza correctamente
  2. La entidad hija borrada de la colección, NO se borra físicamente de la base de datos, simplemente se coloca un valor NULL en su llave foránea, lo cual lo desvincula a su entidad padre, digamos que hace algún tipo de borrado lógico.
  3. Porque este comportamiento? Se debe a que el la definición del mapeo, en la línea 8, del primer archivo (Student.hbm.xml) se encuentra la definición del borrado en cascada, de la siguiente manera: cascade="save-update", es decir no se ha definido ningún comportamiento especial para el borrado.
  4. Como resultado de algunas pruebas he determinado que el método LastOrDefault() no está soportado aun por Linq to NHibernate 3.0, no es el gran problema, pero aun evidencia que no hay una implementación completa de LINQ, lastima.

    image

    image

    En un siguiente post, mas sobre el comportamiento de borrado de entidades hijas, mediante el update del padre.

    Saludos

    Posted: 21/2/2011 0:51 por Enrique Ortuño | con 4 comment(s)
    Archivado en: ,,
    Comidas y bebidas favoritas de un desarrollador

    Hace unos días una compañera de trabajo descubrió que no me gusta el café y su reacción fue decir “… que extraño!... un desarrollador al que no le gusta el café” y la verdad es que yo no lo consumo hace casi 20 años y bueno me entró la curiosidad de sondear en esta nuestra comunidad, los gustos que compartimos los desarrolladores, en esas largas horas de soledad entre tu código y uno mismo. Con la curiosidad del caso, aquí están las preguntas y respuestas más simples con opciones cerradas y comunes en mi país, sin ánimo de ofender o criticar ningún gusto en particular.

    Más artículos Página siguiente >