martes, 20 de noviembre de 2012

Transformación de ficheros de configuración en ASP.NET

Aunque hace tiempo que conocía que se podía utilizar la transformación de los ficheros de configuración, es ahora cuando tengo una necesidad real de utilizar esta característica para intentar optimizar la publicación de aplicaciones web en entornos de hosting compartido.

Está claro que nuestro fichero web.config del entorno de desarrollo no es el mismo fichero web.config de producción o de cualquier otro entorno de publicación. Normalmente serán varias las secciones del web.config que tendremos que cambiar al publicar (appSettings, connectionStrings, customErrors, etc.). De igual forma está claro que no queremos (ni debemos) realizar estos cambios de forma manual cada vez que publiquemos en un entorno distinto al actual, por lo que una solución válida para automatizar esta tarea pasa por utilizar los ficheros de transformación.

Podemos crear un fichero de transformación para cada configuración de compilación (build configuration). Por defecto, cualquier proyecto incorpora las configuraciones Debug y Release, pero podemos crear cualquier otra configuración que necesitemos, por ejemplo: PreProducción, Producción, etc. Por cada configuración tendremos asociado a nuestro fichero web.config base un fichero con el nombre Web.NombreConfiguración.config. Cuando publiquemos y en función de la configuración activa, el fichero de transformación seleccionado aplicará los cambios solicitados al fichero web.config base y dará como resultado un nuevo fichero web.config con las transformaciones aplicadas.

Un ejemplo nos ayudará mejor a entenderlo.

Para crear una nueva configuración, Build > Configuration Manager… > New…

clip_image003

Después y en el menú contextual del fichero web.config, Add Config Tranform

clip_image004

Add Config Transform detectará que configuraciones de compilación no tienen un fichero de transformación y los agregará. Si estás utilizando VB.NET tendrás que activar “Show All Files” en la ventana “Solution Explorer” para ver estos ficheros.

clip_image005

Esta práctica no está limitada al directorio raíz de tu aplicación, sino que en cualquier carpeta que tenga un fichero web.config puedes aplicar la misma técnica.

clip_image006

También puedes borrar en cualquier momento un fichero de transformación y así y durante la publicación, simplemente no se realizará ninguna transformación.

En lo relativo a qué podemos hacer con las transformaciones, principalmente tendremos que trabajar con características principales:

  • Locator. Que nos ayudará a localizar el nodo donde aplicar una transformación.
  • Tranform. Servirá para indicar que tipo de transformación queremos realizar al nodo “localizado”.

La documentación sobre la sintaxis del espacio de nombres XML Document-Transform la puedes encontrar en la MSDN http://msdn.microsoft.com/en-us/library/dd465326(v=vs.100).aspx

Locator admite 3 posibilidades:

  • Condition(expresión XPath)
  • Match(Lista de atributos separada por comas)
  • XPath(expresión XPath)

Veamos algunos ejemplos:

Para empezar tenemos un fichero web.config con el siguiente contenido:

  <add

    name="CONEXIÓN"

    connectionString="CADENA_CONEXIÓN"

    providerName="PROVEEDOR" />

Con Locator="Condition(XPath expression)"

<?xml version="1.0" encoding="utf-8"?>

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

  <connectionStrings>

    <add

      name="NUEVO_NOMBRE"

      connectionString="NUEVA_CADENA_CONEXIÓN"

      providerName="NUEVO_PROVEEDOR"

      xdt:Locator="Condition(@name='CONEXION')"

      xdt:Transform="Replace"/>

  </connectionStrings>

</configuration>

Si quisiéramos ver ahora el resultado del fichero de transformación, la opción más sencilla sería publicar con la configuración “Debug”. Sin embargo, como a veces publicar puede ser algo tedioso (aunque sea en un directorio a disco), también podemos optar por llamar directamente por línea de comandos a MSBUILD con la siguiente sintaxis:
MSBuild “Ruta al fichero.vbproj|.csproj de nuestro proyecto” /t:TransformWebConfig /p:Configuration=NombreConfiguracion que nos volcará en el directorio \obj el resultado de la transformación (fijarse como además también ha trabajado con las subcarpetas).

