[ASP.NET MVC] Detectando DEBUG en código y en Razor

Últimamente he aumentado bastante mi productividad usando un mecanismo muy sencillo: implementando código sólo para depuración que me ayuda en las sesiones de depuración, y ahora no me estoy refiriendo a trazas ni logs. Me refiero a cosas tan sencillas como:

  • Iniciar sesión automáticamente con un usuario y contraseña dados (suponiendo, como en mi proyecto, que se prohíbe por requisito que el navegador recuerde al usuario).
  • Rellenar valores de mi conveniencia en un formulario.

Como estas cosas, aunque las implemente, no quiero que ni por lo más remoto puedan llegar a producción, uso la constante de compilación DEBUG que indica si estamos ejecutando en Debug o en Release (y si tenemos más configuraciones, podremos indicar si la queremos declarar o no). Como mi código en producción irá como Release, no corro el riesgo de ejecutar mis atajos para depuración.

En un proyecto web MVC, según dónde nos encontremos, la forma de detectar si estamos en Debug o Release es distinta como vamos a ver a continuación.

Directivas de precompilación y DEBUG

Las directivas de precompilación de C# permiten que el compilador considere un código u otro en función de unas constantes, como DEBUG en nuestro caso. Por ejemplo, algo como:

#if DEBUG
        string username = "pablo";
#else
        string username = null;
#endif

Hace que los programas generados en Debug y en Release sean distintos: uno llevará una asignación a “pablo” y el otro a null. Ojo que no existe doble declaración de la variable: en un caso se declarará una y el propio compilador ignorará la otra línea, no sólo no se ejecuta, es que ni se compila.

Usando estas directivas no corro el riesgo de olvidarme de quitar este código de depuración cuando genere mi versión para producción: se usará el código que asigna null.

Un código similar he usado en mi MembershipProvider (no pongo el ejemplo porque mi caso es muy particular y dependiente de un motor de bases de datos muy particular) para iniciar una sesión concreta siempre en Debug, mientras que en Release el usuario debe iniciar sesión.

Las directivas de precompilación tienen más usos, como usar un mismo código base para diferentes targets, como Silverlight, Windows Phone… con algo como:

#if SILVERLIGHT

Lo cual nos acerca a aquello de un código, tres pantallas. Aunque no es el tema de hoy.

Detectando modo Debug en Razor

Pensando bien, uno interpreta que esto se puede aplicar al código C# incrustado en Razor. Yo he intentado usarlo para el segundo punto planteado al inicio de este artículo, y de forma natural uno piensa que puede hacer:

@{
#if DEBUG
        string titulo = "pablo";
#else
        string titulo = null;
#endif
}

Cuidado, esto no funciona. Y tiene sentido que no lo haga: este código dentro de Razor no es compilado al publicar el proyecto, sino que es incrustado en las vistas, y luego las vistas son compiladas por IIS según necesidad. Por lo que #if DEBUG siempre devuelve true. No sirve.

Pero hay otra forma de detectar que estamos en Debug desde Razor, y aunque no es exactamente igual, en la mayoría de los casos será equivalente y nos servirá. Se trata de la propiedad:

Context.IsDebuggingEnabled

Que nos ofrecen las páginas Razor a través de su HttpContext. De esta forma sí podemos hacer:

@{
    string titulo;
    if(Context.IsDebuggingEnabled)
        titulo = "pablo";
    else
        titulo = null;
}

Con lo que podemos usar esa variable @titulo como valor del elemento input correspondiente para que al cargar la página tengamos un valor inicial. Ojo, esto se podría (debería) hacer en el modelo (viewmodel) usado en la página, pero entonces podríamos usar directivas de precompilación y no tendría gracia el uso de IsDebuggingEnabled, así que he forzado un poco el ejemplo para mostrar este caso.

Conclusión

