XmlTextWriter (escribiendo en ficheros XML)

Tras nuestro anterior artículo sobre XML en el que veíamos cómo leer ficheros XML de forma muy rápida (XmlTextReader), vamos ahora a por su amigo el XmlTextWriter. Recordad que todos ellos los encontraréis en el namespace System.Xml, por lo que convendría que lo importaráis en vuestas clases .

Y es que escribir ficheros XML es muy sencillo con el XmlTextReader, y con ASP.NET 2.0 ahora es mucho más eficiente y rápido de lo que ya lo era con versiones anteriores.

En este artículo vamos a hablar únicamente de cómo escribir un XML únicamente, no hablaremos en ningún momento de la edición de sus ficheros, pues eso formará parte de otros artículos futuros.

Queremos crear el siguiente fichero, con los mejores jugadores jóvenes de la liga española , y guardarlo en la raíz de nuestra aplicación web:

futbolistas.xml
<futbolistas>
    <jugador pais="España">
       <nombre>David Villa</nombre>
       <equipo>Valencia C.F.</nombre>
    </jugador>
    <jugador pais="Argentina">
       <nombre>Leo Messi</nombre>
       <equipo>Barcelona F.C.</equipo>
    </jugador>
</futbolistas>


Así que allá vamos con el código y más tarde con la explicación:

futbolistas.cs
        string path = Server.MapPath("~/futbolistas.xml");
        using (XmlTextWriter writer = new XmlTextWriter(path, Encoding.UTF8))
        {
            writer.Formatting = Formatting.Indented;

            writer.WriteStartDocument();
            writer.WriteStartElement("futbolistas");

            writer.WriteStartElement("jugador");
            writer.WriteAttributeString("pais", "España");
            writer.WriteElementString("nombre", "David Villa");
            writer.WriteStartElement("equipo");
            writer.WriteString("Valencia C.F.");
            writer.WriteEndElement();
            writer.WriteEndElement();


            writer.WriteStartElement("jugador");
            writer.WriteAttributeString("pais", "Argentina");
            writer.WriteElementString("nombre", "Leo Messi");
            writer.WriteStartElement("equipo");
            writer.WriteString("Barcelona F.C.");
            writer.WriteEndElement();
            writer.WriteEndElement();

            writer.WriteEndElement();
            writer.WriteEndDocument();

            writer.Close();
        }



Analicemos paso a paso:
1.- Lo primero que hacemos es inicializar la variable writer y decirle que el resultado lo guarde en el fichero futbolistas.xml de nuestro directorio raíz. No olvidar hacer el Server.MapPath previamente. Utilizamos la sentencia using, que no sirve sólo para el XmlTextWriter sino para casi cualquier tipo de variable, que se encargará de "cerrar todo lo que moleste" al finalizar el trabajo con la variable o en caso de que se produzca un error. En otra ocasión hablaremos de la sentencia using pues es muy interesante.

2.- Aplicando Indented como formato nos aseguramos de que el fichero XML quedará bonito para la vista, con sus saltos de línea y sus tabulaciones que tanto nos gustan a los humanos . Si no indicáramos nada, el fichero XML estaría escrito en toda un línea.

3.- Vayamos con lo interesante: iniciemos el documento. Con writer.WriteStartDocument() lo comenzamos y permitimos que la variable writer pueda comenzar con su faena real, además de añadir al documento la típica línea "<?xml version="1.0" encoding="utf-8"?>". Comprobemos que al final se cierra el documento con  writer.WriteEndDocument().

4.- Creamos la apertura del elemento "futbolistas", que para nuestro ejemplo será el elemento raíz del documento. Compruébese que al final ese elemento se cierra con un writer.WriteEndElement().

5.- Con:
            writer.WriteStartElement("jugador");
            writer.WriteAttributeString("pais", "España");
... estamos abriendo el elemento jugador y le asignamos un atributo llamado "pais" de valor "España". Personalmente no me gusta establecer atributos ningún elemento XML, por lo que no los recomiendo salvo situaciones excepcionales. En este caso lo hacemos con fines didácticos.

