LDLS: .NET y mas [2008abr21]

ASP.NET

.NET

Silverlight y Sharepoint

SQL Server 2008

Varios

Humor

Saludos,

MCTS 70-561: Consultando data

Construyendo comandos para consultas a base de datos, es un tema que siempre despierta interés y siempre esta un mejoramiento continuo. Muchos framework han tratado han tratado de sobreponerse, desde el antes usado SqlHelper o ahora la ultima versión del DAAB, pero la fin al cabo todas usan los objetos básicos de ADO.NET. En muchos casos cada casa de software siempre necesita acceso a datos personalizados, lo que motiva a la creación de su propio framework de acceso a datos, y nuevamente llegamos a usar las clases básicas de acceso a datos de ADO.NET. Con SqlCommand, podemos crear todo tipo de consultas para una base SQL-Server, desde crear nuestros propios queries, hasta llamar a StoreProcedures, y gracias a SqlParameter podemos parametrizar nuestras consultas, además que siempre vamos a encontrar soporte en ADO.NET para las nuevas features de SQL, por ejemplo en SQL 2005 se agrego una columna para el soporte de Xml, y en ADO.NET también se cree el tipo de dato SqlXml para soportar a esta nueva columna, los comandos también me permiten retornar valores. Y si están usando DataSets con SqlDataAdapter, pueden echarle un vistazo a SqlCommandBuilder, si usan listas para transmitir data entre las capas, no es necesario.

Hay muchos mitos en ADO.NET, de cual es la mejor DataReader vs DataSet, la serie de Carlos Walzer es excelente: Cazando mitos con ADO.NET.

Recuerden que en VS2005 (o superior), a evolucionado el DataAdatper, para crear una clase llamada TableAdatper, en aplicaciones web nunca los he usado, creo su mayor aplicación más va por el lado de Aplicaciones Windows.

Queries asíncronos, en ADO.NET 2.0, se mejor el soporte para este tipo de escenarios. En este artículo: Asynchronous Command Execution in ADO.NET 2.0, Pablo Castro nos comenta sobre los nuevos métodos que nos permitirán hacer tareas asíncronas con ADO.NET 2.0. En este ejemplo: Asynchronous command execution in .NET 2.0, podemos ver rápidamente como podemos usar los nuevos métodos de la clase SqlCommand, para el soporte de tareas asíncronas, pero notar en esta FAQ: ADO.NET 2.0 Asynchronous Command Execution (ASYNC) FAQ, que el uso de ASYNC=TRUE, en la cadena de conexión sólo para tareas asíncronas, más no para tareas comunes. Aquí otros artículos: Executing Database Commands Asynchronously with ADO.NET 2.0, y Asynchronous Data Access using Callback Model, también revisar los detalles detrás de los comandos asíncronos: Shared Memory Provider and Async Commands.

Hay algunos tipos que necesitan un cuidado especial, como por ejemplo el manejo de archivos, en file-system o en la base de datos?, esto básicamente dependerá de los requerimientos que se tenga, dependiendo de tu modelo, las tareas de backup de la información tendrá que ser de la base de datos, y de las carpetas de archivos, tendrás que tener doble manejo de seguridad, entre otras cosas a tener en cuenta. Aquí un ejemplo: Utilize ADO.NET and C# to work with BLOB data, mas de la discusión:file system vs Database images. Con SQL Server 2008, se propone otro modelo para almacenar archivos en la base de datos pero con un híbrido con FileSystem, leer el siguiente artículo: Getting Traction with SQL Server 2008 Filestream. Otro tipo de dato a manejar es el tipo de dato espacial que se liberará en SQL Server 2008: Demystifying Spatial Support in SQL Server 2008. Otra de las nuevas en SQL Server 2008, es el soporte del tipo tabla como parámetro, por su parte ADO.NET 3.5, ya soporta esta feature, aunque aún no se tenga la versión final de SQL Server 2008: SQL Server 2008: Table Value Parameters (TVP).

De LINQ, hay mucho en la red. Un artículo El proyecto LINQ, los post de ScottGu’s de LINQ, incluido una serie que tiene de LINQ to SQL. La presentación del gran Octavio Hernández, en el TechDays 2008: presentación(pptx) y demos. Y esta aproximación de LINQ To SQL en una Aplicación en Capas, teniendo en cuenta los comentarios que se han hecho.

Saludos,

LINQ to SQL y una Aplicacion en capas….

Vayamos directo al grano, vamos a tomar a AdventureWorks como ejemplo.

Veamos a Linq to SQL:

En la forma simple de usarlo agregamos un diagrama Linq to SQL Class, a nuestra aplicación:

 http://galeon.com/solocodigo/images/blog/2008/03Mar/22_Linq_Sql.jpg

Creamos una consulta simple y básica:

   1: static void Main(string[] args)
   2:     {
   3:  
   4:       dcAdventureWorksDataContext dbAW = 
   5:         new dcAdventureWorksDataContext();
   6:  
   7:       colGen.IEnumerable<Product> enumProd = 
   8:               from p in dbAW.Products
   9:               where p.Name.StartsWith("W")
  10:               select p;
  11:  
  12:       foreach (Product prod in enumProd.ToList<Product>())
  13:       {
  14:         Console.WriteLine("Prod: {0} -> {1}", 
  15:                   prod.Name, prod.ProductNumber);
  16:       }
  17:       Console.ReadLine();
  18:     }

Este query muestra todos la lista de productos cuyo nombre empiece con W.

Veamos la magia de Linq to SQL:

  • Para usar el diagrama que hemos agregado debemos usar una instancia del mismo.
  • Hay muchos ejemplos donde sólo se usa “var” para declarar a la variable que contendrá los resultados, y esto podría ser un útil para construir listas con nuevas columnas, por ejemplo columnas agregadas, claro eso en el select. Pero cuando yo agrego un diagrama LinqtoSQL, este también crea una clase por cada entidad en el diagrama:
   1: [Table(Name="Production.Product")]
   2: public partial class Product : INotifyPropertyChanging, INotifyPropertyChanged
   3: {
  • Es por eso que cuando tu pones “p.”, te salen todas las columnas disponibles de la tabla mapeada, y no es que este consultado a la base de datos para saber que columnas tiene, si por ejemplo se agrega una columna a la tabla en la base de datos, tenemos que volver a mapear, o agregar la propiedad respectiva a la clase existente. Y es así que puede usar un IEnumerable<Product>, para almacenar los resultados, recuerden que la clase Product, yo no la he creado, a sido generada en la generación del diagrama.
  • Miren la condición que estamos agregando, creo que la principal ventaja, es que tenemos todo el soporte de .NET, para las condiciones y operaciones que necesitemos realizar en la consulta.
  • Como pueden ve,r fácilmente puede convertir IEnumerable<> a un List<>.
  • Pero que pasa en SQL?, miremos a SQL Profiler:
   1: exec sp_executesql N'SELECT [t0].[ProductID], [t0].[Name], [t0].[ProductNumber], 
   2:    [t0].[MakeFlag], [t0].[FinishedGoodsFlag], [t0].[Color], [t0].[SafetyStockLevel], 
   3:    [t0].[ReorderPoint], [t0].[StandardCost], [t0].[ListPrice], [t0].[Size], 
   4:    [t0].[SizeUnitMeasureCode], [t0].[WeightUnitMeasureCode], [t0].[Weight], 
   5:    [t0].[DaysToManufacture], [t0].[ProductLine], [t0].[Class], [t0].[Style], 
   6:    [t0].[ProductSubcategoryID], [t0].[ProductModelID], [t0].[SellStartDate], 
   7:    [t0].[SellEndDate], [t0].[DiscontinuedDate], [t0].[rowguid], [t0].[ModifiedDate]
   8: FROM [Production].[Product] AS [t0]
   9: WHERE [t0].[Name] LIKE @p0',N'@p0 nvarchar(2)',@p0=N'W%'
  • Primero que notamos?, que es un query parametrizado, es decir que si el usuario quiere hacer SQL Injection, sería algo como esto:
   1: exec sp_executesql N'SELECT [t0].[ProductID], [t0].[Name], [t0].[ProductNumber], 
   2:    [t0].[MakeFlag], .....
   3: FROM [Production].[Product] AS [t0]
   4: WHERE [t0].[Name] LIKE @p0',N'@p0 nvarchar(10)',@p0=N'W'' OR 1=1%'

Ya hablamos de lo bonito, pero….

  • Si tenemos una aplicación en capas, este modelo no nos ayudaría mucho. Ya que tenemos una capa especial para consulta de acceso a datos.
  • Y si quiero usar Linq to SQL, en capas?… a ver veamos

Ahora hagamos un custom Linq to SQL, para usarlo en una aplicación en Capas:

Digamos que yo cuento con mis entidades:

   1: public class miProducto
   2:  {
   3:  
   4:    private Int32 productIDField;
   5:    private String nameField;
   6:    private String productNumberField;
   7:    private Decimal listPriceField;
   8:  
   9:    public Int32 ProductID
  10:    {
  11:      get { return productIDField; }
  12:      set { productIDField = value; }
  13:    }    
  14:    public String Name
  15:    {
  16:      get { return nameField; }
  17:      set { nameField = value; }
  18:    }    
  19:    public String ProductNumber
  20:    {
  21:      get { return productNumberField; }
  22:      set { productNumberField = value; }
  23:    }    
  24:    public Decimal ListPrice
  25:    {
  26:      get { return listPriceField; }
  27:      set { listPriceField = value; }
  28:    }
  29:  }

A nuestra entidad tenemos que agregarle los siguientes atributos:

   1: [Table(Name = "Production.Product")]
   2:   public class miProducto
   3:   {
   4:  
   5:     private Int32 productIDField;
   6:     private String nameField;
   7:     private String productNumberField;
   8:     private Decimal listPriceField;
   9:  
  10:     [Column]
  11:     public Int32 ProductID
  12:     {
  13:       get { return productIDField; }
  14:       set { productIDField = value; }
  15:     }
  16:     [Column]
  17:     public String Name
  18:     {
  19:       get { return nameField; }
  20:       set { nameField = value; }
  21:     }
  22:     [Column]
  23:     public String ProductNumber
  24:     {
  25:       get { return productNumberField; }
  26:       set { productNumberField = value; }
  27:     }
  28:     [Column]
  29:     public Decimal ListPrice
  30:     {
  31:       get { return listPriceField; }
  32:       set { listPriceField = value; }
  33:     }
  34:   }

Con estos atributos estoy habilitando para que mi clase pueda ser usada con Linq to SQL, notar que en las propiedades de los atributos se puede determinar con que tabla voy a mapear, y podría determinar con que columna de la tabla voy a mapear una determinanda propiedad.

En la capa de acceso, mi clase sería así:

   1: public class ProductRepository
   2: {
   3:  
   4:   private DataContext dbAW;
   5:   public ProductRepository(String connectionString)
   6:   {
   7:     dbAW = new DataContext(connectionString);
   8:   }
   9:  
  10:   public IEnumerable<miProducto> getManyByNameStart(String start)
  11:   {
  12:     IEnumerable<miProducto> enumProd;   
  13:     
  14:       enumProd = from prod in dbAW.GetTable<miProducto>()
  15:                  where prod.Name.StartsWith(start)
  16:                  select prod;
  17:  
  18:       return enumProd;
  19:   }
  20: }

Notar que el connectionString lo estamos pasando de la capa que la llame, y es aquí donde iría los queries, Linq to SQL.

Y en la capa de negocios sería así:

   1: public class BLProduct
   2: {
   3:  
   4:   private ProductRepository repProduct;
   5:  
   6:   public BLProduct(String connectionString)
   7:   {
   8:     repProduct = new ProductRepository(connectionString);
   9:   }
  10:  
  11:   public List<miProducto> getManyByNameStart(String start)
  12:   {
  13:     return repProduct.getManyByNameStart(start).ToList<miProducto>();
  14:   }    
  15: }

Sólo un maquillado para la llamada de la capa de presentación.

Y por último el FrontEnd, lo podría usar así:

   1: static void Main(string[] args)
   2:  {
   3:  
   4:    String cnAW = 
   5:      ConfigurationManager.ConnectionStrings["dbAW"].ConnectionString;
   6:    BLProduct blProd = new BLProduct(cnAW);
   7:  
   8:    List<miProducto> lstProd = blProd.getManyByNameStart("L");
   9:    foreach (miProducto prod in lstProd)
  10:    {
  11:      Console.WriteLine("Prod: {0} -> {1}", 
  12:                prod.Name, prod.ProductNumber);
  13:    }
  14:  
  15:    Console.ReadLine();
  16:  }

Algunas conclusiones finales:

  • Esta es una de las formas como podría encajar Linq to SQL, en un modelo en capas. Si de todas maneras estas armando tus queries en la capa de acceso, pues Linq to SQL te puede caer a pelo de gato :D.
  • Ahora, si tienes un modelo donde sólo usas StoreProcedures, Linq to SQL, no va encajar bien en tu escenario.
  • Y noten que el dilema no sería Linq to SQL (en la capa de acceso a datos) vs. StoreProcedures, la verdadera discusión sería “Queries en ADO.NET o usar Store Procedures”…. por cierto aquí se armo una buena discusión: ventajas de usar Procedimientos Almacendos?.

Descargar ejemplo: Linq to SQL en una Aplicacion en Capas.

Nos vemos, y ya viene el post sobre Linq to Objects :D…

Saludos,