Category Archives: 2843

Libro en Español: Visual Basic.NET – Programe con Visual Studio 2008

La Editorial Eni publicó en este mes el libro Visual Basic .NET (VB.NET) Programe con Visual Studio 2008 del cual participé en la traducción y adaptación del Francés al Español.

Autor : Thierry GROUSSARD

Adaptación al Español: Carlos WALZER

 

Presentación

Este libro sobre VB.Net está dirigido a los desarrolladores, incluso principiantes, que quieran dominar Visual Basic.NET. Después de la descripción del entorno de desarrollo (Visual Studio 2008), el lector descubrirá las bases de la programación orientada a objetos con VB.NET y evolucionará progresivamente hacia su uso con el desarrollo de aplicaciones Windows Forms. Los numerosos ejemplos y los consejos sobre la utilización de las herramientas de depuración le proporcionarán una valiosa ayuda durante el desarrollo de una aplicación.
Un capítulo dedicado al acceso a las bases de datos con ayuda de ADO.NET 2.0 y de SQL le permitirá evolucionar hacia el desarrollo de aplicaciones usuario-servidor. Se presentan y detallan las potentes funcionalidades de LINQ que facilitan el acceso y la manipulación de datos. Igualmente, se presenta el lenguaje XML, que facilita el intercambio de información con otras aplicaciones. Los usuarios de las versiones anteriores descubrirán las novedades y mejoras en esta versión 2008 (tipos que aceptan valores NULL, métodos parciales, clases anónimas,…) permitiendo desarrollar aún más rápida y fácilmente las aplicaciones para el framework .NET 3.5 y para Windows Vista.
La distribución de una aplicación también se presenta con el uso de Windows Installer y de la tecnología ClickOnce.

Mas infomación…

Nota: No hay un distribuidor en Sudamérica, con lo que la editorial recomienda dos opciones:
1 )cursar una solicitud de presupuesto a través de nuestra web (tras lo cual se cotizan los gastos de envío, y el cliente acepta o no el pedido)
2) comprarlo a través de Celesa, www.celesa.com

Materiales: Arquitecturas de presentacion WPF

Aqui están los materiales de la charla que dimos con Rodolfo Finochietti en el Run 09:

Arquitecturas de presentación de WPF
WPF no es solo un framework para desarrollar aplicaciones de gran riqueza visual, también incorpora características que facilitan la implementación de patrones que simplifican el desarrollo en la capa de presentación. En esta charla veremos cómo implementar arquitecturas de presentación que utilizando dichas características permiten desarrollar aplicaciones mas escalables y mantenibles.

Descargar Presentación.

El contenido de la presentación fue basado en dos artículos de la revista MSND Magazine:

Gracias a todos por escucharnos 🙂

CodeCamp – Buenos Aires 2008

Evento Académico gratuito con más de 40 conferencias sobre tecnologías Microsoft dicatadas por referentes de la industria.

Fecha:
Saturday, October 4, 2008
Hora:
10:00am – 7:00pm
Lugar:
UAI
Dirección:
San Juan 983
Ciudad:
Buenos Aires, Argentina
 
 

 

Descripción

• Novedades en Tecnologías Microsoft
• Más de 40 conferencias técnicas de Referentes de la Industria
• Sorteos entre los asistentes

Más detalles en el sitio del evento: http://www.microsoft.com.ar/codecamp/

Agenda:

10:00 a 10:30 Registración – Recepción

10:30 a 11:00 Apertura y revisión de agenda

11:15 a 12:15
Programando por un sueño – Patricio Jutard
Introducción a ASP.NET MVC – Pablo Cibraro
Videojuegos Multiplataforma con XNA – Augusto Chesini
Todo Lo Que Hay Que Saber Acerca De Linq – Matías Bonaventura
Mejoras De Desempeño De Aplicaciones .Net – Carlos Walzer
Gestión De Proyectos De Software: Un Enfoque Práctico – Maximiliano Menasches e Ignacio López
Windows Presentation Foundation Para Desarrolladores – Andres Aguiar
Python En La Plataforma .Net – Martín Salías
Introducción A Grafos – Pablo Gauna
Wamp – Maximo Naccarato
IIS7 4×4 – Alberto Ortega Y Horacio Gonzalez