6.- Ahora lo que hacemos es crear un elemento llamado "nombre", le damos un valor ("David Villa") y lo cerramos... todo ello en una línea!!
            writer.WriteElementString("nombre", "David Villa");

7.- Para mostrar que se produce el mismo efecto con varias líneas, para definir el equipo del jugador haremos:
            writer.WriteStartElement("equipo");
            writer.WriteString("Valencia C.F.");
            writer.WriteEndElement();
Tanto en 6 como en 7 se hace lo mismo. A mí me gusta más el modo 6 pues es más compacto, pero comprendería que gustara el 7 ya que puede llegar a ser más claro a la hora de leer el código.

8.- Tras cerrar el primer elemento jugador, hacemos los mismo con Leo Messi.

9.- Cerramos el elemento futbolistas, cerramos el documento y cerramos el writer con writer.close(), aunque esto último no es necesario hacerlo pues la sentencia using ya lo hace (de hecho hace un dispose).

Con lo dicho en este artículo, ya sabemos todo lo necesario para escribir un documento XML entero. Ahora cada uno debe adaptarlo a sus necesidades utilizando listados con sus propias clases, datasets o lo que sea!!

PD: Como consejo y/o observación, tened siempre muy en cuenta que XML es CaseSensitive, por lo que una equivocación en mayúsculas y minúsculas puede resultar un dolor de cabeza tan fuerte como innecesario.



Mostrar procesos que se están ejecutando

Con unas pocas líneas de código podemos ver qué procesos se están ejecutando en el servidor así como diferente información sobre ellos (Ojo, deben tenerse los permisos pertinentes para poder ver esos procesos!!)

Lo que vamos a hacer es recoger esos procesos y mostrarlos muy sencillamente en un listado:

mostrarProcesos.cs
    private void mostrarProcesos()
    {
        System.Text.StringBuilder sb = new StringBuilder();
        System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();

        foreach (System.Diagnostics.Process process in processes)
        {
            sb.Append(process.ProcessName);
            sb.Append(", ");
            sb.Append(process.WorkingSet64 / 1000);
            sb.AppendLine("<br />");  
        } 
    }


Como veis no nos complicamos la vida demasiado. Simplemente recogemos los procesos con System.Diagnostics.Process.GetProcesses();  e iterando uno por uno en un foreach creamos un StringBuilder con el nombre del proceso y los Kb de memoria que utiliza.

Vosotros podéis ampliarlo tanto como queráis, ya que process contiene muchísima más información sobre el proceso en cuestión (SessionId, Threads, StarTime...).



Mostrar el código fuente de un fichero

Mostrar el código fuente de cualquier fichero dentro de vuestro servidor (y siempre y cuando tengáis los permisos oportunos) es increíblemente sencillo.

El ejemplo más sencillo que se me ocurre es el del gran tutorial de ASP.NET 2.0 en inglés (¿Para cuando uno en castellano??).

Por ejemplo, en el primer ejemplo del primer capítulo ("Writing your first ASP.NET Page") se nos enlaza a un "Run It" y a un "View Source", donde el último muestra el código que forma el primero.

Pues bien, yo no sé como lo tienen hecho ellos, lo que sí sé es que con una única línea de código se puede mostrar el código del archivo que queramos:

mostrarcodigo.cs
    Label1.Text = System.IO.File.ReadAllText(Request.PhysicalPath);

Y yendo paso por paso:
- Request.PhysicalPath: devuelve el path físico dentro del servidor de la página en que estamos.
- System.IO.File.ReadAllText(...): Abre el fichero, lee todas sus líneas como texto, devuelve ese texto y cierra el fichero.

Algo que podría parecer difícil es increíblemente sencillo!!



Envío de mails con ASP.NET

El envío de emails con ASP.NET 2.0 es, terriblemente fácil... la de cosas que han pasado desde que había que usar aplicaciones de terceras personas para enviar un email con ASP 3.0!!! Todas las herramientas necesarias se encuentran en el namespace System.Net.Mail.

Tanto es así, que con tres líneas de código se puede solucionar:

envioEmailASPNET.cs
    MailMessage Email = new MailMessage(Origen, Destino, Asunto, Mensaje);
    SmtpClient smtp = new SmtpClient();
    smtp.Send(Email);


Como veis, no hay más que crear una variable MailMessage con el origen, el destino, el asunto y el mensaje. Luego creamos una variable SmtpClient y se envía el email.

Pues eso, así de fácil ... aunque bueno, eso sería en un mundo ideal, porque lo más normal es que haya que definir el host al que se envía el mail, y muy problablemente haya que definir ciertas credenciales (nombre de usuario y contraseña). Pero no os preocupéis, sigue siendo muy sencillo.

Más sobre la creación del MailMessage
Vayamos por pasos, vamos a esmerarnos un poquito más en la creación de la variable MailMessage:

envioEmailASPNET.cs
        MailAddress origen = new MailAddress("webmaster@subgurim.net", "Soy el webmaster");
        MailAddress destino = new MailAddress("webmaster@google.com", "Larry");

        MailMessage Email = new MailMessage(origen, destino, "Mi asunto", "<b>Cuerpo del mensaje</b>");
        //Email.Bcc =""
        //Email.CC=""
        Email.IsBodyHtml = true;
        Email.Priority = MailPriority.High;
        //Email.ReplyTo=""


Donde lo único que cabe destacar es que origen y destino ya no son variables string, sino variable MailAddress, donde se indica la dirección del correo y el nombre que quieres que aparezca. También se muestra otras opciones de envío de email como los típicos Bcc, CC o ReplyTo, además de indicar la prioridad del mensaje así como indicar si es o no un mensaje HTML.


Más sobre SmtpClient (envío con credenciales)
Es bastante probable que queramos mandar un mail desde a un host smtp con nuestro nombre y contraseña incluído. Debéis importar el namespace System.NET:

envioEmailASPNET.cs     
    SmtpClient smtp = new SmtpClient();

    smtp.UseDefaultCredentials = false;
    NetworkCredential credencial = new NetworkCredential("nombreDeUsuario", "Contraseña");
    smtp.Credentials = credencial;

    // smtp.Port = 25; Se usa para indicar un puerto diferente al habitual
    smtp.Host = "smtp.miservidor.com";

    smtp.Send(Email);

Primero creamos la variable SmtpClient, y seguidamente le decimos que no use las credenciales por defecto. Creamos nuestras nuevas credenciales y las asignamos a nuestra variable. Posteriormente le indicamos cuál es nuestro host smtp y enviamos el email.

Sencillo, ¿no?



Prueba de eficiencia: StringBuilder vs. Concatenación de Strings

Ya os comentábamos en el artículo sobre el StringBuilder, que éste ofrecía una notable mejora de eficiencia a la hora de concatenar strings, pero para quitarle la abstracción a la expresión "notable mejora de eficiencia", hemos realizado una prueba de eficiencia.

Ésta es muy sencilla:

eficiencia.cs
       // Prueba 1
       //int Nsb = 5000;
       //int Nstring = 5000;

       // Prueba 2
       //int Nsb = 50000;
       //int Nsb = 50000;

       // Prueba 3
       int Nsb = 10000000;
       int Nsb = 100000;

        // Con StringBuilder
        DateTime inicio = DateTime.Now;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < Nsb ; i++)
        {
            sb.Append(i);
        }
        DateTime final = DateTime.Now;
        TimeSpan duracionA = final - inicio;

        // Con strings
        inicio = DateTime.Now;
        string txt = string.Empty;
        for (int i = 0; i < Nstring ; i++)
        {
            txt += i;
        }
        final = DateTime.Now;
        TimeSpan duracionB = final - inicio;


Como veis la prueba se ha hecho cambiando los valores a mano, podíamos haber preparado algo que nos devolviera un gráfico con estadísticas, etc., pero los resultados son tan esclarecedores que no hace falta . Además, tampoco se ha hecho una media con resultados, etc. Simplemente queremos mostrar el orden de magnitud de diferencia.

Escenario de la prueba:
- AMD 2200+, 1 Gb RAM
- VWD Express
- Muchos otros programas ejecutándose

