Mostrando entradas con la etiqueta tips. Mostrar todas las entradas
Mostrando entradas con la etiqueta tips. Mostrar todas las entradas

Domina los atajos de teclado para Visual Studio

domingo 20 de abril de 2008 | categorías: , , | 0 comentarios -- da clic aquí para dejar el tuyo

Una de las maneras más fáciles de incrementar tu productividad cuando utilizas casi cualquier programa es aprenderte los atajos de teclado—los mentaos keyboard shortcuts, pues.  De hecho tengo amigos que odian levantar las manos del teclado cuando están programando.  Si alguna vez has usado algún editor de texto como vi, quizá hayas apreciado que una vez que dominas la complejidad de los comandos, en realidad se siente como si estuvieras "platicando" con el editor. Y aunque yo no llegaría al extremo de recomendar que manejes Visual Studio con los comandos de vi, sí recomiendo enormemente que te aprendas algunos de los más comúnes que ya vienen de cajón con el IDE.

El problema es que Visual Studio tiene 8 millones 557,934 comandos.  Bueno, quizá algunos menos, pero aún así son un titipuchal.  Y lo que es peor, los atajos de teclado cambian de acuerdo a la configuración general de Visual Studio.  Esta es la configuración que te pregunta la primer vez que arrancas el IDE o si das Tools>Import and Export Settings...>Reset all settings.

Ventana de diálogo de configuración general de Visual Studio

Aquí es donde ayudan algunos documentos de referencia rápida.  Si primordialmente desarrollas en Visual Basic, y utilizas esa configuración (Visual Basic Development Settings), aquí hay un PDF de Microsoft que los resume. 

Si usas la configuración de Visual C# Development Settings, para desarrollar primordialmente en C#, están este para Visual Studio 2005 y este otro para Visual Studio 2008.

Sin embargo, yo utilizo la configuración para desarrollo general (General Development Settings) porque desarrollo tanto aplicaciones Web como Windows, a veces en C# y a veces en Visual Basic, aparte que ya me había aprendido algunos atajos desde versiones anteriores de Visual Studio.  Como no encontré ninguna referencia rápida para esta configuración, creé mi propio documento.  Puedes descargarlo del sitio de la Comunidad.

Mi recomendación personal es, no intentes aprenderlos todos de un zopetón.  Dice un proverbio: "¿Cómo te comes un elefante? Un bocado a la vez".  Así que mejor escoge un grupo de comandos y practica cada uno por separado, repitiéndolo al menos 20 veces.  Lo que yo hice fue agarrar una archivo con código y practicar un comando a la vez en diferentes partes del archivo mientras lo "vocalizaba mentalmente" hasta que quedaba grabado en mi memoria muscular.  Luego practicaba el siguiente comando hasta terminar el grupo.  Al día siguiente me enfocaba en un grupo distinto, y así sucesivamente.

Cómo crear tus propios atajos

Por supuesto, también puedes definir tus propios atajos.  Estos atajos pueden ejecutar un comando del IDE o alguna macro que hayas creado para alguna tarea específica.  Por ejemplo, hay dos atajos que a mi me ha sido útil definir: uno para comparar el archivo actual contra la versión en el controlador de código y otro para obtener el historial del archivo actual en el controlador de código.

Para definirlos, primero ve a Tools>Options... y luego Environment>Keyboard.  Al hacerlo verás la siguiente ventana:

En la parte donde dice Show commands containing, teclea parte del comando, p. ej. "compare".  Esto traerá una lista de posibles comandos.  En este caso, el que busco es File.Compare.  Luego, selecciona el comando adecuado de la lista y pon el cursor en la parte donde dice Press shortcut keys. Presiona la combinación que desees utilizar.  Si la combinación ya está siendo utilizada por algún otro comando, esta información será mostrada en la parte de abajo donde dice Shortcut currently used by.  En mi caso escogí la combinación Ctrl+Alt+F,C para finalmente darle clic en el botón Assign

Repite los mismos pasos para el comando File.ViewHistory y utiliza el atajo Ctrl+Alt+F,H—o cualquier otra combinación que más te guste.

En fin, creo que esto da la idea.  Enjoy smile_shades

Cómo leer archivos planos con ADO.NET (versión Visual Basic 2005)