12:30 a 13:30
Andamios en la web: Desarrollo ágil con ASP.NET Dynamic Data – Miguel Saez
Desarrollo web multi-capa con Volta: un paso hacia adelante – Pablo Zaidenvoren Y Rodolfo Finochietti
Wpf 3d Y Física – Damián Galletini
Microsoft Sql Server 2008 – Introducción A “Microsoft Sql Server Data Platform” – Johnny Halife
Patrones De Arquitectura Con C# – Matías Iacono
Vsts Y Scrum – Daniel Zacharias
Creando Aplicaciones En Wpf – José Villagrán
Parallel Extensions – Nicolás Padula Y Mauricio Lopez
Entorno De Desarrollo Para Windows Mobile 6 – Leonardo Natale
Windows Server 2008 + Iis 7 – Augusto Alvarez
Desmitificando Windows Vista – Mariano Rempel

13:30 a 14:45
Almuerzo – Demo Fest

15:00 a 16:00
El futuro de la ingeniería de software – Santiago Ceria
Silverlight 2.0 Beta 2 – Construyendo Aplicaciones Web Atractivas Utilizando Microsoft Silverlight – Ivana Tilca
3d Games Technology: Computer Animation – Diego Park
Ado.Net Entity Framework En Acción – Maximiliano Menasches Y Hernán Nobile
Técnicas Para Mejorar La Performance En Sitios Web – Juan Ladetto
Scrum – Patricio Jutard
¿Qué Es “Wpf/E”? – Luis Perdomo
Reconocimiento Facial – Armando Meabe
Novedades De Vs 2008 Y Cf 3.5 Para Wm6 – Leonardo Natale
Introducción A La Administración De Sucursales Con Windows Server 2008 – Leonardo Amaya
Seguridad En Windows Server 2008 – Leandro Amore

16:15 a 17:15
Hyper-V en acción – Alejandro Ponicke Y Leandro Amore
Expression Blend – Marcelo Quevedo
Qué Es Linq? – Guillermo Delfino
Programación Intuitiva De Xml En El Marco .Net (Xlinq) – Mauricio Grimberg
Howto: Performance Y Load Testing – Matias Woloski Y Federico Boerr
¿Cómo Lograr Un Proyecto Exitoso? – Patricia Scalzone
Construcción De Servicios Rest Con Wcf – Pablo Cibraro
Microsoft Robotics Studio – Mauro Castagnasso
Introducción A Los Conceptos De Generics Y Delegates – Ezequiel Cura
Monitoreo De Aplicaciones Heterogéneas – Alberto Ortega
Instalando Windows Vista – Francisco Moura

17:15 a 18:30
Break – Demo Fest

18:45 a 20:00
Cierre del Evento y Sorteos

Performance: Datos en Memoria con ADO.NET IV

En este artículo quisiera mostrarles cual es el consumo de memoria de algunas técnicas de acceso a datos. En artículos anteriores hemos estudiado y optimizado performance mejorando el tiempo de procesamiento. Como colorario veremos algunos gráficos que siempre ayudan a la comparación.

Este artículo está relacionado con:

Presentación del escenario

Este es el contexto en el que estoy haciendo las mediciones:

Una aplicación Windows Forms, que utiliza 4 mecanismos para recuperar datos “de solo lectura” de la base de datos AdvertureWorks alojada en SQL Server 2005:

  • DataReader cargado en una lista genérica de objetos de entidad
  • DataSet
  • DataTable
  • DataSet tipificado creado con el asistente de Visual Studio 2005

Aquí subrayo “solo lectura” porque, justamente solo quiero recuperar los datos, y no hacer ninguna operación sobre ellos.

Memoria y Garbage Collector

Si bien sabemos que la administración de la memoria en .NET es un trabajo que le compete al Garbage Collector y que no es terreno en el que debamos hurgar, a no ser que sea por administración de memoria no manejada, siempre es bueno saber que uso hacemos de él. Si bien el Garbage Collector es un mecanismo muy optimizado, y hace un muy buen trabajo de recolección de basura (memoria no utilizada), tiene sus limitaciones y su costo. Sería una buena actitud de parte nuestra considerar al Garbage Collector como un recurso más, así como lo es la memoria. Teniendo en cuenta esto lograríamos minimizar su trabajo, lo cual redundaría en un mejor rendimiento de nuestra aplicación.

El Código

La versión completa del código podrás bajarla de aquí. De todas formas démosle un vistazo:

Esta es la sentencia sql a ejecutar en la base de datos AdventureWorks:

