Nivel intermedio

Patrones de Comportamiento (IV): Patrón Strategy


Objetivo:

“Definir una familia de algoritmos, encapsular cada uno de ellos y hacerlos intercambiables. Strategy permite cambiar el algoritmo independientemente de los clientes que lo utilicen”.

Design Patterns: Elements of Reusable Object-Oriented Software

En el artículo anterior veíamos cómo un patrón de diseño puede ayudarnos a encapsular distintos algoritmos a partir de una “plantilla” cuyos hijos se encargaban de especializar. El patrón Strategy, por su parte, no bucea en los detalles, sino que va un paso más allá: encapsulará un algoritmo completo ignorando los detalles de su implementación, permitiendo intercambiarlo en tiempo de ejecución para permitir actuar a la clase cliente con un comportamiento distinto.

El nombre de este patrón evoca la posibilidad de realizar un cambio de estrategia en tiempo de ejecución sustituyendo un objeto que se encargará de implementarla. No nos preocupará el “cómo”. De hecho, ni siquiera nos importará “el qué”: la clase que actúa como interfaz del patrón únicamente tendrá que exponer el método o métodos que deberá invocar el cliente.

(más…)

Inversión de Control e Inyección de Dependencias


English version here

Los conceptos de Inversión de Control e Inyección de Dependencias no son nuevos, sino que se remontan hasta finales de la década de los 80. Sin embargo, estos conceptos han comenzado a popularizarse debido a la estrecha relación que mantienen con la aparición de Frameworks como Spring en Java o Unity en .NET.

Inversión de Control

El concepto de Inversión de Control fue acuñado originalmente por Martin Fowler, diseñador del patrón MVVM (Model View View-Model). Fowler definió el concepto de forma informal denominándolo como el Principio de Hollywood, en el que, tras una audición, se le decía al actor la famosa frase de No nos llames, nosotros te llamaremos.

El principio establece una diferenciación entre el concepto de biblioteca y framework, definiendo el primero como un simple conjunto de clases, métodos y funciones que son invocadas por el flujo del programa y que posteriormente devuelven el control a éste (control normal) y el segundo como un diseño más abstracto y elaborado que se encargará, en algún momento, de invocar el código que el programador se encargue de codificar (inversión de control).

El ejemplo expuesto por Fowler no puede ser más sencillo: un control normal sería un simple programa secuencial de consola en el que el programa va solicitando datos al usuario y realizando operaciones (cálculos, visualización por pantalla) al recibir las respuestas. Un programa que aplica una inversión de control, sin embargo, se podría representar como una ventana compuesta por cajas de texto, etiquetas y botones. El Framework, en este caso, expondría un bucle de espera que detectaría la emisión de eventos, como la pulsación de un botón, momento en el cual se ejecutaría el código de usuario. El Framework invocará nuestro código en lugar de realizar la operación contraria.

(más…)

Simular teclado y ratón en aplicaciones de terceros


English version here.

La simulación de una pulsación de una combinación de teclas o de una pulsación del ratón en una aplicación propia es relativamente sencillo: basta con hacer uso del método SendKeys para que nuestra aplicación reciba datos desde teclado y la invocación explícita del método adecuado que esté asociado al evento del ratón para simular un click del ratón.

Sin embargo, si deseamos crear un programa cuya misión consista en realizar estas operaciones en un programa de terceros, deberemos hacer uso de código no administrado.

Código administrado y no administrado.

El código administrado es aquel que se ejecuta bajo el control del CLR (Common Language Runtime), es decir, aquel código escrito en .NET que es compilado a un código intermedio MSIL o CIL (dependiendo de la versión del Framework) y que en tiempo de ejecución es transformado en código nativo. Este es el proceso natural de la programación .NET, con el que supongo que todos o casi todos estamos familiarizados.

El código no administrado, en cambio, es aquel código ajeno a este ciclo de vida, tal como el incluido en componentes COM/COM+, C++, ActiveX o la propia API de Windows (que es donde centraremos el objetivo de este artículo).

