Descargar masivamente las presentaciones del MIX 2010

Hace algunos meses hice una aplicación para descargar las sesiones del PDC 2009. Y los objetivos eran básicamente dos:

  1. Descargar archivos masivamente que tengan un patrón de nombres en común. No sólo que me sirva para el PDC o el MIX, si no para cualquier descarga de archivos basada en un patrón de nombres.
  2. Mostrar algunas ventajas del despliegue ClickOnce. En las anteriores versiones no habilite las actualizaciones automáticas, pero en la última versión ya se encuentra disponible. Es decir que no tendremos que estar viendo si hay una versión nueva de esta aplicación, esta opción de ClickOnce lo hará de manera automática.

ST2 – Download for Dummies: http://sergiot2.com/DfD/default.htm.

El uso es simple, un ejemplo de como descargar los Slides del MIX 2010:

http://sergiot2.com/blogimages/2010/03Mar/24_Mix_2010_Donwloader.JPG

Mejoras para una siguiente versión:

  • Mostrar el progreso de la descarga.
  • Usar Open Folder Dialog, para seleccionar la carpeta de destino.
  • ¿Ustedes tiene alguna?

Saludos,

Que hacer cuando IIS no te deja publicar un Sitio Web ASP.NET

Con el paso del tiempo y familiaridad nos daremos cuenta que la publicación de un Sitio Web (Web Site) o Aplicación Web (Web Application), es lo más sencillo gracias a la herramienta, es decir, Visual Studio 2005+ o Visual Web Developer Express, sólo hacemos clic derecho sobre el proyecto Web o el Sitio Web, y seleccionamos Copy Web Site o Publish Web Site, y podemos escoger si publicamos directamente hacia un servidor, un FTP, o File System para copiarlo por RED o llevarlo en un USB.

Reducimos el problema, divide y vencerás forever, a llevar los archivos generados a nuestro Servidor Web IIS, que es donde suceden los mayores problemas al publicar un Sitio Web.

Problema Central

En cuanto a la publicación en un Servidor Web, se pondrá más interesante si es que no tenemos acceso remoto al servidor, y sólo podemos copiar los archivos por FTP o subiéndolos vía web, la identificación de problemas será un poco más difícil porque no tenemos acceso a la servidor (verificar permisos en el sistema de archivos, revisar Event Viewer, etc). Por ejemplo, un error en la definición de la de conexión, puede hacer que no podamos acceder a la pagina y confundir sobre el verdadero error, entonces debemos deshabilitar los errores personalizados, en algunos extremos intentar depurar remotamente la aplicación Web (es lo peor que se puede hacer, evitar llevar este tema a los foros). Si una aplicación funciona bien en desarrollo, debería funcionar también en producción sin la necesidad de hacer una depuración remota, sólo es un tema de configuración y permisos.

Alternativa

Una buena práctica si es que recién estamos empezando y el llevar nuestra aplicación a producción se convierte en un grave problema , es tener un página con código “Inline” que sea nuestro robot “buscaerrores”. Cada vez que deseamos publicar una aplicación Web, primero enviaremos a nuestro robot a verificar que la configuración del servidor Web sea el correcta, si el robot pasa la prueba, la publicación del sitio web será una tarea sencilla.

Algunas Ventajas

Ya sea el equipo infraestructura o el equipo de desarrollo, el que realice la publicación de una Aplicación Web, debemos tener un robot que nos permita detectar los siguientes problemas:

  1. Detectar configuraciones incorrectas en el Application Pool de un Sitio Web. ¿Qué es un Application Pool? Por ejemplo, si no tenemos al usuario por defecto en un Application Pool (NETWORK SERVICE), debemos darle ciertos permisos y hacerlo que pertenezca a un grupo especial para que pueda ser un usuario del Application Pool.
  2. Verificar que las conexiones a la base de datos funcionen correctamente. Hay muchos sitios web, que no hay ninguna página que no funcione sin hacer conexiones a la base de datos, es decir que si por alguna razón no cambiaron la cadena de conexión al pasar a producción, o el servidor de base de datos no tiene los permisos correctos, ninguna página web de toda la Aplicación va a funcionar. Por ejemplo sitios web que generan sus menús desde la base de datos, este menú estará presente en todas las páginas.
  3. Verificar que los permisos en el sistema de archivos sea el correcto. Si habilitamos la carga de archivos en nuestra aplicación debemos dar los permisos necesarios al usuario del Application Pool (por defecto NETWORK SERVICE) para que pueda escribir sobre la carpeta donde vamos a poner los archivos.
  4. Cualquier otra verificación que ustedes requieran hacer antes de pasar una aplicación Web a Producción. La idea es dejar una página base, pero ustedes pueden personalizarla de acuerdo a sus escenarios.