clip_image007

Con Locator="Match(Lista de atributos separada por comas)"

…

  <connectionStrings>

    <add

      name="CONEXIÓN"

      connectionString="NUEVA_CADENA_CONEXIÓN"

      providerName="NUEVO_PROVEEDOR"

      xdt:Locator="Match(name)"

      xdt:Transform="Replace"/>

  </connectionStrings>

…

Si hablamos ahora de Transform, tenemos disponibles las siguientes operaciones:

  • Replace
    • Reemplaza el elemento encontrado.
    • En caso de encontrar varios, sólo reemplaza el primero.
  • Insert
    • Añade el elemento al final de la colección.
  • InsertBefore
    • Añade el elemento antes del elemento seleccionado según una expresión XPath.
  • InsertAfter
    • Igual que InsertBefore, pero añade el elemento después del elemento seleccionado.
  • Remove
    • Eliminar el elemento seleccionado.
    • En caso de encontrar varios, sólo reemplaza el primero.
  • RemoveAll
    • Elimina todos los elementos seleccionados.
  • RemoveAttributes
    • Elimina atributos del elemento seleccionado.
  • SetAttributes
    • Establece el valor de atributos de todos los elementos seleccionados.
    • Es similar a Replace, pero permite especificar que atributos serán reemplazados (mientras que Replace reemplaza el elemento entero). Además, SetAttributes no sólo reemplaza la primera ocurrencia, sino todas las encontradas.

Un ejemplo típico de un fichero de transformación para producción (release) podría ser aquel en que transformaremos los siguientes elementos:

  • appSettings
  • connectionStrings
  • compilation
  • customErrors
  • ELMAH
  • mailSettings

 

<?xml version="1.0" encoding="utf-8"?>

<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">

  <appSettings>

    <add

      key="Value"

      value="Value"

      xdt:Locator="Match(key)"

      xdt:Transform="Replace" />

  </appSettings>

  <connectionStrings xdt:Transform="Replace">

    <add

      name="Value"

      connectionString="Value"

      providerName="Value" />

  </connectionStrings>

  <system.web>

    <compilation xdt:Transform="RemoveAttributes(debug)" />

    <customErrors mode="RemoteOnly" xdt:Transform="SetAttributes(mode)">

    </customErrors>

  </system.web>

  <system.net>

    <mailSettings xdt:Transform="Replace">

      <smtp from=" Value ">

        <network

          host="Value"

          password="Value"

          userName="Value" />

      </smtp>

    </mailSettings>

  </system.net>

  <elmah>

    <security

      allowRemoteAccess="false"

      xdt:Transform="Replace" />

    <errorMail

      from="Value"

      to="Value"

      subject="Value"

      async="true"

      smtpPort="25"

      smtpServer="Value"

      userName="Value"

      password="Value"

      xdt:Transform="Replace">

    </errorMail>

  </elmah>

</configuration>

Las transformaciones realizadas han sido:

  • En appSettings hemos tenido que localizar primero con Match (porque asumimos que no queremos remplazar toda la sección sino sólo algún valor) y después se ha optado por Reemplazar (Replace). También se podría haber utilizado SetAttributes o incluso SetAttributes(value).
  • En connectionStrings se ha utilizado Replace pero sin ningún Locator. Con esto conseguimos remplazar la sección entera y no ha sido necesario utilizar Locator porque sólo hay una sección connectionStrings en un fichero web.config y por ello se localiza automáticamente.
  • En compilation se elimina el atributo debug con RemoveAttributes.
  • En customErrors simplemente se cambia el valor de mode con SetAttributes(mode).
  • Para mailSettings, elmah/security y elmah/errorMail se utilizar Replace sin Locator.

Espero que te haya quedado más o menos claro las posibilidades que ofrecen la transformación de ficheros de configuración y lo utilices de ahora en adelante, en detrimento del cambio manual que, insisto, ni nos gusta ni tampoco debemos.