Windows expone su API a través de un conjunto de bibliotecas de enlace dinámico (dll) tales como user32.dll (permite manejar ventanas y sus eventos), shell32.dll (procesos), winspool.drv (impresión)… Para hacer uso del teclado y el ratón en aplicaciones de terceros nos centraremos en la biblioteca user32.dll, que como podemos imaginar, se tratará como código no administrado.

(más…)

Bases de datos portables (II): Combinando Entity Framework con SQLite


English version here.

En el artículo anterior vimos que es posible hacer uso de ADO.NET para crear una aplicación que utilice SQLite como base de datos. De este modo, con una base de datos local y ampliamente utilizada es posible aumentar radicalmente la portabilidad de nuestra aplicación si ésta no requiere la potencia de una base de datos “tradicional”. Sin embargo, la utilización de ADO.NET de forma directa implica dejar de lado todos los avances que Microsoft ha implementado a lo largo de estos últimos años en materia de mapeo objeto-relacional.

Ya que hemos aprendido a utilizar Entity Framework, ¿por qué no hacer uso de él con SQLite y dejarnos de “picar” código SQL de forma manual? A continuación veremos cómo podemos lograrlo.

(más…)

Bases de datos portables: Utilizando SQLite en una aplicación .NET


English version here.

La mayor parte de las aplicaciones que desarrollamos en .NET de modo profesional suelen implicar la existencia de una base de datos, por norma SQL Server, Oracle, DB2 o MySQL. Sin embargo, algunas veces, pese a necesitar soporte de base de datos, resulta innecesario el montaje y mantenimiento de un gestor de base de datos, bien por portabilidad, bien por licencia, bien porque el volumen de datos y la complejidad de las tablas implicadas no sea muy complejo…

Vimos en artículos relacionados con Android que cada aplicación hace uso de una base de datos SQLite a nivel local. ¿Por qué no aplicar la misma filosofía a una aplicación .NET. Pues bien, es posible encapsular la base de datos en un fichero .db dentro de un directorio local del mismo modo que lo hacen las aplicaciones en Android. Veremos a continuación cómo instalar SQLite para .NET y realizar una pequeña aplicación que realice operaciones CRUD (Create, Retrieve, Update, Delete) sobre una entidad. A partir de ahí, será extrapolable a entidades más complejas.

(más…)

Tutorial de LINQ y Entity Framework


Con el artículo anterior finalizamos con la introducción a LINQ y Entity Framework. A lo largo de esta serie de artículos hemos aprendido desde qué es LINQ hasta su aplicación a objetos, ficheros XML, bases de datos SQL Server y, por extensión, a cualquier otra fuente de datos.

Esta entrada servirá de recopilatorio y puerta de entrada a todo lo que hemos visto acerca de estas tecnologías a lo largo de los últimos días.

  1. Introducción
  2. Construcción de ficheros XML mediante LINQ
  3. Sentencias en LINQ
    1. Selecciones simples. Tipado implícito y tipos anónimos.
    2. Joins y multiselects
    3. Agrupaciones (group by)
    4. Particionado. Delegados y expresiones lambda.
    5. Funciones de agregación (count, sum, average, max, min)
    6. Ordenación (order by)
    7. Operaciones sobre conjuntos
    8. Cuantificadores, generadores y conversores
  4. LINQ to SQL
    1. Mapeo objeto-relacional
    2. Relaciones
    3. Consultas compiladas. Ejecución nativa de SQL. Procedimientos almacenados
    4. Modificación de datos (insert, update, delete)
  5. Entity Framework
    1. Creación de un Entity Model
    2. ObjectContext y Entity SQL
    3. Select, Insert, Update, Delete
    4. Mapeo mediante procedimientos almacenados
    5. Enlazado y desenlazado de entidades. Estados
    6. Entity Model y Data Services

Entity Framework (VI): Webservices


Hasta ahora hemos visto el funcionamiento de LINQ y Entity Framework. La siguiente serie de artículos estarán orientados hacia los servicios web, por lo que haremos una pequeña introducción aplicando los conocimientos que hemos obtenido hasta el momento.