Select HumanResources.Employee.EmployeeID, Person.Contact.FirstName,
          Person.Contact.MiddleName, Person.Contact.LastName,
          HumanResources.Employee.Title, HumanResources.Employee.BirthDate, 
          Person.Address.AddressLine1, Person.Address.AddressLine2, 
          Person.Address.City, Person.Address.PostalCode, Person.Contact.EmailAddress, 
          Person.Contact.Phone, HumanResources.Employee.MaritalStatus, HumanResources.Employee.Gender 
          FROM HumanResources.Employee
          INNER JOIN Person.Contact ON HumanResources.Employee.ContactID = Person.Contact.ContactID
          INNER JOIN HumanResources.EmployeeAddress ON HumanResources.Employee.EmployeeID = HumanResources.EmployeeAddress.EmployeeID
          INNER JOIN Person.Address ON HumanResources.EmployeeAddress.AddressID = Person.Address.AddressID
          AND HumanResources.EmployeeAddress.AddressID = Person.Address.AddressID

La clase DataAccess

public class DataAccess
{
   
static readonly string _connString;
   
static readonly string _sqlCmd;

    static DataAccess()
    {
        _connString =
“Password=;User ID=sa;Initial Catalog=AdventureWorks;Data Source=WALZER3”;
       
//Obtengo la sentencia SQL que está en el archivo de texto Consulta.sql
       
StreamReader sr = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(“Walzer.Antipracticas.Consulta.sql”));
        _sqlCmd = sr.ReadToEnd();
    }

    static public DataSet TraerDataSet()
    {
        
DataSet ds = null;
        
try
        
{
             
using (SqlConnection conn = new SqlConnection(_connString))
              {
                  conn.Open();
                 
SqlCommand cmd = new SqlCommand();
                  cmd.CommandText =
“GetEmployees”;
                  cmd.Connection = conn;
                  cmd.CommandType =
CommandType.StoredProcedure;
                 
SqlDataAdapter da = new SqlDataAdapter(cmd);
                  ds =
new DataSet();
                  da.Fill(ds);
              }
         }
        
catch
        
{
         }
        
return ds;
     }

     static public List<Employee> TraerEmployeesOptimizado()    
     {
    
     List<Employee> employees = new List<Employee>();
         
try
         
{
         
using (SqlConnection conn = new SqlConnection(_connString))
          {
             
SqlCommand cmd = new SqlCommand();
              cmd.CommandText =
“GetEmployees”;
              cmd.Connection = conn;
              cmd.CommandType =
CommandType.StoredProcedure;
              conn.Open();
             
using (SqlDataReader dr = cmd.ExecuteReader())
              {
                 
int colEmployeeId = dr.GetOrdinal(“EmployeeId”);
                 
int colFirstName = dr.GetOrdinal(“FirstName”);
                  // Omito las líneas similares por cuestión de lectura
                 
int colCount = dr.FieldCount;
                 
object[] values = new object[colCount];
                 
while (dr.Read())
                  {
                   
Employee employee = new Employee();
                    dr.GetValues(values);
                    employee.EmployeeID =
Convert.ToInt32(values[colEmployeeId]);
                    employee.FirstName =
Convert.ToString(values[colFirstName]);
                   
employees.Add(employee);
                  }
                }
             }
          }
         
catch
         
{
          }
         
return employees;
     }

     static public DataTable TraerDataTableOptimizado()
    
{
         
//Este método está optimizado para cargar un DataTable con datos de SOLO LECTURA
         
DataTable dt = null;
         
try
         
{
              
using (SqlConnection conn = new SqlConnection(_connString))
               {
                    conn.Open(); 
                   
SqlCommand cmd = new SqlCommand();cmd.CommandText = “GetEmployees”; 
                    cmd.Connection = conn; 
                    cmd.CommandType =
CommandType.StoredProcedure; 
                   
SqlDataAdapter da = new SqlDataAdapter(cmd);dt = new DataTable(); 
                    da.Fill(dt); 
               } 
          } 
          
catch 
          

          } 
          
return dt; 
     } 
}

El DataSet tipificado fue creado arrastrando la consulta SQL sobre la superficie de diseño del DataSet, lo único que escribí fue las siguientes líneas para cargar el DataSet tipificado:

DsEmployeesTableAdapters.GetEmployeesTableAdapter da = new Walzer.Antipracticas.DsEmployeesTableAdapters.GetEmployeesTableAdapter();
_dsEmployees = da.GetData();

