Uno de los atributos menos comprendidos del archivo web.config es el relacionado con la compilación (compilation). Cuando yo desarrollaba y estaba encargado de manejar sitios en producción, siempre me preocupaba de que el atributo estuviese en false en producción, ya que había leído que así debía ser y no me lo había cuestionado mayormente, fundamentado en lo que yo creía que significaba.
La principal confusión, al menos para mí, venía por lo que yo entendía por compilación. Cuando uno realiza un proyecto web en Visual Studio 2003, se puede generar código en dos modalidades. Una de ellas de Code Behind que corresponde a un archivo del lenguaje (VB.NET o C# por ejemplo) donde incluimos el código asociado a la página, y la otra modalidad es incluir directamente el código dentro de la página.
Siempre he sido un fanático del primer tipo de proyecto y su compilación, la cual fue drásticamente eliminada en Visual Studio 2005 y reemplazado por una cantidad casi infinita de posibles formas de compilar y generación de ensamblados (assemblies), no repetibles y que son bastante más engorrosas para subir a producción. Más adelante veremos esto y cómo Microsoft enmendó el camino.
Con la primera metodología para trabajar proyectos en VS 2003, al compilar, se genera un ensamblado en la carpeta bin del sitio web. Este ensamblado tiene todo el código de los archivos Code Behind compilado en su interior. Utilizando la otra forma, se genera también un ensamblado en la carpeta bin, pero éste contiene información mínima de la aplicación, como las referencias. Todo el código queda en las páginas y sube al sitio (con todos los riesgos que esto implica).
Bueno, volviendo a mi confusión, yo creí, al igual que varios que conozco, que esta opción debug=”true” del atributo compilation aplicaba sólo para los proyectos generados en la segunda modalidad y no para los con Code Behind, ya que todo estaba compilado en el ensamblado.
Ahí estaba mi error. Repasemos antes cual es el atributo al que estoy haciendo referencia. En el archivo web.config existe el siguiente bloque de configuración.
< compilation defaultLanguage =”c#” debug =”false” />Además de la compilación que se realiza al presionar Build Solution o Rebuild Solution, y que genera este ensamblado en la carpeta bin, existe otra compilación que se realiza cuando la aplicación es requerida.
Tenemos una aplicación que tiene 1 página, igual a la que por defecto se nos propone al crear un nuevo proyecto web. Este proyecto se ve como el de la siguiente imagen. |
Si genero el proyecto con build, se genera un ensamblado y su archivo de símbolos (.pdb) como vemos ahora:
Primer requerimiento
Antes de que mi aplicación sea requerida por un browser, nada ha ocurrido ni se ha compilado (además de lo que ya compilamos en la carpeta bin). Eso lo podemos ver examinando la siguiente carpeta:
%windir%\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files |
Seguramente encontrarás cosas que no pensabas que existían, pero en mi caso, no hay nada porque la limpié hace unos momentos. Entonces, si le hago un requerimiento a mi sitio http://localhost/WebCompilation/WebForm1.aspx, en esta carpeta aparece lo siguiente, después de expandir todos los directorios. |
Las únicas carpetas que tienen información son:
…\Temporary ASP.NET Files\webcompilation\f7e6afd0\61bb37a7 …\Temporary ASP.NET Files\webcompilation\f7e6afd0\61bb37a7\assembly\dl2\a61b0a98\2a9e47df_4890c701 |
El contenido de la primera carpeta se despliega en la imagen de más arriba a la derecha. Obviamente los nombres con números de las carpetas se generan automática y azarosamente. La segunda carpeta tiene una copia del ensamblado generado por la compilación del proyecto, ese de la carpeta bin y un archivo ini con información adicional. Es importante notar que hay una cantidad importante de archivos y de diversos tipos, los que pasamos a revisar a continuación:
aluvtjbz.0.cs: Archivo de código c# generado a partir del archivo global.asax.
aluvtjbz.cmdline: archivo con las instrucciones de compilación del archivo interior. Muy interesante.
aluvtjbz.dll: ensamblado con el resultado de la compilación del archivo aluvtjbz.0.cs
aluvtjbz.err: en mi caso, vacío. Podría deducir que se almacenan lo errores de compilación.
aluvtjbz.out: Salida generada por pantalla cuando se compiló el archivo aluvtjbz.0.cs, producto de la ejecución del comando escrito en aluvtjbz.cmdline.
aluvtjbz.pdb: Símbolos asociados al ensamblado generado en la compilación.
Notar que cuando digo el archivo de código generado, no me refiero al archivo global.asax.cs sino que al archivo que se genera para la compilación del archivo global.asax (sin .cs) propiamente tal. Complementariamente, los mismos archivos existen para el archivo WebForm1.aspx, almacenados en los archivos de nombre amyrvbql.
Adicionalmente hay algunos archivos más, que almacenan información de dependencias de archivos. Estos son:
hash.web que almacena algún hash identificador del sitio web
global.asax.xml, almacena las dependencias de global.asax (el mismo archivo global.asax y el ensamblado de la carpeta bin)
de1daaf3.web información al menos desconocida para mi (archivo de tamaño 0)
WebForm1.aspx.de1daaf3.xml dependencias de WebForm1.aspx (contiene una referencia a el mismo archivo WebForm1.aspx y al ensamblado de la carpeta bin)
Después del primer requerimiento se han compilado los archivos requeridos para servir el contenido necesario. Como se solicitó el archivo WebForm1.aspx, fue generado éste y el global.asax, utilizado siempre en cada requerimiento.
Ésta compilación es la que es modificada de acuerdo al valor de la configuración. En inglés lo podrán encontrar como Batch Compilation. |
Antes de ver qué sucede si cambiamos la configuración a debug=”false”, agregaremos algunas páginas más a la aplicación, y otras carpetas con más páginas. Además, he agregado un par de controles de usuario en cada carpeta, los cuales son usados por los archivos WebForm1.aspx y WebForm4.aspx.
Después de limpiar la carpeta temporal y acceder WebForm1.aspx (la que tiene el control de usuario), el contenido de la nueva carpeta temporal es: |
También se han generado archivos de dependencias para todos los archivos compilados, con nombres similares a los anteriores. Si se fijan además, no se ha compilado nada de WebForm2.aspx ni ninguno de los archivos de la carpeta newfolder1.
![]() | ![]() |
Archivos en otras carpetas
Si ahora accedo a los archivos WebForm3.aspx y WebForm4.aspx, considerando que esta última tiene incrustado el control de usuario WebUserControl2.ascx, ocurre que ahora tengo muchos archivos en esta carpeta, los cuales han sido compilados cada uno en un ensamblado diferente. Pueden probar abriéndolos con [Reflector], como se muestra en la imagen de arriba a la derecha, para el archivo nmxwjakq.dll.
Cambiemos la configuración
Al cambiar la configuración a debug=”false” y al hacer el primer requerimiento a la página WebForm1.aspx, nuestro directorio se limpia automáticamente y se generan nuevos archivos, pero esta vez son muchos menos.
Una peculiaridad del resultado de éste proceso es ver cómo se han agrupado los archivos para la compilación. Si recordamos, el archivo WebForm1.aspx tiene una instancia del control WebUserControl1.ascx pero no así el archivo WebForm2.aspx. Sin embargo, si vieron con detenimiento la imagen donde se mostraba la vista desde Reflector, habrán notado que el compilado de WebForm1.aspx va en un ensamblado (fqv6no_s.dll) y todo el resto de archivos aspx y ascx (menos global.asax) se han compilado en otro ensamblado (uwsjh005.dll). El archivo global.asax se compiló en m9mdpgdl.dll. Agregué un archivo WebForm5.aspx sólo para comprobar que agregaba todos los archivos restantes.
Notemos también que no se han compilado los archivos de la carpeta NewFolder1. No obstante, al acceder WebForm3.aspx (que no contiene una instancia del control WebUserControl2.ascx, se compilan todos los archivos de esa carpeta, en 2 ensamblados. Uno de los ensamblados contiene a WebForm3.aspx y WebUserControl2.ascx, y el otro ensamblado a WebForm4.aspx. Mostraremos más abajo una imagen desde Reflector.
No estoy seguro de porque los agrupa así, pero me inclino con casi total seguridad que se debe a las dependencias. Para poder compilar un archivo que contiene un control incrustado, necesitará primero compilar el control y una vez que éste fue compilado, compilar la página que lo contiene.
Referencias a otras carpetas
¿Qué sucede si agregamos en WebForm1.aspx (que ya contiene una referencia a WebUserControl1.ascx) una referencia a WebUserControl2.ascx?
El resultado obtenido es el esperado aunque con una pequeña sorpresa. Lo esperado es que se compilan todos los archivos ya que el requerimiento de la página la WebForm1.aspx hace que se compile toda la carpeta donde está este archivo, como también que se compilen todos los archivos de la carpeta donde está WebUserControl2.ascx, el cual ahora era utilizado desde WebForm1.aspx. La sorpresa, al menos para mí, vino del hecho de que se utiliza el mismo patrón de agrupación de archivos para la compilación.
Si se fijan en las imágenes de más abajo, a la izquierda está el resultado de requerimientos por separado para cada WebForm1.aspx y WebForm3.aspx, ocurrida en dos momentos diferentes. La de la derecha, sólo para el llamado de WebForm1.aspx. Las agrupaciones de archivos son equivalentes.
![]() | ![]() |
Por último, ¿qué sucede si ambas páginas, o mejor dicho, los tres formularios de la raíz (WebForm1.aspx, WebForm2.aspx y WebForm5.aspx) tienen incrustado una instancia de WebUserControl1.aspx?
Antes de hacer la prueba, voy a dar mi vaticinio. Ustedes podrán confiar o no de que estoy dando mi vaticinio sin haber el resultado [;)], en el caso de apuntarle correctamente. Creo que pueden pasar 2 cosas. O bien se compilan todos pos separado o se compilan los aspx todos juntos (ya que no tienen dependencias entre ellos). Mi apuesta va por la segunda. Mi intuición fue acertada. Efectivamente agrupó a todos los WebForm*.aspx en un solo ensamblado, el WebUserControl1.ascx en otro, y por último, el global.asax en otro. La imagen de Reflector nos muesta el resultado a la derecha. |
Para la segunda parte, dejaremos el estudio de las opciones disponibles en el ítem de compilación en el archivo web.config, como también las conclusiones de porque no debo definir el atributo debug=”true” en producción. Aún nos falta ver qué sucede con las modificaciones a los archivos y las re-compilaciones.
Diferencias con Visual Studio 2005
Como ya había mencionado, en Visual Studio 2005, la forma de compilación y entrega para proyectos web cambió considerablemente. Sin entrar en justificaciones como tampoco en detalles, sólo agregaré que Microsoft enmendó el rumbo proveyendo algo similar a como funcionaba en Visual Studio 2003. Éste se llama Visual Studio 2005 Web Application Projects, disponible en la dirección http://msdn2.microsoft.com/es-cl/asp.net/aa336618.aspx. Si ya instalaste Service Pack 1, ya lo tienes.
Si te interesa además saber todos los detalles sobre la compilación en Visual Studio 2005 y formas de hacer entregas (deployment) te recomiendo el siguiente artículo de Rick Strahl, llamado Compilation and Deployment in ASP.NET 2.0 disponible en http://www.code-magazine.com/Article.aspx?quickid=0609061
Tareas para la casa
Prueben cambiando el lenguaje de compilación y vean que ocurre. Háganlo eso sí con debug=”true” para que vean los archivos de código generados.
Patrick.Sao Paulo, Brasil.