Lo primero que deberemos aclarar es el propio concepto de servicio web. Ya vimos en artículos anteriores de qué se tratan, cómo se crean y cómo se consumen estas pequeñas aplicaciones cuyo objetivo es el intercambio de información entre distintas plataformas y lenguajes de modo estándar.

Para crear un nuevo servicio web, crearemos un nuevo proyecto web de tipo ASP.NET Empty Web Application y le asociaremos un nuevo nombre. Los servicios web reciben ese nombre porque operan sobre el protocolo HTTP, así que este será nuestro punto de partida.

(más…)

Entity Framework (V): Enlazado y desenlazado de entidades. Estados


Una vez que tenemos claro que un DbContext es el encargado de velar por la integridad en el esquema objeto-relacional, ¿qué ocurriría si manejamos objetos que, por alguna razón, escapan a su control? Nos referimos, por ejemplo, al envío de los datos de un cliente a través de un formulario web y ponernos a la espera de que el usuario los modifique en otra página distinta. Este escenario implicaría, con toda seguridad, que el DbContext en el que se recuperó el objeto sea distinto al DbContext que se encargará de modificarlo.

Por lo tanto, tendremos dos opciones a la hora de tratar este problema:

  • Volver a recuperar el objeto completo, copiar todos los datos del objeto enviado por el usuario al objeto que acabamos de recuperar y guardar los cambios.
  • Enlazar directamente el objeto al DbContext y decirle que actualice los cambios.

Para la primera opción no es necesaria mucha explicación, puesto que se trata de una modificación normal y corriente. Para la segunda opción habrá que trabajar un poco más, pero se considera una opción más correcta. Además, el proceso dependerá de si estamos haciendo uso de un ObjectContext (versión anterior a 4.1) o un DbContext (versión posterior a ésta).

(más…)

Entity Framework (IV): Mapeo mediante procedimientos almacenados


En el anterior artículo aprendimos cómo mapear un procedimiento almacenado en nuestro Entity Model y hacer uso de él mediante una llamada explícita para eliminar en cascada todos los elementos asociados tanto a los clientes como a los pedidos.

Entity Framework proporciona la opción de personalizar los métodos de inserción, eliminación y actualización a través de procedimientos almacenados, haciendo que éstos se realicen mediante la sintaxis natural de Entity Framework pero permitiéndonos a nosotros indicar, a través de procedimientos almacenados, la forma de hacerlo.

Es importante saber que Entity Framework tiene una política de todo o nada para hacer uso de esta característica, es decir: si se decide utilizar un procedimiento almacenado para realizar las eliminaciones, será obligatorio definir también los métodos de inserción y actualización.

Ya definimos antes un procedimiento para las eliminaciones, llamado sp_Cliente_DeleteCascade. Codificaremos ahora un procedimiento almacenado para realizar las inserciones y otro para las actualizaciones.

El procedimiento de inserción recibirá como parámetros los componentes del registro salvo el identificador, que será generado automáticamente. Además, devolverá como valor de retorno la llamada al método SCOPE_IDENTITY(), que almacenará el valor del IdCliente generado para el registro insertado. A este valor le daremos un nombre para poder identificarlo en nuestro mapeo, por ejemplo, “id”.

(más…)

Entity Framework (III): Select, Insert, Update, Delete


Pese a que LINQ to Entities es bastante parecido a LINQ to SQL, a la hora de trabajar con ambas tecnologías es necesario conocer las diferencias más importantes a nivel práctico (dejaremos los fundamentos teóricos a un lado). Ambas tecnologías tienden a la convergencia, puesto que Microsoft está intentando coger lo mejor de cada una de ellas y adaptarlo a ambos mundos. Sin embargo, siguen existiendo diferencias importantes, especialmente en las primeras versiones de LINQ to Entities.

Select. Carga diferida explícita.