POST ACTUALIZADO: Por cierto, si utilizas configSource para sacar a un fichero externo las cadenas de conexión o los settings de la aplicación, la transformación no los tendrá en cuenta, con lo cual tenemos varias opciones para solucionarlo. Una es utilizar distintos ficheros connectionStrings.config (p. ej. connectionStrings.Debug.config, connectionsString.Release.config y aplicar transformaciones al web.config para que apunte a uno u otro según configuración… cosa que no me gusta mucho, porque además de repetir código, no impide que en el directorio de publicación se escriban todos los ficheros, sean o no necesarios según la configuración activa). Otra es utilizar el addin de Visual Studio SlowCheetah que habilita la transformación de ficheros .config, no sólo al web.config sino a cualquier fichero de configuración.  Aunque la documentación de la herramienta es muy buena, también te dejo un enlace de un post de Scott Hanselman al respecto.

El único problema que me ha dado SlowCheetah ha sido un error durante la publicación del proyecto web porque no encontraba ciertos ficheros. Para solucionarlo he tenido que copiar todos los ficheros de la carpeta C:\Users\<TuUsuario>\AppData\Local\Microsoft\MSBuild\SlowCheetah\v1 a la carpeta v2.5.10. Entiendo que esto lo solucionará el autor en un siguiente release, seguro que sí!

POST ACTUALIZADO: Una mejor solución que copiar los ficheros parece modificar el fichero .csproj e indicar allí la ruta correcta a los ficheros:

<PropertyGroup>
  <SlowCheetahTargets Condition=" '$(SlowCheetahTargets)'=='' ">$(LOCALAPPDATA)\Microsoft\MSBuild\SlowCheetah\v2.5.10\SlowCheetah.Transforms.targets</SlowCheetahTargets>
</PropertyGroup>

POST ACTUALIZADO: Parece que hay un problema si se quiere reemplazar el nodo entero. Por ejemplo, algo como lo siguiente falla:

<connectionStrings xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform" xdt:Transform="Replace">
…
</connectionStrings>

Lanzado el siguiente error:

Could not write Destination file: Cannot insert the node in the specified location

Siendo así, en el caso de connectionStrings optaremos por tener distintos ficheros según configuración en vez de aplicar transformaciones. Donde sí tenemos que aplicar una transformación es en los ficheros web.config para que apunten a uno u otro fichero connectionString desde la propiedad configSource.

<connectionStrings configSource="connectionStrings.Release.config" xdt:Transform="Replace"></connectionStrings>

Esto conlleva por otro lado, que al publicar (en mi caso lo hago a disco) tengamos tanto el fichero connectionStrings.config (el que utilizamos en debug) como el fichero connectionStrings.Release.config (el que hemos creado para release). Como no queremos (no debemos) tener que eliminar a mano el fichero connectionStrings.config después de publicar, podemos solucionar esto incluyendo algo de código MSBuild en el fichero .pubxml que se creó en el directorio PublishProfiles debajo de Properties en nuestra aplicación web.

El código a incluir es el siguiente… y ojito que este código he tardado en escribirlo 3 horas, así que utilízalo sabiamente ;-)

<Target Name="panicoenlaxbox" AfterTargets="GatherAllFilesToPublish">
  <Delete Files="$(_PackageTempDir)\connectionStrings.config" />   
</Target>

POST ACTUALIZADO: Al volver mucho después a este post he visto que ahora también hay un paquete de Nuget SlowCheetah. Esto significa que ya no es necesario instalar el plugin en VS. Personalmente me parece ahora mejor opción porque así te olvidas de tener que estar copiando manualmente y en una ruta concreta los ficheros .targets en el servidor de integración continua, simplemente ahora son parte del proyecto.

image

Un saludo!

lunes, 19 de noviembre de 2012

Una y no más, Santo Crystal Reports

