El DataDirectory en las ConnectionStrings

Trabajando en fase de desarrollo, es más que común usar motores de base de datos de tipo fichero, como por ejemplo Access o, más comunmente SqlServer Express.

Por otra parte, en ASP.NET tenemos el directorio especial "App_Data", en el que es aconsejable tener esos ficheros de base de datos (los famosos .mdf, .mdb o .mdbx). Y es aconsejable por muchos motivos. Por ejemplo, que ASP.NET lo considera un directorio que requiere una seguridad especial (nadie podrá descargarse nada que esté dentro de ese directorio usando el navegador), o que el Visual Studio reconoce lo que hay dentro como bases de datos y nos facilita manejar esa base de datos con sólo un doble click.

Pero una de las funcionalidades que más me gusta de tener los ficheros de base de datos en el App_Data es que te permite usar la palabra especial |DataDirectory| dentro de tus ConnectionStrings. Es muy común tener una ConnectionString similar a esto:

Data Source=.\SQLEXPRESS;AttachDbFilename=C:\MisWebs\WebDePrueba\App_Data\personal.mdf;Integrated Security=True;

Al tener la ruta completa "C:\MisWebs\WebDePrueba\App_Data\personal.mdf", esa ConnectionString nos va a fallar si:
- Cambiamos el nombre de cualquiera de los directorios.
- Si movemos la aplicación a otra carpeta.
- Si queremos ver la aplicación en otro ordenador.
- Todo lo que se os ocurra que cambia el path de "personal.mdf".

Y eso es lo que soluciona el "DataDirectory". Veamos un ejemplo de uso del DataDirectory:

Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\personal.mdf;Integrated Security=True;

Y sí, como nos imaginamos, la función de |DataDirectory|  es la de apuntar al directorio App_Data de nuestra aplicación...

¿¿Y tanto rollo sólo para esto?? Pues sí, es que hacía tiempo que no escribía un artículo y me apetecía explayarme



GoogleMaps.Subgurim.NET versión 2.5

Sin duda el control de Google Maps para ASP.NET es mi control preferido.

La respuesta de la gente es extraordinaria (¡¡más de 3000 descargas!!), los foros funcionan muy dinámicamente y yo consigo tenerlo bastante actualizado.

Además, gracias a la ayuda de la gente, la microweb está disponible... ¡¡¡en 5 idiomas!!! Castellano, inglés, francés, sueco y holandés...

Al principio estaba, obviamente, en castellano pero la gente iba pidiendo más idiomas y querían ayudar de forma totalmente altruista, lo cual es muy gratificante. Por cierto, si a alguien le apetece traducir del castellano al inglés (o a otro idioma) le estaría muy agradecido, no tiene más que ponerse en contacto conmigo (en la barra izquierda está el "contacto").

Durante las últimas semanas, con el poco tiempo que me queda a lo largo del día, he estado trabajando en una nueva versión del control... y por fin he conseguido algo que me ha ocupado muuucho tiempo pensar: que el GoogleMaps pueda estar dentro de un UpdatePanel.

Algo que parece tan sencillo es bastante complicado por el hecho de que el control debe estar preparado para TODO. El problema era el siguiente:
- El Control debía poder ubicarse dentro de un UpdatePanel.
- El control se compone de un "div" y el resto es puro javascript.
- Si pones javascript dentro de un UpdatePanel y haces un postback... no funciona.
- Debe registrarse el javascript usando el ScriptManager.
- No es seguro que todos los usuarios tengan ASP.NET AJAX instalado en su servidor, luego no puede asegurarse que la clase ScriptManager sea accesible.
- Toca hacer un poquito de magia de forma que se mire (¡en código) si el ScriptManager esa accesible. Si lo es devuelve una cosa, y si no lo es devuelve otra.

Pues parece mentira que esta serie de 6 puntos me haya costado tanto esfuerzo y dolor de cabeza

Pero bueno, ahora ya está hecho y estoy contento

Por otra parte he incluído las novedades que trae la API oficial de Google, así como corrección de algunos bugs y algunos extras (al que le interese que lea el log de cambios).