Las primeras versiones de Entity Framework contenían ciertas carencias que, con posteriores actualizaciones, han sido solventadas y corregidas. Una de las carencias más importantes se correspondería con la imposibilidad de las versiones anteriores a 4.1 de realizar una carga automática de los elementos referenciados por un objeto. Es decir, si tenemos el siguiente código:


            // Instanciamos el contexto
            var contexto = new testdbEntities();

            // Lanzamos una consulta
            var clientes = from cliente in contexto.Clientes
                           select cliente;

            // Recorremos los clientes
            foreach (Cliente cliente in clientes)
            {
                Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
                    cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));

                // Recorremos los pedidos
                foreach (Pedido pedido in cliente.Pedidos)
                {
                    Console.WriteLine(string.Format("\tPEDIDO: {0}\tFECHA: {1}",
                        pedido.IdPedido, pedido.FechaPedido));

                    // Recorremos las líneas de pedido
                    foreach (LineaPedido linea in pedido.LineasPedido)
                    {
                        Console.WriteLine(string.Format("\t\tPRODUCTO: {0}\tCANTIDAD: {1}\tTOTAL: {2}",
                            linea.Producto.Descripcion, linea.Cantidad, (linea.Producto.Precio*linea.Cantidad)));
                    }
                }
                Console.WriteLine(" -----------------------------------------\n");
            }

 

(más…)

Entity Framework (II): ObjectContext y Entity SQL


Una de las mayores ventajas de Entity Framework es que es una tecnología agnóstica respecto a la base de datos que tiene por debajo. Con ADO.NET era necesario utilizar clases específicas para cada base de datos (SqlCommand para SQLServer, OracleCommand para Oracle, etc.). Sin embargo, Entity Framework no se casa con nadie: hace uso de elementos genéricos (EntityConnection, EntityCommand, etc.) que genera instrucciones en un lenguaje intermedio denominado Entity SQL, que es muy similar al lenguaje SQL estándar.

En última instancia, el ADO.NET de toda la vida estará trabajando por debajo, pero Entity Framework establece una capa de abstracción superior que evita la necesidad de atarnos a una fuente de datos concreta. No obstante, es posible descender un poco en el nivel de abstracción y, en lugar de hacer uso de LINQ to Entities tal y como hicimos en el artículo anterior, lanzar directamente consultas sobre el ObjectContext usando para ello Entity SQL.

Dependiendo de la versión de Entity Framework que se esté usando, esto se realizará de un modo u otro, ya que la versión 4.1 introdujo como novedad la utilización del DbContext por defecto, envolviendo el ObjectContext dentro de él.

Por lo tanto, si queremos acceder al ObjectContext del DbContext que el diseñador habrá generado por nosotros, será necesario realizar un casting explícito a IObjectContextAdapter y acceder a su propiedad ObjectContext, tal y como mostramos en el siguiente ejemplo, en el que se ejecuta una sentencia Entity SQL en la que se recuperan todos los clientes de la tabla Clientes.


            // Instanciamos el DbContext
            var dbContext = new testdbEntities();

            // Extraemos el ObjectContext del DbContext (a partir de Entity Framework 4.1)
            var objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;

            // Ejecutamos una sentencia de Entity SQL que recupere todos los clientes
            string sqlQuery = "SELECT VALUE c FROM Clientes AS c";
            var clientes = new ObjectQuery<Cliente>(sqlQuery, objectContext);

            foreach (Cliente cliente in clientes)
            {
                Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tAÑO NAC: {2}",
                    cliente.IdCliente, cliente.Nombre, cliente.FechaNacimiento.Year));
            }

El nombre de la tabla Clientes está en plural porque es el nombre que recibe la propiedad en la clase de contexto generada por el editor, no porque sea el nombre de la tabla de la base de datos. Recordemos que, Entity SQL realiza consultas sobre entidades, es decir, objetos, no sobre la base de datos.

Nuevamente, comprobamos que la consulta recupera los datos correctamente.

Entity Framework (I): Creación de un Entity Model


Entity Framework es la evolución natural de ADO.NET hacia el tratamiento de los datos almacenados en una base de datos relacional a través del paradigma objetual. Por lo tanto, se trata, al igual que LINQ to SQL, de un mapper objeto-relacional que es capaz de abstraer las tablas y columnas de una base de datos y tratarlas como objetos u entidades y relaciones.

Una entidad, por tanto, no es más que un objeto persistente, que cumple las condiciones de unicidad e identidad.