Tristemente, en mi empresa tenemos alguna aplicación que utiliza Crystal Reports. Más tristemente, la versión de Crystal Reports es 2008. Digo esto último porque quizás futuras versiones faciliten más la vida al desarrollador, porque desde luego la versión 2008 no es nada agradecida.

A modo de diario y previendo futuras publicaciones de esta misma aplicación, dejo a continuación los pasos que hemos tenido que llevar a cabo para hacer que los informes de Crystal funcionen en ASP.NET.

El sistema operativo de despliegue es un Windows Web Server 2008 R2. La versión del framework de la aplicación de ASP.NET es 3.5… y lo dicho, la versión de Crystal es 2008.

Lo primero es descargar el runtime de CR. Podría parecer sencillo, pero no lo es. Los señores de SAP han decidido que sea todo un galimatías encontrar algo en su página, así que si no tiene el enlace directo podrías vértelas canutas para descargar el runtime, pero bueno…

La página de descarga de diversas utilidades (entre ellas el runtime) es https://websmp230.sap-ag.de/sap%28bD1lcyZjPTAwMQ==%29/bc/bsp/spn/bobj_download/main.htm Aquí tienes que localizar “Crystal Reports 2008 Service Pack 4 – Redist Install”. https://smpdl.sap-ag.de/~sapidp/012002523100008782532011E/cr2008sp4_redist.zip Me hubiera gustado bajarme el SP5 que se anuncia en https://wiki.sdn.sap.com/wiki/pages/viewpage.action?pageId=56787567 pero me ha sido imposible registrarme y la descarga pide nombre de usuario y contraseña.

Una vez descargado, se instala y listo. Crea una aplicación web llamada “crystalreportviewers12” en el sitio web por defecto (tampoco pregunta donde quieres crearla no vaya a ser que sea de utilidad, que aquí estamos para complicar los cosas… habrá pensado quién hizo el paquete de instalación). Como nuestra aplicación no está en el sitio web por defecto (está en otro sitio web), pues toca crear un directorio virtual en nuestro sitio web de la aplicación, apuntando a C:\Program Files (x86)\Business Objects\Common\4.0\crystalreportviewers12 con el nombre “crystalreportviewers12”. Además, también he tenido que editar el fichero web.config de la anterior ruta y quitar la siguiente sección

<system.web>

<!-- request length is 20MB -->

<httpRuntime maxRequestLength="20000"/>

<sessionState timeout="20" />

</system.web>

Como parte de la instalación también ha agregado al sitio web por defecto, una carpeta aspnet_client. No preguntes y cópiala también en la raíz de tu sitio web.

Otra cosa un tanto hiriente es que tendrás que compilar tu aplicación en x86. En el siguiente foro lo explican bien http://scn.sap.com/thread/1519581, pero básicamente “Only the .NET 2005 (CR 10.2) and .NET 2008 (CR 10.5) bundles are 64 bit. You must compile the app as 32 bit and use the 32 bit CR runtime.”

Además, también tendrás que habilitar en el grupo de aplicaciones en que esté asignada tu aplicación “Habilitar aplicaciones de 32 bits”.

clip_image001

Aunque en esta publicación no me ha pasado, recuerdo también haber encontrado problemas con la típica imagen de logo del cliente en un informe. En su día me ayudaron los siguientes enlaces (problemas con permisos, problemas con CrystalImageHandler.aspx, etc.) http://www.sdn.sap.com/irj/boc/go/portal/prtroot/docs/library/uuid/a0437ea8-97d2-2b10-2795-c202a76a5e80?QuickLink=index&overridelayout=true

También es posible que tengas que mapear CrystalImageHandler.aspx en el IIS. Visita este enlace para más información http://crmtogether.com/mwCrystal/index.php?title=Troubleshoot

La moraleja de todo esto es que “una no más, santo tomás”. No volveré a usar Crystal Reports para ninguna tarea de reporting. Probaré con otros soluciones de terceros o incluso me planteo hacerme los informes a mano. De hecho, creo que los informes, cada vez más, son un recurso en desuso que solo sirven a un pocos y durante un tiempo limitado (después los requerimientos cambian y nos quedamos con una ristra de informes que no valen para nada).