Lectura del uso de Memoria

Vamos medir el uso de memoria de cada una de estas técnicas de acceso a datos utilizando 3 herramientas:

El CLR Profiler nos revela en una primera lectura de 290 registros representados en memoria por cada una de las técnicas.

En este gráfico podemos observar que el objeto del tipo AntiPracticas.frmMemoria, que es nuestra ventana, y sus referenciados consumen 836 Kb. Aunque la variable que apunta a esta estructura es de solo 368 bytes.

AntiPracticas.frmMemoria tiene cuatro campos privados que apuntan a:

  • un DataSet Tipado (DsEmployees.GetEmployeesDataTable): 313 Kb
  • un DataSet (Data.DataSet): 185 Kb
  • un DataTable (Data.DataTable ): 184 Kb
  • una colección genérica de objetos del tipo Employee (Generic.List<T>): 138 Kb

Aquí mismo podemos apreciar que el DataSetTipado es la estructura más costosa en cuanto a consumo de memoria. Que no hay casi diferencia entre un DataSet y un DataTable, y que la colección de objetos es la más barata. No está demás destacar que todas las estructuras contienen “los mismos datos”.

La misma información podemos verla en JetBrains DotTrace.


Observen la columna “Held Memory, bytes”, que es la memoria referenciada por cada instancia:

  • _dsEmployees (Walzer.Antipracticas.DsEmployees.GetEmployeesDataTable)
  • _ds (System.Data.DataSet)
  • _dt (System.Data.DataTable)
  • _employees (System.Collection.Generic.List<Employee>)

Estructura de Objetos en memoria

Comparemos en las siguientes dos capturas la complejidad de una y otra estructura, las cuales almacenan los mismos datos, de solo lectura en nuestro caso.

La primera figura nos muestra la lista genérica _employees, la cual está implementada internamente por un vector de _items, que contiene un conjunto de objetos Employee, la cual contiene finalmente los datos.

Observemos ahora la estructura de un DataSet tipificado, y el camino para llegar al dato final.

La estructura es mucho más compleja, pero no perdamos de vista que un DataSet fue diseñado con la premisa de propósito general, y mucho de su funcionalidad es útil. Debemos usar nuestro criterio a la hora de decidir que es mejor para nuestro sistema.

Inspeccionado contenido de las variables

Usemos ahora la herramienta .NET Memory Profiler para ver el contenido de un objeto del tipo Employee. Esta figura nos muestra las referencias a la que hace este objeto, que son System.String.

Pero, ¿dónde está el campo _idEmployee que es del tipo int o _birthDate que es de tipo DateTime? Bien, estos están contenidos en el mismo espacio de memoria que el objeto del tipo Employee ya que son tipos básicos, int y ulong respectivamente. En cambio System.String es una referencia al espacio de memoria donde está guardada la cadena de caracteres. La solapa Field Values nos muestra el contenido de la instancia #12,729 del objeto del tipo Employee. Además de esta información podemos apreciar, cuales son los caminos al Root de este objeto, y cuál fue el Call Stack que instanció este objeto en memoria.

Cantidad de Objetos referenciados

Un dato que no es menor aquí es el que nos muestra la columna “Held Objects”. Esta nos dice cuantos objetos son referenciados en toda la estructura en memoria.

En este caso la cantidad de filas en memoria para cada estructura es de 10, valor que se asemeja más a la realidad, ya que no es buena práctica pasar todas las filas del resultado entre capas, sino usar técnicas de paginación.

  • _dsEmployees (Walzer.Antipracticas.DsEmployees.GetEmployeesDataTable): 320
  • _ds (System.Data.DataSet): 213
  • _dt (System.Data.DataTable): 206
  • _employees (System.Collection.Generic.List<Employee>): 122

Más allá de la cantidad de memoria en bytes, la cantidad de objetos referenciados nos da una idea del trabajó que tendrá el Garbage Collector al momento de deshacerse de estos objetos. Cuantas más referencias en memoria, más recursos consumidos por este algoritmo.

Comparación de resultados

Veamos una serie de gráficos que resumen las lecturas realizadas. Tomé lecturas de 290 registros, 10 registros (que es el típico caso del tamaño de una página cuando se realiza paginación) y 1 registro.

 