Añadiendo un Entity Data Model

Lo primero que deberemos hacer una vez que hayamos creado nuestro proyecto (por ejemplo, de consola) será añadir un nuevo Entity Data Model. Para ello haremos click derecho sobre nuestro proyecto y seleccionaremos la opción Add > New Item…

Una vez en el diálogo de selección, navegamos hasta la sección Data y seleccionamos el elemento ADO.NET Entity Data Model, al que le daremos un nombre identificativo.

(más…)

LINQ to SQL (III): Consultas compiladas, ejecución SQL y procedimientos almacenados


Compilación de consultas

En ocasiones podemos encontrarnos con consultas LINQ cuyo coste computacional puede ser alto, haciendo que el sistema se sobrecargue. LINQ to SQL proporciona algunas herramientas que mitigan, dentro de lo posible, el impacto de este tipo de operaciones sobre el rendimiento. Una de ellas es la compilación de consultas.

Es posible precompilar una consulta LINQ, de modo que durante su primera invocación realice un plan de ejecución que pueda mantenerse cacheado para ser reutilizado posteriormente. Así, la siguiente consulta:


            int idCliente = 2;

            var consulta = (from cliente in dbContext.Cliente
                            where (cliente.IdCliente == idCliente)
                            select cliente).FirstOrDefault();

Podría ser compilada y almacenada en una variable, que podrá ser reutilizada posteriormente como si fuese un método normal y corriente. Vemos, además, que la consulta puede parametrizarse, de modo que es posible asignarle cierto grado de generalización.


            var getClientePorId = CompiledQuery.Compile(
                (Testdb contexto, int id) =>
                    (from cliente in contexto.Cliente
                     where (cliente.IdCliente == id)
                     select cliente).FirstOrDefault()
                );

(más…)

Sentencias en LINQ (VIII): Cuantificadores, generadores y conversores


Hasta ahora hemos visto las operaciones más comunes que pueden realizarse mediante LINQ. Por lo tanto, sólo nos queda echarle un vistazo a un subconjunto de sentencias de carácter más auxiliar, tales como la cuantificación, generación y conversión de elementos.

Any

El método Any toma como argumento un delegado o expresión lambda que tomará como parámetro un elemento de la lista y devolverá un valor booleano que indicará si al menos un elemento de la lista cumple la condición especificada.

Así, si quisiéramos saber si se ha vendido al menos un producto cuyo ID sea 3, podríamos optar por, bien crear un método que realice la comparación y que será pasado como delegado, bien utilizar una expresión lambda que realice la misma operación.


        // OPCIÓN 1: Usamos un delegado de una función que toma un elemento del listado como parámetro
        // y devuelve un booleano que devuelve el resultado de la comparación
        public bool PedidoConProductoIgualA3(LineaPedido linea)
        {
            return linea.IdProducto == 3;
        }
        bool existe = DataLists.ListaLineasPedido.Any(PedidoConProductoIgualA3);

        // OPCIÓN 2: Usamos una expresión lambda que realice la misma operación
                  bool existe = DataLists.ListaLineasPedido.Any(linea => (linea.IdProducto == 3));

(más…)

Sentencias en LINQ (VII): Operaciones sobre conjuntos


Las listas generadas mediante el álgebra relacional no son, en última instancia, más que conjuntos de datos. Y si nos ceñimos a una definición formal de conjunto, tenemos que se trata de una agrupación de elementos que es, a su vez, un elemento.

Sobre estos conjuntos pueden realizarse una serie de operaciones básicas:

  • Distinción: selección de los elementos del conjunto que no son iguales a ningún otro.
  • Unión: creación de un conjunto a partir de los elementos de dos o más conjuntos, sin tener en cuenta los elementos repetidos.
  • Concatenación: similar a la unión, pero teniendo en cuenta los elementos repetidos.
  • Comparación: comprueba si los conjuntos poseen exactamente los mismos elementos.
  • Intersección: creación de un conjunto a partir de los elementos comunes a dos o más conjuntos.
  • Diferencia: creación de un conjunto a partir de los elementos de un conjunto cuyos elementos NO se encuentran en otro conjunto.