Un saludo!

miércoles, 10 de octubre de 2012

Javascript: The Good Parts y JSLint

Recientemente parece que el libro Javascript: The Good Parts me persigue allá donde vaya.

Hace poco lo conocí a través de twitter, pero justo hoy, en el evento html tour de Madrid de Plain Concepts, se ha vuelto a recomendar la lectura de este libro.

Lo cierto es que cuando lo tuve en mi poder lo devoré en un solo fin de semana. Tampoco es ningún mérito, el libro es pequeño y no se anda con rodeos.

Después de su lectura, una sola idea ronda por mi cabeza “Javascript es un lenguaje incomprendido”.  Yo al menos no tengo ningún reparo en reconocer que, antes de la lectura de este libro, mi opinión sobre Javascript distaba de ser algo positivo. Además, también distaba de tener el suficiente conocimiento como para juzgarlo con conocimiento de causa.

Hoy durante el html tour, he oído una de las mejores frases que uno podría oír sobre Javascript “Javascript: the worst part. El principal problema es nuestra ignorancia”.

Creo que realmente el problema de Javascript es que cualquiera puede programar en Javascript. Da igual si eres un consumado experto o un tierno novato, escribe cuatro líneas de código y Javascript intentará por todos los medios ocultar tus vicios y ejecutar tu código.

Javascript es algo más que la función alert y la función confirm. Es un lenguaje de programación en toda regla pero con un pésimo interprete que, por no romper la web, no se quejará casi nunca y evitará así que aprendas de tus errores.

Además tampoco esperes encontrar en Javascript un lenguaje POO al estilo de C#. Javascript tiene su propia identidad y su forma singular de hacer las cosas, te gustará o no pero es lo que hay, es mejor que no intentes cambiar a Javascript sino cambiar tu forma de relacionarte con Javascript. Como dice Bruce Lee, “Be water my friend”.

Como lo cierto es que sí queremos aprender de nuestros errores y queremos ser mejores programadores, en mi opinión es indispensable realizar las siguientes tareas:

  • Leer Javascript: The Good Parts.
  • Utilizar JSLint para que valide nuestro código Javascript.

Como leerte el libro mientras te duermes plácidamente me parece un exceso, en este post lo que voy a intentar es explicarte brevemente cómo instalar JSLint y como usarlo y configurarlo.

JSLint es una herramienta creada por el mismo autor del libro, Douglas Crockford. El sitio web de la herramienta es http://www.jslint.com/. Según sus propias palabras, JSLint es una herramienta de calidad de código, es decir, una herramienta que busca problemas en tú código Javascript.

La mayoría de los problemas son bien conocidos y no admiten debate alguno, otros son guías de estilo propuestos por el autor, que podrías ignorar si así lo crees oportuno.

Aunque puedes utilizar un validador en línea desde su página, hay un proyecto en CodePlex que integra la herramienta en Visual Studio http://jslint4vs2010.codeplex.com/.

Para instalarlo, puedes descargar la extensión para Visual Studio 2010 http://visualstudiogallery.msdn.microsoft.com/961e6734-cd3a-4afb-a121-4541742b912e

También está disponible para su descarga desde el administrador de extensiones

clip_image002

Una vez instalado, tenemos disponible una nueva opción de menú: Herramientas > JS Lint Options…

clip_image004

 clip_image006

El uso de JSLint es bastante sencillo, por defecto hará lo siguiente.

  • Mostrará los errores como “warnings”
  • Sólo validará los ficheros .js
  • Ejecutará la herramienta en cada Build
  • Cancelará el Build si encuentra algún error

Como vemos en las pantallas anteriores, podemos configurar multitud de aspectos de la herramienta: desde validar ficheros .html, no cancelar el Build, ejecutarlo cuando guardamos el fichero, excluir ciertas secciones de nuestro código a través de comentarios, etc.