El robot, debe ser muy simple y no depender de ninguna otra página o recurso para funcionar:

Y el código fuente debe ser Inline para que funcione independiente si es un Sitio Web o una Aplicación Web:

Descargar Robot para Verificar IIS para ASP.NET.

Anécdota sobre el tema

Recuerdo hace algunos años, en el team “epica” estábamos liberando una nueva versión de un producto Web para uno de nuestros clientes, el Project Manager tuvo problemas durante la presentación de esta nueva versión, sólo llamo para decirme: “La Web no funciona, revisa que puede estar pasando”. El equipo de infraestructura del cliente (un empresa multinacional) no pudo detectar el problema y sólo se liberaba de la responsabilidad diciendo que la aplicación Web no Funcionaba. Después de un par de intentos fallidos por solucionar el problema, el equipo de desarrollo tuvo que ir a resolver el problema. No recuerdo exactamente el problema, pero era más o menos así: anteriormente habían instalando una Aplicación Web, que había cambiado la estructura común del IIS, ellos al instalar nuestra aplicación Web no lo habían hecho correctamente y nuestra Web estaba heredando el archivo de configuración de la aplicación instalada anteriormente, y por eso no funcionaba correctamente. Después de un par de cambios en el IIS Manager, nuestra aplicación Web estaba funcionando correctamente.

Saludos,

[CodeSnippet] SSH/SFTP en C# usando SharpSSH

.NET tiene una clase llamada FtpWebRequest, para el acceso a un FTP, un ejemplo completo en el siguiente artículo: Simple FTP Demo Application Using C#.NET 2.0.

Pero esa clase no soporte SFTP, como lo comentan en este foro: SSH/SFTP Communications in C# (revisar las respuestas tiene otras opciones).

Una librería gratuita es: SharpSSH, gracias a Tamir Gal.  Cuando descarguen la librería, también hay una aplicación consola de ejemplo, y con Reflector podemos ver como funciona esta librería.

Y nada, el código para subir un archivo usando el protocolo SFTP, es el siguiente. Archívese, y compártase:

   1: using System;

   2: using System.Configuration;

   3: using tss = Tamir.SharpSsh;

   4:  

   5: namespace DemoSFTP

   6: {

   7:   class Program

   8:   {

   9:     static void Main(string[] args)

  10:     {

  11:  

  12:       String hostServer = ConfigurationManager.AppSettings["host"];

  13:       String userName = ConfigurationManager.AppSettings["userName"];

  14:       String password = ConfigurationManager.AppSettings["password"];

  15:       Int32 port = Int32.Parse(ConfigurationManager.AppSettings["port"]);

  16:  

  17:       String  fromFile= @"D:Datosprueba.txt";

  18:       String toFile = "/carpeta01/prueba.txt";

  19:  

  20:       //Create object

  21:       tss.SshTransferProtocolBase sftpClient;

  22:       sftpClient = new tss.Sftp(hostServer, userName);

  23:       sftpClient.Password = password;

  24:  

  25:       //connect to server

  26:       sftpClient.Connect(port);

  27:  

  28:       Console.WriteLine("conectado al servidor");

  29:       Console.ReadLine();

  30:  

  31:       //subir archivo

  32:       sftpClient.Put(fromFile,toFile);

  33:  

  34:       Console.WriteLine("archivo publicado");

  35:       Console.ReadLine();

  36:  

  37:       //close connection

  38:       sftpClient.Close();

  39:  

  40:     }

  41:   }

  42: }

Saludos,

Recuperando una cadena de conexion SqlConnectionString desde una cadena EntityConnectionString

Desde hace algunos proyectos, en lugar de usar frameworks o librerías de acceso a datos para un escenario Ado.Net y SQL Server, prefiero usar Ado.Net Entity Framework. Partiendo de las premisas que se cuenta con Visual Studio 2008, son proyectos nuevos, y claro, siempre y cuando pueda intervenir en decidir la arquitectura de la aplicación.