Bytes en Memoria 290 reg 10 reg 1 reg
DataSet 189112 19028 14856
DataTable 188848 18764 14588
List<> 141774 4790 514
DataSet Tipado (wizard) 319478 33114 28902

 Fig1: Tabla Comparativa de Bytes en Memoria

Fig2: Bytes en memoria para 290 registros

Fig3: Bytes en memoria de 10 y 1 registro.

 

Referencias 290 reg 10 reg 1 reg
DataSet 3581 213 105
DataTable 3574 206 98
List<> 3477 122 13
DataSet Tipado (wizard) 3788 300 192

Fig4: Tabla Comparativa de Objetos Referenciados

Fig5: Instancias referenciadas para 290 registros

Fig6: Instancias referenciadas para 10 y 1 registro

Estos gráficos muestran claramente que la técnica más económica es pasar entre capas una lista genérica de un tipo específico. Y que la ferretería utilizada por las estructuras del tipo DataSet se puede despreciar cuanto mayor es el volumen que contienen.

Conclusión

Hemos comprobado que el uso correcto de las técnicas de acceso a datos en ADO.NET nos permite lograr un mayor rendimiento en nuestras aplicaciones. También hemos aprendido algo de cómo funciona internamente ADO.NET, y como son las estructuras en memoria y el uso que se hace de ellas.

Siempre es bueno conocer cómo funcionan internamente los frameworks que utilizamos para construir nuestras aplicaciones para poner en la balanza, facilidad y agilidad de uso contra rendimiento y consumo de recursos.

Como saber si un assembly esta compilado en modo Debug o Release [Ampliado]

En estos días me topé con la necesidad de conocer cual era el modo en el cual fue compilado cierto assembly. La primer herramienta a la que acudí me dió una respuesta: Reflector de Lutz Roeder.

El compilador agrega el attributo DebuggableAttribute como información del assembly. Los modos de debug fijados por el compilador dependerán de los parámetros que le pasemos al compilador por línea de comandos o el tipo de compilación que definamos en el proyecto de Visual Studio.

En el caso de haber compilado con Visual Studio 2005 ó 2008 en modo Release, nos encontraremos con la siguiente línea:

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]

En caso de haber compilado en modo Debug:

[assembly: Debuggable(DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.EnableEditAndContinue | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.Default)]

Les recomiendo la lectura de la documnetación correspondiente a los valores del enumerado System.Diagnostics.DebuggableAttributes.DebuggingModes

A continuación un ejemplo de código que revela el modo de compilación:
La propiedad DebuggableAttributes.IsJITTrackingEnabled controla si en tiempo de ejecución el CLR hace un seguimiento de la información que es importante para el depurador mientras se genera el código. Esta información contribuye a que el depurador mejore la depuración.

static

 

 

void Main(string[] args)
{
     string filePath = args[0];
     Assembly asm = Assembly.LoadFile(Path.GetFullPath(filePath));
     foreach (Attribute att in asm.GetCustomAttributes(false))
     {
          if (att is System.Diagnostics.DebuggableAttribute)
          Console.WriteLine(“Modo Debug: {0}”,((DebuggableAttribute

)att).IsJITTrackingEnabled);
     }
}

MSDN Briefing: Lanzamiento Visual Studio 2008, SQL Server 2008, Windows 2008

Este año Technet y MSDN organizan un lanzamiento en línea para público de habla hispana el cual tendrá lugar el día 29 de Abril.

Ya puedes ingresar si quieres y ver el material de lectura previa.

El día del evento, además de poder ver las presentaciones multimedia, podrás interactuar en un chat con los expertos de las diversas tecnologías presentadas.

Yo estaré ahí hablando de Herramientas para Asegurar la Calidad utilizando Visual Studio Team System 2008. Lectura previa

Cualquier duda que pueda surgir de esta presentación podrán charlar conmigo el mismo día 29 de Abril:

Descubre las novedades de Visual Studio 2008 y el .net Framework en desarrollo Web (Silverlight 2.0, ASP.NET 3.5, ASP.NET AJAX, MVC, IIS 7.0)
México, Costa Rica:
10 am (GMT -6)
Perú, Colombia, Panamá:
11 am (GMT -5)
Venezuela:
11.30 am (GMT -4.30)
Chile, Rep. Dominicana y Miami:
12 pm (GMT -4)
Argentina, Uruguay, Paraguay y Bolivia:
13 hs. (GMT -3)

Los espero