Así que ya sabéis, el que quiera manejar un mapa de google con ASP.NET lo tiene sencillísimo gracias al GoogleMaps.Subgurim.NET



¿Cómo le decimos a ASP.NET que estamos en etapa de desarrollo?

Por simplificar, podemos distinguir dos etapas básicas en el desarrollo de nuestras aplicaciones: la etapa de desarrollo y la etapa de producción.

La primera es aquella en la que estamos desarrollando nuestro programa, añadiendo, modificando e insertando cosas nuevas en todo momento. Nos conviene hacerle saber a nuestro compilador ASP.NET que estamos en etapa de desarrollo porque de este modo le estamos pidiendo que nos eche una mano, que nos vigile, y si ve que algo sale mal que sea comprensivo con nosotros (por ejemplo cuando estamos superando el tiempo máximo de espera) o que nos comente con la máxima precisión cual es el error o errores (mostrando el primer mensaje de error del compilador así como una completa traza con los últimos pasos realizados).

Aunque ya estemos acostumbrados a este comportamiento, en la época pre-ASP.NET, los programadores Web lo teníamos harto difícil para encontrar el más simple de los errores.

Como os podréis imaginar, la etapa de producción es aquella en la que nuestro programa es totalmente operativo, y lo tenemos expuesto en Internet o en una Intranet... y es en este momento donde no deseamos ni de lejos que se superen los límites marcados o se muestren completas intrucciones de cómo y por qué han surgido los errores.

Pero vale ya de palabrería, qué significa eso de "decirle a ASP.NET que estamos en etapa de desarrollo". Pues muy sencillo. Como todo en ASP.NET, es en el web.config donde informaremos al motor de ASP.NET en qué etapa estamos.

Para decirle que estamos en etapa de desarrollo añadiremos el elemento compilation con el atributo debug puesto a true, dentro del elemento system.web... ¿¿¿¿y esto como se come???? Más vale un ejemplo que mil palabras:

web.config (etapa de desarrollo)
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
    ...
    <system.web>
       ...
       <compilation debug="true"/>
       ...
    </system.web>
    ...
</configuration>


Donde los tres puntos significan "y ahí pueden estar otras cosas".

Así que como os podréis imaginar, cuando por el contrario estemos en etapa de producción, el atributo debug lo pondremos a false... aunque vamos a añadir otro elemento para dejar el tema bien cerradito:

web.config (etapa de producción)
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
    ...
    <system.web>
       ...
        <compilation debug="false"/>
        <trace enabled="false" pageOutput="false"/>
       ...
    </system.web>
    ...
</configuration>


No quiero ser demasiado insistente, pero es que SUELE ocurrir que se nos olvide cambiar el atributo debug desde la etapa de desarrollo a la de producción... y eso produce resultados desastrosos, no sólo porque le estamos mostrando al usuario demasiada información en caso de error, sino porque la velocidad de ejecución de la aplicación se ve altamente dañada. Por poner un ejemplo claro, recuerdo que en cierto momento dejé varios minutos la web www.es-asp.net en el servidor de producción con el debug="true", y las páginas iban hasta 3 y 4 veces más lentas que con la configuración conveniente.

Para finalizar la diserción, comentaros que en el caso de que tengáis la suerte de poder acceder al fichero machine.config, bien por tener a vuestra disposición un servidor privado (caso de internet) o bien por ser responsables de una aplicación Web para Intranet, lo más conveniente es añadir el elemento "deployment" con el atributo "retail" pues a "true", dentro de vuestro system.web. Es decir:

machine.config
<configuration>
    ...
    <system.web>
    ...
          <deployment retail=”true”/>
    ...
    </system.web>
    ...
</configuration>




Crear en código botones con eventos asociados

De nuevo, tras una pregunta en los foros de ASP.NET en castellano he decidido responder con un artículo en lugar de escribir una larga respuesta

El objetivo es que mediante nuestro propio código, podamos crear controles del tipo Button de modo que se muestren en pantalla donde nosotros decidamos, y además tengan asociado un evento Click.