Si estamos empezando a usar una nueva tecnología, tomemos como referencia el acceso a datos que siempre podemos usar frameworks, apis, generadores de código, o un ORM, como decía si, estamos empezando a usar una nueva tecnología para el acceso a datos, no debemos ser estrictos en el uso, sobre todo si no tenemos la experiencia necesario o no tenemos información disponible, bajo la premisa que no podemos retrasar los tiempos del proyecto. Si en un proyecto estoy usando Ado.Net EF o NHibernate, y tengo problemas para traducir alguna consulta, no debemos obsesionarnos en hacerlo con la tecnología usada, si en un par de días no encontramos alguna solución, podemos hacer ese método o consulta compleja con ado.net puro (SqlConnection, etc). Es una opinión personal, y bajo la premisa que no podemos retrasar los tiempos del proyecto por intentar ser puristas y estrictos en el uso de algunas tecnologías. Ojo, esto tampoco quiere decir vayamos hacer una mezcolanza una mazamorra con nuestros proyectos y código, siempre debemos mantener la legibilidad.

Dejemos el rollo, y vayamos al tema del post:

Necesitamos crear una conexión SlqConnection en un proyecto que esta usando Ado.Net Entity Framework, pero no deseamos agregar una nueva cadena de conexión al archivo de configuración para la misma base de datos.

Una cadena de conexión para EntityClient (EntityConnection) es así:

   1: <add name="AdventureWorksEntities" 

   2:      connectionString="metadata=.AdventureWorks.csdl|.AdventureWorks.ssdl|.AdventureWorks.msl;

   3:      provider=System.Data.SqlClient;provider connection string='Data Source=localhost;

   4:      Initial Catalog=AdventureWorks;Integrated Security=True;multipleactiveresultsets=true'" 

   5:      providerName="System.Data.EntityClient" />

Pero una conexión para SqlClient (SqlConnection) puede tener la siguiente estructura:

   1: <add name="AdventureWorksSql" 

   2:      connectionString="Data Source=localhost;Initial Catalog=AdventureWorks;

   3:      Integrated Security=True;" 

   4:       providerName="System.Data.SqlClient" />

Con el siguiente código, podemos recuperar una cadena de conexión SqlClient, desde una cadena de conexión EntityClient:

   1: EntityConnectionStringBuilder entCn = new EntityConnectionStringBuilder(

   2:     ConfigurationManager.ConnectionStrings["AdventureWorksEntities"].ConnectionString);

   3:  

   4: SqlConnectionString = 

   5:       entCn.ProviderConnectionString.Replace("MultipleActiveResultSets=True","");

Y con este código evitamos la necesidad de tener dos cadenas de conexión, que apuntan a la misma base de datos pero con distinto proveedor. Evidentemente que hay un costo de operación al recuperar el valor de la otra de conexión, porque internamente debe estar haciendo un split o algo parecido. El uso dependerá del escenario, y podemos encontrar algunas variantes bajo la misma idea, como por ejemplo partir de una cadena SqlClient y convertirla en una cadena EntityClient. Pero ahí esta el código.

Nota: Sobre remover el atributo MultipleActiveResultSets, cuando creamos una cadena de conexión Sql y no indicamos este atributo por defecto es False (revisar aquí los valores por defecto en una cadena de conexión Sql). Esta opción llamada MARS, esta disponible desde Ado.Net 2.0 y permite la ejecución de varios SqlCommand sin tener que cerrar la conexión y usando la misma cadena de conexión, sólo si necesitarán usar y ejecutar varios SqlCommand en una misma conexión, se debe dejar el atributo.

Recursos Adicionales (con ejemplos):

Saludos,

Que hacer… cuando necesitas descargar todas las sesiones del PDC09 de golpe…

Nada, que estaba descargando todas las presentaciones (pptx) junto a Marcello, para después escoger cuales videos finalmente descargamos. Vamos, que descargar todo el contenido y no ver ninguno es una perdida de tiempo.

Y como que la paciencia de Marcello se iba agotando, así que tuvimos que hacer algo para descargar todas las sesiones de golpe y no impacientar a Macello. Así que pensando un poco y usando la clase WebClient, llegamos a lo siguiente:

Esta aplicación te permite descargar archivos que tiene un patrón numerado. Y nada, que pones los datos  y descargas los archivos.

Pueden descargar e instalar al vuelo la aplicación desde el siguiente enlace (vía ClickOnce): http://sergiot2.com/DfD/default.htm. Por favor, no traten de romper la aplicación que no validado los inputs (para la versión 1.0.0.2). Se aceptan sugerencias para la siguiente versión.

Pero si quieren se hacen una misma con el siguiente código fuente:

   1: private void DownloadFiles()

   2: {

   3:   rtxtLog.Text = "";

   4:  

   5:   String urlBase = txtUrlBase.Text;

   6:   String folderBase = txtFolder.Text;

   7:  

   8:   Int32 intMin = Int32.Parse(txtMin.Text);

   9:   Int32 intMax = Int32.Parse(txtMax.Text);

  10:  

  11:   WebClient webDown = new WebClient();

  12:   for (Int32 i = intMin; i <= intMax; i++)

  13:   {

  14:     String urlComplete = String.Format("{0}{1}{2}.{3}",

  15:         urlBase, txtPre.Text, i.ToString(txtFormato.Text), txtExt.Text);

  16:     String fileComplete = String.Format("{0}{1}{2}.{3}",

  17:         folderBase, txtPre.Text, i.ToString(txtFormato.Text), txtExt.Text);

  18:     try

  19:     {

  20:       webDown.DownloadFile(urlComplete, fileComplete);

  21:       rtxtLog.AppendText("BIEN: Se descargo la url: " + urlComplete + "n");          

  22:     }

  23:     catch (Exception ex)

  24:     {

  25:       rtxtLog.AppendText("ALERT: Hubo problemas en la descarga de " + urlComplete + "n");

  26:     }    

  27:   }

  28: }

Saludos,

[WebCast] Pruebas Web y de Carga usando VSTS

  1. ¿Si ponemos un cubo más a esta persona, soportará? ¿o está en su límite (cubo/hombre)?
  2. ¿Cómo supo cuantos cubos soporta? ¿Puso uno por uno hasta sentir dolor en la espalda?
  3. ¿Conoces el límite de tu aplicación Web (Req/s))?
  4. ¿Cómo haces para saber cuántos usuarios soporta tu Aplicación Web? ¿Le pides a 800 amigos de Hi5, a tus 380 amigos del facebook, y a tus 900 amigos de Twitter, que entren todos a la vez?

Y nada, el día Jueves 05/Nov a las 04:00 p.m. (GMT-05:00), estaremos presentando el siguiente WebCast:

Título Original: Webcast MSDN: Prácticas pruebas web

Descripción: En esta sesión se revisa el pasado, presente y futuro de las pruebas Web. Comenzamos al revisar cómo se lograron las pruebas Web antes de la llegada de Team System en Microsoft Visual Studio. A continuación, revisamos las herramientas de pruebas Web y de carga disponibles en Team System 2005/2008 de Visual Studio. Y por último, examinamos el futuro de las pruebas Web y de carga con un vistazo a Team System 2010 de Visual Studio.

Al final del WebCast, trataremos de dar algunas recomendaciones básicas de como detectar el posible origen de un cuello de botella; si está en la aplicación o está en la base de datos.

Enlace de Registro: http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032426182&Culture=es-AR.

Si desean agregar el evento a su calendario, pueden descargar el siguiente archivo: WebCast-WebTests.ics.

Saludos,

WCF Service Configuration Editor y WCF Test Client

Dos herramientas muy útiles durante el desarrollo de Servicios WCF.

 

WCF – Microsoft Configuration Services Editor

Permite configurar el Binding de un Servicio WCF a través de un “Wizard”, simple para configurar un servicio básico y aprender de la configuración que genera en el app.config. Si crean un proyecto del tipo WCF, haciendo clic derecho sobre el archivo de configuración aparece la opción por defecto, si no es así pueden irse a Tools –> WCF Service Configuration Editor:

Referencias:

 

WCF Test Client

Esta herramienta permite explorar y probar los métodos disponibles en un Servicio WCF, al estilo Web Services en la misma máquina y con tipos de datos primitivos. Pero con la diferencia, que no es sólo para tipos primitivos, si no para cualquier DataContract definido:

Referencias:

Mas detalles sobre herramientas de WCF: WCF – WcfServiceHost, WcfTestClient, SvcConfigEditor y SvcTraceViewer.