Podemos hacer trucos en nuestro código que nos faciliten la tarea rutinaria de ejecutar una y otra vez una aplicación en desarrollo para llegar a la página que estamos implementando, saltando los pasos que un usuario debería recorrer. Esta simplificación puede aumentar considerablemente nuestra productividad, pero cuando lo hagamos, debemos seguir los cauces que existen para ello (detección de DEBUG con #if, propiedad IsDebuggingEnabled) para que al pasar la aplicación a producción no quede ni rastro de nuestros trucos. Y ojito también con lo que subimos al control de código fuente, a ver si otro compañero se va a encontrar con nuestros atajos y quizá no le vayan a hacer mucha gracia.

Un placer.

Primer contacto con Code Contracts (no es un tutorial)

Hola, me llamo Pablo y hoy he sufrido mi primer contacto forzoso con Code Contracts. Lo de forzoso no es porque no me guste ese proyecto, sino porque ha sido completamente involuntario. Pero antes de nada, ¿qué es Code Contracts?

Code Contracts

Code Contracts es un proyecto de Microsoft Research para incluir en nuestro código las precondiciones y poscondiciones que deben cumplirse antes y después de su ejecución, así como los invariantes que deben cumplirse siempre. Muchos no habrán usado estas palabrejas desde que dejaron la Universidad, otros ni eso, pero al programar todos estamos asumiendo premisas que deben cumplirse (por ejemplo, que un método debe llamarse antes que otro, o que una variable tenga un rango de valores concreto), sólo que no los reflejamos en ningún sitio, sólo están en nuestra cabeza. Ahí es donde interviene Code Contracts, permitiendo poner negro sobre blanco (pixel negro sobre pixel blanco, quiero decir) esas condiciones, y además verificando que se cumplan tanto durante la ejecución de nuestro programa como de nuestros tests.

Como idea es muy interesante, y permite incrementar la calidad de nuestro código, difuminando un poco la división entre programa y tests, ya que estas comprobaciones (equivalentes a los Assert de un test) están en el propio programa. También ayuda a la legibilidad del código y a su comprensión por parte de quien venga detrás (que normalmente somos nosotros mismos dentro de un tiempo). Y su uso no es nada difícil, como muestra un botón:

public void Bind(FrameworkElement bindingObject, Func<FrameworkElement, FrameworkElement> bindingObjectParentFunc)
{
    Contract.Requires<ArgumentNullException>(bindingObject != null, "Binding object cannot be null.");
    Contract.Requires<ArgumentNullException>(bindingObjectParentFunc != null, "Binding object function cannot be null.");
…

En lugar de comprobar si nos han pasado bien los atributos con un if, lo estamos declarando usando Contract, el punto de acceso principal a la librería Code Contracts. Se entiende fácil, ¿verdad? Pues si queréis saber más, os dejo aquí una presentación de un chico que parece que sabe de esto.

¿Pero qué te ha pasado hoy?

Pues nada, que estaba tratando de localizar un error en la versión que estamos preparando para WPF de SilverDiagram, y entre cambios de código y recompilaciones de librerías me he encontrado con este error:

image

El texto es bastante más extenso, pero qué os voy a contar de esas hermosas pilas de llamadas listadas en enormes MessageBox. Todo un símbolo, y perfecto para demostrar a nuestro jefe/usuarios lo difícil que es nuestra profesión.

La cuestión es que este error viene provocado por Code Contract, porque se da una situación curiosa: Tengo dos librerías, A y B. La librería B referencia a A (pero están en soluciones distintas, así que referencia el ensamblado A.dll, pongamos).

Yo estaba probando B sin problemas, tratando de corregir el error, haciendo cambios y pruebas normalmente. Cuando identifico que el error parece estar en A, abro su solución, lo corrijo y compilo para usar esta nueva A.dll. Esta compilación no se queja.

Compilo B con la nueva A.dll y de nuevo sin problema. Pero al ejecutar, el error, ah, el error: Must use the rewriter when using Contract.Requires<TException>.

Está claro que la culpa es de Code Contracts. Echo una búsqueda rápida, y encuentro en el blog de Derik Whittacker una coincidencia. Y hace especial hincapié en que la culpa no es de B, sino del proyecto A, a pesar de no haber dado errores en su compilación. Y el motivo es sencillo: no tengo instalado Code Contracts en mi ordenador.

Instalando… y solucionado

Así que tras cerrar los Visuales Estudios, descargo e instalo desde la web de Code Contracts (hay versión comercial y académica, pero no me preguntéis sobre eso). Una vez instalado, conseguimos dos cosas:

  1. En las Propiedades de los proyectos hay una nueva pestaña Code Contracts donde podemos definir el comportamiento de esta librería en nuestro proyecto.
  2. Al volver a compilar el proyecto, ya podemos usar A.dll con normalidad en otros proyectos (si estaba bien configurada la comprobación en tiempo de ejecución de las propiedades de Code Contracts, pero esto es harina de otro costal).

Espero que esto pueda servir de ayuda para quienes sufran el mismo problema, y también para que algunos nos empecemos a introducir en Code Contracts (…por mi primero).

Un placer.

Carencias de Sql CE 4.0 en Visual Studio 2010

Hoy domingo he estado preparando una demo introductoria a Entity Framework, con el objetivo de que fuera lo más sencilla posible. Para ello, no he querido trabajar ni en un proyecto web ni en una aplicación Windows, sino en un proyecto de consola, para no exigir ningún conocimiento previo en el destinatario. Y desde un principio había elegido basarme en SQL Server Compact Edition (Sql CE), y su última versión 4.0, para evitar la necesidad de instalar un motor de bases de datos y profundizar en su configuración, pero todavía manteniendo un proveedor nativo de .net y de Entity Framework. Todo sobre Visual Studio 2010. También debo adelantar que mi contacto previo con Sql CE es mínimo, sólo como registro de Elmah y poco más. Pero como hoy he aprendido algunos hechos, los quiero compartir en este artículo.

Aunque yo suelo trabajar con EF aplicando la metodología Code-First, de nuevo por simplicidad decido plantear la demo como Model-First, por aquello del interfaz gráfico y demás (que está sobrevalorado, donde se ponga un buen interfaz de código fluido…). Bueno, creo un archivo .edmx y defino en él 2 tablas con su propio Id autonumérico como clave primaria y una clave foránea entre ambas. Cuando pido Crear base de datos a partir del modelo en el menú contextual, me pide crear una conexión a base de datos, y me ofrece 3 proveedores: SQL Server, archivo mdf y Sql CE 3.5.

image

Decido continuar con mi idea de usar CE aunque sea 3.5 (ya veré después… sólo estoy preparando una demo). Creo un nuevo archivo sdf para la conexión y Visual Studio me genera un archivo edmx.sqlce con el script de base de datos para generar las tablas. Lo conecto al sdf y ejecuto sin problemas. Pero al incluir código para crear una entidad y ejecutarlo, obtengo el error:

Server-generated keys and server-generated values are not supported by SQL Server Compact.

Lo cual no es del todo cierto, bueno, casi lo era cuando se escribió ese mensaje de error, ya que Sql CE 3.5 no soporta autonuméricos generados en el servidor a través de Entity Framework, es decir, realmente es una carencia del provider y no del motor. Con esto, Sql CE 3.5 no me sirve para mi sencillo ejemplo, donde no voy a montar ningún generador de claves. Esta carencia ha sido subsanada en Sql CE 4.0, así que doy marcha atrás y me lanzo a la búsqueda de esta versión.

Hasta ahora, cuando lo había necesitado (para Code-First y para Elmah) había usado el paquete nuget. Pero esto no sirve para EDMX, ya que debe estar registrado el proveedor en Visual Studio. De ahí que me descargo las SQL CE Tools for Visual Studio SP1 (que ya lo tenía) desde este artículo de ScottGu (no sé si también estará en español, he buscado un poco y no lo he encontrado). Pero tras instalarlo, me siguen saliendo los mismos proveedores que antes, por lo que investigo un poco y descubro que el proveedor Sql CE 4.0 sólo se ofrece en el EDMX dentro de proyectos web, no en mi humilde proyecto de consola. De hecho si en un proyecto web creo el sdf de la versión 4.0 y añado una conexión a él en el Explorador de servidores de Visual Studio, al tratar de usarlo desde el EDMX no se incluye en el combo (sólo se incluyen las conexiones con Sql CE 3.5 o con Sql Server). Es realmente desagradable ver cómo un EDMX añadido a una librería de clases (que es lo aconsejable, extraer el modelo fuera del proyecto web) sólo permite usar Sql CE 3.5 mientras que en un EDMX dentro de un proyecto web sí se ofrece Sql CE 4.0:

image

Todo esto me parece una situación extraña y limitadora, ya que Sql CE 4.0 es una solución ideal para pequeñas aplicaciones o utilidades, o para la primera fase de algunas aplicaciones mayores, incluso para instalaciones pequeñas de esas mismas aplicaciones; y con esta carencia se dificulta su utilización práctica para quienes prefieren usar el EDMX en lugar de la aproximación Code-First. Sólo me queda añadir que esta restricción puede salvarse siguiendo este truco (en inglés), que implica modificar el EDMX a mano, y que puede servir a los más tenaces (los demás habrán desistido antes, tristemente).

Y para terminar tengo que dar las gracias a mis agazapados del Twitter, Marc Rubiño y Rodrigo Corral, por echarme una mano y compartir experiencias acerca de Sql CE (incluso siendo el día del Señor). Es un gustazo tener a gente así leyendo mis tonterías para ayudar. Otra vez gracias.

Un placer.