Resultado Prueba 1 (Nsb = 5.000, Nstring = 5.000)
duracion A = 0
duracion B = 0,21875
Conclusión: aún pidiendo un bajo rendimiento, el string ya necesita 2 décimas que pueden llegar a ser muy valiosas, mientras que el StringBuilder ni se inmuta.

Resultado Prueba 2 (Nsb = 50.000, Nstring = 50.000)
duracion A = 0,046875
duracion B = 70,5625
Conclusión: Cuando comenzamos a pedir un rendimiento más serio, la concatenación con strings crece exponencialmente (más de un minuto!!), mientras que el StringBuilder no llega ni a cinco centésimas. Eso son 3 órdenes de magnitud!!

Resultado Prueba 3 (Nsb = 10.000.000, Nstring = 100.000)
duracion A = 8,9375
duracion B = 212,421875
Conclusión: nos hemos querido poner serios y le hemos pedido al stingBuilder dos órdenes de magnitud más que a la concatencación con strings (10 millones vs 100 mil), y aún así el resultado sigue siendo abrumador para el StringBuilder, ganándole en dos órdenes de magnitud.


Explicación
Internamente, para concatenar un string, lo que se hace es crear una nueva instancia de string, copiar el string actual en esa nueva instancia y borrar la instancia original, que es lo que en el ámbito de la ingeniería avanzada suele llamarse "el cuento de la vieja"

Consejo
Siempre que podáis, aunque sea para las operaciones más simples, utilizad el StringBuilder frente a la concatención de strings... y si es para operaciones más costosas ni os lo penséis!




TimeSpan

Con el TimeSpan tenemos una sencilla manera de medir el tiempo, por ejemplo para calcular la diferencia entre dos fechas.

La unidad de medida del TimeSpan son los Ticks, pero nosotros le podemos pedir que nos muestre los datos de la manera que queramos. Veamos un pequeños ejemplo en que queremos medir el tiempo que tarda en ejecutarse cierto proceso

TimeSpan.cs
        DateTime inicio = DateTime.Now;

        // EJECUCIÓN de un proceso

        DateTime final = DateTime.Now;
        TimeSpan duracion = final - inicio;
        double segundosTotales = duracion.TotalSeconds;
        int segundos = duracion.Seconds;



Como vemos, se recoge el momento de inicio, se ejecuta el proceso (por ejemplo mil accesos a una base de datos) y se recoge el final. Calculamos la diferencia, que nos da el resultado en TimeSpan, y almacenamos en una variable double el total de segundos transcurridos, y en una variable int los segundos... pero, ¿Cuál es la diferencia?

Por ejemplo, si el proceso ha tardado en ejecutarse un minuto y medio, TotalSeconds nos devuelve el total de los segundos, es decir 90, mientras que Seconds nos devuelve los segundos que veríamos, por ejemplo, en un reloj digital, es decir 30.

Esto es muy importante saberlo, y lo mismo sucede con Days - TotalDays, Minutes - TotalMinutes y el resto de unidades de medida... personalmente tuve algunos problemas con esto, pues yo quería averiguar los segundos totales y le pedía duracion.Seconds... y errores como éste son muy difíciles de localizar!!

Para finalizar sólo decir que el TimeSpan se puede sumar y restar entre sí muy fácilmente con los típicos operadores '+' y '-' o con sus funciones Add y Substract.




Gridview. Introducción

El GridView es un nuevo control de ASP.NET 2.0 que sustituye al DataGrid de las versiones previas. Se trata de un control muy poderoso, con muchas funcionalidades que nos van a hacer la vida mucho más fácil.

La idea es mostrar por pantalla un listado de lo que sea. Por ejemplo, en nuestra sección Todos los artículos, tenemos un gridview donde se listan todos los artículos publicados hasta el momento.

Pero vayamos poquito a poco. Os presento al GridView:

gridview.aspx
        <asp:gridview runat="server" ID="GridView1">
        </asp:gridview>