Saludos,

[Enlace] Ejemplo de uso LINQ y ADO.NET EF con DB2, IDS, U2

Buscando referencias sobre ADO.NET EF y DB2 encontré este artículo-ejemplo, con imágenes incluidas, de como podemos usar ADO.NET Entity Framework contra servidores de datos IBM: DB2, Informix Dynamic Server, y U2.

El resumen del artículo traducido (o eso se intento):

El “IBM Data Server Provider for .NET” habilita a los desarrolladores tomar ventaja de ADO.NET Entity Framework y LINQ. Puedes desarrollar fácilmente el acceso a datos en tus aplicaciones .NET usando cualquier de los servidores de datos IBM (DB2®, Informix® Dynamic Server, o U2), ADO.NET Entity Framework y LINQ. El artículo muestra como empezar a usar Entity Framework con un servidor de datos IBM, y te muestra paso a paso, el proceso de como crear una aplicación desde cero.

Los requerimientos son:

Algunas imágenes del artículo:

  1. Arquitectura
    http://sergiot2.com/blogimages/2009/08Ago/04_Architecture_EntityFramework.gif
  2. Los primeros pasos son similares a ADO.NET EF contra SQL
    http://sergiot2.com/blogimages/2009/08Ago/04_Add_New_Item_ADOEF.jpg
  3. La diferencia esta en cambiar el proveedor de la conexión
    http://sergiot2.com/blogimages/2009/08Ago/04_Add_Connection_IBM_Servers.jpg
  4. Los demás es conocido
    http://sergiot2.com/blogimages/2009/08Ago/04_U2_association.jpg

 

Enlace del artículo: Develop a sample application using LINQ programming and the ADO.NET Entity Framework with IBM DB2, IDS, and U2 servers.

P.D.: Si tiene preguntas sobre el ejemplo pueden enviarlas a los autores del artículo que para eso pusieron sus correos:

Saludos,

[ADO.Net EF] Insercion de registros relacionados

Una de las operaciones que se puede volver un dolor de cabeza (cuando empezamos a usar EF) es la inserción o actualización de tablas que están relacionadas. Miremos a Northwind:

Entity Framework

Como se pueden ver si queremos insertar un registro en la tabla Products, necesitamos los CategoryID y SupplierID que están relacionados con otras dos tablas.

El sentido común te dice que el insertar debería ser algo así:

   1: public Int32 InsertarProduct(Products objProd)

   2: {

   3:   Int32 prodId = 0;

   4:  

   5:   using (NorthwindEntities edmNorth = new NorthwindEntities())

   6:   {        

   7:     edmNorth.AddToProducts(objProd);        

   8:     edmNorth.SaveChanges();

   9:     prodId = objProd.ProductID;

  10:    }

  11:   return prodId;

  12: }

Y la llamada al método sería la siguiente:

   1: [TestMethod()]

   2:  public void InsertarProductTest()

   3:  {

   4:    RepositoryOrders target = new RepositoryOrders(); 

   5:    Products objProd = new Products(); 

   6:    objProd.ProductName = "My Product 2";

   7:    objProd.Suppliers = new Suppliers() { SupplierID = 18 };

   8:    objProd.Categories = new Categories() { CategoryID = 1 };          

   9:    objProd.QuantityPerUnit = "24 - 50 g pkgs.";

  10:    objProd.UnitPrice = 34.45m;

  11:    objProd.UnitsInStock = 34;

  12:    objProd.UnitsOnOrder = 3;

  13:    objProd.ReorderLevel = 3;

  14:    objProd.Discontinued = false;

  15:  

  16:    int expected = 0; // TODO: Initialize to an appropriate value

  17:    int actual;

  18:    actual = target.InsertarProduct(objProd);

  19:    Assert.AreNotEqual(expected, actual);     

  20:  }

Pero al ejecutar el código te dará un error como el siguiente: “An error occurred while updating the entries”. Debido a que el EF va intentar actualizar el registro SupplierID y CategoryID, pero el nombre no permite nulos, como estos objetos han sido creados manualmente, EF piensa que quieres actualizar ellos también, cuando sólo quieres insertar un registro de Product.