jueves 13 de diciembre de 2007 | categorías: , , , , | 4 comentarios -- da clic aquí para dejar el tuyo

Hace poco más de un año escribí este artículo que describe una técnica para leer archivos planos utilizando el OleDB provider de ADO.NET.  Es uno de los artículos de este sitio que ha recibido más comentarios, y entre ellos está uno que dejó fredy que me hizo re-hacer el ejemplo en Visual Basic 2005 para comprobar que no fuera un error de código—en realidad él hizo la mayor parte de la chamba para "traducir" la rutina.

No voy a explicar mucho la lógica del código—para eso te dejo de tarea que leas el artículo original—aquí simplemente te comparto cómo se vería la rutina en VB:

Imports System.IO
Imports System.Data
Imports System.Data.OleDb
 
Module Utilerias
   Public Enum TipoDeArchivoPlano
        Delimited
        Fixed
   End Enum
 
   Public Function LeerArchivoPlano(ByVal archivo As FileInfo, _
        ByVal tieneEncabezado As Boolean, _
        ByVal tipoDeArchivo As TipoDeArchivoPlano) As DataTable
 
        If (Not archivo.Exists) Then
            Throw New FileNotFoundException("No se encontró el archivo especificado")
        End If
 
        Dim conEncabezado As String = IIf(tieneEncabezado, "YES", "NO")
 
        Dim connectionString As String = _
            String.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};" + _
            "Extended Properties='text;HDR={1};FMT={2}'", _
            archivo.DirectoryName, conEncabezado, tipoDeArchivo.ToString())
 
        Dim dt As DataTable = New DataTable("miTabla")
 
        Using conn As OleDbConnection = New OleDbConnection(connectionString)
            Using da As OleDbDataAdapter = New OleDbDataAdapter( _
               "SELECT * FROM " + archivo.Name, conn)
 
               da.Fill(dt)
            End Using
        End Using
 
        Return dt
   End Function
End Module

Para probarla, hice una aplicación sencilla en ASP.NET que mostrara los datos de un archivo .CSV que está dentro de un subdirectorio del sitio web.

El archivo jason.csv contiene:

Producto,Cantidad,Precio
Sierra eléctrica,1,250
Máscara de hockey,1,15.50
Machete,5,2.70
Detergente para ropa (con quita-manchas),1,10
Delantal,2,7.25
Afilador,3,5

La página dentro de la solución que en realidad solo tiene un GridView.  Este es el contenido de Default.aspx:

<%@ Page Language="VB" AutoEventWireup="false" 
   CodeFile="Default.aspx.vb" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
   <title>Leer un archivo plano con VB 2005</title>
</head>
<body>
   <form id="miFormulario" runat="server">
   <div>
        <asp:GridView ID="miGridView" runat="server">
        </asp:GridView>
   </div>
   </form>
</body>
</html>

Finalmente, para mandar llamar la rutina y bindear—¿enlazar?—los datos al GridView, solo agregué esto en el code-behind:

Partial Class _Default
   Inherits System.Web.UI.Page
 
   Protected Sub miFormulario_Load(ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles miFormulario.Load
 
        If Not Page.IsPostBack Then
            Dim archivo As FileInfo = _
               New FileInfo("D:\WebSites\LeerArchivosPlanosVB\Archivos\jason.csv")
 
            Dim tabla As New DataTable
            tabla = Utilerias.LeerArchivoPlano(archivo, True, TipoDeArchivoPlano.Delimited)
 
            If tabla.Rows.Count > 0 Then
               miGridView.DataSource = tabla
               miGridView.DataBind()
            Else
               Response.Write("No hay datos para mostrar.")
            End If
 
        End If
   End Sub
End Class

El resultado de correr la página:

 

Whew! Funcionó smile_teeth

Enjoy. smile_shades


PD.  Puedes descargar el código de este ejemplo del sitio de la Comunidad .NET de Cd. Juárez.

Cómo subir un archivo a tu servidor web mediante el control FileUpload de ASP.NET

jueves 6 de diciembre de 2007 | categorías: , , , | 10 comentarios -- da clic aquí para dejar el tuyo

Este es un tip súper sencillo, pero bastante útil, y como ya van varias personas que me lo preguntan, mejor lo explico aquí.

A menudo se ofrece que los usuarios de nuestra aplicación web nos envíen (o "suban") algún archivo al servidor web donde corre nuestra aplicación ASP.NET. Pudiera ser que el gerentoide tiene un archivo de texto o .CSV que necesitamos leer para cargarlo en la base de datos, o una imágen que vamos a poner como avatar en el perfil del usuario, qué se yo.

Con ASP.NET 2.0 o posterior, subir un archivo a tu servidor es sencillísimo mediante el control FileUpload. Supongamos que tenemos una forma de ASP.NET como esta:

Una forma ASP.NET para subir un archivo a tu servidor web

El markup sería el siguiente:

<%@ Page Language="C#" AutoEventWireup="true" 
   CodeFile="Default.aspx.cs" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
   <title>Subir un archivo</title>
</head>
<body>
   <form id="form1" runat="server">
        <div>
            <h3>
               Cómo subir un archivo a tu servidor web mediante ASP.NET</h3>
            <p>
               Elige el archivo a subir:
               <asp:FileUpload ID="trepador" runat="server" />
            </p>
            <p>
               <asp:Button ID="subir" runat="server" 
                    Text="Subir Archivo" OnClick="subir_Click" />
            </p>
            <p>
               <asp:Label ID="estatus" runat="server" 
                    Style="color: #0000FF"></asp:Label>
            </p>
        </div>
   </form>
</body>
</html>

Como puedes ver la forma en realidad consiste de únicamente de un control FileUpload, un botón para iniciar el postback, y un Label que usaré para mostrar un mensaje. Al botón le asociamos un manejador para el evento Click—que veremos un poquito más abajo.

Las propiedades y métodos relevantes del control FileUpload son:

  • SaveAs(), que se usa para decir dónde quieres guardar el archivo que se está subiendo. Debes proporcionar la ruta completa y el nombre.
  • HasFile, que indica si el usuario ya escribió el nombre de un archivo o lo seleccionó mediante el botón "Browse..."

Ahora, este es el código para manejar el evento:

using System;
using System.IO;
 
public partial class _Default : System.Web.UI.Page
{
   protected void subir_Click(object sender, EventArgs e)
   {
      if (trepador.HasFile)
      {
         string directorio = @"C:\Archivos de Usuario\";
 
         if (Directory.Exists(directorio))
         {
            string archivo = directorio + trepador.FileName;
 
            if (File.Exists(archivo))
            {
               // ya existe un archivo con el mismo nombre en el directorio,
               // así que hay hacer algo al respecto (p.ej. renombrar el que 
               // está en el servidor o asignarle otro nombre al que se está 
               // subiendo), de lo contrario el archivo en el servidor será 
               // sobreescrito
            }
            else
            {
               trepador.SaveAs(archivo);
               estatus.Text = "Tu archivo ha sido enviado exitosamente.";
 
               // 
               // TODO: código para procesar el archivo va aquí...
               //
            }
         }
         else
         {
            throw new DirectoryNotFoundException(
               "El directorio en el servidor donde se suben los archivos no existe");
         }
      }
   }
}

Consideraciones adicionales

Hay dos aspectos más que debes cuidar con esto de la "trepada" de archivos.

Primero, debes asegurarte que la cuenta de usuario bajo la que corre el ASP.NET Worker Process (típicamente la cuenta se llama ASPNET) tenga suficientes permisos de seguridad sobre el directorio donde quieres escribir los archivos en el servidor, de lo contrario verás una excepción de seguridad. Creo que el permiso mínimo que necesita es de Write, aunque yo casi siempre uso el de Modify en mi máquina local.

 Permisos necesarios para que ASP.NET pueda subir (escribir) archivos en el servidor

Y segundo, ASP.NET, por default, está configurado para subir archivos de hasta 4 Mb únicamente (4096 bytes). Esto es porque la petición la recibe IIS primero y debe "tragársela" completa antes de pasársela al Worker Process. Si lo que vas a subir son archivos de texto, pues 4 Mb es un chorro, pero si es otro tipo de archivos—imágenes p0rno, archivos de Word, etcétera—pues esto podría ser terriblemente insuficiente. Si el archivo es muy grande, el otro problema es que la petición de HTTP pudiera exceder el tiempo máximo por petición y hacer timeout. Afortunadamente podemos alterar esto mediante el archivo de configuración del sitio (web.config):

<?xml version="1.0"?>
<configuration>
   <system.web>
      <!--  Requerido para subir archivos grandes. En este caso 
         especificas un tiempo máximo de 10 minutos (600 segundos)
         y un tamaño máximo de archivos de 50Mb
   -->
      <httpRuntime executionTimeout="600" maxRequestLength="51200"/>
   </system.web>
</configuration>

En la configuración anterior exageré un bastante, y deberías tener cuidado de valores como estos, ya que estás indicando que puede haber peticiones de ASP.NET que duren hasta 10 minutos ejecutándose, y esto podría llevar a que tu servidor se sature si de pronto hay varias peticiones grandes. Para la mayoría de las aplicaciones el valor default de 110 segundos (minuto y medio) como executionTimeout es suficiente. Especificar valores grandes para el maxRequestLength, de manera similar, expone tu servidor a ser saturado, así que debes alterar estos valores solo si en verdad lo requieres, y solo para los valores máximos que se necesiten para tu aplicación.

Por último, la documentación de ASP.NET dice que se debe especificar el valor de executionTimeout debe especificarse como un TimeSpan (p.ej. "00:01:50"), pero en la práctica, no he podido utilizarlo así. Siempre he tenido que especificarlo como el número de segundos.

Conclusión

El control FileUpload hace que el problema de subir achivos a tu servidor web sea pan comido. Solo necesitas agregarlo a tu página ASPX y agregar un botón u algún otro control para que invoque la lógica necesaria para copiar el archivo a tu servidor mediante el método SaveAs(). Solo debes cuidar que la cuenta ASPNET tenga los permisos adecuados, y que la configuración del servidor sea apropiada para los tipos de archivos que vayas a subir.

Hay muchas mejoras que se le puede hacer al ejemplo de este artículo. La primera que se me ocurre a mi es proporcionar algún tipo de retroalimentación al usuario de que el archivo se está procesando, o deshabilitar el botón inmediatamente después del click para que el usuario no le de click dos veces hasta que termine de subirse el archivo—estos son problemas bastante comunes cuando quieres subir archivos grandes y ya sea que tu servidor esté algo ocupado o tu usuario sea un neurótico desesperado que no puede esperar ni los 7 segundos promedio como el resto de los seres humanos normales... smile_baringteeth smile_wink pero en fin, esa ya es harina de otro costal, y tendría que hablar de ASP.NET AJAX. Quizá en la próxima.

Enjoy. smile_shades

Diferencias en herramientas de diferencias e integración con SourceGear Vault

domingo 10 de junio de 2007 | categorías: , , | 0 comentarios -- da clic aquí para dejar el tuyo

Cuando desarrollas software como parte de un equipo, es bastante común que en algún momento tú y Fulanito De Tal modifiquen el mismo archivo para dos cambios distintos y que tengas que reconciliar las diferencias antes de que puedas hacer el check-in. Pero a menudo, las herramientas que vienen con el controlador de código son HO-rrendas (SourceSafe 6.0 anyone?)

Es por eso que una buena herramienta de diferencias es escencial en el toolkit de un desarrollador, y si eres igual de codo ejem, digo... práctico que yo, pues siempre buscas lo bueno, bonito y barato (léase gratis, de preferencia).  Así que voy a mencionar 3 de mis favoritas en esta categoría: WinMerge, DiffMerge y BeyondCompare.  Todas ellas soportan básicamente las mismas características: comparación de fólderes, múltiples archivos, y archivos con diferentes encodings, pero también tienen uno que otro extra que las hace diferentes e interesantes.

Finalmente, veremos también cómo hacerle para integrarlas a SourceGear Vault (el controlador de código que uso en mi trabajo) de manera que solo tengas que darle clic derecho en el Solution Explorer de Visual Studio, selecciones "Compare..." y voilá, compares la versión local contra la versión controlada.

WinMerge

WinMerge es una herramienta de esas que son gratis en todo el sentido de la palabra.  Es gratis para descargar y open source.  De las cosas interesantes que ofrece son que permite la creación de los parches de código (para aquellos controladores como CVS, supongo) y tiene integración al shell de Windows, incluso en las versiones de 64 bits (osea que sale si le das un clic-derecho a un archivo en Windows Explorer, te sale la opción para comparar archivos). 

De las cosas que no me gustan (y dirás que qué payaso, pero en fin) son que la interfaz está un poco fea, estéticamente hablando y no tan intuitiva. Solo corre en Windows, y la capacidad de editar archivos a la hora de hacer el merge es limitada.  Pero aún así es una herramienta bastante competente.

Puedes ver algunos screenshots aquí, y descargalo desde acá.

DiffMerge

Vault viene con una herramienta llamada DiffMerge la cual solo la obtenías con el producto y no era muy robusta.  Sin embargo, la semana pasada Eric Sink anunció que DiffMerge 3.0 ahora está disponible de manera gratis y separada.  La nueva versión tiene varias bondades que la anterior carecía: comparación de 3 archivos a la vez, soporte para edición de archivos y oh sí, corre en Windows, en Mac OS X o en Linux (!)

De las cosas que no me gustaron está que no tiene integración con el shell de Windows, limitando un poco la capacidad de hacer comparaciones rápidas.  Además, cuando comparas fólderes enteros tarda un poco ya que lee todos los archivos en todos los subfólderes de un solo golpe.  Pero si no te importa esto, pues vale la pena que lo pruebes.

Puedes ver algunos screenshots aquí, y descargarlo desde acá.

BeyondCompare

BeyondCompare es una de las herramientas que cuesta dinero, pero no mucho ($30 dlls por una licencia individual).  De hecho, nosotros compramos 10 licencias a $13 dlls cada una, lo cual es una ganga si lo comparan con otras como Araxis Merge, donde UNA licencia cuesta $130 dlls.  He usado BC por años y cada versión solo se pone mejor.  Soy fan, y al parecer no soy el único.

Aparte de lo estándar, el feature que nos mantiene con este producto es la habilidad de exportar el reporte de diferencias a un archivo HTML (o XML si así lo deseas). Ninguna otra herramienta barata o gratis que yo haya visto trae esto.  Nosotros lo utilizamos para la documentación de los cambios al código, y como parte de nuestro proceso de revisión de código para que otros desarrolladores puedan darle el visto bueno antes de que se le haga check-in a los cambios. 

En la columna de "contras" está que carece de la capacidad de hacer un merge automático--lo cual podría ser irrelevante si eres igual de desconfiado que yo en esas operaciones.  Y su capacidad de edición de archivos es limitada.  Sin embargo, parece que van a agregarle todo eso a la versión 3.0 de BC que va a salir a finales de este año.  No estoy seguro si la integración al shell que trae funcione bajo 64 bits. 

Puedes descargar una versión shareware de 30 días desde aquí.

Integración de herramientas de diferencia a SourceGear Vault

En mi equipo usamos Vault de SourceGear como nuestro controlador de código.  No les voy a escribir una carta de amor sobre el producto, pero sí les diré que es lo que SourceSafe debió ser desde un principio (¿y les comenté que está hecho en .NET?)

El caso es que una de las razones por las que Vault me cae bien, es que te permite configurar la herramienta que desees utilizar para las comparaciones (diff) o para la reconciliación de cambios (merge). Para hacerlo solo tienes que ir a las opciones de Visual Studio y de ahí acceder a las de Vault. 

  • Desde Visual Studio, selecciona "Options..." del menú "Tools".
  • Selecciona "Plug-In Settings" que está debajo de "Source Control" al lado izquierdo.
  • Da clic en "Advanced…" (esto te llevará a las opciones de Vault)
  • Selecciona "Diff/Merge" del lado izquierdo.

Lo único que tienes que hacer despues de eso, es especificar el programa y los argumentos que le vas a pasar.

Si quieres usar WinMerge, tienes que especificar lo siguiente (tomados de esta página):

Diff Program: C:\Program Files\WinMerge\WinMerge.exe\
Diff Arguments: /dl "%LEFT_LABEL%" /dr "%RIGHT_LABEL%"  "%LEFT_PATH%" "%RIGHT_PATH%"
Merge Program: C:\Program Files\WinMerge\WinMerge.exe\
Mege Arguments: /dl "%WORKING_LABEL%" /dr "%OTHER_LABEL%" "%WORKING_PATH%" "%OTHER_PATH%" "%DEST_PATH%"

Si quieres utilizar DiffMerge 3.0, entonces solo tienes que modificar la ruta al ejecutable que viene con la configuración de Vault.  Quedaría entonces, así:

Diff Program:

C:\Program Files\SourceGear\DiffMerge\DiffMerge.exe

Diff Arguments:

/ro1 /ro2 /title1:"%LEFT_LABEL%" /title2:"%RIGHT_LABEL%" "%LEFT_PATH%" "%RIGHT_PATH%"

Merge Program:

C:\Program Files\SourceGear\DiffMerge\DiffMerge.exe

Mege Arguments:

/ro1 /ro3 /merge /title1:"%WORKING_LABEL%" /title2:"%DEST_LABEL%" /title3:"%OTHER_LABEL%" /result:"%DEST_PATH%" "%WORKING_PATH%" "%BASELINE_PATH%" "%OTHER_PATH%"

Y finalmente, si quieres utilizar BeyondCompare, entonces los argumentos serían:

Diff Program: C:\Program Files\Beyond Compare 2\BC2.exe
Diff Arguments: /title1="%LEFT_LABEL%" /title2="%RIGHT_LABEL%" "%LEFT_PATH%" "%RIGHT_PATH%"

O puedes mezclar un poco las cosas.  Por ejemplo yo uso BeyondCompare para la funcionalidad de comparación y DiffMerge 3.0 para la funcionalidad de reconciliación.

Conclusión

Una buena herramienta de diferencias es escencial para tu kit de desarrollador.  Hay varias en el mercado que son gratis o bastante baratas y que pueden integrarse con tus herramientas de desarrollo.  Tómate un tiempecito para echarles un ojo y escoger la que mejor se acomode a tus necesidades.

¿Cómo era ese (bleep) connection string?

sábado 11 de noviembre de 2006 | categorías: , | 0 comentarios -- da clic aquí para dejar el tuyo

Una de las cosas que más panzonas me caen de acceder a datos, es que para cada tipo de base de datos los strings de conexión siempre son distintos. Oracle, SQLServer y Access (por mencionar algunos) utilizan cada uno su sintaxis... y siempre se me olvida. ¿Cómo era? ¿DataSource? ¿Database? ¿Initial Catalog? (maldición)

Por eso ConnectionStrings.com se me hizo tan chida. En una sola página encuentras cómo conectarte a un montón de bases de datos. ¿Quieres conectarte a una AS/400 con el OLEDB Provider de .NET? Ahí viene. ¿A SQL Server 2005 con MARS habilitado? También viene.

Échale un ojo. Probablemente acabarás bookmarkeando el sitio como yo.

Cómo leer archivos planos con ADO.NET

| categorías: , , , | 38 comentarios -- da clic aquí para dejar el tuyo

(Antes que me la rieguen, sí, ya sé que son "archivos de texto sencillo" pero este blog está en pocho, ¿no? Digo, no hay archivos de computadora planos, o cúbicos o esféricos, que yo sepa, pero así les dicen muchos desarrolladores)

Aunque hoy en día ya todo mundo debería de estar utilizando (según yo) XML para el intercambio de información, sigue siendo muy comun que para mandar información de un sistema a otro se haga a través de archivos "planos" de texto.

Si alguna vez has lideado con sistemas legacy o en plataformas chiples (mainframe, or SAP anyone?) estoy seguro que sabes de lo que hablo. Otro caso común es cuando requieres que tu usuario genere algún archivo para cargar esos datos en tu aplicación. Harta cantidad de gerentoides y chalanes no saben ni (bleep) de sistemas o de computadoras, pero eso sí, son masters sensai del Excel, así que es fácil decirles que le den un "Save As... CSV" a un archivo para subir sus datos.

Ahora, ¿cómo le harías para leer esos archivos con .NET? Si eres entusiasta probablemente luego luego te las ingeniarías para usar un FileStream y parsear el contenido línea por línea, dar 3 maromas... qué se yo. Sin embargo, existe un truco sencillo que puede ahorrarte broncas: utilizar el OLEDB Provider de ADO.NET para hacerlo. Esta técnica funciona bastante bien para leer archivos CSV o archivos de texto con columnas en posiciones fijas (si son secuenciales, ya valiste cake).

Leyendo un archivo CSV

Por ejemplo, imagina que eres achichincle del Jason (el de las películas de terror) y te manda a hacer sus compras, de lo contrario terminarás con tu cabeza y cuerpo en diferentes sectores de la ciudad. El vato hace su "chopin list" en Excel y lo guarda en el siguiente archivo llamado jason.csv:

Producto,Cantidad,Precio
Sierra eléctrica,1,250
Máscara de hockey,1,15.50
Machete,5,2.70
Detergente para ropa (con quita-manchas),1,10
Delantal,2,7.25
Afilador,3,5

Entonces, como buen dotnetero, podrías leerlo con una rutina como esta:

// asumiendo que tenemos
// using System.Data.OleDb;
// using System.Data;
 
// en este connection string:
//     HDR=Yes       : indica que el primer registro contiene los encabezados 
//                     (nombres) de las columnas, no datos.
//     FMT=Delimited : indica que el los campos están delimitados por un caracter
//                     (coma por default).
string connectionString =
    @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\DirectorioDeArchivosCSV;" +
    "Extended Properties='text;HDR=Yes;FMT=Delimited'";
 
DataTable dt = new DataTable("miTabla");
using (OleDbConnection conn = new OleDbConnection(connectionString))
using (OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM jason.csv", conn))
{
    da.Fill(dt);
}
 
// hacer algo con los datos en el DataTable

El código es bastante sencillo, es el patrón estándar para usar un DataAdapter. Lo único de especial que tiene es que utiliza el OLEDB Data Provider, y que en el connection string le especificamos el directorio donde se encuentra el archivo, así como el formato que tiene. Nota que emites un SELECT de SQL común y corriente, por lo cual podrías agregar una cláusula WHERE si así lo quisieras.

En fin, para comprobar que en realidad funcionara el código, puse un breakpoint e invoqué el DataSet Visualizer desde Visual Studio. El resultado:

Pero, ¿qué tan inteligente es el OLEDB Provider? ¿Adivinó correctamente el tipo de mis datos?

Leyendo un archivo de texto con posiciones fijas

Ahora, asume que el méndigo Jason te la puso más difícil y en lugar de darte un archivo CSV, te da un archivo de texto sencillo como este (jason.txt):

El código sería muy similar al anterior:

// asumiendo que tenemos
// using System.Data.OleDb;
// using System.Data;
 
// en este connection string:
//     HDR=Yes   : indica que el primer registro contiene los encabezados 
//                 (nombres) de las columnas, no datos.
//     FMT=Fixed : indica que el los campos están en posiciones fijas y el tamaño
//                 de cada campo se especifican con un archivo SCHEMA.INI
//                 en el mismo directorio donde está el archivo a leer.
string connectionString =
    @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\DirectorioDeArchivosTXT;" +
    "Extended Properties='text;HDR=Yes;FMT=Fixed'";
 
DataTable dt = new DataTable("miTabla");
using (OleDbConnection conn = new OleDbConnection(connectionString))
using (OleDbDataAdapter da = new OleDbDataAdapter("SELECT * FROM jason.txt", conn))
{
    da.Fill(dt);
}
 
// hacer algo con los datos en el DataTable


De hecho, lo único que cambió fue el parámetro FMT en el connectionString, el nombre del archivo y el directorio donde localizarlo. Sin embargo, para que esto funcione con archivos de posiciones fijas es necesario un paso adicional: especificar el tamaño (y tipo) de las columnas en el archivo.

Esto se hace mediante un archivo schema.ini que debe estar en el mismo directorio que el archivo que vas a leer. Consulta esta página para saber todas las opciones disponibles. En nuestro caso un archivo como el siguiente sería suficiente:

[jason.txt]
Format=FixedLength
Col1=Producto Char Width 40
Col2=Cantidad Long Width 10
Col3=Precio Double Width 10


Factorizando código

Si generalizamos el código un poco, podemos extraer una función sencilla que pueda ser reutilizada en varios de nuestros programas. Esa rutina podría ser como esta, en donde le pasas como parámetros el tipo y archivo a leer y te regresa un DataTable poblado ya con los datos:

// asumiendo que tenemos 
// using System.Data.OleDb;
// using System.Data;
// using System.IO;
 
public enum TipoDeArchivoPlano { Delimited, Fixed }
 
public static DataTable LeerArchivoPlano(
    FileInfo archivo, bool tieneEncabezado, TipoDeArchivoPlano tipoDeArchivo )
{
    if (!archivo.Exists)
        throw new FileNotFoundException(
            "No se encontró el archivo especificado");
 
    string conEncabezado = tieneEncabezado ? "YES" : "NO";
 
    string connectionString = String.Format(
        @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};" +
        "Extended Properties='text;HDR={1};FMT={2}'",
        archivo.DirectoryName, conEncabezado, tipoDeArchivo.ToString());
 
    DataTable dt = new DataTable("miTabla");
    using (OleDbConnection conn = new OleDbConnection(connectionString))
    using (OleDbDataAdapter da =
        new OleDbDataAdapter("SELECT * FROM " + archivo.Name, conn))
    {
        da.Fill(dt);
    }
 
    return dt;
}


De manera que pueda ser llamado así:

DataTable dt = LeerArchivoPlano(
    new FileInfo(@"C:\DirectorioDeArchivosCSV\jason.csv"),
    true, TipoDeArchivoPlano.Delimited);


En fin, la idea es esa.

Enjoy.


Actualización, 13 diciembre 2007:

Gracias a los comentarios de fredy, publiqué otro artículo con el código en Visual Basic 2005, para aquellos que les interese.

Usando using

lunes 28 de agosto de 2006 | categorías: , , , , | 4 comentarios -- da clic aquí para dejar el tuyo

El día de hoy aprendí un tip bastante bueno de como usar using gracias a Eber Irigoyen.

Todo comenzó porque él hizo un comentario en el último artículo recalcando la importancia de usar using para TODOS los objetos relacionados con la base de datos (conexiones, comandos, transacciones, etcétera). A mí se me hacía un poco de overkill, pero ahora comprendo que en realidad sí es un buen consejo.

Por si no sabes ni de qué hablo, esta instrucción define un alcance (scope) explícito para un objeto, fuera del cual el objeto es automáticamente "dispuesto" (manda llamar su método Close() o Dispose()), incluso si se levanta una excepción dentro de ese bloque.

Aunque estrictamente hablando no es necesario, ya que hay un scope implícito cada vez que abres y cierras llavecitas en C#, para recursos limitados como conexiones de red, el scope explícito que crea el using ayuda a que el garbage collector reclame esos recursos más fácil y rápidamente.

Si no lo tuvieras, tendrías que hacer todo a punta de bloques try/catch/finally. Eso era algo que no me gustaba de VB 7--bueno VB .NET o "VB 2003", como le quieran llamar. Afortunadamente VB 8 (2005) ya lo trae.

En el artículo original no la quise complicar mucho con esto, pero así quedaría el "patrón de uso de un DataReader" usando try/catch/finally:

string miStringDeConexion =
    @"Data Source=.\SQLEXPRESS;Initial Catalog=AdoNetDemo;" +
    "Integrated Security=True";
string miQuery = "SELECT * FROM Peliculas;";
 
SqlDataReader speedy = null;
SqlConnection miConexion = null;
SqlCommand miCommando = null;
try
{
    // en cualquiera de las instrucciones de este bloque se podría
    // levantar una excepción
 
    miConexion = new SqlConnection(miStringDeConexion);
    miCommando = new SqlCommand(miQuery, miConexion);
    miConexion.Open();
    speedy = miCommando.ExecuteReader();
    while (speedy.Read())
    {
        // hacer mi desmater 
    }
}
catch (SqlException)
{
    // hacer algo para intentar recuperarme o re-lanzar exception
 // si no pienso hacer nada, es mejor omitir el catch
}
catch
{
    // hacer algo para intentar recuperarme o re-lanzar exception
 // si no pienso hacer nada, es mejor omitir el catch
}
finally
{
    // haya o no haya ocurrido excepción, siempre debemos asegurarnos
    // de cerrar las conexiones, DataReaders, etc.
    if (!speedy.IsClosed)
        speedy.Close();
    if (miConexion != null)
        miConexion.Close();
    if (miCommando != null)
        miCommando.Dispose();
}

Como puedes ver, es medio engorroso. Por eso el using es tan cool.

Pero, yo tenía mis reservas, porque cuando tienes varios objetos, es fácil--pensaba yo--que acabes con un chorro de usings anidados, lo cual no me gustaba:

// asumiendo que TipoA, TipoB y TipoC son clases que
// implementan IDisposable
using (TipoA a =