A continuación os muestro un código totalmente genérico y aplicable a vuestras aplicaciones en particular.
Está comentado:

ejemploButton.aspx
        <asp:Label ID="Label1" runat="server">No has hecho click sobre ningún botón</asp:Label>
        <br /><br />
        <asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>



ejemploButton.aspx.cs
    protected void Page_Load(object sender, EventArgs e)
    {
        // Declaramos el Button y un literal que nos servirá para hacer saltos de línea
        Button bt;
        Literal lt;

        // Por ejemplo crearemos 5 botones,
        // pero nuestra fuente de información puede ser cualquier otra
        for (int i = 0; i < 5; i++)
        {
            // Inicializamos el botón
            bt = new Button();

            // Le asignamos un text
            bt.Text = "Botón " + i;

            // Hacemos que maneje el evento Click mediante la función bt_Click
            bt.Click += new EventHandler(bt_Click);

            // Inicializamos el literal y hacemos que sea un salto de línea HTML
            lt = new Literal();
            lt.Text = "<br />";

            // Ubicamos ambos controles en el PlaceHolder que hemos puesto en nuestra página
            // El uso del PlaceHolder no es obligatorio ni mucho menos,
            // podéis añadir los controles donde os haga falta:
            // una Cell de un Table, un Panel, a la propia Page, etc.
            PlaceHolder1.Controls.Add(bt);
            PlaceHolder1.Controls.Add(lt);
        }
    }

    // Los métodos manejadores de eventos tienen una estructura muy sencilla.
    // Son métodos como cualquier otro, pero (habitualmente) tienen dos parámetros de entrada.
    // "sender" es el objeto que ha lanzado el evento. Deberemos parsearlo al tipo que nosotros consideremos.
    // "e" son los argumentos del evento. Deberemos parsearlos al tipo que consideremos si lo quisieramos usar.
    protected void bt_Click(object sender, EventArgs e)
    {
        // Como sabemos que quien lanza el evento es un Button, lo parseamos y lo recogemos en una variable
        Button bt = (Button)sender;

        // Ahora podemos hacer lo que creamos conveniente con el Button. Ej:
        Label1.Text = "Has hecho click en el botón " + bt.Text;
    }


Espero que os sirva!!



SCOPE_IDENTITY: Recoger un identificador recién insertado

Os confieso que he estado más tiempo pensando en el título del artículo que en el artículo en sí... y como me parece que de autodescriptivo no tiene nada, espero poder explicarme en el siguiente párrafo.

Comúnmente, cuando creamos una tabla en una base de datos, uno de los campos suele ser un entero que se incrementa automáticamente cada inserción de registro, de modo que no puede haber dos enteros autoincrementales iguales. Esta propiedad la solemos utilizar para nombrar a ese campo como clave principal de la base de datos, dado que estamos totalmente seguros de que es única, y en definitiva es un entero que ocupa poco espacio y es fácilmente indexable.

Personalmente he oído llamar a esos campos de diferentes modos: autoincrementales, autonuméricos, identities... el nombre da igual, lo importante es que sepamos de lo que hablamos.

Pero a lo que vamos, len este artículo trataremos el escenario en que el programador necesita saber cuál es ese autonumérico recién insertado. Un ejemplo típico es el de realizar la inserción en base de datos e inmediatamente redireccionar a una página con la información recién insertada, para la que necesitamos saber el autonumérico que la identifica.

Estoy seguro de que a muchos de vosotros se os ha planteado esta situación... y estoy seguro de que muchos de vosotros habéis cometido el error (¡¡yo mismo lo hacía en su momento!!) de:
1.- Insertar en base de datos.
2.- Hacer un SELECT que recoge el último registro insertado para saber cuál es su autonumérico.

Esto no sólo supone dos viajecitos de ida y vuelta a la Base de Datos (lo cual en momentos de exceso de estrés porculiano podemos suponer aceptable ) sino que corremos el riesgo real de que dos usuarios hagan sendas inserciones y que ambos SELECT devuelvan el mismo autonumérico...