Una de las cosas que estaba haciendo era remover las relaciones manualmente (editando el modelo Xml) y el modelo no sabía que estaban relacionadas así que sólo pasaba los datos. Pero actualizar el modelo era una tarea muy trabajosa, y llegas a esos momentos en que dices: “creo que ya es hora, de buscar una forma más simple de hacer esto”.

En este foro: How to insert a row using Entity Framework?, te dan una luz de la solución, y con unos adicionales puede ser así:

1. Vamos a crear una clase parcial de Product con los campos de relación a las otras tablas. Clase parcial, para que cuando se actualice el modelo esta no se actualice.

   1: public partial class Products

   2: {

   3:   public Int32 SupplierID { get; set; }

   4:   public Int32 CategoryID { get; set; }

   5: }

2. Vamos a cambiar el código de inserción. En el foro, se habla de dos opciones, pero la primera involucra hacer una consulta a la base datos, lo cual no es necesario en una inserción simple, por eso optamos por el segundo.

   1: public Int32 InsertarProduct(Products objProd)

   2: {

   3:    Int32 prodId = 0;

   4:  

   5:    using (NorthwindEntities edmNorth = new NorthwindEntities())

   6:    {

   7:      objProd.SuppliersReference.EntityKey = 

   8:              new EntityKey("NorthwindEntities.Suppliers",

   9:                "SupplierID", objProd.SupplierID);

  10:      objProd.CategoriesReference.EntityKey = 

  11:              new EntityKey("NorthwindEntities.Categories",

  12:                "CategoryID", objProd.CategoryID);  

  13:      

  14:      edmNorth.AddToProducts(objProd);        

  15:      edmNorth.SaveChanges();

  16:      prodId = objProd.ProductID;

  17:    }

  18:  

  19:    return prodId;

  20:  }

3. Y finalmente la llamada al método será así:

   1: [TestMethod()]

   2: public void InsertarProductTest()

   3: {

   4:    RepositoryOrders target = new RepositoryOrders(); 

   5:    Products objProd = new Products(); 

   6:    objProd.ProductName = "My Product 2";   

   7:    //usando clase parcial

   8:    objProd.SupplierID = 18;

   9:    objProd.CategoryID = 1;    

  10:    objProd.QuantityPerUnit = "24 - 50 g pkgs.";

  11:    objProd.UnitPrice = 34.45m;

  12:    objProd.UnitsInStock = 34;

  13:    objProd.UnitsOnOrder = 3;

  14:    objProd.ReorderLevel = 3;

  15:    objProd.Discontinued = false;

  16:  

  17:    int expected = 0; // TODO: Initialize to an appropriate value

  18:    int actual;

  19:    actual = target.InsertarProduct(objProd);

  20:    Assert.AreNotEqual(expected, actual);     

  21: }

Se intento pasar los valores en los mismos objetos del modelo, pero hubo algunos errores porque se cruzaba la entidad enviada y la referencia que creamos antes de insertar el producto. Y esto de usar clases parciales, es forma transparente y simple de hacerlo, a propósito si van exponer su modelo en un servicio WCF, tiene que serializar estos atributos de la clase parcial para que también puedan ser expuestos en los contratos.

Se pudo mostrar otros detalles y los errores de otras pruebas que hice, pero si hacía eso nunca terminaba el artículo, así que mejor enviarlo masticado :D.

P.D.: Si tienen otras formas de hacer un Insert o Update con tablas relacionadas, lo pueden enviar en los comentarios.

Saludos,

lstGeeksMs.Where(u => u.Estado == "papafeliz").Select(u => u).ToList().Add(new UserGeek() { Nombre = "sergiotarrillo", Estado = "papafeliz", Hijo = “marcello” });

Marcelo en el tercer planeta

Pues que nada, me uno a lista de padres en Geeks.ms.

Ser papá es un sentimiento difícil de describir con palabras, pero, si ser papá es hablarle a la barriguita de tu esposa durante nueve meses, si ser papá es emocionarte con cada patadita del bebé dentro de la barriguita, si ser papá es llorar junto con el bebé cuando da su primer llanto, si ser papá es ensuciarte con el bebé cuando le cambias los pañales, si ser papá es poner cara de tonto cuando el bebé da su primera sonrisa, si ser papá es contarlo públicamente en tu blog, pues si, que soy un papá feliz!

Y eso, que es una nueva motivación en la vida para ser una persona mejor.

Saludos,