Mi recomendación (copiada vilmente del ponente del html tour) es que utilicemos JSLint para cualquier nuevo proyecto y que, para proyectos existentes, hagamos lo que podamos. Es decir, hoy mismo me decía un compañero que lo había activado para un proyecto en mantenimiento y el número de errores había sido demasiado alto… Yo directamente en mis proyectos antiguos no lo activaré porque tengo amor propio y no me apetece que JSLint me saque los colores cuando analice mi código antiguo.

Para ver a JSLint en acción, crearemos un nuevo Sitio Web y agregaremos un fichero .js en el que escribiremos el siguiente código:

function foo() {

    bar = "baz";

    return bar;

}

for (var i = 0; i < 5; i++) {

    alert(foo());

}

 

Que nos devolverá los siguientes warnings:

clip_image008

La compilación ha sido cancelada porque JSLint encontró errores de validación.

  • Estamos utilizando la variable “bar” pero no la hemos definido previamente (hemos olvidado var y eso implica que la variable bar se está creando en el contexto global y no el ámbito privado de la función foo).
  • En Javascript no existe el ámbito de bloque, luego “for (var i” podría llevarnos a engaño. Después de la instrucción for, “i” vale 5 porque “i” sigue existiendo.

Vamos a intentar solucionar los errores:

function foo() {

    var bar = "baz"; // hemos agregado var

    return bar;

}

var i = 0; // hemos declarado la variable fuera del for

for (i; i < 5; i++) {

    alert(foo());

}

 

clip_image010

Pues lo hemos logrado a medias, ahora nos informa que “no esperaba ++” y que “alert” ha sido utilizada antes de ser definida.

En cuanto al operador ++, según Douglas Crockford, no le gusta. Yo no voy a discutir a este señor (no podría), pero como ahora mismo no sé que espera encontrar y además con este excusa podemos ver como “desactivar” ciertas validaciones de JSLint en la pantalla JSLint Options.

clip_image012

Ahora ya no tenemos errores de JSLint:

clip_image014

Respecto a los errores relacionados con las guías de estilo, bastaría con quitar un espacio entre “var bar y =”

function foo() {

    var bar= "baz";

    return bar;

}

 

Ahora JSLint nos informará sobre este nuevo error relacionado con el estilo:

clip_image016

Para los que trabajamos con Visual Studio, muchos de estos problemas se solucionan simplemente con el atajo Ctrl + K, Ctrl + D (Menú Editar > Avanzadas > Dar formato al documento). De hecho, yo también utilizo este comando para ver si mi código tiene algún error, si Visual Studio no es capaz de dar formato al documento Javascript… algo no va bien.

Otros comandos que agrega JSLint a Visual Studio son nuevas opciones en el menú contextual desde el explorador de soluciones (Solution Explorer).

clip_image017

La opción “Skip on build (item)” cobra especialmente importancia si trabajamos con librerías de terceros. Por ejemplo, jQuery 1.8.2 lanza 455 advertencias en JSLint! De este modo o lo excluimos o estamos en un serio aprieto.

Si estás trabajando con VS2012 e instalas la extensión WebEssentials (un must-have en toda regla) http://vswebessentials.com/ automáticamente dispondrás de JSHint (ver que ahora hemos cambiado la L por una H) y podrás configurar todo lo relacionado con la herramienta desde Herramientas > Opciones > Web Essentials > JSHint

image

Las diferencias entre L y H (JSLint y JSHint) radican en que JSHint es un fork de JSLint. Es decir, ambos están vigentes y ambos son válidos, utilizar uno u otro es cuestión de gustos. El porqué aquí http://anton.kovalyov.net/2011/02/20/why-i-forked-jslint-to-jshint/ Por supuesto JSHint también tiene su propio validador on-line http://www.jshint.com/

Espero que este post te sirva para entender que Javascript es un lenguaje que merece toda tu atención y que hay herramientas que podrían ayudarnos a escribir mejor código.

Un saludo!