Pero bueno, no hace falta explicaciones, hacerlo así es una basura y absolutamente irrecomendable .

El modo de proceder en estos casos es el siguiente:
1.- El procedimiento de datos que realiza la inserción debe devolver el SCOPE_IDENTITY().
2.- El código que ejecuta la llamada al procedimiento almacenado debe recoger el valor devuelto por el procedimiento alamcenado.

Sí, ya lo sé, la teoría es muy bonita, pero un ejemplo vale más que mi palabras, así que vayamos por partes:

1.- El procedimiento almacenado.
Dando por supuesto que se va a realizar la inserción en una tabla con un registro autonumérico. no hay más que devolver como valor de retorno el SCOPE_IDENTITY(). Ejemplo de procedimiento almacenado:

    CREATE PROCEDURE dbo.InsertaEjemplo
    (
        @T_Titulo nvarchar(50),
        @T_Texto nvarchar(MAX)
    )

    AS
        SET NOCOUNT ON
   
    INSERT INTO TablaEjemplo
                      (T_Titulo , T_Texto)
    VALUES     (@T_Titulo,@T_Texto)

    RETURN SCOPE_IDENTITY()


2.- Método de capa DAL
A continuación tenemos un método de la capa DAL a la que sólo se le entregan dos campos.

    public static int InsertarEjemplo(string Titulo, string Texto)
    {
        // Valor de retorno. Sabemos que si acaba valiendo -1 ha habido un error
        int Id = -1;

        // Parametros de la BBDD
        SqlParameter[] sqls = new SqlParameter[2];
        sqls[0] = new SqlParameter("T_Titulo", Titulo);
        sqls[1] = new SqlParameter("T_Texto", Texto);

        // Se puede recoger la ConnectionString de mil formas.
        // A mí este modo no me gusta, pero valga como ejemplo :D
        string constr = @"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\personal.mdf;Integrated Security=True;User Instance=True";
       
        // Creamos la conexión
        using (SqlConnection con = new SqlConnection(constr))
        {
            // Creamos el Comando
            using (SqlCommand cmd = new SqlCommand("InsertaEjemplo", con))
            {
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddRange(sqls);

                // Esta es la clave, hemos de añadir un parámetro que recogerá el valor de retorno
                SqlParameter retValue = new SqlParameter("@RETURN_VALUE", SqlDbType.Int);
                retValue.Direction = ParameterDirection.ReturnValue;
                cmd.Parameters.Add(retValue);

                // Abrimos la conexión y ejecutamos el ExecuteReader
                con.Open();
                using (SqlDataReader dr = cmd.ExecuteReader())
                {
                    Id = Convert.ToInt32(retValue.Value);
                }
            }
        }

        return Id;
    }



Ahora ya tenemos el Id, y podemos hacer con él lo que creamos conveniente

Cabe reseñar que junto con el SCOPE_IDENTITY hay otras dos funciones que devuelven información similar: IDENT_CURRENT y @@IDENTITY.

La diferencia entre las tres es básicamente sobre el ámbito y la sesión sobre la que actúan, pero ese ya es otro tema. Ante el escenario que os planteo, el  SCOPE_IDENTITY me parece la mejor opción



TreeView: mantener el nodo expandido entre enlaces

Si habéis hecho uso del TreeView, seguro que se os ha planteado el escenario en que tenéis varias "ramas" con varias "hojas", y en algún momento enlazáis con otra url que contiene ese mismo treeview... pero no encontráis la manera de mantener expandido el nodo original sobre el que habéis presionado.

Mmmm, ¿algo liosa la explicación? Si habéis tenido ese problema seguro que lo entendéis, pero para lo que no lo hayan tenido, aquí tenemos un TreeView ejemplo:

ejemploTreeView.aspx
        <asp:TreeView ID="TreeView1" runat="server" ExpandDepth="0" OnSelectedNodeChanged="TreeView1_SelectedNodeChanged">
            <Nodes>
                <asp:TreeNode Text="Madre" Value="Madre">
                    <asp:TreeNode Text="Hija 1" Value="Hija 1">
                        <asp:TreeNode Text="Nieta 1_1" Value="1_1"></asp:TreeNode>
                        <asp:TreeNode Text="Nieta 1_2" Value="1_2"></asp:TreeNode>
                        <asp:TreeNode Text="Nieta 1_3" Value="1_3"></asp:TreeNode>
                    </asp:TreeNode>
                    <asp:TreeNode Text="Hija 2" Value="Hija 2">
                        <asp:TreeNode Text="Nieta 2_1" Value="2_1"></asp:TreeNode>
                        <asp:TreeNode Text="Nieta 2_2" Value="2_2"></asp:TreeNode>
                    </asp:TreeNode>
                </asp:TreeNode>
            </Nodes>
        </asp:TreeView>


Como veis es un sencillo TreeView con una madre, dos hijas, la primera de las cuales tiene tres hijas y la segunda  dos.

El quid de la cuestión es que queremos dirigirnos a la URL en la que estamos, pero cambiando el querystring. Lo veremos mejor en el manejo del evento SelectedNodeChanged:

ejemploTreeView.aspx.cs
    protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
    {
        TreeView tv = (TreeView)sender;
        string TreeId = HttpUtility.UrlEncode(tv.SelectedNode.ValuePath);

        Response.Redirect("?TreeId=" + TreeId);
    }


Estamos recogiendo el ValuePath del nodo sobre el que presionamos y dirigiéndonos a la misma url pero cambiando el valor TreeId del QueryString.

Ahora lo que vamos a hacer es recoger ese valor del Querystring (si lo hay) y seleccionar el nodo sobre el que hemos presionado, de modo que se abra el árbol desde el nodo nieto hasta la madre:

ejemploTreeView.aspx.cs
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!Page.IsPostBack)
        {
             // Seleccionamos el TreeId
            string TreeId =
HttpUtility.UrlDecode(Request.QueryString["TreeId"]);

            if (!string.IsNullOrEmpty(TreeId))
            {
                // Elegimos el nodo sobre el que hemos hecho click y comprobamos que realmente exista
                TreeNode node = TreeView1.FindNode(TreeId);
                if (null != node)
                {
                       // Lo ponemos en negrita
                    node.Text = "<b>" + node.Text + "</b>";
                      // Viajamos desde esta nieta hasta la madre usando recursividad y hacemos un expand.
                    while ((node.Parent != null) && (node.Parent.Depth != -1))
                    {
                        node = selectParent(node);
                        node.Expand();
                    }
                }
            }
        }
    }

    // Función recursiva
    private TreeNode selectParent(TreeNode node)
    {
        return node.Parent;
    }



Como os podéis imaginar, este ejemplo es académico, dado que es extraño que se quiera viajar a una Url con un QueryString igual al valuePath del nodo al que le hemos hecho click. Sin embargo esto es el equivalente a una "clase madre" de la que vosotros deberéis heredar y adaptarlo a vuestras necesidades



Subgurim Chat: un Chat ASP.NET para tu Web

No hace ni dos semanas que salió el Subgurim Sitemap... y ya vuelvo a las andadas con otro control Web muy interesante: el Subgurim Chat.

Se trata de una primera versión con características básicas pero plenamente funcionales. La idea es la de siempre: con sólo arrastrar a tu página ".aspx" puedes tener un completo Chat en tu web.

Algunas de sus características son:
- Es 100% AJAX.
- Son estilos son 100% configurables.
- No utiliza ni bases de datos ni archivos XML para guardar conversaciones.
- No requiere más que arrastrar y configurar en menos de dos minutos. Además, con la descarga se incluye una sencilla Web de ejemplo.
- Obviamente es totalmente gratis, y desde ya estamos trabajando en nuevas funcionalidades.

MicroWeb: es.chat.subgurim.net (castellano) y chat.subgurim.net (inglés).




Uri

Todo aquel que ha trabajado con ASP.NET sabe que sus funcionalidades tienden al infinito.

Un día te acuestas habiendo resuelto un problemilla complejo y al día siguiente te das cuenta de que ASP.NET ya lo tenía hecho...

