<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8837851</id><updated>2011-09-24T15:22:42.483-03:00</updated><category term='flash'/><category term='flv'/><category term='ASP'/><category term='ubuntu'/><category term='MVC'/><category term='Internet'/><category term='algoritmos geneticos'/><category term='experiencias'/><category term='ASP.NET'/><title type='text'>Desarrollo Web Profesional</title><subtitle type='html'>La diferencia entre programar sitios web y desarrollar aplicaciones profesionales para Internet.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>36</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8837851.post-4659363345646100791</id><published>2011-08-11T12:47:00.006-03:00</published><updated>2011-08-11T13:21:43.819-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Configurar ProFTPD en Ubuntu para que los usuarios vean sólo el home folder</title><content type='html'>Me costó mucho encontrar información sobre cómo configurar ProFTPD Server para que los usuarios, al conectarse, vean la carpeta correspondiente como si fuera un root ( / ).&lt;br /&gt;&lt;h3&gt;Ejemplo de lo que sucede normalmente&lt;/h3&gt;1) Tenemos Ubuntu Server (o similar) con ProFTPD Server.&lt;br /&gt;2) Creamos un usuario con el único fin de ser un login de FTP. Por ejemplo: usuarioftp.&lt;br /&gt;3) Seteamos el "home folder" de este usuario en /home/usuarioftp&lt;br /&gt;4) Entramos por ftp con ese usuario y vemos lo siguiente:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-du9hAJI_Dkg/TkP9pWluvvI/AAAAAAAABHQ/5VKo3d_rhuU/s1600/ProFTPD.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 267px;" src="http://1.bp.blogspot.com/-du9hAJI_Dkg/TkP9pWluvvI/AAAAAAAABHQ/5VKo3d_rhuU/s400/ProFTPD.jpg" alt="" id="BLOGGER_PHOTO_ID_5639630045278289650" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Vemos el home folder del usuario creado pero también podemos ir hacia "arriba" y ver el resto de las carpetas.&lt;br /&gt;&lt;h3&gt;Solución&lt;/h3&gt;1) Creamos un grupo en Ubuntu que contenga a los usuarios que crearemos sólo con el objetivo de conectarse por FTP. Ejemplo: usuariosftpexternos.&lt;br /&gt;&lt;br /&gt;2) Creamos nuestro primer usuario "usuarioftp", seteamos su home folder y lo agregamos al grupo "usuariosftpexternos".&lt;br /&gt;&lt;br /&gt;3) Editamos el archivo proftpd.conf que normalmente está en: /etc/proftpd/proftpd.conf&lt;br /&gt;&lt;br /&gt;4) Agregamos la siguiente línea:&lt;br /&gt;DefaultRoot ~ usuariosftpexternos&lt;br /&gt;(el símbolo ~ hace referencia al home folder del usuario).&lt;br /&gt;&lt;br /&gt;5) Reiniciamos el servicio de ProFTPD.&lt;br /&gt;&lt;br /&gt;Listo! De ahora en más, todos los usuarios que creemos en Ubuntu y que pertenezcan al grupo "usuariosftpexternos", accederán a su Home Folder cuando se conecten por FTP y verán esa carpeta como root. Es decir, no podrán ver el resto de las carpetas del servidor.&lt;br /&gt;&lt;h3&gt;Explicación&lt;/h3&gt;El comando DefaultRoot tiene la siguiente sintáxis:&lt;br /&gt;&lt;br /&gt;DefaultRoot folder grupo, [!grupo2]&lt;br /&gt;&lt;br /&gt;se lee: "Definí como root la carpeta "folder" para los usuarios que estén "grupo", pero no aplica si el usuario pertenece a "grupo2" (opcional).&lt;br /&gt;&lt;br /&gt;La instrucción en el paso 4 se lee: "Definí como root la carpeta "Home Folder" respectiva de los usuarios que estén en el grupo "usuariosftpexternos".&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-4659363345646100791?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/4659363345646100791/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=4659363345646100791' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4659363345646100791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4659363345646100791'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2011/08/configurar-proftpd-en-ubuntu-para-que.html' title='Configurar ProFTPD en Ubuntu para que los usuarios vean sólo el home folder'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/-du9hAJI_Dkg/TkP9pWluvvI/AAAAAAAABHQ/5VKo3d_rhuU/s72-c/ProFTPD.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-2348304683621021679</id><published>2010-11-03T13:28:00.008-03:00</published><updated>2010-11-03T13:39:17.096-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Internet'/><title type='text'>Diarios online el día de la muerte de Nestor Kirchner</title><content type='html'>Será por "deformación profesional" pero, cuando sucede un evento de  semejante naturaleza como la muerte del ex presidente, no puedo evitar  navegar los sitios de noticias para ver su comportamiento.&lt;br /&gt;&lt;br /&gt;Este es el resumen:&lt;br /&gt;&lt;br /&gt;Clarin, funcionó correctamente, tanto la portada...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Aqo-TwbVz90/TNGN_bRSSNI/AAAAAAAABC8/eCdQpNPv5Lw/s1600/clarin_home.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 282px;" src="http://3.bp.blogspot.com/_Aqo-TwbVz90/TNGN_bRSSNI/AAAAAAAABC8/eCdQpNPv5Lw/s400/clarin_home.jpg" alt="" id="BLOGGER_PHOTO_ID_5535361537806584018" border="0" /&gt;&lt;/a&gt;... como las notas internas&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Aqo-TwbVz90/TNGOHJg7XFI/AAAAAAAABDE/bRlBkF0BuXk/s1600/clarin_interna.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 287px;" src="http://3.bp.blogspot.com/_Aqo-TwbVz90/TNGOHJg7XFI/AAAAAAAABDE/bRlBkF0BuXk/s400/clarin_interna.jpg" alt="" id="BLOGGER_PHOTO_ID_5535361670479305810" border="0" /&gt;&lt;/a&gt;La Nación, con la portada estática respondió muy lento...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Aqo-TwbVz90/TNGOwGD1viI/AAAAAAAABDM/B8Bx3qo4fe8/s1600/lanacion_home.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 273px;" src="http://1.bp.blogspot.com/_Aqo-TwbVz90/TNGOwGD1viI/AAAAAAAABDM/B8Bx3qo4fe8/s400/lanacion_home.jpg" alt="" id="BLOGGER_PHOTO_ID_5535362373926632994" border="0" /&gt;&lt;/a&gt;... pero el resto del sitio (las notas) no funcionaron.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_Aqo-TwbVz90/TNGO_aCFWdI/AAAAAAAABDU/IZSYtd7TwBo/s1600/lanacion_interna.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 273px;" src="http://2.bp.blogspot.com/_Aqo-TwbVz90/TNGO_aCFWdI/AAAAAAAABDU/IZSYtd7TwBo/s400/lanacion_interna.jpg" alt="" id="BLOGGER_PHOTO_ID_5535362636986014162" border="0" /&gt;&lt;/a&gt;Crónica rápidamente dejó de funcionar&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Aqo-TwbVz90/TNGPNKzx8FI/AAAAAAAABDc/djScuHCV6cY/s1600/cronica_home.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 273px;" src="http://1.bp.blogspot.com/_Aqo-TwbVz90/TNGPNKzx8FI/AAAAAAAABDc/djScuHCV6cY/s400/cronica_home.jpg" alt="" id="BLOGGER_PHOTO_ID_5535362873417658450" border="0" /&gt;&lt;/a&gt;Lo mismo pasó con Perfil&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Aqo-TwbVz90/TNGPWTvPjeI/AAAAAAAABDk/ptarY2LKWcc/s1600/perfil_home.jpg"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 273px;" src="http://4.bp.blogspot.com/_Aqo-TwbVz90/TNGPWTvPjeI/AAAAAAAABDk/ptarY2LKWcc/s400/perfil_home.jpg" alt="" id="BLOGGER_PHOTO_ID_5535363030433369570" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;TN no funcionó durante la mañana, pero solucionaron el problema por la tarde. Por su parte, Infobae funcionó sin inconvenientes.&lt;br /&gt;&lt;br /&gt;Internet es hoy el principal medio de información y, mientras algunos se lo toman en serio, otros parece que todavía les falta.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-2348304683621021679?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/2348304683621021679/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=2348304683621021679' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/2348304683621021679'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/2348304683621021679'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2010/11/diarios-online-el-dia-de-la-muerte-de.html' title='Diarios online el día de la muerte de Nestor Kirchner'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Aqo-TwbVz90/TNGN_bRSSNI/AAAAAAAABC8/eCdQpNPv5Lw/s72-c/clarin_home.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-6497354474759556626</id><published>2010-01-15T19:02:00.003-03:00</published><updated>2010-01-15T19:06:48.875-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='algoritmos geneticos'/><title type='text'>Algoritmos genéticos – demo versión 2</title><content type='html'>&lt;object height="255" width="420"&gt;&lt;param name="movie" value="http://www.youtube.com/v/LIcEdUypJdc&amp;amp;hl=es_ES&amp;amp;fs=1&amp;amp;rel=0"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/LIcEdUypJdc&amp;amp;hl=es_ES&amp;amp;fs=1&amp;amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="255" width="420"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Debo decir dos cosas acerca de los algoritmos genéticos. Primero &lt;span style="font-weight: bold;"&gt;¡están buenísimos!&lt;/span&gt;, una vez que te metés en tema resulta totalmente atrapante. Segundo: &lt;span style="font-weight: bold;"&gt;en la diversidad está la solución&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;El video muestra una actualización de la demo. Esta actualización tiene &lt;span style="font-weight: bold;"&gt;cinco diferentes algoritmos de cruce&lt;/span&gt; (para crear descendencia) y &lt;span style="font-weight: bold;"&gt;cinco diferentes algoritmos de mutación&lt;/span&gt;. La aplicación permite seleccionar con cuales y cuantos algoritmos se quiere trabajar.&lt;br /&gt;&lt;br /&gt;Los resultados son bastante elocuentes: &lt;span style="font-weight: bold;"&gt;cuanto más algoritmos utilizo, menor población inicial&lt;/span&gt; (y por consiguiente menor tiempo) necesitamos para resolver el problema. Cuanto más diversidad, más probabilidad de encontrar mejores soluciones.&lt;br /&gt;&lt;br /&gt;En la primer demo, para 20 ciudades necesitábamos una población inicial de 1000 individuos y ahora (utilizando los 10 algoritmos juntos) con sólo 250 llegamos al mismo resultado y en forma mucho más veloz.&lt;br /&gt;&lt;br /&gt;Ahora empezó la etapa de optimización y “emprolijamiento” de código, agregar algunas validaciones para los campos y demás.&lt;br /&gt;&lt;br /&gt;¡Si se les ocurre alguna problemática para resolver con algoritmos genéticos avisen!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-6497354474759556626?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/6497354474759556626/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=6497354474759556626' title='3 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/6497354474759556626'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/6497354474759556626'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2010/01/algoritmos-geneticos-demo-version-2.html' title='Algoritmos genéticos – demo versión 2'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-3526287867169573980</id><published>2010-01-06T16:31:00.004-03:00</published><updated>2010-01-06T16:42:32.048-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='algoritmos geneticos'/><title type='text'>Algoritmos Genéticos – Mi primer demo</title><content type='html'>Finalmente puedo mostrar avances en mi aprendizaje de &lt;a href="http://es.wikipedia.org/wiki/Algoritmo_gen%C3%A9tico"&gt;Algoritmos Genéticos&lt;/a&gt;. Luego de varias semanas de lectura y algunas pruebas me puse a construir mi primera aplicación.&lt;br /&gt;&lt;h3&gt;Problema del Viajante&lt;/h3&gt;La aplicación busca una solución al &lt;a href="http://es.wikipedia.org/wiki/Problema_del_viajante"&gt;problema del viajante&lt;/a&gt;. Elegí este problema porque es el más conocido para explicar algoritmos genéticos y además porque tiene características impactantes.&lt;br /&gt;&lt;br /&gt;Tiene un enunciado muy simple: &lt;span style="font-weight: bold;"&gt;un viajante debe recorrer una cantidad dada de ciudades, ¿cuál es la ruta que pasa por todas las ciudades y que recorre la menor distancia?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Tienen una solución muy complicada: deberíamos calcular todas las rutas posibles y seleccionar la más corta. El problema es que para sólo 12 ciudades existen 479.001.600 rutas posibles. Si una computadora pudiera calcular la distancia de una ruta en un microsegundo (millonésima parte de un segundo) tardaría 3 segundos en resolver el problema para 10 ciudades, más de medio minuto para 11 y &lt;span style="font-weight: bold;"&gt;777.146 años para 20 ciudades&lt;/span&gt; (fuente Wikipedia).&lt;br /&gt;&lt;h3&gt;Mi implementación con Algoritmos Genéticos&lt;/h3&gt;Para crear descendencia, la aplicación elige un operador de cruce al azar. Hasta ahora sólo incluí tres: PMX (Correspondencia Parcial, Goldberg y Lingle, 1985), CX (Cruce basado en ciclos, Oliver y col., 1987) y OX1 (Cruce basado en orden, Davis, 1985).&lt;br /&gt;&lt;br /&gt;Y sólo implementé un algoritmo de mutación: DM (basado en desplazamiento, Michalewizc, 1997).&lt;br /&gt;&lt;br /&gt;Para mi sorpresa, los resultados son muy buenos incluso usando un solo algoritmo de cruce (cualquiera de ellos) y sin mutación.&lt;br /&gt;&lt;h3&gt;Sobre la Demo&lt;/h3&gt;En la demo dispuse 20 ciudades en círculo (ya que es muy evidente cual es la mejor ruta), una población inicial de 1000 individuos, un máximo de 50 generaciones (aunque la solución la encuentra por la generación 35 aprox.), de cada generación se queda con el 25% “mejor adaptado”, y el 10% de la descendencia incluye alguna mutación.&lt;br /&gt;&lt;br /&gt;La aplicación está construída en C# .NET 3.5 y la idea es agregar más algoritmos tanto de cruce como de mutación.&lt;br /&gt;&lt;br /&gt;&lt;object height="255" width="420"&gt;&lt;param name="movie" value="http://www.youtube.com/v/KdrfFFWwWiU&amp;amp;hl=es_ES&amp;amp;fs=1&amp;amp;"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/KdrfFFWwWiU&amp;amp;hl=es_ES&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="255" width="420"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-3526287867169573980?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/3526287867169573980/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=3526287867169573980' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/3526287867169573980'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/3526287867169573980'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2010/01/algoritmos-geneticos-mi-primer-demo.html' title='Algoritmos Genéticos – Mi primer demo'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-206774055252180859</id><published>2009-10-16T17:12:00.001-03:00</published><updated>2009-10-16T17:12:19.098-03:00</updated><title type='text'>Exportar Microsoft Project día por día versión Office 2007 Español</title><content type='html'>&lt;p&gt;Hace varios años publique un post sobre &lt;a title="Exportar Microsoft Project día por día" href="http://webdevpro.blogspot.com/2006/05/exportar-microsoft-project-da-por-da.html"&gt;Exportar Microsoft Project día por día&lt;/a&gt; basado en Office 2003.&lt;/p&gt;  &lt;p&gt;Este post es creado en respuesta a todos aquellos que me pidieron actualizar la &lt;strong&gt;macro para la versión 2007 de Office&lt;/strong&gt;.&lt;/p&gt;  &lt;h3&gt;Cambios en las referencias&lt;/h3&gt;  &lt;p&gt;&lt;a href="http://lh5.ggpht.com/_Aqo-TwbVz90/StjTnMrAxWI/AAAAAAAAA90/CpOtW5NeyyY/s1600-h/References%5B10%5D.gif"&gt;&lt;img title="References" style="border-right: 0px; border-top: 0px; display: block; float: none; margin-left: auto; border-left: 0px; margin-right: auto; border-bottom: 0px" height="327" alt="References" src="http://lh5.ggpht.com/_Aqo-TwbVz90/StjTny2JU2I/AAAAAAAAA94/57SOZ25Iuug/References_thumb%5B3%5D.gif?imgmax=800" width="404" border="0" /&gt;&lt;/a&gt; Esta es la principal diferencia con la versión Office 2003.&lt;/p&gt;  &lt;h3&gt;Cambios en el formulario de exportación&lt;/h3&gt;  &lt;p&gt;&lt;img title="frmTimeScaled" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="287" alt="frmTimeScaled" src="http://lh3.ggpht.com/_Aqo-TwbVz90/StjToenlP-I/AAAAAAAAA98/Snbwyl6qd04/frmTimeScaled%5B5%5D.gif?imgmax=800" width="266" border="0" /&gt; &lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Aproveché y modifiqué el formulario para que &lt;strong&gt;Start Date&lt;/strong&gt; sea la fecha de inicio configurada en el proyecto y &lt;strong&gt;End Date&lt;/strong&gt; sea la fecha actual.&lt;/p&gt;  &lt;h3&gt;Descarga y tutorial de uso&lt;/h3&gt;  &lt;p&gt;Ya pueden descargar el archivos de Project 2007 con la macro para &lt;a title="Exportar Microsoft Project día por día versión Office 2007" href="http://www.bdamian.com.ar/webdevpro/Macro%20Export%20TimeScaled%202007.mpp"&gt;Exportar Microsoft Project día por día versión Office 2007&lt;/a&gt;. El mismo archivo explica como instalarla y utilizarla.&lt;/p&gt;  &lt;p&gt;Para más datos y explicaciones técnicas pueden ver el post de la &lt;a title="Exportar Microsoft Project día por día" href="http://webdevpro.blogspot.com/2006/05/exportar-microsoft-project-da-por-da.html"&gt;versión original&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-206774055252180859?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/206774055252180859/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=206774055252180859' title='8 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/206774055252180859'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/206774055252180859'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2009/10/exportar-microsoft-project-dia-por-dia.html' title='Exportar Microsoft Project día por día versión Office 2007 Español'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_Aqo-TwbVz90/StjTny2JU2I/AAAAAAAAA94/57SOZ25Iuug/s72-c/References_thumb%5B3%5D.gif?imgmax=800' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-4301468737491079775</id><published>2009-09-15T17:48:00.001-03:00</published><updated>2009-09-15T17:48:45.369-03:00</updated><title type='text'>Mantener viva una aplicación .NET en IIS con Web Scheduler Tasks</title><content type='html'>&lt;p&gt;&lt;a title="Web Scheduled Tasks en ASP.NET 2.0, 3.0, 3.5 y MVC" href="http://webdevpro.blogspot.com/2009/09/web-scheduled-tasks-en-aspnet-20-30-35.html" target="_blank"&gt;Web Scheduled Tasks en ASP.NET&lt;/a&gt; nos sirve para ejecutar tareas programadas cada determinado intervalo de tiempo. Está pensado para Aplicaciones Web alojadas en Hostings que no nos permiten acceder al servidor.&lt;/p&gt;  &lt;p&gt;Pero para asegurarnos de que las tareas se ejecuten, &lt;strong&gt;debemos mantener “viva” la aplicación&lt;/strong&gt;. Este se convierte entonces en la primer tarea a configurar en Web Scheduled Tasks.&lt;/p&gt;  &lt;h3&gt;WebRequestTask para Web Scheduled Tasks&lt;/h3&gt;  &lt;p&gt;Actualicé el &lt;a title="El sitio Web demo en ASP.NET 2.0 con Web Scheduled Tasks implementado" href="http://www.bdamian.com.ar/webdevpro/DemoScheduler.rar" target="_blank"&gt;sitio demo de Web Scheduled Tasks&lt;/a&gt; para incorporar un objeto &lt;strong&gt;IScheduleable&lt;/strong&gt; cuya misión es mantener viva una aplicación a través de un WebRequest.&lt;/p&gt;  &lt;p&gt;El código es muy sencillo y es el siguiente:&lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh6.ggpht.com/_Aqo-TwbVz90/Sq_9pIAWzkI/AAAAAAAAA8s/K11l25xuJMQ/s1600-h/WebRequestTask%5B3%5D.gif"&gt;&lt;img title="WebRequestTask" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="374" alt="WebRequestTask" src="http://lh5.ggpht.com/_Aqo-TwbVz90/Sq_9rOuxK1I/AAAAAAAAA8w/Ou-m5P8Qf10/WebRequestTask_thumb%5B1%5D.gif?imgmax=800" width="404" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Lo que hace es tomar todas las entradas del Web.config cuyo &lt;strong&gt;“name”&lt;/strong&gt; comience con &lt;strong&gt;“webRequest”&lt;/strong&gt; y hace un request a la URL indicada en &lt;strong&gt;“value”&lt;/strong&gt;. El sitio demo tiene la siguiente entrada en el Web.config:&lt;/p&gt;  &lt;p&gt;&lt;font color="#8000ff"&gt;&amp;lt;&lt;/font&gt;&lt;font color="#804040"&gt;add&lt;/font&gt; &lt;font color="#ff0000"&gt;key&lt;/font&gt;=&amp;quot;&lt;font color="#8000ff"&gt;webRequest01&lt;/font&gt;&amp;quot; &lt;font color="#ff0000"&gt;value&lt;/font&gt;=&amp;quot;&lt;a href="http://localhost:38652/DemoScheduler/Default.aspx&amp;quot;"&gt;http://localhost:38652/DemoScheduler/Default.aspx&amp;quot;&lt;/a&gt; &lt;font color="#8000ff"&gt;/&amp;gt;&lt;/font&gt;&lt;/p&gt;  &lt;p&gt;Deberán modificar la URL. En mi PC, el sitio está en el puerto 38652, si el puerto es 80 no hay necesidad de indicarlo.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-4301468737491079775?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/4301468737491079775/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=4301468737491079775' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4301468737491079775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4301468737491079775'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2009/09/mantener-viva-una-aplicacion-net-en-iis.html' title='Mantener viva una aplicación .NET en IIS con Web Scheduler Tasks'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/_Aqo-TwbVz90/Sq_9rOuxK1I/AAAAAAAAA8w/Ou-m5P8Qf10/s72-c/WebRequestTask_thumb%5B1%5D.gif?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-3082680930936220947</id><published>2009-09-14T15:42:00.001-03:00</published><updated>2009-09-14T15:42:58.754-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Web Scheduled Tasks en ASP.NET 2.0, 3.0, 3.5 y MVC</title><content type='html'>&lt;p&gt;¿Cómo hacer para automatizar tareas en nuestro sitio Web si no tenemos acceso al servidor ni a los Scheduled Tasks? &lt;/p&gt;  &lt;p&gt;&lt;a title="Omar Al Zabir on CodeProject" href="http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=138289" target="_blank"&gt;Omar Al Zabir&lt;/a&gt; nos ofrece una solución muy creativa: utilizar el evento que genera un objeto al ser removido del caché. &lt;a title="Simulate a Windows Service using ASP.NET to run scheduled jobs" href="http://www.codeproject.com/KB/aspnet/ASPNETService.aspx" target="_blank"&gt;Aquí la solución de Omar&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;Tomando la solución propuesta pero intentando convertirla en lo más portable y menos invasiva posible desarrolle el &lt;strong&gt;Web Scheduled Tasks&lt;/strong&gt; para ASP.NET 2.0 o superior. Funciona también con MVC. &lt;/p&gt;  &lt;h3&gt;Cómo funciona Web Scheduled Tasks &lt;/h3&gt;  &lt;p&gt;Web Scheduled Tasks se inicia en el &lt;strong&gt;Application_Start&lt;/strong&gt; del &lt;strong&gt;global.asax&lt;/strong&gt; y crea una lista de objetos &lt;strong&gt;IScheduleable&lt;/strong&gt; obteniendo los datos del &lt;strong&gt;web.config&lt;/strong&gt;. &lt;/p&gt;  &lt;p&gt;En el web.config se indica cada cuanto tiempo el Web Scheduled Tasks llamará a las tareas y lo único que les proveerá son los objetos &lt;strong&gt;Application&lt;/strong&gt; y &lt;strong&gt;Cache&lt;/strong&gt; ya que de otra forma no estarían disponibles. El Resto depende de la tarea: cuando ejecutarse, cómo controlar si ya se está ejecutando, log de errores, etc. &lt;/p&gt;  &lt;p&gt;Las tareas se ejecutarán en un thread que no estará vinculado con un request de un usuario por lo que &lt;strong&gt;no se puede utilizar cosas como HttpContext.Current o Server.MapPath&lt;/strong&gt;. &lt;/p&gt;  &lt;h3&gt;Implementar Web Scheduled Tasks en 4 pasos &lt;/h3&gt;  &lt;p&gt;&lt;strong&gt;Paso 1:&lt;/strong&gt; copiar&amp;#160; &lt;a title="WebScheduledTasks.dll" href="http://www.bdamian.com.ar/webdevpro/bd.WebScheduledTasks.dll.rar" target="_blank"&gt;WebScheduledTasks.dll&lt;/a&gt; en la carpeta &lt;strong&gt;BIN&lt;/strong&gt; de nuestra Aplicación Web. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Paso 2:&lt;/strong&gt; crear las clases que implementen &lt;strong&gt;IScheduleable&lt;/strong&gt;. La interfaz es muy sencilla con un método y dos propiedades. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Aqo-TwbVz90/Sq6OpOdNFQI/AAAAAAAAA8M/xDb4Z_nexZM/s1600-h/IScheduleable%5B5%5D.gif"&gt;&lt;img title="IScheduleable" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="225" alt="IScheduleable" src="http://lh6.ggpht.com/_Aqo-TwbVz90/Sq6OpkjhnMI/AAAAAAAAA8Q/QdHlDTZIuCE/IScheduleable_thumb%5B3%5D.gif?imgmax=800" width="404" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;El método &lt;strong&gt;Run&lt;/strong&gt; recibe el objeto Application y el objeto Cache ya que de otra forma serían inaccesibles. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Paso 3:&lt;/strong&gt; configurar Web Scheduled Tasks a través del web.config. Para hacer esto utilicé el muy buen tutorial &lt;a title="Creating Custom Configuration Sections in Web.config using .NET 2.0" href="http://aspnet.4guysfromrolla.com/articles/032807-1.aspx" target="_blank"&gt;Creating Custom Configuration Sections in Web.config using .NET 2.0&lt;/a&gt;. &lt;/p&gt;  &lt;p&gt;Son dos bloques. El primero hay que agregarlo dentro de &lt;strong&gt;&amp;lt;configSections&amp;gt;&lt;/strong&gt; y define la nueva sección del Web.config y la clase que lo controla. El segundo va dentro de &lt;strong&gt;&amp;lt;configuration&amp;gt;&lt;/strong&gt; y son los settings propios de Web Secheduled Tasks. El primer seteo es el &lt;strong&gt;intervalo&lt;/strong&gt; que define cada cuantos minutos el sistema revisará las tareas para ejecutarlas. &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Aqo-TwbVz90/Sq6OpxQCecI/AAAAAAAAA8U/6gFnJEch460/s1600-h/webconfig%5B3%5D.gif"&gt;&lt;img title="web.config" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="279" alt="web.config" src="http://lh5.ggpht.com/_Aqo-TwbVz90/Sq6OqZKx3aI/AAAAAAAAA8Y/CniLcOXLux0/webconfig_thumb%5B1%5D.gif?imgmax=800" width="404" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Dentro de &lt;strong&gt;&amp;lt;tasks&amp;gt;&lt;/strong&gt; están definidos los objetos que deben ejecutarse periódicamente. Debe colocarse el &lt;strong&gt;AssemblyQualifiedName&lt;/strong&gt;, de otra forma no sabrá como instanciarlo. El AssemblyQualifiedName de mi tarea de ejemplo es &lt;strong&gt;&amp;quot;ActualizaHoraTask, App_Code.owmojsxy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null&amp;quot;&lt;/strong&gt;. Si no saben como armarlo, pueden ejecutar esto: &lt;strong&gt;Response.Write((new Objeto()).GetType.AssemblyQualifiedName)&lt;/strong&gt;. &lt;/p&gt;  &lt;p&gt;&lt;strong&gt;Paso 4:&lt;/strong&gt; modificar el &lt;strong&gt;Global.asax&lt;/strong&gt;. Simplemente debemos agregar la siguiente línea al evento &lt;strong&gt;Application_Start&lt;/strong&gt;: &lt;strong&gt;bd.WebScheduledTasks.Scheduler.RunTimer();&lt;/strong&gt; &lt;/p&gt;  &lt;p&gt;&lt;a href="http://lh3.ggpht.com/_Aqo-TwbVz90/Sq6OrCDoOwI/AAAAAAAAA8c/k_XfC1a1bDs/s1600-h/globalasax%5B3%5D.gif"&gt;&lt;img title="globalasax" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="206" alt="globalasax" src="http://lh3.ggpht.com/_Aqo-TwbVz90/Sq6OromeP0I/AAAAAAAAA8g/QgAsfsuIQLo/globalasax_thumb%5B1%5D.gif?imgmax=800" width="404" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;h3&gt;Cómo saber el estado de Web Scheduled Tasks &lt;/h3&gt;  &lt;p&gt;Scheduler expone la propiedad &lt;strong&gt;“Status”&lt;/strong&gt; que devuelve un &lt;strong&gt;XML&lt;/strong&gt; con el estado de &lt;strong&gt;Web Scheduled Tasks&lt;/strong&gt; y las tareas. &lt;/p&gt;  &lt;p&gt;En en XML se puede ver:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;strong&gt;StartTime&lt;/strong&gt;: fecha y hora en que se inicializó Web Scheduled Tasks&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;Interval&lt;/strong&gt;: definido en el Web.config&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;LastTickTime&lt;/strong&gt;: hora a la que se ejecutó por última vez&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;CacheTimerExist&lt;/strong&gt;: muestra si existe el objeto en Cache que permite que todo funcione.&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;LastError&lt;/strong&gt;: en “object” veremos el objeto que generó el último error y en “error” su descripción.&lt;/li&gt;    &lt;li&gt;&lt;strong&gt;ScheduledTasks&lt;/strong&gt;: veremos la lista de los objetos que invocará el sistema. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/_Aqo-TwbVz90/Sq6OsGYhmrI/AAAAAAAAA8k/AmTZysfv5PU/s1600-h/status%5B3%5D.gif"&gt;&lt;img title="Web Scheduled Tasks Status" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="321" alt="Web Scheduled Tasks Status" src="http://lh3.ggpht.com/_Aqo-TwbVz90/Sq6Osh7iHrI/AAAAAAAAA8o/5lHdA8myQ3Q/status_thumb%5B1%5D.gif?imgmax=800" width="404" border="0" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p&gt;Además de Status, Scheduler expone el método &lt;strong&gt;“ScheduleTick”&lt;/strong&gt; que nos permite, tal vez desde algún administrador, ejecutar las tareas programadas manualmente. En realidad lo que lograremos es “despertar” a Web Scheduled Tasks para que revise cuales tareas deben ser ejecutadas. &lt;/p&gt;  &lt;h3&gt;Descargar Web Scheduled Tasks&lt;/h3&gt;  &lt;p&gt;Pueden descargar:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;&lt;a title="WebScheduledTasks.dll" href="http://www.bdamian.com.ar/webdevpro/bd.WebScheduledTasks.dll.rar" target="_blank"&gt;WebScheduledTasks.dll&lt;/a&gt; para copiarla a la carpeta BIN de sus sitios web.&lt;/li&gt;    &lt;li&gt;&lt;a title="El sitio Web demo en ASP.NET 2.0 con Web Scheduled Tasks implementado" href="http://www.bdamian.com.ar/webdevpro/DemoScheduler.rar" target="_blank"&gt;El sitio Web demo en ASP.NET 2.0 con Web Scheduled Tasks implementado&lt;/a&gt;. En este caso, el objeto IScheduleable simplemente actualiza la hora en una variable de Aplicación que es mostrada en la página inicial. También se ve un link a una página que nos muestra el Status de Web Scheduled Tasks.&lt;/li&gt;    &lt;li&gt;&lt;a title="Código fuente de Web Scheduled Tasks" href="http://www.bdamian.com.ar/webdevpro/bd.WebScheduledTasks.rar" target="_blank"&gt;Código fuente de Web Scheduled Tasks&lt;/a&gt;.&lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;&lt;/p&gt;  &lt;p&gt;Espero que les sea útil. Cualquier sugerencia, corrección, o aporte será más que bienvenido.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-3082680930936220947?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/3082680930936220947/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=3082680930936220947' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/3082680930936220947'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/3082680930936220947'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2009/09/web-scheduled-tasks-en-aspnet-20-30-35.html' title='Web Scheduled Tasks en ASP.NET 2.0, 3.0, 3.5 y MVC'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh6.ggpht.com/_Aqo-TwbVz90/Sq6OpkjhnMI/AAAAAAAAA8Q/QdHlDTZIuCE/s72-c/IScheduleable_thumb%5B3%5D.gif?imgmax=800' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-8033073277187509195</id><published>2009-09-07T14:56:00.007-03:00</published><updated>2009-09-08T13:07:59.185-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MVC'/><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Includes en ASP.NET MVC</title><content type='html'>De pronto me surgió la necesidad de usar &lt;span style="font-weight: bold;"&gt;INCLUDES &lt;/span&gt;en el sitio que estoy creando en &lt;span style="font-weight: bold;"&gt;ASP.NET y MVC&lt;/span&gt;. Como siempre fui a Google pero esta vez no encontré ninguna respuesta satisfactoria (alguno recomendó hacer un Html helper para leer el contenido de un archivo).&lt;br /&gt;&lt;br /&gt;Alguien podría preguntar cual es la necesidad de usar Includes teniendo tantas otras alternativas. En mi caso, tengo un proceso que genera HTMLs estáticos y necesito mostrarlos en una "caja" en el sitio.&lt;br /&gt;&lt;br /&gt;Luego de algunas pruebas, estas son las mejores formas, según mi criterio, de usar &lt;span style="font-weight: bold;"&gt;Includes en ASP.NET y MVC&lt;/span&gt;.&lt;br /&gt;&lt;h3&gt;Los includes tradicionales siguen funcionando&lt;/h3&gt;En principio los Includes tradicionales de ASP siguen funcionando, de modo que podemos usar:&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0); font-weight: bold;"&gt;&amp;lt;!--#include virtual="/includes/pagina.htm"--&amp;gt;&lt;/span&gt;&lt;br /&gt;para incluír un html a partir del root. También podemos usar&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0); font-weight: bold;"&gt;&amp;lt;!--#include virtual="../includes/pagina.htm"--&amp;gt;&lt;/span&gt;&lt;br /&gt;para incluir un html en forma relativa a donde "estamos parados".&lt;br /&gt;&lt;br /&gt;Esto funciona incluso si el TAG Include se encuentra dentro de un Partial View. El inconveniente es el de siempre: no podemos usar variables para definir cual archivos queremos incluir. Es decir, &lt;span style="font-weight: bold;"&gt;no podemos hacer esto:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0); font-weight: bold;"&gt;&amp;lt;!--#include virtual="../includes/&lt;span style="color: rgb(0, 0, 0);"&gt;&lt;%= nombreArchivo %&gt;&lt;/span&gt;"--&amp;gt;&lt;/span&gt;&lt;br /&gt;ya que el TAG include se interpretará antes que ASP.NET (aún con MVC donde todo pasa por ASP.NET).&lt;br /&gt;&lt;h3&gt;Includes dinámicos&lt;/h3&gt;La forma que encontré para lograr "Includes dinámicos" es usar la sentencia&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Html.RenderPartial("PartialView")&lt;/span&gt;&lt;br /&gt;La única restricción es que &lt;span style="font-weight: bold;"&gt;el archivo debe tener extensión .ascx&lt;/span&gt; (o aspx) pero &lt;span style="font-weight: bold;"&gt;no necesita la cabecera que lo define como Partial View si guardamos el archivo en la carpeta /Views/Shared&lt;/span&gt;. Es decir, podemos tomar una HTML, le cambiamos la extensión y lo incluimos con Html.RenderPartial.&lt;br /&gt;&lt;br /&gt;En mi caso, el proceso que genera el HTML, ahora lo graba a disco con la extensión .ascx&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-8033073277187509195?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/8033073277187509195/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=8033073277187509195' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8033073277187509195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8033073277187509195'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2009/09/includes-en-aspnet-mvc.html' title='Includes en ASP.NET MVC'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-6700126921215065061</id><published>2009-03-29T20:08:00.001-03:00</published><updated>2009-03-29T20:08:18.834-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Mostrar AdSense sólo cuando el usuario viene de Google en ASP.NET</title><content type='html'>&lt;p&gt;Es bastante común hoy en día encontrarnos con sitios que muestran publicidad de AdSense sólo a los usuarios que llegan a través de Google.&lt;/p&gt;  &lt;p&gt;Por eso se me ocurrió hacer esto mismo pero de la forma más sencilla posible. Nada de estilo refinado en este caso, simplemente lo más fácil de implementar.&lt;/p&gt;  &lt;p&gt;Así nació este User WebControl llamado &lt;strong&gt;refererAdSense&lt;/strong&gt; contenido todo en un solo archivo .ascx. Seguramente el código puede ser mejorado o escrito con un poco más de estilo, sin embargo esto es lo que hice:&lt;/p&gt;  &lt;p&gt;El WebControl comienza con una propiedad “&lt;strong&gt;AdPosition&lt;/strong&gt;” que usaremos para identificar el código de AdSense que se debe mostrar. Un método booleano “&lt;strong&gt;UserFromGoogle&lt;/strong&gt;” que devuelve true si encuentra la cadena “.google.” en el referer del request.&lt;/p&gt;  &lt;p&gt;Por último, un simple SWITCH donde pondremos todos los códigos AdSense de nuestro sitio.&lt;/p&gt;  &lt;p&gt;Para poder usar este User WebControl, simplemente lo copiamos en una carpeta (por ejemplo “/controls”) y, donde queremos la publicidad ponemos el tag “register” al principio de la página:&lt;/p&gt;  &lt;p&gt;&amp;lt;%@ Register Src=&amp;quot;~/controls/refererAdSense.ascx&amp;quot; TagName=&amp;quot;refererAdSense&amp;quot; TagPrefix=&amp;quot;rad&amp;quot; %&amp;gt;&lt;/p&gt;  &lt;p&gt;Y finalmente ponemos el TAG correspondiente donde queremos la publicidad de AdSense:&lt;/p&gt;  &lt;p&gt;&amp;lt;rad:refererAdSense &lt;strong&gt;AdPosition=&amp;quot;topBanner&amp;quot;&lt;/strong&gt; runat=&amp;quot;server&amp;quot; /&amp;gt;&lt;/p&gt;  &lt;p&gt;Como valor de la propiedad “AdPosition” ponemos el nombre que identifica al bloque de AdSense del archivo .ascx.&lt;/p&gt;  &lt;p&gt;Les dejo el &lt;a href="http://www.bdamian.com.ar/webdevpro/refererAdSense.zip"&gt;refererAdSense.asxc&lt;/a&gt; para que lo bajen. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-6700126921215065061?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/6700126921215065061/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=6700126921215065061' title='4 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/6700126921215065061'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/6700126921215065061'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2009/03/mostrar-adsense-solo-cuando-el-usuario.html' title='Mostrar AdSense sólo cuando el usuario viene de Google en ASP.NET'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-8367759280448938406</id><published>2009-02-04T16:49:00.007-02:00</published><updated>2009-02-04T17:01:23.154-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><category scheme='http://www.blogger.com/atom/ns#' term='flv'/><title type='text'>Simulando streaming de video Flash FLV con ASP.NET</title><content type='html'>Esta solución permite reproducir video Flash FLV tal como lo hace &lt;a href="http://www.youtube.com/"&gt;YouTube.com&lt;/a&gt; de forma que podemos adelantar el video aún cuando no se haya descargado la parte que queremos ver.&lt;br /&gt;&lt;br /&gt;La verdadera solución se encuentra en &lt;a href="http://blogs.ugidotnet.org/kfra/archive/2006/10/04/50003.aspx"&gt;kfra&lt;/a&gt; de donde podremos descargar la solución tanto para .NET 1.1 como 2.0.&lt;br /&gt;&lt;br /&gt;Pero para los que no podemos hacer modificaciones en el servidor IIS o (como yo) son amantes de las soluciones sencillas, no intrusivas y contenidas en una sola página es que armé esta versión.&lt;br /&gt;&lt;br /&gt;Se trata de una página ASPX llamada &lt;span style="font-weight: bold;"&gt;getFlv.aspx&lt;/span&gt; que simplemente copiamos en nuestro sitio web e invocamos desde Flash con 2 parametros:&lt;br /&gt;- &lt;span style="font-weight: bold;"&gt;v&lt;/span&gt;: para el nombre del video. Si comienza con “/” será el path relativo al root del sitio. Si no comienza con “/” buscará a partir del lugar donde se encuentre esta página.&lt;br /&gt;- &lt;span style="font-weight: bold;"&gt;start&lt;/span&gt;: para indicar el byte a partir del cual quieren reproducir el video. Si no le pasamos nada, entonces devolverá el video completo.&lt;br /&gt;&lt;br /&gt;Ejemplos:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;http://misitio.dominio.com/videos/getFlv.aspx?v=video.flv (simplemente traerá el video ubicado en la mísma carpeta que getFlv.aspx)&lt;/li&gt;&lt;li&gt;http://misitio.dominio.com/videos/getFlv.aspx?v=/video.flv (traerá el video desde el root del sitio)&lt;/li&gt;&lt;li&gt;http://misitio.dominio.com/videos/getFlv.aspx?v=nuevos/video.flv&amp;amp;start=580014 (traerá el video desde la carpeta "nuevos" ubicada en la misma carpeta que getFlv.aspx y lo entregará a partir del byte 580014)&lt;/li&gt;&lt;/ul&gt;Hay que tener en cuenta que esta página ASPX lo único que hace es devolver el video FLV a partir del byte indicado por parámetro. El resto dependerá del player.&lt;br /&gt;&lt;br /&gt;En la el mismo post de &lt;a href="http://blogs.ugidotnet.org/kfra/archive/2006/10/04/50003.aspx"&gt;kfra&lt;/a&gt; hay un player que pueden usar, pero si quieren desarrollar uno ustedes, deberán tener en cuenta que, para poder reproducir el video, deben pasar un start que coincida con un Key Frame. Para esto deben buscar en la metadata del streaming donde encontrarán dos arrays, Times y FilePositions. No todos los FLV contienen esta metadata y en estos casos deberán usar alguna aplicación para agregarla.&lt;br /&gt;&lt;br /&gt;Aquí les dejo el link a &lt;a href="http://www.bdamian.com.ar/webdevpro/getFlv.rar"&gt;getFlv.aspx&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Por supuesto que todo el crédito es de &lt;a href="http://blogs.ugidotnet.org/kfra/archive/2006/10/04/50003.aspx"&gt;kfra&lt;/a&gt;. Yo sólo hice uso de la programación orientada al copy-paste.&lt;br /&gt;&lt;br /&gt;Saludos&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-8367759280448938406?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/8367759280448938406/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=8367759280448938406' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8367759280448938406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8367759280448938406'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2009/02/simulando-streaming-de-video-flash-flv.html' title='Simulando streaming de video Flash FLV con ASP.NET'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-5046943809689658343</id><published>2008-07-31T15:19:00.001-03:00</published><updated>2008-07-31T15:20:41.195-03:00</updated><title type='text'>Cambio</title><content type='html'>Desde hace más de cuatro años que estoy en una posición de gestión. Esto me ha alejado de la tarea de escribir código. Por este motivo es poco frecuente que encuentre algo para postear en este blog.&lt;br /&gt;&lt;br /&gt;Sin embargo, desarrollar sitios Web no sólo es escribir código por lo que decidí seguir manteniendo este blog y actualizarlo con otro tipo de posts más relacionado con lo que estoy haciendo ahora.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-5046943809689658343?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/5046943809689658343/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=5046943809689658343' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/5046943809689658343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/5046943809689658343'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2008/07/desde-hace-ms-de-cuatro-aos-que-estoy.html' title='Cambio'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-4384746578656076677</id><published>2008-03-03T17:39:00.003-02:00</published><updated>2008-03-03T17:53:52.610-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP'/><title type='text'>ASP en capas: Acceso a Datos</title><content type='html'>El principal punto a proteger en una aplicación Web es la base de datos. En este sentido hay que tener en cuenta que, cuanto más rápido liberemos la conexión, más rápida será nuestra aplicación. Una aplicación Web rápida es indudablemente más robusta que otra que tarda más tiempo en ejecutar.&lt;br /&gt;&lt;br /&gt;Para recuperar datos y mostrarlos a los usuarios generalmente se utiliza un recordset más o menos así:&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;While not &lt;/span&gt;RS.EOF &lt;span style="color: rgb(51, 0, 153);"&gt;Then&lt;/span&gt;&lt;br /&gt;Response.Write(...)&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Wend&lt;/span&gt;&lt;br /&gt;Este método no sólo es lento sino que además estamos utilizando, en la capa de presentación, un objeto de la capa de datos.&lt;br /&gt;&lt;br /&gt;Para solucionar esto hice la clase "clsSql" con la que podemos ejecutar una consulta y obtendremos un Array de dos dimensiones donde la primera son los campos y la segunda son los registros. Para el resto de las operaciones en las que no se obtienen datos, debemos usar el clásico CONN.EXECUTE(query).&lt;br /&gt;&lt;br /&gt;La clase tiene cuatro métodos solamente:&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Public Sub &lt;/span&gt;Conectar (&lt;span style="color: rgb(51, 0, 153);"&gt;byRef&lt;/span&gt; Conn, &lt;span style="color: rgb(51, 0, 153);"&gt;ByVal &lt;/span&gt;ConnectionString)&lt;br /&gt;Para conectarse a la base de datos.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Public Sub&lt;/span&gt; Desconectar (&lt;span style="color: rgb(51, 0, 153);"&gt;byRef&lt;/span&gt; Conn)&lt;br /&gt;Para liberar la conexión.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Public Sub &lt;/span&gt;EjecutarQuery(&lt;span style="color: rgb(51, 0, 153);"&gt;ByRef &lt;/span&gt;Conn, &lt;span style="color: rgb(51, 0, 153);"&gt;ByVal &lt;/span&gt;Query, &lt;span style="color: rgb(51, 0, 153);"&gt;ByRef &lt;/span&gt;Resultado)&lt;br /&gt;Este método ejecuta un "Query" en la conexión indicada y devuelve un Array con los datos obtenidos. Si no obtiene datos entonces "Resultado" estará vacío.&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Public Sub &lt;/span&gt;EjecutarQueryPaginado(&lt;span style="color: rgb(51, 0, 153);"&gt;ByRef &lt;/span&gt;Conn, &lt;span style="color: rgb(51, 0, 153);"&gt;ByVal &lt;/span&gt;Query, &lt;span style="color: rgb(51, 0, 153);"&gt;ByRef &lt;/span&gt;Resultado, &lt;span style="color: rgb(51, 0, 153);"&gt;ByVal &lt;/span&gt;TamanoPagina, &lt;span style="color: rgb(51, 0, 153);"&gt;ByRef &lt;/span&gt;Pagina, &lt;span style="color: rgb(51, 0, 153);"&gt;ByRef &lt;/span&gt;CantPaginas, &lt;span style="color: rgb(51, 0, 153);"&gt;ByRef &lt;/span&gt;CantReg)&lt;br /&gt;Este método ejecuta un "Query" en la conexión indicada y devuelve un Array conteniendo los datos correspondientes a la "Pagina" indicada teniendo en cuenta en "TamanoPagina". También devuelve la cantidad de páginas posibles y la cantidad de registros totales. Si el valor proporcionado de "Pagina" es inválido, lo modifica por el correcto.&lt;br /&gt;&lt;br /&gt;Un ejemplo de uso de esta clase es:&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;Bases&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;conn&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;arrResultado&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;set &lt;/span&gt;Bases = &lt;span style="color: rgb(51, 0, 153);"&gt;new &lt;/span&gt;clsSQL&lt;br /&gt;Bases.Conectar(conn, "File DSN=BaseDeDatos.dsn")&lt;br /&gt;Bases.EjecutarQuery(conn, "Select IdUsuario, Email from usuarios", arrResultado)&lt;br /&gt;Bases.Desconectar(conn)&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;set &lt;/span&gt;clsSQL = &lt;span style="color: rgb(51, 0, 153);"&gt;Nothing&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;a&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;If&lt;/span&gt; IsArray(arrResultado) &lt;span style="color: rgb(51, 0, 153);"&gt;Then &lt;/span&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;'Si arrResultado no es un array es que no se obtuvieron datos&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt; For&lt;/span&gt; a = 0 &lt;span style="color: rgb(51, 0, 153);"&gt;to&lt;/span&gt; &lt;span style="color: rgb(51, 0, 153);"&gt;Ubound&lt;/span&gt;(arrResultado,2)&lt;br /&gt; ... &lt;br /&gt;     IdUsuario: &lt;%=arrresultado(0,a)%&gt;&lt;br /&gt;&lt;br /&gt;     Email: &lt;%=arrresultado(1,a)%&gt;&lt;br /&gt; ...     &lt;span style="color: rgb(51, 0, 153);"&gt;Next&lt;br /&gt;&lt;/span&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;End If&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Les dejo la clase para que la descarguen: &lt;a href="http://www.bdamian.com.ar/webdevpro/clssql.rar"&gt;clsSql&lt;/a&gt;&lt;br /&gt;La URL del post anterior:  &lt;a href="http://webdevpro.blogspot.com/2008/01/asp-en-capas-obtener-valores-por.html"&gt;ASP en capas: obtener valores por QueryString&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-4384746578656076677?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/4384746578656076677/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=4384746578656076677' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4384746578656076677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4384746578656076677'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2008/03/asp-en-capas-acceso-datos.html' title='ASP en capas: Acceso a Datos'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-8948024393100048625</id><published>2008-01-07T20:39:00.000-02:00</published><updated>2008-01-07T20:56:42.669-02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP'/><title type='text'>ASP en capas: obtener valores por QueryString</title><content type='html'>Vamos a comenzar a desarrollar la aplicación que mencionamos en el &lt;a href="http://webdevpro.blogspot.com/2007/12/asp-en-capas.html"&gt;post anterior&lt;/a&gt;, y como queremos hacerlo con estilo, vamos a necesitar un poco de abstracción.&lt;br /&gt;&lt;br /&gt;Lo primero que necesitamos es una forma para obtener valores desde "QueryString". Hace un tiempo comentamos como &lt;a href="http://webdevpro.blogspot.com/2005/05/obteniendo-valores-por-querystring.html"&gt;obtener valores por QueryString en .NET&lt;/a&gt;, ahora necesitamos hacerlo en ASP.&lt;br /&gt;&lt;br /&gt;Hace algún tiempo construí la clase clsRequester para obtener valores por los tres métodos: querystring, form y cookie. Lo más importante de esta clase es que, al llamar a alguno de sus tres métodos públicos, debemos proporcionar un valor por defecto que será el resultado de la función cuando el obtenido no sea válido o no exista. Este valor por defecto debe ser del subtipo que esperamos conseguir (Integer, Byte, String, Date, etc).&lt;br /&gt;&lt;br /&gt;Los métodos públicos de la clase clsRequester son:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;public function &lt;/span&gt;Obtener(&lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;metodo, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;key, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;def)&lt;br /&gt;Este método obtiene el valor de "key" por el "metodo" indicado. El dato obtenido se intenta convertir al subtipo de dato de "def". En caso de error se devuelve "def".&lt;br /&gt;Ej: p = objRequester.Obtener("QueryString", "pagina", cint(1))&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;public function &lt;/span&gt;ObtenerB(&lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;metodo, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;key, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;def, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;min, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;max)&lt;br /&gt;Obtiene el valor de "key" por el "metodo" indicado pero valida que dicho valor se encuentre entre un valor mínimo y un máximo (la B de ObtenerB es por Between). En caso de error o superar los límites, devuelve "def".&lt;br /&gt;Ej: p = objRequester.ObtenerB("Cookie", "BirthDate", cdate("1901-01-01"), cdate("1910-01-01"), cdate("2000-12-31"))&lt;br /&gt;Obtiene el de la cookie "BirthDate" y verifica que este comprendida entre las fechas indicadas. En caso de error devuelve la fecha 1 de enero del 1901.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;public function&lt;/span&gt; ObtenerR(&lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;metodo, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;key, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;def, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;regex)&lt;br /&gt;Obtiene el valor de "key" por el "metodo" indicado y lo valida con una Regular Expression. Este método es, por supuesto, para obtener "strings".&lt;br /&gt;Ej: p = objRequester.ObtenerR("Form", "email", "", "\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*")&lt;br /&gt;En este ejemplo, el email debe ser validado por la Reqular Expression, en caso contrario devuelve un string vacío.&lt;br /&gt;&lt;br /&gt;Los métodos privados son:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;Private Sub&lt;/span&gt; Class_Initialize&lt;br /&gt;Para inicializar las variables.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;Private Sub&lt;/span&gt; Class_Terminate&lt;br /&gt;Si usamos un objeto Regular Expression lo destruye.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;private sub &lt;/span&gt;MetodoDefault(&lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;metodo)&lt;br /&gt;Verifica que exista el método indicado exista.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;private function&lt;/span&gt; getValue(&lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;metodo, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;key)&lt;br /&gt;Obtiene el valor por el método indicado.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;private function&lt;/span&gt; Castear(&lt;span style="color: rgb(51, 0, 153);"&gt;byref &lt;/span&gt;dato, &lt;span style="color: rgb(51, 0, 153);"&gt;byval &lt;/span&gt;def)&lt;br /&gt;Convierte el valor obtenido al subtipo del valor por defecto.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold; color: rgb(51, 0, 153);"&gt;private Function &lt;/span&gt;ValidaExpresionesRegulares(&lt;span style="color: rgb(51, 0, 153);"&gt;ByVal &lt;/span&gt;texto, &lt;span style="color: rgb(51, 0, 153);"&gt;ByVal &lt;/span&gt;patron)&lt;br /&gt;Valida el valor con la Regular Expression&lt;br /&gt;&lt;br /&gt;Esta clase la utilizo en cada una de las aplicaciones que debo construir en ASP.&lt;br /&gt;&lt;a href="http://www.bdamian.com.ar/webdevpro/clsRequester.rar"&gt;Aquí les dejo la clase Requester para que se la puedan bajar&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-8948024393100048625?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/8948024393100048625/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=8948024393100048625' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8948024393100048625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8948024393100048625'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2008/01/asp-en-capas-obtener-valores-por.html' title='ASP en capas: obtener valores por QueryString'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-4067474544820579084</id><published>2007-12-25T13:31:00.000-03:00</published><updated>2007-12-25T18:47:36.133-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP'/><title type='text'>ASP en capas</title><content type='html'>Una de las cosas que me gustan del lenguaje ASP es que, como no tiene más actualizaciones desde 2001, mi conocimiento sigue siendo válido aún cuando hace tiempo que no lo uso ;)&lt;br /&gt;&lt;br /&gt;Me sorprendí mucho al entrarme de que, aún hoy, mucha gente sigue desarrollando en ASP. He recibido muchos comentarios acerca de la nota que escribí aquí mismo hace un tiempo sobre "&lt;a href="http://webdevpro.blogspot.com/2004/12/separacin-en-capas.html"&gt;Separación en capas&lt;/a&gt;" pidiéndome ejemplos en ASP.&lt;br /&gt;&lt;br /&gt;ASP no es un lenguaje demasiado elegante, pero igualmente podemos desarrollar con estilo.&lt;br /&gt;&lt;br /&gt;En las siguientes notas vamos a ver los requisitos para desarrollar en capas con ASP y finalizaremos con un ejemplo utilizando todos los elementos mencionados.&lt;br /&gt;&lt;br /&gt;Lo que vamos a lograr es separar nuestro código en las siguientes capas:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Capa de presentación&lt;/span&gt;: optimizada para mostrar datos rapidamente.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Capa de negocio&lt;/span&gt;: donde se define el qué y el cuando.&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Capa de datos&lt;/span&gt;: que sabe como almacenar y recuperar datos.&lt;/li&gt;&lt;/ul&gt;La aplicación que vamos a desarrollar es una página simple que muestre un "drop down" de categorías y, al seleccionar alguna, veremos un listado de elementos. Vamos a tener en cuenta todas aquellas cosas que mencionamos en posts anteriores como:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://webdevpro.blogspot.com/2005/05/obteniendo-valores-por-querystring.html"&gt;Obtener valores por QueryString&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://webdevpro.blogspot.com/2004/11/administracin-de-errores-parte-1.html"&gt;Administración de errores 1&lt;/a&gt; y &lt;a href="http://webdevpro.blogspot.com/2004/11/administracin-de-errores-parte-2.html"&gt;2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://webdevpro.blogspot.com/2004/11/paginas-de-proceso-de-datos.html"&gt;Páginas de proceso de datos&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://webdevpro.blogspot.com/2004/11/qu-y-cundo-vs-cmo.html"&gt;Qué y cuándo vs cómo&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-4067474544820579084?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/4067474544820579084/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=4067474544820579084' title='1 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4067474544820579084'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4067474544820579084'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2007/12/asp-en-capas.html' title='ASP en capas'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-8939168092330273901</id><published>2007-11-01T11:36:00.000-03:00</published><updated>2007-11-01T11:41:24.585-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Internet'/><title type='text'>Los efectos de la publicidad online</title><content type='html'>Un estudio realizado con diferentes tipos de publicidad online, buscando datos sobre la sensación de intrución sobre los usuarios y sus efectos, arrojó resultados muy interesantes.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;El tipo de publicidad "pop-under" resulta intrusivo para el &lt;span style="font-weight: bold;"&gt;28%&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Los "pop-ups" resultaron intrusivos para el &lt;span style="font-weight: bold;"&gt;26,1%&lt;/span&gt; de las personas&lt;/li&gt;&lt;li&gt;Los inline banners son intrusivos para el &lt;span style="font-weight: bold;"&gt;21,1%&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;Otra forma de leer estos números sería remarcar que los popups resultan un&lt;span style="font-weight: bold;"&gt; 24%&lt;/span&gt; y los pop-under un &lt;span style="font-weight: bold;"&gt;33,1&lt;/span&gt; más intrusivos que la publicidad inline.&lt;br /&gt;&lt;br /&gt;Un dato adicional es que esta percepción no varía significativamente si la publicidad tiene o no relación con el contenido de los sitios.&lt;br /&gt;&lt;br /&gt;En todos los casos, a mayor intrución, menor es la intención de regresar al sitio visitado.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Fuente:&lt;/span&gt; Communications of the ACM, March 2007&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-8939168092330273901?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/8939168092330273901/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=8939168092330273901' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8939168092330273901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/8939168092330273901'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2007/11/los-efectos-de-la-publicidad-online.html' title='Los efectos de la publicidad online'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-2263597966157150424</id><published>2007-10-16T17:35:00.000-03:00</published><updated>2007-10-16T17:45:41.682-03:00</updated><title type='text'>GeoIP WebService con ASP.NET y C#</title><content type='html'>Para este ejemplo me enfoqué en los elementos necesarios para crear un WebService en ASP.NET para obtener el país de origen de una IP. Por este motivo deben tener en cuenta que temas como separación de capas, administración de errores, configuraciones en web.config y otras cosas muy importantes deberán ser implementadas por ustedes.&lt;br /&gt;&lt;br /&gt;Este ejemplo consta de una sola página .asmx y, aunque no es lo recomendado, debo admitir que hay veces en las que me gusta que toda la solución pueda ser fácilmente portada copiando un solo archivo.&lt;br /&gt;&lt;br /&gt;Con todas estas salvedades, pasemos a la receta.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ingredientes&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Una base de datos de IP. Recomiendo la de &lt;a href="http://www.maxmind.com/app/geoip_country"&gt;MaxMind - GeoLite Country / Open Source IP Address to Country Database&lt;/a&gt;. Para este ejemplo elegimos la versión gratuita.&lt;/li&gt;&lt;li&gt;Una base de datos de una sola tabla. Para este ejemplo elegimos Microsoft Access para hacer el ejemplo más sencillo y transportable.&lt;/li&gt;&lt;li&gt;Una Regular Expression para validar IPs.&lt;/li&gt;&lt;li&gt;Un algoritmo para convertir un string IP en un double.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Preparación&lt;/span&gt;&lt;br /&gt;1.- Nos bajamos la base gratuita de IPs desde la página de MaxMind (para una solución comercial deberíamos adquirir la versión full) desde haciendo “clic aquí”.&lt;br /&gt;&lt;br /&gt;2.- Creamos una nueva base de datos Access y creamos una tabla que llamaremos “IPSegments”. Esta tabla deberá contener los siguientes campos (si respetamos el orden será luego más fácil importar la base).&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;beginIP &lt;/span&gt;– Text&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;endIp &lt;/span&gt;– Text&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;beginNro &lt;/span&gt;– Numbrer (double)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;endNro &lt;/span&gt;– Numbrer (double)&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;CountryCode &lt;/span&gt;– Text&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;ContryName &lt;/span&gt;- Text&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;3.- Importamos la base de IPs a la base que creamos en Access. Para este ejemplo la base debe estar en la misma carpeta del Web Server donde estará nuestro Webservice.&lt;br /&gt;&lt;br /&gt;4.- Creamos nuestro archivo .asmx.&lt;br /&gt;Luego de la declaración de WebService nos aseguramos de incluir las siguientes líneas “using”:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;using &lt;/span&gt;System.Data;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;using &lt;/span&gt;System.Data.OleDb;&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;using &lt;/span&gt;System.Text.RegularExpressions;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;El único método web será algo así:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;[WebMethod]&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;public string &lt;/span&gt;GetCountryCode(&lt;span style="color: rgb(51, 0, 153);"&gt;string &lt;/span&gt;ip) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  &lt;span style="color: rgb(51, 0, 153);"&gt;return &lt;/span&gt;GetCountryByIp(ip);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;} &lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;El primer método privado será el validador de IPs. Lo podemos resolver de forma elegante con una Regular Expression.&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;private bool &lt;/span&gt;IsValidIp(&lt;span style="color: rgb(51, 0, 153);"&gt;string &lt;/span&gt;ip) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;string &lt;/span&gt;pattern = @"^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$";&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   Regex reg = new Regex(pattern, RegexOptions.Singleline | RegexOptions.ExplicitCapture);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   r&lt;span style="color: rgb(51, 0, 153);"&gt;eturn &lt;/span&gt;reg.IsMatch(ip);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Luego necesitamos el algoritmo para convertir una IP en un double&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;private double &lt;/span&gt;Ip2Double(&lt;span style="color: rgb(51, 0, 153);"&gt;string &lt;/span&gt;ip) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;string&lt;/span&gt;[] octetos = ip.Split('.');&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;int &lt;/span&gt;w = &lt;span style="color: rgb(51, 0, 153);"&gt;int&lt;/span&gt;.Parse(octetos[0]);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;int &lt;/span&gt;x = &lt;span style="color: rgb(51, 0, 153);"&gt;int&lt;/span&gt;.Parse(octetos[1]);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;int &lt;/span&gt;y = &lt;span style="color: rgb(51, 0, 153);"&gt;int&lt;/span&gt;.Parse(octetos[2]);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;int &lt;/span&gt;z = &lt;span style="color: rgb(51, 0, 153);"&gt;int&lt;/span&gt;.Parse(octetos[3]);&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;double &lt;/span&gt;result = 16777216 * (&lt;span style="color: rgb(51, 0, 153);"&gt;double&lt;/span&gt;)w + 65536 * (&lt;span style="color: rgb(51, 0, 153);"&gt;double&lt;/span&gt;)x + 256 * (&lt;span style="color: rgb(51, 0, 153);"&gt;double&lt;/span&gt;)y + (&lt;span style="color: rgb(51, 0, 153);"&gt;double&lt;/span&gt;)z;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;return &lt;/span&gt;result;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Finalmente creamos el método que busca el país en la base de datos&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;private string &lt;/span&gt;GetCountryByIp(&lt;span style="color: rgb(51, 0, 153);"&gt;string &lt;/span&gt;ip) {&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;if &lt;/span&gt;(!IsValidIp(ip)) &lt;span style="color: rgb(51, 0, 153);"&gt;throw new &lt;/span&gt;Exception("Invalid IP format");&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;object &lt;/span&gt;result;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;double &lt;/span&gt;ipNum = Ip2Double(ip);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;string &lt;/span&gt;connectionString = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=";&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   connectionString += System.Web.HttpContext.Current.Server.MapPath("GeoIPCountry.mdb");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        &lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   OleDbConnection oConn = new OleDbConnection(connectionString);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;string &lt;/span&gt;query = "SELECT CountryCode FROM IPSegments WHERE @IpNum BETWEEN beginNro AND endNro";&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  OleDbCommand dbCommand = new OleDbCommand(query, oConn);&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;  dbCommand.Parameters.Add("@IpNum", DbType.Double).Value = ipNum;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   oConn.Open();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;try &lt;/span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;       result = dbCommand.ExecuteScalar();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   } &lt;span style="color: rgb(51, 0, 153);"&gt;finally &lt;/span&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;       oConn.Close();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;if &lt;/span&gt;(result == null) &lt;span style="color: rgb(51, 0, 153);"&gt;throw new&lt;/span&gt; Exception("IP not in Database, maybe is a local IP");&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   &lt;span style="color: rgb(51, 0, 153);"&gt;return &lt;/span&gt;result.ToString();&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En este link tienen el “plato terminado”: &lt;a href="http://www.bdamian.com.ar/webdevpro/GeoIP_ASPNET.zip"&gt;GeoIP WebService con ASP.NET y C#&lt;/a&gt;. Pueden copiarlo en una instancia de IIS configurada para ejecutar ASP.NET (funciona en todas las versiones) y probarlo. Sin embargo les recomiendo hacer sus propias implementaciones tomando este ejemplo solo como una especie de “starter kit”.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-2263597966157150424?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/2263597966157150424/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=2263597966157150424' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/2263597966157150424'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/2263597966157150424'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2007/10/geoip-webservice-con-aspnet-y-c.html' title='GeoIP WebService con ASP.NET y C#'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-4696435861406066996</id><published>2007-08-13T12:42:00.000-03:00</published><updated>2007-08-13T12:54:08.888-03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='experiencias'/><title type='text'>Desarrollando aplicaciones</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_Aqo-TwbVz90/RsB-czi6hNI/AAAAAAAAAN0/OZnWCl6m00Q/s1600-h/Desarrollando.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_Aqo-TwbVz90/RsB-czi6hNI/AAAAAAAAAN0/OZnWCl6m00Q/s400/Desarrollando.jpg" alt="" id="BLOGGER_PHOTO_ID_5098213811521488082" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Desde hace unos años que mi trabajo diario no consiste en escribir código. Sin embargo continúo participando del proceso de desarrollo de aplicaciones.&lt;br /&gt;&lt;br /&gt;Uno de los errores más comunes que noto entre los programadores es la idea de que desarrollar una aplicación es escribir su código.&lt;br /&gt;&lt;br /&gt;Muchas veces, cuando desarrollamos una aplicación pequeña, y que es similar a otras tantas que hemos hecho previamente, nos sentamos frente al teclado y hacemos el análisis y el código casi al mismo tiempo. Aún en estos momentos primero analizamos y luego codificamos.&lt;br /&gt;&lt;br /&gt;Codificar, compilar, debugear e implementar es sólo una parte del trabajo. De hecho es la segunda parte y, probablemente, nos insuma cerca de la mitad del tiempo que necesitamos para desarrollar una aplicación.&lt;br /&gt;&lt;br /&gt;Gran parte del trabajo, muchas veces la que lleva más horas, la hacemos con papel y lápiz ó, como en la foto, con fibrón y pizarra.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-4696435861406066996?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/4696435861406066996/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=4696435861406066996' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4696435861406066996'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/4696435861406066996'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2007/08/desarrollando-aplicaciones.html' title='Desarrollando aplicaciones'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_Aqo-TwbVz90/RsB-czi6hNI/AAAAAAAAAN0/OZnWCl6m00Q/s72-c/Desarrollando.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-1217079336546566601</id><published>2007-05-29T18:57:00.000-03:00</published><updated>2007-05-29T18:59:35.107-03:00</updated><title type='text'>Oscar Turquet</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Aqo-TwbVz90/RlyiIdhPMPI/AAAAAAAAAHU/Ah75FecqDbg/s1600-h/oscarturquet.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_Aqo-TwbVz90/RlyiIdhPMPI/AAAAAAAAAHU/Ah75FecqDbg/s400/oscarturquet.jpg" alt="" id="BLOGGER_PHOTO_ID_5070105546759024882" border="0" /&gt;&lt;/a&gt;Hay varias personas de quienes aprendí mucho en mi vida, pero la persona que marcó el camino que iba a tomar profesionalmente se llama Oscar Turquet.&lt;br /&gt;&lt;br /&gt;Fue él quien me enseño los fundamentos principales del desarrollo de software. Aún hoy sigo utilizando su ejercicio de crear un algoritmo que encuentre los primeros 500 números primos (con sus posteriores optimizaciones) cada vez que quiero enseñarle a programar a alguna persona.&lt;br /&gt;&lt;br /&gt;Pero lo más importante que me enseñó fue aquello de que: “Cuando las cosas se ponen complicadas, hay que volver a la definición del problema.”. Muchas veces la definición misma contiene la clave de la solución.&lt;br /&gt;&lt;br /&gt;Oscar Turquet estuvo presente, como no podía ser de otra forma, en uno de los días más importantes de mi vida (como se ve en la foto).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-1217079336546566601?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/1217079336546566601/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=1217079336546566601' title='1 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/1217079336546566601'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/1217079336546566601'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2007/05/oscar-turquet.html' title='Oscar Turquet'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_Aqo-TwbVz90/RlyiIdhPMPI/AAAAAAAAAHU/Ah75FecqDbg/s72-c/oscarturquet.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-115436019342205271</id><published>2006-07-31T12:13:00.000-03:00</published><updated>2006-07-31T12:36:33.473-03:00</updated><title type='text'>Export SQL data in Insert statements</title><content type='html'>Cuando pasamos del ambiente de desarrollo al productivo muchas veces nos encontramos con la necesidad de crear nuevamente la base de datos y, junto con esta tarea, copiar los datos que deben estar precargados o de las tablas de relación.&lt;br /&gt;&lt;br /&gt;Muchas veces, estas tablas, sólo tienen un par de registros. Crear un archivo CSV o crear un DTS resulta ser bastante engorroso.&lt;br /&gt;&lt;br /&gt;Para estos casos es que hice esta simple página en ASP.NET para exportar los datos de una base MS Sql Server a sentencias del tipo insert.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Solución&lt;/span&gt;&lt;br /&gt;Primero agregamos un campo para ingresar el Connection String correspondiente a la base de nuestro desarrollo. De esta forma traemos información de las tablas existentes (no de sistema).&lt;br /&gt;&lt;br /&gt;Para no tener que ejecutar una sentencia COUNT(*) por cada tabla, consultamos a la tabla de sistema &lt;span style="font-weight: bold;"&gt;dbo.sysindexes&lt;/span&gt; para obtener los últimos datos estadísticos de cada tabla. Es importante entender que de esta forma obtendremos información sólo de las tablas que contengan algún indice o Primary Key y que esta información puede estar desactualizada. Aún así, tendremos una idea de cuantos registros tiene una tabla.&lt;br /&gt;&lt;br /&gt;Debido a que el objetivo de esta página es crear un archivo de texto con las "sentencias insert" para ser ejecutado en un &lt;span style="font-weight: bold;"&gt;Query Analyzer&lt;/span&gt;, debemos poner un límite de cantidad de registros. Para este fin es que existe la variable &lt;span style="font-weight: bold;"&gt;MaxRows2Export&lt;/span&gt;. Arbitrariamente puse esta variable en 100.&lt;br /&gt;&lt;br /&gt;Una vez seleccionadas las tablas que queremos exportar, se genera un archivo de texto con las sentencias insert adecuadas. Para que este script funcione debemos asegurarnos de que podemos insertar &lt;span style="font-weight: bold;"&gt;Identities&lt;/span&gt;. Por esta razón, cada bloque se inicia con un:&lt;br /&gt;SET IDENTITY_INSERT table ON&lt;br /&gt;y finaliza con un&lt;br /&gt;SET IDENTITY_INSERT table OFF&lt;br /&gt;&lt;br /&gt;El siguiente es un ejemplo de archivo de texto que genera esta aplicación considerando que seleccionamos la tabla "Status" y "Genders":&lt;br /&gt;&lt;span style="font-style: italic;font-size:85%;" &gt;&lt;span style="font-family: courier new;"&gt;-- Status&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;SET IDENTITY_INSERT Status ON&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;GO&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;INSERT INTO Status(ID,Name) VALUES (1,'Pending')&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;INSERT INTO Status(ID,Name) VALUES (2,'Approved')&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;INSERT INTO Status(ID,Name) VALUES (3,'Disapproved')&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;GO&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;SET IDENTITY_INSERT Status OFF&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;GO&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;-- Genders&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;SET IDENTITY_INSERT Genders ON&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;GO&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;INSERT INTO Genders(GenderId,Gender) VALUES (1,'Male')&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;INSERT INTO Genders(GenderId,Gender) VALUES (2,'Female')&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;GO&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;SET IDENTITY_INSERT Genders OFF&lt;br /&gt;&lt;/span&gt;&lt;span style="font-family: courier new;"&gt;GO&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Espero que les sea útil. En el siguiente link encontrarán un ZIP que contiene sólo una página ASPX con esta aplicación.&lt;br /&gt;&lt;a href="http://www.bdamian.com.ar/webdevpro/sql2insert.zip"&gt;sql2insert.zip&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-115436019342205271?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/115436019342205271/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=115436019342205271' title='15 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/115436019342205271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/115436019342205271'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2006/07/export-sql-data-in-insert-statements.html' title='Export SQL data in Insert statements'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-114893576685415711</id><published>2006-05-29T16:47:00.002-03:00</published><updated>2009-08-28T17:41:20.063-03:00</updated><title type='text'>Exportar Microsoft Project día por día</title><content type='html'>&lt;p&gt;Este artículo explica cómo exportar las horas  asignadas a cada recurso en Microsoft Project día por día a Microsoft Excel en  un formato que permita crear una tabla pívot.&lt;/p&gt;&lt;p&gt;Realicé las siguientes búsquedas en Internet sin  resultado:&lt;br /&gt;&lt;/p&gt;&lt;ul type="disc"&gt;&lt;li&gt;Exportar MS Project día por día&lt;/li&gt;  &lt;li&gt;Export MS Project in daily       basis&lt;/li&gt;  &lt;li&gt;Microsoft Project export       timescaled&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Finalmente me enteré que MS Project no incluye esta  función y, en caso de necesitarla, había que programarla en VBA (Visual Basic for Applications).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Problema&lt;/strong&gt;&lt;br /&gt;Cuando exportamos los datos a otro formato, Microsoft  Project crea un registro por cada tarea asignada a cada recurso y esta tarea  incluye los datos de fecha de inicio de la tarea, fecha de finalización y  cantidad de horas que la tarea requiere. Esto significa que, si una tarea  comienza el día 1 y requiere 60 horas, sólo obtendremos un registro con esos  datos. Cuando hacemos la tabla pívot, crearemos un reporte en el que dice que  el recurso trabajará 60 horas el día 1 y ninguna los días siguientes. &lt;/p&gt;Lo que necesitamos exportar son los datos que  representan la vista de “Resource Usage”&lt;br /&gt;&lt;p&gt;    &lt;img src="http://www.bdamian.com.ar/webdevpro/ResourceUsage.gif" alt="Resource usage" height="280" width="400" /&gt;&lt;/p&gt;&lt;p&gt;En mi caso particular debía exportar los datos desde  un archivo Project que funciona como “Resource Pool”. La particularidad de este  archivo es que no contiene tareas, sólo recursos y asignaciones.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solución&lt;/strong&gt;&lt;br /&gt;Primero debemos abrir el entorno de VBA para crear la  nueva macro con la combinación de teclas ALT + F11.&lt;br /&gt;&lt;img src="http://www.bdamian.com.ar/webdevpro/VBA.gif" alt="Visual Basic for Applications" height="250" width="400" /&gt;&lt;/p&gt;Creé un nuevo formulario llamado “frmExportTimescaled”  dentro de “ProjectGlobal (Global.MPT)”. Esto significa que la macro estará  disponible para cualquier archivo de Project que abramos.&lt;br /&gt;&lt;p&gt;Armamos la interfaz de usuario con fecha inicial,  fecha final, tipo de datos a exportar, dos barras de progreso (una para  recursos y otro para asignaciones) y un botón para exportar.&lt;br /&gt;&lt;img src="http://www.bdamian.com.ar/webdevpro/interfaz.gif" alt="Interfaz macro" height="265" width="250" /&gt;&lt;br /&gt;&lt;br /&gt;Para poder agregar los controles “Date Time Picker” primero  debemos ir al menú &lt;strong&gt;Tools&lt;/strong&gt; -&amp;gt; &lt;strong&gt;References&lt;/strong&gt; y agregar “Microsoft Windows Common Controls 2  (MSCOMCT2.OCX)”. Si no tenemos esta librería, la pueden obtener desde &lt;a href="http://activex.microsoft.com/controls/vb6/mscomct2.cab"&gt;http://activex.microsoft.com/controls/vb6/mscomct2.cab&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Luego vamos al menú &lt;strong&gt;Tools&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Additional&lt;/strong&gt; &lt;strong&gt;Controls&lt;/strong&gt; y seleccionar “Microsoft Date and Time Picker Control 6.0” para agregarlo al Toolbox.&lt;/p&gt;Los nombres que les puse a los controles para  referenciarlos desde código son:&lt;br /&gt;&lt;ul type="disc"&gt;&lt;li&gt;Start Date: &lt;strong&gt;sDate&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;End Date: &lt;strong&gt;eDate&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;Type of data: &lt;strong&gt;ddAssignType&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;Label resources: &lt;strong&gt;lblResources&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;Resources progress bar: &lt;strong&gt;pBarResources&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;Label assignments: &lt;strong&gt;lblAssign&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;Assignment progress bar: &lt;strong&gt;pBarAssignments&lt;/strong&gt;&lt;/li&gt;  &lt;li&gt;Export button: &lt;strong&gt;btnExport&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Para poder poblar la lista de tipos de datos y para  crear un botón con nuestra macro en una barra de herramientas de Project,  creamos un nuevo módulo (yo le puse por nombre Export_Timescaled). Este módulo,  además de poblar los tipos de datos, invoca al formulario.&lt;/p&gt;El siguiente es el código del módulo Export_Timescaled:&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;Sub  Export_Timescaled()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    frmExportTimescaled.ddAssignType.Clear&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    frmExportTimescaled.ddAssignType.AddItem  ("Work")&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    frmExportTimescaled.ddAssignType.AddItem  ("Actual Work")&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    frmExportTimescaled.ddAssignType.AddItem  ("Cumularive Work")&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    frmExportTimescaled.ddAssignType.AddItem  ("Overallocation")&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    frmExportTimescaled.ddAssignType.AddItem  ("Cost")&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    frmExportTimescaled.Show&lt;/span&gt;&lt;/span&gt;&lt;span style=";font-family:courier new;font-size:85%;" class="style1"  &gt;&lt;br /&gt;End Sub&lt;/span&gt;&lt;br /&gt;&lt;p&gt;El siguiente es el código del formulario  frmExportTimescaled:&lt;br /&gt;&lt;span style=";font-family:courier new;font-size:85%;" class="style1"  &gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Option Explicit&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim  &lt;/span&gt;xlApp &lt;span style="color: rgb(51, 0, 153);"&gt;As &lt;/span&gt;Excel.Application&lt;/span&gt;&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Private Sub&lt;/span&gt;  btnExport_Click()&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;t As Task&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;Asgn As Assignment&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;a As &lt;span style="color: rgb(51, 0, 153);"&gt;Integer&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;b As &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Integer&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;currRow  As &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Integer&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;i As &lt;span style="color: rgb(51, 0, 153);"&gt;Double&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;c As Range&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;TSV As TimeScaleValues&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Dim &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;AssignType As PjAssignmentTimescaledData&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;p class="style1"  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;    Call  ControlsEnable(False) 'Disable  all form controls&lt;br /&gt;Call  CreateExcelFile&lt;br /&gt;Application.ActiveWindow.Refresh 'This makes form get focus&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="style1"  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;    Set c =  xlApp.ActiveCell&lt;br /&gt;currRow = 0&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 153, 0);"&gt;'Set column titles&lt;/span&gt;&lt;br /&gt;c.Offset(currRow, 0) = "Task  Name"&lt;br /&gt;c.Offset(currRow, 1) = "Employee"&lt;br /&gt;c.Offset(currRow, 2) = "Date"&lt;br /&gt;c.Offset(currRow, 3) = "Hours"&lt;br /&gt;c.Offset(currRow, 4) = "Week"&lt;br /&gt;&lt;br /&gt;Call  InitProgressBarResources(ActiveProject.Resources.Count)&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="style1"  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;'Determine  Assignment Type to export&lt;/span&gt;&lt;br /&gt;Select Case ddAssignType.Value&lt;br /&gt;Case  "Actual Work":&lt;br /&gt; AssignType =  PjAssignmentTimescaledData.pjAssignmentTimescaledActualWork&lt;br /&gt;Case  "Cumularive Work":&lt;br /&gt; AssignType = PjAssignmentTimescaledData.pjAssignmentTimescaledCumulativeWork&lt;br /&gt;Case  "Overallocation":&lt;br /&gt; AssignType =  PjAssignmentTimescaledData.pjAssignmentTimescaledOverallocation&lt;br /&gt;Case  "Cost":&lt;br /&gt; AssignType =  PjAssignmentTimescaledData.pjAssignmentTimescaledCost&lt;br /&gt;Case Else&lt;br /&gt; AssignType =  PjAssignmentTimescaledData.pjAssignmentTimescaledWork&lt;br /&gt;End Select&lt;br /&gt;&lt;br /&gt;For a = 1 To ActiveProject.Resources.Count&lt;br /&gt;pBarResources.Value = a &lt;span style="color: rgb(0, 153, 0);"&gt;'Update progress bar&lt;/span&gt;&lt;br /&gt;lblResources.Caption = a &amp;amp;  "/" &amp;amp; pBarResources.Max &lt;span style="color: rgb(0, 153, 0);"&gt;'Update label&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;If Not ActiveProject.Resources.Item(a) Is Nothing And              ActiveProject.Resources.Item(a).Assignments.Count  &amp;gt; 0 Then&lt;br /&gt; Call  InitProgressBarAssing(ActiveProject.Resources.Item(a).Assignments.Count)  &lt;span style="color: rgb(0, 153, 0);"&gt;'Initialize second progress bar&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  For  b = 1 To  ActiveProject.Resources.Item(a).Assignments.Count&lt;br /&gt;&lt;br /&gt;     If  Not  ActiveProject.Resources.Item(a).Assignments.Item(b) Is  Nothing Then&lt;br /&gt;         Set TSV =  ActiveProject.Resources.Item(a).Assignments(b).TimeScaleData(sDate.Value,  eDate.Value + 1, AssignType, pjTimescaleDays)&lt;br /&gt;         pBarAssignments.Value = b  &lt;span style="color: rgb(0, 153, 0);"&gt;'Update second progress bar&lt;/span&gt;&lt;br /&gt;         lblAssign.Caption = b &amp;amp;  "/" &amp;amp; pBarAssignments.Max &lt;span style="color: rgb(0, 153, 0);"&gt;'Update  second label&lt;/span&gt;&lt;br /&gt;         For  i = 1 To TSV.Count&lt;br /&gt;             DoEvents&lt;br /&gt;             If IsNumeric(TSV.Item(i).Value) Then  'Check for data&lt;br /&gt;                 currRow = currRow +  1&lt;br /&gt;                 c.Offset(currRow, 0) = ActiveProject.Resources.Item(a).Assignments.Item(b).TaskName&lt;br /&gt;                 c.Offset(currRow, 2) = ActiveProject.Resources.Item(a).Name&lt;br /&gt;                 c.Offset(currRow, 3) = TSV.Item(i).StartDate&lt;br /&gt;                 c.Offset(currRow, 4) = Round(TSV.Item(i).Value / 60, 2)&lt;br /&gt;                 c.Offset(currRow, 5) =  DateAdd("d", 5 - (Weekday(TSV.Item(i).StartDate, vbSunday)),  TSV.Item(i).StartDate)&lt;br /&gt;             End If&lt;br /&gt;         Next&lt;br /&gt;     End  If&lt;br /&gt;&lt;br /&gt; Next&lt;br /&gt;&lt;br /&gt;End If&lt;br /&gt;Next&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class="style1"  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;    MsgBox ("Exportación completa")&lt;br /&gt;ControlsEnable True&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;End Sub&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Sub&lt;/span&gt;  ControlsEnable(status As Boolean)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    btnExport.Enabled = status&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    sDate.Enabled = status&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    eDate.Enabled = status&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    ddAssignType.Enabled = status&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);font-family:courier new;" &gt;End Sub&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;p class="style1"  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Sub&lt;/span&gt;  InitProgressBarResources(cant As Integer)&lt;br /&gt;pBarResources.Min = 0&lt;br /&gt;pBarResources.Max = cant&lt;br /&gt;pBarResources.Value = 0&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;End Sub&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Sub  &lt;/span&gt;InitProgressBarAssing(cant As Integer)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    pBarAssignments.Min = 0&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    pBarAssignments.Max = cant&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    pBarAssignments.Value = 0&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);font-family:courier new;" &gt;End Sub&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;p class="style1"  style="font-family:courier new;"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="color: rgb(51, 0, 153);"&gt;Sub&lt;/span&gt;  CreateExcelFile()&lt;br /&gt;Dim xlBook As Excel.Workbook&lt;br /&gt;Dim xlSheet  As Excel.Worksheet&lt;br /&gt;Dim a As Integer&lt;/span&gt;&lt;/p&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;'Create Excel  Object&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    Set xlApp =  New Excel.Application&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    xlApp.Visible = True&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    AppActivate "Microsoft Excel"&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;p class="style1"&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family:courier new;"&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;'Create Workbooks  and Output Sheet&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    Set xlBook = xlApp.Workbooks.Add&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    Set xlSheet = xlBook.Worksheets.Add&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;'Create Sheet  name. Must be less than 31 characters&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    xlSheet.Name = Mid(Mid(ActiveProject.Name,  1, Len(ActiveProject.Name) - 4) &amp;amp; " (" &amp;amp; ddAssignType.Value  &amp;amp;amp; ")", 1, 31)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    &lt;span style="color: rgb(0, 153, 0);"&gt;'Delete extra  sheets&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    For a = 1 To 3&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;        xlBook.Worksheets(2).Delete&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;    Next&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(51, 0, 153);font-family:courier new;" &gt;End Sub&lt;/span&gt;&lt;/span&gt; &lt;/p&gt;Finalmente, para poder compartir la macro con otras  personas, la copié a un archivo de Project vacío utilizando el menú &lt;strong&gt;Tools&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Organizer&lt;/strong&gt;, seleccionando “Modules”. De esta forma, abrimos el  archivo en otra computadora y copiamos la macro hacia el &lt;strong&gt;Global.MPT&lt;/strong&gt; y ya queda disponible en nuestro MS Project.&lt;br /&gt;&lt;p&gt;&lt;a href="http://www.bdamian.com.ar/webdevpro/ExportTimescaled.zip"&gt;Descargar archivos fuente &lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.bdamian.com.ar/webdevpro/Macro%20Export%20TimeScaled.mpp"&gt;Descargar archivo MS Project con la macro&lt;/a&gt;&lt;/p&gt;&lt;span style="font-weight: bold;"&gt;IMPORTANTE&lt;br /&gt;&lt;/span&gt;Si al correr la macro aparece el error "User-defined type not defined"&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_Aqo-TwbVz90/R7M_3xsQtFI/AAAAAAAAAUU/q-z3uu9brFo/s1600-h/error.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_Aqo-TwbVz90/R7M_3xsQtFI/AAAAAAAAAUU/q-z3uu9brFo/s400/error.jpg" alt="" id="BLOGGER_PHOTO_ID_5166543424988427346" border="0" /&gt;&lt;/a&gt;significa que falta la Referencia al objeto Excel dentro de la macro. Para solucionar esto vamos a la pantalla de VBA (ALT+F11), seleccionamos Tools --&gt; References&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_Aqo-TwbVz90/R7NAARsQtGI/AAAAAAAAAUc/2duj-I57PtI/s1600-h/toosReferences.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://3.bp.blogspot.com/_Aqo-TwbVz90/R7NAARsQtGI/AAAAAAAAAUc/2duj-I57PtI/s400/toosReferences.jpg" alt="" id="BLOGGER_PHOTO_ID_5166543571017315426" border="0" /&gt;&lt;/a&gt;Y seleccionamos "Microsoft Excel 11.0 Object Library" o similar. Les muestro mis referencias:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_Aqo-TwbVz90/R7NAHBsQtHI/AAAAAAAAAUk/EZgL9zOMyfI/s1600-h/references.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://2.bp.blogspot.com/_Aqo-TwbVz90/R7NAHBsQtHI/AAAAAAAAAUk/EZgL9zOMyfI/s400/references.jpg" alt="" id="BLOGGER_PHOTO_ID_5166543686981432434" border="0" /&gt;&lt;/a&gt;Guardamos y ejecutamos la macro nuevamente.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-114893576685415711?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/114893576685415711/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=114893576685415711' title='25 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/114893576685415711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/114893576685415711'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2006/05/exportar-microsoft-project-da-por-da.html' title='Exportar Microsoft Project día por día'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_Aqo-TwbVz90/R7M_3xsQtFI/AAAAAAAAAUU/q-z3uu9brFo/s72-c/error.jpg' height='72' width='72'/><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-111756955503080045</id><published>2005-05-31T16:51:00.000-03:00</published><updated>2005-05-31T16:59:15.036-03:00</updated><title type='text'>Obteniendo valores por QueryString</title><content type='html'>En el post anterior hablamos de zonas de riesgo y de que debemos verificar los datos que nuestra aplicación recibe de los usuarios. También mencionamos que es mucho el código adicional que debemos escribir si necesitamos verificar muchos datos y que deberíamos abstraer esa problemática. Pues bien, ahora vamos a ver un ejemplo de cómo hacer esto.&lt;br /&gt;&lt;br /&gt;El problema que intentamos resolver es el siguiente. Nuestra aplicación presenta un listado en varias páginas y obtiene el número de página que debe mostrar a través del parámetro “pagina” pasado por QueryString. En un punto del código, necesito obtener un valor entero y en caso de no conseguirlo, el valor debe ser “1”, es decir, la primera página.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;font-size:85%;"&gt;&lt;span style="color:#330099;"&gt;using&lt;/span&gt; System;&lt;br /&gt;&lt;span style="color:#330099;"&gt;using&lt;/span&gt; System.Web;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#330099;"&gt;namespace&lt;/span&gt; DWP.Web&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#009900;"&gt;/// Definimos la clase como sellada&lt;br /&gt;/// para impedir la herencia&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#330099;"&gt;public sealed class&lt;/span&gt; Requester&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;{&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#009900;"&gt;/// Constructor privado para impedir&lt;br /&gt;/// la creación de instancias.&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#330099;"&gt;private&lt;/span&gt; Requester(){}&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#009900;"&gt;/// Método estático para obtener&lt;br /&gt;/// parámetros por QueryString&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#330099;"&gt;public static object&lt;/span&gt; FromQueryString(&lt;span style="color:#330099;"&gt;string&lt;/span&gt; key, &lt;span style="color:#330099;"&gt;object&lt;/span&gt; defaulValue)&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;{&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#009900;"&gt;/// Obtenemos el objeto Request actual&lt;/span&gt;&lt;br /&gt;HttpRequest Request = HttpContext.Current.Request;&lt;br /&gt;&lt;span style="color:#330099;"&gt;object&lt;/span&gt; o;&lt;br /&gt;&lt;span style="color:#330099;"&gt;string&lt;/span&gt; val = "";&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#009900;"&gt;/// Verificamos si el valor existe&lt;br /&gt;/// en el QueryString. Si no, devolvemos&lt;br /&gt;/// el valor por default&lt;/span&gt;&lt;br /&gt;&lt;span style="color:#330099;"&gt;if&lt;/span&gt; (Request.QueryString[key]!=&lt;span style="color:#330099;"&gt;null&lt;/span&gt; &amp;&amp;amp; Request.QueryString[key].ToString()!="") &lt;span style="color:#ff0000;"&gt;{&lt;br /&gt;&lt;/span&gt;val = Request.QueryString[key].ToString();&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;}&lt;/span&gt; else &lt;span style="color:#ff0000;"&gt;{&lt;/span&gt;&lt;br /&gt;return defaulValue;&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#009900;"&gt;/// Intentamos convertir el valor obtenido&lt;br /&gt;/// al tipo de dato del valor default.&lt;br /&gt;/// Si esto da error, devolvemos el&lt;br /&gt;/// valor default.&lt;/span&gt;&lt;br /&gt;try &lt;span style="color:#ff0000;"&gt;{&lt;/span&gt;&lt;br /&gt;o = Convert.ChangeType(val, defaulValue.GetType());&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;}&lt;/span&gt; catch &lt;span style="color:#ff0000;"&gt;{&lt;/span&gt;&lt;br /&gt;o = defaulValue;&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;}&lt;br /&gt;&lt;/span&gt;return o;&lt;br /&gt;&lt;span style="color:#ff0000;"&gt;}&lt;br /&gt;}&lt;br /&gt;}&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Como podemos ver, el método “FromQueryString” tiene como tipo de retorno un object. Necesitamos que sea así porque vamos a devolver un objeto de tipo similar al valor por default. De esta forma, si estamos esperando un entero y por QueryString nos llegó un texto, el método devuelve el valor predeterminado, es decir, garantiza el tipo de dato devuelto.&lt;br /&gt;&lt;br /&gt;Las siguientes son formas válidas de utilizar nuestra función:&lt;br /&gt;&lt;span style="font-family:courier new;font-size:85%;"&gt;&lt;span style="color:#009900;"&gt;///Esperamos un entero, si no, devuelve el valor 1.&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#330099;"&gt;int&lt;/span&gt; currPage = (&lt;span style="color:#330099;"&gt;int&lt;/span&gt;)Requester.FromQueryString("pagina",1);&lt;br /&gt;&lt;br /&gt;&lt;span style="color:#009900;"&gt;///Esperamos una fecha, si no, 1 de enero de 2005.&lt;/span&gt;&lt;br /&gt;DateTime currDate = (DateTime)Requester.FromQueryString("fecha", &lt;span style="color:#330099;"&gt;new&lt;/span&gt; DateTime(2005,01,01));&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Les dejo a ustedes la tarea de crear los métodos que obtienen valores por Form y Cookies.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-111756955503080045?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/111756955503080045/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=111756955503080045' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111756955503080045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111756955503080045'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2005/05/obteniendo-valores-por-querystring.html' title='Obteniendo valores por QueryString'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-111143291449370163</id><published>2005-03-21T16:17:00.000-03:00</published><updated>2005-03-21T16:21:54.496-03:00</updated><title type='text'>Zonas de riesgo</title><content type='html'>Todas las aplicaciones tienen zonas en las que la probabilidad de que se produzca un error es más alta que en otras partes de del desarrollo. En general son zonas de riesgo los límites de nuestra aplicación. Aquel código que interactúa con el "exterior".&lt;br /&gt;&lt;br /&gt;Si analizamos una aplicación de Internet basada en formularios que almacena la información ingresada por los usuarios en una base de datos, entonces, rápidamente identificamos dos zonas de riesgo:&lt;br /&gt;&lt;br /&gt;&lt;u&gt;La captura de datos&lt;/u&gt;&lt;br /&gt;Recordemos que la información que proviene de los usuarios (esto incluye formularios, querystrings y cookies) no es confiable, puede venir con un formato erróneo o puede no llegar en absoluto. Debemos verificar los datos que recibimos y no confiar en que siempre llegarán correctamente.&lt;br /&gt;Un caso muy común es cuando enviamos, por querystring, el número de página que debemos mostrar. Entonces redirigimos a los usuarios a URLs parecidas a esta:&lt;br /&gt;&lt;span style="font-weight: bold;font-size:85%;" &gt;&lt;span style="font-family: courier new;"&gt;listado.aspx?pagina=3&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Luego en nuestro código escribimos algo así:&lt;br /&gt;&lt;span style="font-family: courier new;font-size:85%;" &gt;&lt;span style="font-weight: bold;"&gt;int pagina = int.Parse(Request.QueryString["pagina"].ToString());&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;Los desafío a que busquen páginas de este tipo en Internet y vean lo que sucede si modifican el valor de "pagina" a "abc".&lt;br /&gt;Para que no sucedan estas cosas (las páginas de error no son muy agradables) deberíamos preguntar: "si vino el dato que estoy esperando, y si el dato no es vacío, y si puedo crear un entero, entonces tomar el dato, si no, debo asignar un valor predeterminado". En nuestro ejemplo, ante cualquier problema, pagina valdría "1".&lt;br /&gt;Es cierto que, si por cada dato que queremos obtener debemos escribir tanto código, nuestro programa sería muy extenso, pero también es cierto que, si creamos un objeto que se encargue de obtener estos valores, habremos resuelto el problema para los próximos desarrollos que hagamos.&lt;br /&gt;&lt;br /&gt;&lt;u&gt;El almacenamiento de datos&lt;/u&gt;&lt;br /&gt;Ya hemos obtenido la información y ahora necesitamos guardarla en una base de datos. Aquí también podemos encontrarnos con errores inesperados ya que existen factores que no podemos controlar. Pueden aparecer errores por clave principal repetida, valores incorrectos, conexiones agotadas, comunicación con la base interrumpida, etc. Otra vez, las páginas de error no son muy amigables. Tenemos que estar preparados y saber que estas cosas, y otras tantas, pueden suceder.&lt;br /&gt;&lt;br /&gt;Existen otras zonas de riesgo dependiendo del tipo de aplicación que estemos desarrollando. Debemos identificarlas y tomar los recaudos necesarios para que nuestros desarrollos sean cada vez más robustos.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-111143291449370163?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/111143291449370163/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=111143291449370163' title='1 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111143291449370163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111143291449370163'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2005/03/zonas-de-riesgo.html' title='Zonas de riesgo'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-111089384753589757</id><published>2005-03-15T10:31:00.000-03:00</published><updated>2005-03-15T10:37:27.540-03:00</updated><title type='text'>Caching</title><content type='html'>La llegada de .NET hizo que muchos programadores comenzaran a agregar caching en sus aplicaciones.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;ASP.NET&lt;/span&gt; nos brinda dos opciones para aplicar cache en nuestros sitios:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Output cache:&lt;/span&gt; con este mecanismo se mantiene una copia de la respuesta que produce una página, de modo tal que, mientras dura el cache, el código de la página se habrá ejecutado una sola vez.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Objeto cache:&lt;/span&gt; a través del cual podemos almacenar datos para no tener que ir a buscarlos cada vez al repositorio original&lt;br /&gt;&lt;br /&gt;Todo muy útil, pero no debemos olvidar que el cache es una solución para cuando nuestro sitio tiene un nivel de uso que se hace necesario "proteger" al servidor web y al repositorio de datos del uso intensivo que generan los usuarios que ingresan al sitio. No tiene sentido utilizar output cache si la página es solicitada una vez cada 5 minutos y la información que muestra la página es válida por más o menos por el mismo tiempo.&lt;br /&gt;&lt;br /&gt;Pero lo más importante es que &lt;span style="font-weight: bold;"&gt;no debemos diseñar una aplicación en función de que vamos a usar cache&lt;/span&gt;. En otras palabras, no debemos hacer aplicaciones que no funcionen sin cache siendo que tienen un uso moderado.&lt;br /&gt;&lt;br /&gt;Por último, todos nuestros desarrollos que usan cache deberían poder activarlo y desactivarlo fácilmente. Salvo alguna excepción en particular, nuestros desarrollos no deben ser dependientes del caching, deben poder usarlo cuando lo necesitan.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-111089384753589757?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/111089384753589757/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=111089384753589757' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111089384753589757'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111089384753589757'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2005/03/caching.html' title='Caching'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-111021819068648987</id><published>2005-03-07T14:54:00.000-03:00</published><updated>2005-03-07T14:56:30.690-03:00</updated><title type='text'>Criterio</title><content type='html'>Conocer en profundidad las características de un lenguaje de programación es muy importante. Esto se consigue leyendo, estudiando, practicando y trabajando. De esta forma nos convertiremos en expertos. Es probable que, con tiempo, logremos ser expertos en varios lenguajes y tecnologías.&lt;br /&gt;&lt;br /&gt;Pero lo que nos diferencia, lo que nos convierte en verdaderos profesionales es el &lt;span style="font-weight: bold;"&gt;criterio&lt;/span&gt;. Necesitamos criterio para definir la mejor solución. Necesitamos criterio para decidir cual es la tecnología más apropiada para cada caso. Para conocer los riesgos que estamos tomando.&lt;br /&gt;&lt;br /&gt;El criterio no se estudia, no está escrito. Se desarrolla en base a la experiencia.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Es necesario analizar, luego de cada desarrollo, que cosas hicimos bien y cuales podríamos haberlas hecho mejor. &lt;/span&gt;Es fundamental que cada trabajo que hagamos amplíe nuestro criterio y, también, aprender de la experiencia de quien eventualmente tengamos al lado.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-111021819068648987?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/111021819068648987/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=111021819068648987' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111021819068648987'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/111021819068648987'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2005/03/criterio.html' title='Criterio'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-110432754837424991</id><published>2004-12-29T10:35:00.000-03:00</published><updated>2004-12-29T10:39:08.373-03:00</updated><title type='text'>Separación en capas</title><content type='html'>Existen lenguajes con los que resulta natural la programación en capas y otros con los cuales parecería que no es necesario. En apariencia, si desarrollamos en C#.NET, debemos separar en capas puesto que el lenguaje nos “obliga a programar bien”. En cambio si hacemos una aplicación en ASP, entonces está “bien” poner “todo junto” ya que es un lenguaje de scripting en donde “todo vale”.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Nada de lo anterior es cierto&lt;/span&gt;. Es muy fácil programar en C#.NET sin separar en capas, de hecho, he visto muchos desarrollos en los que casi todo el código estaba concentrado en el evento Page_Load. También se puede programar en ASP separando el código en capas.&lt;br /&gt;&lt;br /&gt;La separación en capas es, en principio, algo teórico, formal. &lt;span style="font-weight: bold;"&gt;Se diseña en capas&lt;/span&gt;. Podemos desarrollar una aplicación en C# con capa de presentación, de negocios y de acceso a datos en una sola DLL y, aún así, las capas seguirán existiendo. También se puede separar en capas una aplicación íntegramente desarrollada en ASP.&lt;br /&gt;&lt;br /&gt;Son muchos los beneficios que obtenemos con esta práctica y, de entre ellos, podemos citar:&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;Clara distribución de las responsabilidades.&lt;/li&gt;   &lt;li&gt;Nos permite tener múltiples presentaciones para una misma aplicación (ASPX, WebService, Win32)&lt;/li&gt;   &lt;li&gt;Podemos cambiar de repositorio de datos sin impacto en el resto de la aplicación.&lt;/li&gt;   &lt;li&gt;Es más fácil trabajar en equipo con otros desarrolladores y hasta armar equipos de desarrolladores para cada capa.&lt;/li&gt;   &lt;li&gt;El código se vuelve mucho más claro y fácil de mantener.&lt;/li&gt; &lt;/ul&gt; En los próximos artículos vamos a hablar específicamente de cada capa. También &lt;span style="font-weight: bold;"&gt;vamos a ir mostrando cómo se puede implementar en ASP tradicional&lt;/span&gt;.Esto lo haremos por dos razones fundamentales. En primer lugar, no existen muchos ejemplos de cómo separar en capas con ASP tradicional en Internet. Además intento mostrar lo importante que es diseñar en capas aún cuando la implementación no separe estrictamente el código en DLLs.&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-110432754837424991?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/110432754837424991/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=110432754837424991' title='8 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110432754837424991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110432754837424991'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/12/separacin-en-capas.html' title='Separación en capas'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-110364354294667512</id><published>2004-12-21T13:36:00.000-03:00</published><updated>2004-12-21T12:39:02.946-03:00</updated><title type='text'>Diseño independiente de la plataforma</title><content type='html'>El desarrollo de aplicaciones web tiene varias etapas que, por lo general, son estas:&lt;br /&gt;&lt;ul&gt;   &lt;li&gt;Enumeración de necesidades&lt;/li&gt;   &lt;li&gt;Creación de especificaciones&lt;/li&gt;   &lt;li&gt;Análisis&lt;/li&gt;   &lt;li&gt;Diseño&lt;/li&gt;   &lt;li&gt;Desarrollo&lt;/li&gt;   &lt;li&gt;Implementación&lt;/li&gt;   &lt;li&gt;Mantenimiento&lt;/li&gt; &lt;/ul&gt; Cada una de estas etapas debe ser independiente de las demás. Esto significa que no debemos permitir que nuestro conocimiento o experiencia acerca de las etapas siguientes interfieran en nuestro criterio sobre la etapa actual.&lt;br /&gt;&lt;br /&gt;Particularmente me interesa hacer un comentario sobre la etapa de Diseño debido a que esta y la de Desarrollo suelen ser cumplidas por las mismas personas o, mejor dicho, parte del equipo de desarrollo suele participar en la etapa de Diseño.&lt;br /&gt;Muchas veces he visto, a miembros del equipo, invalidar parte de un diseño porque sería más o menos complicado de desarrollar. Este es uno de los errores más comunes en el desarrollo de aplicaciones en general. Un diseño bien hecho es muy importante como para que sólo quede en una frase de compromiso.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;La etapa de Diseño es independiente de la plataforma sobre la que se va a desarrollar&lt;/span&gt;. No podemos pensar en que si fuera en ASP entonces estaría bien, pero si fuera en Flash entonces el diseño estaría mal. &lt;span style="font-weight: bold;"&gt;Si el diseño está bien hecho, seguirá estando bien cualquiera sea la plataforma que se decida utilizar el la etapa de Desarrollo&lt;/span&gt;.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-110364354294667512?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/110364354294667512/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=110364354294667512' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110364354294667512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110364354294667512'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/12/diseo-independiente-de-la-plataforma.html' title='Diseño independiente de la plataforma'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-110307644341649903</id><published>2004-12-14T22:53:00.000-03:00</published><updated>2004-12-14T23:07:23.416-03:00</updated><title type='text'>Simple es mejor</title><content type='html'>Cuando veo un desarrollo de esos en los que una página ASP usa un objeto SOAP para conectar a un WebService .NET, el cual, vía DCOM se comunica con un servidor que, a través de un motor de persistencia logra actualizar un contador de visitas, lo primero que pienso es: "El programador que hizo esto ha leído mucho".&lt;br /&gt;Lo que verdaderamente despierta mi admiración son aquellas soluciones que resuelven problemas de forma tan sencilla que, luego de implementadas, hacen que todos digan: "era muy fácil".&lt;br /&gt;&lt;br /&gt;La regla número uno de los desarrolladores debe ser &lt;strong&gt;KISS&lt;/strong&gt;: "&lt;strong&gt;Keep it simple, stupid&lt;/strong&gt;" (&lt;strong&gt;mantenlo simple, estúpido&lt;/strong&gt;).&lt;br /&gt;&lt;span&gt;&lt;span&gt;Si un problema admite más de una solución, entonces seguramente, &lt;strong&gt;la más sencilla será la mejor&lt;/strong&gt; y la que tendrá menos problemas en el futuro. Por otra parte, cuanto más sencillo se mantenga un desarrollo, más fácil será para un compañero continuarlo.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;En una ocación, mi jefe me pidió que resolviera el problema de una página ASP que daba error de "Time Out" debido a que ejecutaba un proceso que habitualmente demoraba mucho tiempo. Luego de analizarlo, llamé a mi jefe y le mostré la solución: la página ya no ejecutaba más ese proceso. No sé si fue mi mejor trabajo, pero seguro que estuvo libre de "bugs".&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-110307644341649903?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/110307644341649903/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=110307644341649903' title='1 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110307644341649903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110307644341649903'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/12/simple-es-mejor.html' title='Simple es mejor'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-110173630157608799</id><published>2004-11-29T10:40:00.000-03:00</published><updated>2004-11-29T10:51:41.576-03:00</updated><title type='text'>Administración de errores (parte 2)</title><content type='html'>Entre las cosas que &lt;span style="font-weight: bold;"&gt;no debemos hacer&lt;/span&gt; cuando administramos errores están:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;C# .NET&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;span class="codeKey"&gt;try&lt;/span&gt; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;Function1();&lt;br /&gt;} &lt;span class="codeKey"&gt;catch&lt;/span&gt; {}&lt;br /&gt;Function2();&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;ASP&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;span class="codeKey"&gt;on error resume next&lt;/span&gt;&lt;br /&gt;…&lt;br /&gt;Function1()&lt;br /&gt;Function2()&lt;br /&gt;…&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;En ambos casos se está &lt;span style="font-weight: bold;"&gt;enmascarando &lt;/span&gt;a los errores y no administrándolos. Si bien esto hará que el usuario no vea ningún error, el peligro es muy grande, tal como se describió en el artículo anterior.&lt;br /&gt;&lt;br /&gt;Veamos otro ejemplo de lo que &lt;span style="font-weight: bold;"&gt;no debemos hacer&lt;/span&gt;:&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;C#.NET&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;span class="codeKey"&gt;public bool&lt;/span&gt; DeleteFile(&lt;span class="codeKey"&gt;string&lt;/span&gt; fileName) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;bool&lt;/span&gt; canDelete=&lt;span class="codeKey"&gt;false&lt;/span&gt;;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;try&lt;/span&gt; {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;System.IO.File.Delete(fileName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;canDelete=&lt;span class="codeKey"&gt;true&lt;/span&gt;;&lt;br /&gt; } &lt;span class="codeKey"&gt;catch&lt;/span&gt; {}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;return&lt;/span&gt; canDelete;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;ASP&lt;/span&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;span class="codeKey"&gt;Function&lt;/span&gt; DeleteFile(fileName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;On error resume next&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;Dim&lt;/span&gt; fso, canDelete&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;Set&lt;/span&gt; fso = CreateObject("Scripting.FileSystemObject")&lt;br /&gt;fso.DeleteFile(fileName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;Set&lt;/span&gt; fso = &lt;span class="codeKey"&gt;Nothing&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;canDelete = (Err.number=0)&lt;br /&gt;&amp;nbsp;&amp;nbsp;Err.Clear&lt;br /&gt;&amp;nbsp;&amp;nbsp;DeleteFile = canDelete&lt;br /&gt;&lt;span class="codeKey"&gt;End Function&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Esta es una muy mala práctica. Hemos creado una función que intenta borrar un archivo y devuelve el éxito de la operación. Además de las razones dadas en el ejemplo anterior, acá se agrega que quién llama a la función debe revisar si la función tuvo éxito revisando su valor de verdad. Pero en el caso de que la función devuelva falso, no tenemos ninguna otra información útil que nos sirva para tomar decisiones. No es lo mismo que la función haya fallado porque no tiene permisos a que el archivo no exista.&lt;br /&gt;&lt;br /&gt;En el próximo artículo veremos algunas buenas prácticas en la administración de errores.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-110173630157608799?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/110173630157608799/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=110173630157608799' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110173630157608799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110173630157608799'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/11/administracin-de-errores-parte-2.html' title='Administración de errores (parte 2)'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-110113234197662091</id><published>2004-11-22T11:01:00.000-03:00</published><updated>2004-11-22T11:05:41.976-03:00</updated><title type='text'>Administración de errores (parte 1)</title><content type='html'>Nuestro trabajo exige constantemente tomar decisiones. Una de las más importantes es decidir si vamos a administrar errores o no.&lt;br /&gt;Podemos decidir no hacerlo, de hecho podemos definir, en ASP.NET, una página a la que nos debe redirigir la aplicación en caso de error.&lt;br /&gt;Si decidimos administrar errores debemos saber que es toda una responsabilidad. Pocas cosas suponen un riesgo superior a administrar mal los errores de nuestra aplicación.&lt;br /&gt;Empecemos por el principio (que es, generalmente, el mejor lugar para comenzar). &lt;span style="font-weight: bold;"&gt;Un error es una excepción que no previmos que podía suceder&lt;/span&gt;. Lo mejor y menos peligroso que nos puede suceder cuando se produce un error, es que nuestra aplicación interrumpa su ejecución pues, en caso de continuar, estaría ejecutando en “estado de error” y las consecuencias son difíciles de imaginar.&lt;br /&gt;Un amigo me contó la historia del "Medidor de Presión". Imaginemos una caldera que tiene un medidor de presión. Este tiene unos números sobre un fondo blanco y, a partir de cierto valor, otros con fondo rojo que indica peligro. Cuando la presión supera la línea roja suena la alarma. Cuanto más tiempo transcurra la aguja en la zona de peligro más posibilidades hay de que explote la caldera.&lt;br /&gt;Lo mismo sucede con nuestras aplicaciones. Cuanto más rápido salgamos del "estado de error", menos peligro correremos.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-110113234197662091?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/110113234197662091/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=110113234197662091' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110113234197662091'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110113234197662091'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/11/administracin-de-errores-parte-1.html' title='Administración de errores (parte 1)'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-110061193482306126</id><published>2004-11-16T10:27:00.000-03:00</published><updated>2004-11-16T10:32:14.823-03:00</updated><title type='text'>Paginas de proceso de datos</title><content type='html'>En nuestra vida como Desarrolladores Web nos encontraremos muchas veces frente a la tarea de hacer formularios en donde los usuarios deberán ingresar información que será almacenada en una base de datos.&lt;br /&gt;&lt;br /&gt;Esta tarea, que es tan sencilla como aburrida, hace caer a muchos en uno de los errores más comunes: &lt;strong&gt;el terrible F5 destructor&lt;/strong&gt;. ¿Qué es eso?&lt;br /&gt;&lt;br /&gt;Por lo general, para realizar este trabajo, creamos una página con un formulario cuyo destino es la misma página. De esta forma es mucho más sencillo validar del lado del servidor los datos ingresados y, en caso de error, mostrar un mensaje al usuario manteniendo los datos que ha ingresado previamente.&lt;br /&gt;&lt;br /&gt;Hasta acá está todo muy bien, pero he visto muchas veces que una vez guardado los datos, se muestra un mensaje de éxito … ¡en la misma página!&lt;br /&gt;Esto significa que si el usuario presiona F5 y recarga la página, el proceso de guardar los datos se ejecutará nuevamente. Este error es muy común cuando usamos objetos como el DataGrid de .NET.&lt;br /&gt;&lt;br /&gt;Imaginemos ahora que en lugar de almacenar los datos, debemos enviar un mail. ¿Qué pasará cuando el usuario presione F5?&lt;br /&gt;&lt;br /&gt;El peligro que esto encierra es variado y puede ir desde una sobrecarga en el uso de recursos hasta grabar datos repetidos.&lt;br /&gt;&lt;br /&gt;Por extensión, se podría decir que: &lt;strong&gt;"Toda página de proceso exitoso debe concluir en un Redirect"&lt;/strong&gt;. &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-110061193482306126?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/110061193482306126/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=110061193482306126' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110061193482306126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110061193482306126'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/11/paginas-de-proceso-de-datos.html' title='Paginas de proceso de datos'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-110003468568679351</id><published>2004-11-09T17:39:00.000-03:00</published><updated>2004-11-09T18:18:21.963-03:00</updated><title type='text'>Qué y cuándo vs cómo</title><content type='html'>&lt;p&gt;Ya es hora de que empecemos a tratar cosas un poco más concretas y, para empezar, quiero hablar sobre algo que no sólo agrega "estilo" a nuestro trabajo, también lo hace mucho más mantenible.&lt;/p&gt;&lt;p&gt;En una empresa, existen personas que saben &lt;strong&gt;lo que hay que hacer&lt;/strong&gt; y &lt;strong&gt;cuándo hay que hacerlo&lt;/strong&gt;, y otras que saben &lt;strong&gt;cómo hacerlo&lt;/strong&gt;.&lt;/p&gt;&lt;p&gt;En nuestro código debería haber métodos que sepan qué y cuándo hacer las cosas y métodos que sepan cómo hacerlas. No me parece una buena práctica construir métodos que mezclan ambos roles.&lt;/p&gt;&lt;p&gt;Mantener esa separación nos permite ver rápidamente las reglas de negocio por un lado y, de ser necesario, revisar los algoritmos en forma individual. Además, estaremos creando métodos que podrán ser reutilizados en otras lugares de nuestro desarrollo.&lt;/p&gt;&lt;p&gt;Para graficar un poco más este concepto, veamos un ejemplo en ASPX. Necesitamos un método que envía un mail a la dirección especificada en un formulario. Tenemos un HTML en disco con la etiqueta "$$nombre$$" que debemos reemplazar por el nombre del usuario para personalizar el mail. Finalmente, grabamos el mail en nuestra base de datos.&lt;/p&gt;&lt;p&gt;El que sigue es un ejemplo de código muy poco profesional:&lt;br /&gt;	&lt;code&gt;&lt;span class="codeKey"&gt;private void&lt;/span&gt; Button1_Click(&lt;span class="codeKey"&gt;object&lt;/span&gt; sender, System.EventArgs e)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// Configuraciones&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;NameValueCollection config = ConfigurationSettings.AppSettings;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// Leer el archivo desde disco&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;string&lt;/span&gt; mailFile = config["MailFileName"];&lt;br /&gt;&amp;nbsp;&amp;nbsp;FileStream fs = &lt;span class="codeKey"&gt;new&lt;/span&gt; FileStream(mailFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);&lt;br /&gt;&amp;nbsp;&amp;nbsp;StreamReader sr = &lt;span class="codeKey"&gt;new&lt;/span&gt; StreamReader(fs, Encoding.Default);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;string&lt;/span&gt; message = sr.ReadToEnd();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// Reemplazar etiqueta&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;message = message.Replace("$$nombre$$", campoNombre.Text);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// Enviar mail&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;MailMessage mail= &lt;span class="codeKey"&gt;new&lt;/span&gt; MailMessage();&lt;br /&gt;&amp;nbsp;&amp;nbsp;mail.Subject = config["MailSubject"];&lt;br /&gt;&amp;nbsp;&amp;nbsp;mail.From = config["MailFrom"];&lt;br /&gt;&amp;nbsp;&amp;nbsp;mail.Body = message;&lt;br /&gt;&amp;nbsp;&amp;nbsp;mail.BodyFormat = MailFormat.Html;&lt;br /&gt;&amp;nbsp;&amp;nbsp;mail.To = campoEmail.Text;&lt;br /&gt;&amp;nbsp;&amp;nbsp;SmtpMail.SmtpServer = config["SmtpServer"];&lt;br /&gt;&amp;nbsp;&amp;nbsp;SmtpMail.Send( mail );&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;//Grabar a SQL&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;string&lt;/span&gt; connString = config["ConnectionString"];&lt;br /&gt;&amp;nbsp;&amp;nbsp;SqlConnection oConn = &lt;span class="codeKey"&gt;new&lt;/span&gt; SqlConnection(connString);&lt;br /&gt;&amp;nbsp;&amp;nbsp;SqlCommand dbCommand = &lt;span class="codeKey"&gt;new&lt;/span&gt; SqlCommand();&lt;br /&gt;&amp;nbsp;&amp;nbsp;dbCommand.CommandText = "Exec Insert_Email @email=@@email";&lt;br /&gt;&amp;nbsp;&amp;nbsp;dbCommand.Connection = oConn;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;int&lt;/span&gt; rowsAffected = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;oConn.Open();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;try&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rowsAffected = dbCommand.ExecuteNonQuery();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;catch&lt;/span&gt; (Exception ex)&lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;throw new&lt;/span&gt; Exception(ex.Message);&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;finally&lt;/span&gt; &lt;br /&gt;&amp;nbsp;&amp;nbsp;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;oConn.Close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Algo con mucho más estilo sería:&lt;br /&gt;&lt;code&gt;&lt;span class="codeKey"&gt;private void&lt;/span&gt; Button1_Click(&lt;span class="codeKey"&gt;object&lt;/span&gt; sender, System.EventArgs e)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;// Nunca debemos escribir más código del necesario &lt;br /&gt;&amp;nbsp;&amp;nbsp;// en los métodos que capturan eventos !!!&lt;br /&gt;&amp;nbsp;&amp;nbsp;EnviarMailAUsuario(campoNombre.Text, campoEmail.Text);&lt;br /&gt;}&lt;br /&gt;&lt;span class="codeKey"&gt;private void&lt;/span&gt; EnviarMailAUsuario(&lt;span class="codeKey"&gt;string&lt;/span&gt; nombre, &lt;span class="codeKey"&gt;string&lt;/span&gt; email)&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;//Este metodo sabe ques y cuandos&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;NameValueCollection config = ConfigurationSettings.AppSettings;&lt;br /&gt;	&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// 1.- Leer archivo de disco&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeKey"&gt;string&lt;/span&gt; emailSource = LeerArchivoDeDisco(config["MailFileName"]);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// 2.- Reemplazar etiqueta&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;emailSource = message.Replace("$$nombre$$", campoNombre.Text);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// 3.- Enviar mail&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;EnviarMailDelSitio(campoEmail.Text, emailSource);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&lt;span class="codeComment"&gt;// 4.- Guardar el mail&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;GuardarMail(campoEmail.Text);&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Los métodos invocados seguramente tendrán un código parecido a las líneas correspondientes del primer ejemplo. Noten que no estoy especificando en que capa de mi aplicación se encuentran estos métodos, pero deberían estar en una capa diferente de la de presentación.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-110003468568679351?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/110003468568679351/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=110003468568679351' title='1 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110003468568679351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/110003468568679351'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/11/qu-y-cundo-vs-cmo.html' title='Qué y cuándo vs cómo'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-109967891294914050</id><published>2004-11-05T15:01:00.000-03:00</published><updated>2004-11-05T15:21:52.950-03:00</updated><title type='text'>Todo es importante</title><content type='html'>&lt;p&gt;&lt;em&gt;- Lo más difícil de un sitio Web es el diseño. Una vez que tenemos un buen diseño, el resto es hacer que las páginas funcionen.&lt;/em&gt; (Un diseñador)&lt;br /&gt;&lt;em&gt;- Una vez que tenemos una aplicación que funciona bien, el resto es ponerle colorcitos.&lt;/em&gt; (Un desarrollador)&lt;br /&gt;&lt;em&gt;- Lo verdaderamente complicado es satisfacer los requerimientos del cliente con los recursos que contamos y el tiempo del que disponemos.&lt;/em&gt; (Un líder de proyecto)&lt;br /&gt;&lt;em&gt;- Lo importante es conseguir los clientes, la gente para hacer las cosas se consiguen así de fácil.&lt;/em&gt; (Un ejecutivo de cuentas)&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Señores: todo es igual de importante.&lt;/strong&gt; Sin clientes no hay desarrollo, sin gente que lidere el proyecto nunca terminaremos en tiempo y forma, un sitio Web gusta por cómo se ve, si el desarrollo funciona mal nada sirve finalmente.&lt;/p&gt;&lt;p&gt;Todos los involucrados en un proyecto deberían trabajar a la par y sin menospreciar el trabajo de los demás.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-109967891294914050?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/109967891294914050/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=109967891294914050' title='6 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109967891294914050'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109967891294914050'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/11/todo-es-importante.html' title='Todo es importante'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-109940525628194042</id><published>2004-11-02T11:18:00.000-03:00</published><updated>2004-11-02T11:20:56.280-03:00</updated><title type='text'>Conocer los riesgos</title><content type='html'>  &lt;p class="MsoNormal"&gt;Si queremos ser desarrolladores de sitios web profesionales debemos &lt;em&gt;conocer los riesgos de lo que hacemos&lt;/em&gt;. A mí, particularmente, no me alcanza con que algo "haga lo que tiene que hacer", quiero además que lo haga bien.&lt;/p&gt;   &lt;p class="MsoNormal"&gt;Una vez que identificamos la mejor opción para un escenario dado, podemos asumir ciertos riesgos. Pero lo que no debemos hacer es elegir una solución sin estar preparados para lo que pueda suceder de acuerdo a los riesgos de dicha solución.&lt;/p&gt;   &lt;p class="MsoNormal"&gt;Supongamos que tenemos que hacer una aplicación web muy sencilla como por ejemplo una serie de páginas que piden datos del usuario y al final los graba en una base. Podríamos tomar la decisión de ir guardando los datos en variables de sesión, pero debemos saber que, si por alguna razón las sesiones se pierden (el sitio está hosteado en más de un servidor, los usuario superan el tiempo de expiración de sesión, etc.), nuestra aplicación dejará de funcionar correctamente o aparecerán errores aparentemente aleatorios.&lt;/p&gt;   &lt;p class="MsoNormal"&gt;Como regla general debemos conocer cómo funcionan y que hacen todos aquellos componentes que vayamos a usar en nuestros desarrollos. Personalmente no me gusta utilizar componentes de terceros en aplicaciones críticas a menos que tenga disponible el código fuente.&lt;/p&gt;   &lt;p class="MsoNormal"&gt;Finalmente les cuento que mi estrategia (no digo que sea la mejor, es sólo la mía) es siempre comenzar pensando que mi desarrollo estará en múltiples servidores y visitado por muchos usuarios concurrentemente. Esa sería mi aplicación ideal. Luego, en función de los requerimientos reales, puedo ir asumiendo determinados riesgos.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-109940525628194042?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/109940525628194042/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=109940525628194042' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109940525628194042'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109940525628194042'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/11/conocer-los-riesgos.html' title='Conocer los riesgos'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-109905789054858430</id><published>2004-10-29T10:51:00.001-03:00</published><updated>2004-10-29T11:04:33.823-03:00</updated><title type='text'>Cookies</title><content type='html'>&lt;p class="MsoNormal"&gt;El protocolo &lt;acronym title="Protocolo de transferencia de texto enriquecido"&gt;HTTP&lt;/acronym&gt; es desatendido. Esto significa que no existe, en principio, relación alguna entre dos peticiones aunque estas sean consecutivas. Para poder mantener una sesión de usuario dentro de una aplicación WEB, tenemos 2 métodos.El usado por la mayoría de los sitios está basado en cookies, el otro en agregar a la  &lt;acronym title="Uniform Resource Locators"&gt;URL&lt;/acronym&gt; el ID de se sesión del usuario.&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Cada vez que nuestro navegador hace un pedido HTTP, envía todas las cookies válidas para el dominio donde se encuentran los archivos buscados.&lt;br /&gt;&lt;br /&gt;He visto sitios que escriben muchas cookies y, además, con nombres y valores muy largos como por ejemplo:&lt;br /&gt;&lt;code&gt;UltimaVisita=23/04/2004 03:25:37 PM&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Imaginemos una página que tiene HTML, JS, CSS y 20 GIFs, entonces estaremos enviando la cookie UltimaVisita unas 23 veces.&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Debemos usar las cookies en forma racional y responsable. Muchas cookies harán que los usuarios conectados por DialUp naveguen más lentamente. Otra ventaja es que usando menos cantidad de cookies y más pequeñas, estaremos ahorrando ancho de banda de nuestro sitio.&lt;br /&gt;En lugar de&lt;br /&gt;&lt;code&gt;UltimaVisita=23/04/2004 03:25:37 PM&lt;/code&gt;&lt;br /&gt;tal vez, podamos usar&lt;br /&gt;&lt;code&gt;UV=20040423&lt;/code&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Los grandes sitios de Internet, utilizan servidores dedicados a entregar imágenes. Estos usan un dominio diferente del sitio que navegamos. De esta forma, si el sitio se encuentra en &lt;strong&gt;www.sitio.com&lt;/strong&gt; y las imágenes en &lt;strong&gt;www.img.com&lt;/strong&gt; entonces, nuestro navegador no estará enviando las cookies del sitio cada vez que pide una imagen.&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;Finalmente quiero comentar algo que no todos tienen en cuenta. Un compañero decía: &lt;em&gt;"Lo que viene del usuario no es confiable"&lt;/em&gt;. Es muy fácil modificar las cookies que el navegador va a entregar al servidor. Sólo hace falta una sentencia javascript. Por lo tanto, no hay que poner cookies del tipo: &lt;code&gt;admin=si&lt;/code&gt; ó &lt;code&gt;idUsuario=27&lt;/code&gt; y si no hay otra alternativa tendremos que encriptarlas.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-109905789054858430?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/109905789054858430/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=109905789054858430' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109905789054858430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109905789054858430'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/10/cookies.html' title='Cookies'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-109874221258074627</id><published>2004-10-25T19:06:00.000-03:00</published><updated>2004-10-25T19:10:12.580-03:00</updated><title type='text'>HTML</title><content type='html'>&lt;p class="MsoNormal"&gt;Siendo desarrolladores web, en la mayoría de los casos, estaremos produciendo páginas HTML. No pretendo hacer aquí un tutorial. Simplemente creo que, para ser mejores desarrolladores, debemos tener un conocimiento profundo sobre HTML. He visto muchos desarrollos que producen páginas inválidas o exageradamente grandes que degradan la calidad de nuestro trabajo&lt;/p&gt;&lt;p class="MsoNormal"&gt;Existen muchísimos recursos disponibles en Internet para aprender. &lt;strong&gt;¡Cuanto más sepamos de HTML, mejor quedarán nuestros sitios!&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt; &lt;p class="MsoNormal"&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.w3.org/MarkUp/" target="_Blank"&gt;http://www.w3.org/MarkUp/&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.htmlhelp.com/" target="_Blank"&gt;http://www.htmlhelp.com/&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.csszengarden.com/" target="_Blank"&gt;http://www.csszengarden.com/&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-109874221258074627?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/109874221258074627/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=109874221258074627' title='2 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109874221258074627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109874221258074627'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/10/html.html' title='HTML'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8837851.post-109870989011445478</id><published>2004-10-25T10:04:00.000-03:00</published><updated>2004-10-25T10:20:11.093-03:00</updated><title type='text'>Desarrollo de aplicaciones web</title><content type='html'>  &lt;p class="MsoNormal"&gt;Hacer que ciertas páginas web tengan funcionalidad, como por ejemplo que envíe un mail, no requiere mucho más que saber escribir el código correspondiente. De hecho, no hace falta ser programador, simplemente se puede buscar en Internet y seguro encontraremos el líneas que necesitamos.&lt;/p&gt;   &lt;p class="MsoNormal"&gt;Para hacer una pequeña aplicación web, como mostrar un formulario y guardar la información en una base de datos, requiere además que tengamos algunos conceptos de programación.&lt;/p&gt;     &lt;p class="MsoNormal"&gt;&lt;strong&gt;Desarrollar aplicaciones web profesionales&lt;/strong&gt; no es tan sencillo. Debemos saber mucho más que un lenguaje de programación y un poco de &lt;acronym title="HyperText Markup Language - Lenguaje de texto enriquecido por etiquetas"&gt;HTML&lt;/acronym&gt;. Debemos conocer más profundamente cómo funciona Internet, la plataforma sobre la que vamos a desarrollar, tener en cuenta la envergadura de nuestra aplicación, la cantidad de usuarios que van a ingresar, etc.&lt;/p&gt;     &lt;p class="MsoNormal"&gt;En este weblog voy a intentar explicar la forma en la que a mí me gusta desarrollar aplicaciones web y mostrar lo que para mí son errores comunes. Si bien, los ejemplos de código serán en ASP o ASPX, la mayoría de los conceptos creo que son aplicables al desarrollo web en general.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8837851-109870989011445478?l=webdevpro.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://webdevpro.blogspot.com/feeds/109870989011445478/comments/default' title='Comentarios de la entrada'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8837851&amp;postID=109870989011445478' title='0 Comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109870989011445478'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8837851/posts/default/109870989011445478'/><link rel='alternate' type='text/html' href='http://webdevpro.blogspot.com/2004/10/desarrollo-de-aplicaciones-web.html' title='Desarrollo de aplicaciones web'/><author><name>Damián Bacalov</name><uri>http://www.blogger.com/profile/02958166542492709958</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://4.bp.blogspot.com/_Aqo-TwbVz90/SQ9BbXGNq9I/AAAAAAAAAwc/ua9i9Bmro9M/S220/Damian.gif'/></author><thr:total>0</thr:total></entry></feed>