La operación de distinción (Distinct) la hemos visto en artículos anteriores, por lo que nos centraremos en el resto de operaciones. Comenzaremos por realizar tres consultas compuestas. Una de ellas contendrá información sobre las compras realizadas con valor superior a 20 euros. Otra contendrá las compras por valor entre 7 y 22 euros. La tercera contendrá información sobre compras con valor inferior a 10 euros.

(más…)

Sentencias en LINQ (VI): Ordenación


Otra de las funcionalidades básicas de cualquier consulta es la ordenación de los datos. Por norma nos bastará con dos criterios: campo por el que filtrar y orden en el que filtrar (ascendente o descendente).

La cláusula LINQ para realizar el filtrado es orderby. Si no se añade ningún parámetro de orden, los campos se recuperarán por defecto de forma ascendente. En caso de querer un orden descendente, añadiremos la palabra reservada descending a continuación del campo por el que queremos ordenar. La palabra reservada ascending fuerza a que la ordenación sea, en todo caso, ascendente.


            var consulta = from cliente in DataLists.ListaClientes
                           orderby cliente.Nombre ascending
                           select cliente;

            foreach (var cliente in consulta)
            {
                Console.WriteLine(string.Format("ID: {0}\tNOMBRE: {1}\tF.NACIMIENTO: {2}",
                    cliente.Id, cliente.Nombre, cliente.FechaNac));
            }

En este caso, los registros se ordenan por orden alfabético por el campo nombre, siempre de menor a mayor.

(más…)

Sentencias en LINQ (V): Funciones de agregación


Agregación, agrupación… son dos conceptos que parecen iguales pero que corresponden a características distintas dentro del álgebra relacional.

La agrupación, tal y como vimos en artículos anteriores, consiste en generar un conjunto de datos que poseen una característica común. Por ejemplo, agrupar los pedidos por cliente implicaría la obtención de varios subconjuntos (un subconjunto por cliente) compuestos por un dato que identifique unívocamente al cliente y sus pedidos asociados. Para realizar esta operación hacíamos uso de la sentencia group by.

La agregación está relacionada con la agrupación, ya que en lugar de proyectar un conjunto de datos asociados a otro, realiza una operación aritmética sobre uno o varios de estos datos. Por ejemplo, en lugar de obtener toda la información de los pedidos asociados a un cliente, una operación de agregación consistiría en obtener, por ejemplo el número de pedidos asociados a un cliente. Como podremos imaginar, todo cálculo de agregación debe tener asociada, por definición, una operación de agregación.

Las operaciones de agregación, por lo tanto, son de carácter matemático, y se suelen corresponder con las siguientes cinco operaciones:

  • Cuenta (Count): devuelve el número de registros pertenecientes a la agrupación.
  • Sumatorio (Sum): devuelve la suma de todos los valores de un campo numérico concreto perteneciente a la agrupación.
  • Máximo (Max): devuelve el máximo de los valores de un campo numérico concreto perteneciente a la agrupación.
  • Mínimo (Min): devuelve el mínimo de los valores de un campo numérico concreto perteneciente a la agrupación.
  • Media (Average): devuelve la media aritmética de un campo numérico concreto perteneciente a la agrupación.

Existen muchas otras posibles operaciones de agrupación, generalmente de carácter estadístico, como la varianza o desviación típica. Sin embargo, las cinco operaciones anteriores son las más extendidas en cualquier modelo de datos relacional, y por lo tanto, las más utilizadas.

(más…)

Sentencias en LINQ (III): Agrupaciones (group by)


Tras saber cómo filtrar elementos, es buen momento para aprender a agruparlos. Agrupar elementos, como su propio nombre indica, es concentrar los datos de un registro a partir de una característica común. Por ejemplo, saber los pedidos que se corresponden a cada uno de los clientes.

Su sintaxis es la siguiente:


            var agrupacion = from p in DataLists.ListaPedidos
                             group p by p.IdCliente into grupo
                             select grupo;