Una de esas "cosas" es la clase Uri. Resumiré el artículo en una simple frase: siempre que tengais que trabajar con Urls, debéis utilizar la clase Uri.

La clase Uri tiene un montón de funcionalidades relacionadas con el correcto trato de las urls (entendidas como cadenas de texto). Probablemente, antes de conocer la clase Uri hayais fabricado alguna de estas funcionalidades por vuestra cuenta... y la clase Uri no sólo tienes esas funcionalidades sino que las corrige y amplia.

Pero vale ya de tanto "Uri", "Uri" y vayamos a la práctica, que es lo importante. De las muchos métodos y propiedades que tiene la clase Uri, voy a destacar sólo unos pocos, los que yo considero más significativos.


Request.Url
Lo más destacable es quizás que desde nuestras páginas podemos recibir una instancia Uri de la url en la que nos encontramos. Por tanto, sin más que hacer...

    Uri url = Request.Url;

... tenemos un buen ejemplo con el que trabajar. A partir de ahora vamos a suponer que nos encontramos insertando un mensaje en el foro de principiantes de www.es-asp.net, cuya Url es "http://www.es-asp.net/Foro/insertar.aspx?IdCabeza=0&IdForo=principiantes"

AbsoluteUri
Esta propiedad devuelve el string completo de la url con la que estamos trabajando, en nuestro caso url.AbsoluteUri devolvería "http://www.es-asp.net/Foro/insertar.aspx?IdCabeza=0&IdForo=principiantes"

Authority
Recoge el nombre del dominio o la ip, seguida del puerto (si es que lo tiene). En nuestro caso url.Authority es "www.es-asp.net"

LocalPath
Como su propio nombre indica, nos devuelve el Path local dentro del website en que estamos. Es importante reseñar que no se incluye el querystring. Para nuestro ejemplo url.LocalPath es "/Foro/insertar.aspx".

Query
Nos devuelve el QueryString de la url, en caso de que lo tenga. Para los que han trabajado previamente con este parámetro, sabrán apreciar que, en caso de que haya QueryString, la cadena comienza con el caracter '?', por lo que nos nos hemos de preocupar en añadirlo nosotros. Para nuestro ejemplo, url.Query es "?IdCabeza=0&IdForo=principiantes".

IsAbsoluteUri
Cambiando un poco de tercio, la clase Uri permite que sus Urls constructoras sean absolutas o relativas, entendiendo por relativas el que no tengan el nombre del dominio (por ejemplo "../imagenes" o "/foro"). Está claro que nuestro ejemplo se trata de una Url absoluta, por tanto esta propiedad devolverá true.

Propiedades estáticas de Uri
Además de las instancias que devuelve Uri, ésta tiene también propiedades estáticas muy útiles que podemos acceder. Por ejemplo, tenemos "IsWellFormedUriString", que indica si una Url está bien formada o no. Hay que darle los parámetros, el primero un string correspondiente a la url con la que queremos trabajar y el segundo es un enumerador que indica si la url es absoluta, estática o cualquiera de las dos.

Usando nuestro ejemplo:
    1.- Uri.IsWellFormedUriString(url.AbsolutePath, UriKind.Absolute); // Será true
    2.- Uri.IsWellFormedUriString(url.AbsolutePath, UriKind.Relative);  // Será false
    3.- Uri.IsWellFormedUriString(url.LocalPath, UriKind.Absolute);  // Será false

Otras propiedades estáticas interesantes son las que definen el delimitador de esquema (siempre valdrá "://"), así como un montón de esquemas típicos ("http", "ftp", "https", y hasta 8 más).

Así pues, podríamos crear nosotros mismos una Url con esto:

        string miUrl = Uri.SchemeDelimiter + Uri.UriSchemeHttp + "www.es-asp.net"; // http://www.es-asp.net


Conclusión
Como veréis tan sencillo que hasta explicarlo resulta raro

La clase Uri no termina aquí, y como siempre, os emplazo a la documentación oficial para enteraros de más: Uri en MSDN