De momento este GridView no haría absolutamente nada, pues en ningún momento le hemos dicho de dónde tiene que coger los datos que debe de mostrar. Esto es importante, pues cabe tener claro que el GridView por sí solo no sabe hacer nada, simplemente es un control que gestiona los datos que le dan y es capaz de hacer con ellos cosas como ordenarlos o paginarlos, así como sabe (si se lo decimos) con quién tiene que hablarse en caso de que queramos modificar, borrar o elegir una columna.

Por tanto entre los datos que hay en la BBDD y el GridView debe haber algún intermedio... y tenemos varias opciones, pero sin duda las más destacados son el SqlDatasource y el ObjectDataSource. Ambos son los encargados de coger los datos de la BBDD y proporcionarlos a los controles que enseñan datos al usuario, como por ejemplo el datalist, el repeater, formview... y nuestro gridview.

Un ejemplo sencillito:

gridview.aspx
    <asp:GridView ID="GridView1" DataSourceID="SqlDataSource1" runat="server" />
    <asp:SqlDataSource ID="SqlDataSource1" runat="server" 
        SelectCommand="SELECT Titulo, Texto, Autor, Id FROM miForo" ConnectionString="<%$ ConnectionStrings:Pubs %>" />


Sólo con estas dos líneas de código, el GridView muestra un listado con todos los datos de nuestra tabla llamada miForo. Lo que sucede es que el SqlDataSource1 ejecuta la sentencia SQL "SELECT Titulo, Texto, Autor, Id FROM miForo", conectándose a la base de datos mediante el ConnectionString llamado Pubs definido en el apartado connectionStrings de nuestro archivo web.config, que tendrá un aspecto como este:

web.config
  <connectionStrings>
        <add name="Pubs" connectionString="EscribirAquíLaConnectionString" providerName="System.Data.SqlClient"/>
    </connectionStrings>


Pero no nos vayamos por las ramas. Hemos quedado que el SqlDataSource sabe recoger los datos, pero, ¿Cómo se los pide el GridView al SqlDataSource? Lo hace con el atributo DataSourceID, que como veis apunta a SqlDataSource1.

Ahora ya existe esa figura que proporciona los datos al GridView, y como no le damos ninguna otra instrucción lo que va a hacer éste es mostrarnos por pantalla un listado de todos los registros con los campos de miForo.

El problema es que nosotros puede que no queramos que nos muestre todos los campos, probablemente nos interese que nos muestre sólo el Título, el Autor, un previa con los primeros 200 caracteres del texto, que no nos muestre el campo Id, y que además el Título sea un enlace hacia otra página.

Pero no sólo eso, también vamos a querer
- Borrar algunos registros y modificar algunos campos.
- Paginar y ordenar el listado.
- Obtener los datos NO desde sentencias SQL, sino de otros dos modos: mediante procedimientos almacenados y mediante una clase que nos hayamos creado nosotros.

Pues bien, todos esos aspectos son los que vamos a tratar en sucesivos artículos, de modo que os aconsejo que estéis atentos




Quitar acentos y signos con C#

En ocasiones puede resultar útil quitar los acentos de un texto, o sustituir la 'ñ' por la 'n', la 'ç' por la 'c', etc.

Aquí os dejo un simple ejemplo. Si queréis, podéis echarle una miradita a otro artículo "Algunas cositas del tipo string en C#" para aclarar conceptos sobre los string en C#

quitaAcentosySignos.cs
        string consignos = "áàäéèëíìïóòöúùuñÁÀÄÉÈËÍÌÏÓÒÖÚÙÜÑçÇ";
        string sinsignos = "aaaeeeiiiooouuunAAAEEEIIIOOOUUUNcC";
        for (int v = 0; v < sinsignos.Length; v++)
        {
            string i = consignos.Substring(v, 1);
            string j = sinsignos.Substring(v, 1);
            texto = texto.Replace(i, j);
        }


Donde texto contiene el texto al que le queremos quitar los acentos.

Como veis, lo único que se hace es ir iterando por cada caracter de los string consignos y sinsignos, e ir sustituyendo las apariciones de los primeros por los segundos en la variable texto.