Lo cual nos devolverá un listado de agrupaciones (objeto que implementa la interfaz IGrouping<tipoClave, tipoObjetoAgrupado>) compuesto por dos elementos principales:

  • Key: contiene la clave de la agrupación, es decir, el campo por el cual se está agrupando. En este caso se trataría del valor de p.IdCliente.
  • <Implícito>: el objeto en sí también es un listado compuesto por los objetos sobre los que itera la cláusula from, es decir, contenidos en DataList.ListaPedidos. En este caso sería un listado de objetos de tipo Pedido. Dado que están agrupados, el objeto grupo sólo contendrá aquellos objetos de la clase Pedido cuyo valor Pedido.IdCliente sea el mismo en todos los elementos de la lista.

(más…)

Sentencias en LINQ (II): Filtrado, Join y Multiselect


Hasta el momento hemos visto cómo realizar consultas sencillas sobre una colección, recuperando el objeto de rango sobre el que iteramos, componentes de éste o un nuevo objeto, bien de un tipo existente o anónimo. Es lo que se conoce en álgebra relacional como operaciones de proyección.

En el presente artículo aprenderemos a realizar operaciones de filtrado a través de la sentencia where. También reciben el nombre de operaciones de restricción.

Para el siguiente artículo haremos uso de las clases definidas en el artículo anterior, en el que devolvíamos la totalidad de los registros de una lista, siendo uno o varios campos de la entidad. A partir de ahora utilizaremos la sentencia where de LINQ, que es bastante similar a la que usamos en SQL.

Para filtrar una consulta en LINQ añadiremos la sentencia where <condición>. La condición, a diferencia de SQL, deberá utilizar el formato estándar del lenguaje, tal y como hacemos en una sentencia if o while. Así, si queremos recuperar aquellos productos cuyo precio sea superior a 7 Euros, lanzaríamos la siguiente consulta sobre la lista ListaProductos:


            var productosDeMasDeSieteEuros = from p in DataLists.ListaProductos
                                             where p.Precio > 7
                                             select p;

Simple, ¿verdad? El resultado sera un único elemento (Mochila escolar).

(más…)

Sentencias en LINQ (I): Selecciones simples. Tipado implícito y tipos anónimos.


Antes de profundizar un poco más en las posibilidades que ofrece LINQ para acceso a distintas fuentes de datos, conviene hablar un poco de las operaciones que podemos realizar con él.

LINQ, como buen lenguaje de consultas, permite la realización de múltiples operaciones sobre conjuntos de datos, tales como proyecciones, ordenaciones, particiones (útiles para realizar paginaciones) o agregaciones.

A continuación construiremos un pequeño conjunto de ejemplos que nos servirán como punto de inicio para comprender el funcionamiento de la sintaxis de este lenguaje. Comenzaremos creando en primer lugar un pequeño conjunto de datos sobre los que operar. Crearemos para ello cuatro entidades relacionadas entre sí. Asumiremos que tratamos de crear una aplicación para una papelería que quiere llevar un registro de clientes y ventas de productos, por lo que haremos uso de las siguientes entidades:

  • Cliente: simboliza un cliente de la papelería.
    • Id (int)
    • Nombre (string)
    • FechaNac (DateTime)
  • Producto: simboliza cada uno de los materiales que la papelería venderá a los clientes.
    • Id (int)
    • Descripcion (string)
    • Precio (float)
  • Pedido: simboliza un conjunto de productos proporcionados a un cliente concreto, por lo que se compondrá de una o más líneas de pedido.
    • Id (int)
    • IdCliente (int)
    • FechaPedido (DateTime)
  • LineaPedido: simboliza la venta de un número de un único producto concreto a un cliente en particular. Se asocia a un único pedido.
    • Id (int)
    • IdPedido (int)
    • IdProducto (int)
    • Cantidad (int)

Hemos definido las relaciones pese a que para este ejemplo no vamos a hacer uso de una base de datos relacional. Sin embargo, nuestro objetivo es simular esta opción (veremos cómo acceder a una fuente de datos relacional en posteriores artículos), por lo que haremos algo que nunca, bajo ninguna circunstancia, debería hacerse: codificar los datos en una clase de forma manual. Todo sea por el afán didáctico J

(más…)