martes, 30 de septiembre de 2014

Receta T-SQL No. 2.2: Recuperar un Valor de la Base de Datos y Almacenarlo en una Variable

Índice

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
4. Práctica: Código T-SQL
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

En esta ocasión conoceremos cómo recuperar un valor desde la base de datos y guardarlo en una variable. Los valores obtenidos nos podrá servir, por ejemplo, para realizar otra consulta para filtrar resultados en una cláusula WHERE. Veremos que una de las formas más naturales de obtención estos valores de interés consiste en la especificación de una cláusula que tenga como parte del predicado un valor de llave primaria o de una llave única. De forma rápida introduciremos el uso de la cláusula IF para validar que una consulta retorne únicamente un registro. ¡Empecemos!

1. Problema

Requerimos establecer el valor de una variable a partir de los valores recuperados de un registro.

2. Solución

El primer paso de la solución consiste en declarar una variable y a partir de una consulta recuperar el valor de una columna y almacenarlo en la variable de interés.

3. Discusión de la Solución

Ahora entremos a ver con más detalle los pasos a seguir para realizar para la obtención de un valor generado desde una consulta:

1. Declarar la variable:

DECLARE @NombreVariable Tipo_Dato;

2. Diseñar la consulta para recuperar el valor y asignarlo en la variable

SELECT @NombreVariable = Columna [,...]
FROM Tabla
WHERE ID_UNICO = 'Valor';

3. Usar el valor de la variable:

SELECT @NombreVariable [AS 'Nuevo Nombre Columna'];

En el primer paso lo único que hemos hecho es declarar la variable a través de la palabra DECLARE [2], especificamos el identificador de la variable -@NombreVariable-, seguidamente la asociación de un tipo de dato. En el segundo paso, usamos la variable recién declarada sobre la cláusula SELECT para asignar el valor recuperado por la consulta. Aquí hay que tener en cuenta que el predicado de la cláusula WHERE comprueba un valor contra una columna de llave primaria ID_UNICO. En el tercer paso, volvemos a usar una cláusula SELECT para mostrar el valor almacenado en la variable NombreVariable.



Respecto al segundo paso es importante tener esta advertencia dada en [1]:
Nota acerca del uso de llave primara o llave única
Figura 1. Nota acerca del uso de llave primara o llave única [1].

4. Práctica: Código T-SQL

En este primer ejemplo declararemos dos variables para contener las dos direcciones (columnas AddressLine1, y AddressLine2) de un una persona (tabla Dirección) con ID de dirección 53 (AddressID):

DECLARE @Direccion1 nvarchar(64);
DECLARE @Direccion2 nvarchar(64);

SELECT @Direccion1 = AddressLine1, @Direccion2 = AddressLine2
FROM Person.Address
WHERE AddressID = 53;

SELECT @Direccion1 AS 'Dirección No 1.', @Direccion2 AS 'Dirección No. 2';


Declaramos las variables @Direccion1, y @Direccion2; a continuación escribimos la consulta T-SQL para recuperar la dirección con ID igual a 53. Del registro recuperado, usamos los valores de las columnas AddressLine2, y AddressLine2 para almacenarlos en las variables @Direccion1, y @Direccion2, respectivamente. Al final, a través de la cláusula SELECT, mostramos los valores almacenados en las variables mencionadas. En la Figura 2 se muestran los resultados de esta consulta:
Resultado consulta valores de variables
Figura 2. Resultado consulta valores de variables.
Para este segundo ejemplo, usaremos la variable global @@ROWCOUNT [3] para validar que la consulta retorna solo un registro. Adicional a este caso, asignaremos valores iniciales a las variables en caso de que la consulta no retornara ningún valor:

DECLARE @Direccion1 nvarchar(64) = '5379 Treasure Island Way';
DECLARE @Direccion2 nvarchar(64) = '# 14';

SELECT @Direccion1 = AddressLine1, @Direccion2 = AddressLine2
FROM Person.Address
WHERE AddressID = 49861;

IF @@ROWCOUNT = 1
SELECT @Direccion1 AS 'Dirección No 1.', @Direccion2 AS 'Dirección No. 2'
ELSE
SELECT 'La consulta no retorno ningún registro o una cantidad superior a 1.';


A las variables @Direccion1, y @Direccion2 asignamos valores de inicialización. En la consulta pretendemos encontrar los datos de las dos direcciones para el ID de dirección -AddressID- igual a 49861. Más adelante, con la sentencia IF comprobamos que el número de registros devueltos por la consulta previa sea igual a 1. De ser así, se mostrarán los valores de las variables @Direccion1, y @Direccion2; en caso contrario, se mostrará el mensaje de advertencia en la sentencia ELSE. Resultado de estas operaciones en la Figura 3:
Resultado consulta valores de variables y validación de número de registros
Figura 3. Resultado consulta valores de variables y validación de número de registros.

5. Conclusiones

Hemos comprendido los básicos para la declaración de variables y asignación de valores por defecto. Estas variables pueden ser usadas como contenedores de valores de columnas generadas por una consulta. Para validar el número de registros que retorna una consulta, usamos la variable global @@ROWCOUNT en conjugación con la sentencia IF. En la próxima receta T-SQL, conoceremos más acerca de la sentencia IF...THEN...ELSE.

6. Glosario

  • Columna
  • Consulta
  • Llave primaria
  • Llave única
  • Sentencia
  • T-SQL
  • Variable

7. Literatura & Enlaces

[1]: SQL Server 2012 T-SQL Recipes - A Problem-Solucion Approach by Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield. Copyright 2012 Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield, 978-1-4302-4200-0.
[2]: DECLARE @local_variable (Transact-SQL) - http://msdn.microsoft.com/en-us/library/ms188927.aspx
[3]: @@ROWCOUNT (Transact-SQL) - http://msdn.microsoft.com/en-us/library/ms187316.aspx
[4]: Receta T-SQL No. 2-1: Declaración de Variables | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/09/receta-t-sql-no-2-1-declaracion-de-variables.html


J

lunes, 29 de septiembre de 2014

Métodos de Extensión en C#

Índice

0. Introducción
1. Métodos de Extensión
2. Encadenamiento de Métodos de Extensión
3. Resolución y Ambigüedad
3.1 Resolución
3.2 Métodos de extensión vs. métodos de instancia
3.3 Métodos de extensión vs. métodos de extensión
4. LINQ y Métodos de Extensión
5. Enumeraciones y Métodos de Extensión
6. Métodos de Extensión en IntelliSense de Visual Studio
7. Conclusiones
8. Glosario
9. Literatura & Enlaces

0. Introducción

A través de este nuevo artículo C#, estudiaremos los métodos de extensión. Comprenderemos su utilidad a través de ejemplos prácticos y simples. Veremos, en un principio que este tipo de extensión de miembros de un tipo de la biblioteca base de clases (BCL) o de los tipos definidos por el programador, comprenden una vía idónea para la agregación de funcionalidad extra a un tipo sin necesidad de definir un subtipo en una jerarquía de herencia. Además, estudiaremos los casos de encadenamiento de métodos de extensión, resolución y ambigüedad de métodos de extensión, y la agregación de funcionalidad a una enumeración a través de este tipo de extensión de funcionalidad de tipos.

1. Métodos de Extensión

La definición simple y directa de un método de extensión, consiste en un miembro método aislado (es decir, que reside en un archivo de definición de tipo y de miembros diferente al original) que extiende la funcionalidad que provee un tipo definido en la biblioteca base de clases de Microsoft .NET Framework y de cualquier otro tipo definido por el programador.

Lo anterior, quiere decir que al encontrarse la definición del método de extensión aislada en un archivo de definición de tipos de C# no se requiere la modificación del tipo objetivo a extender su funcionalidad. (Como dato histórico de versionamiento de C#, los métodos de extensión fueron agregados a partir de la versión 3.0 [1].)

Continuando, más técnicamente, se define a un método de extensión como un miembro static dentro de una clase la misma naturaleza; además, el primer parámetro de la firma del método de extensión DEBE seguir la siguiente sintaxis:

this Tipo_A_Extender Identificador_Paramentro

En términos generales de definición del tipo y de (los) método(s) de extensión, tendríamos el siguiente ejemplo:

[modificador_acceso] static class Nombre_Clase_Estatica
{
[modificador_acceso] static bool Nombre_Metodo(this Tipo_A_Extender nombre [,otros_parametros]
{
// Implementación
}
}

Un ejemplo particular de esta sintaxis de formación de métodos de extensión consiste en la definición del método EstaCapitalizada que determina si una cadena de caracteres representada como un objeto String sigue el modo de capitalización de primera letra en mayúscula:

A partir de esta definición, desde código cliente es posible invocar el método EstaCapitalizada de igual modo que cualquier otro método static:

Console.WriteLine ("Blog xCSw".EstaCapitalizada());

De acuerdo con [1], la compilación de la clase anterior, producirá una llamada al método estático usando el operador . (punto) para acceder al miembro estático de la clase AyudanteString:

Console.WriteLine(AyudanteString.EstaCapitalizada ("Blog xCSw"))


En general la traducción de métodos de extensión a llamadas de miembros estáticos de un tipo, funciona del siguiente modo [1]:

arg0.MetodoExtension(arg1, arg2, ...);  // Llamada a método de extensión

ClaseEstatica.Metodo(arg0, arg1, arg2, ...); // Llamada a método estático


Además de poder extender los métodos de clases, también es posible realizar esta operación interfaces. Como sigue:

public static T Primero<T>(this IEnumerable<T> secuencia)
{
   foreach (T elemento in secuencia)
        return elemento

   throw new InvalidOperationException ("No hay elementos.")
}

Como es evidente, también es mandatorio especificar la palabra clave this antepuesta al tipo de interfaz a extender, en este caso particular, IEnumerable<T>. Ahora desde una clase que implemente esta interfaz genérica, como String podemos hacer una llamada al método estático Primero:


Console.WriteLine ("xCSw".Primero()); // Salida: x

2. Encadenamiento de Métodos de Extensión

Para empezar podemos recordar que el proceso de encadenamiento de métodos consiste en realizar llamadas consecutivas a métodos a través del operador . (punto) sobre una misma sentencia:

objeto.Metodo1().Metodo2().MetodoN()


Las llamadas a métodos empiezan en el orden izquierda a derecha, es decir, que primero se ejecuta el enésimo método MetodoN, luego con el resultado generado por éste, se invoca Metodo2, y así sucesivamente.


Podemos poner en práctica este concepto agregando otro método de extensión para pluralizar una cadena de caracteres: Pluralizar.

Archivo C# AyudanteString.cs [enlace alternativo]:


Con los métodos de extensión:
  • EstaCapitalizada, y
  • Pluralizar
Podemos hacer invocaciones encadenadas de la la siguiente manera:

string str1 = "cadena".Pluralizar().Capitalizar();
string str2 = AyudanteString.Capitalizar (AyudanteString.Pluralizar("cadena"))


Aquí vale resaltar las siguientes diferencias entre las expresiones para capitalizar y pluralizar una cadena de caracteres:
  • La primera expresión invoca los métodos de extensión para la clase String.
  • La segunda expresión invoca explícitamente métodos estáticos de la clase estática AyudanteString.

3. Resolución y Ambigüedad

3.1 Resolución

Antes de que podamos usar un método de extensión es necesario que incluyamos dentro de las directivas de importación de namespaces la ubicación de la clase estática que incluye los métodos de extensión para un tipo de dato particular.

Ttomemos como referencia los métodos de extensión definidos en la clase AyudanteString, e incluyamoslos en una aplicación de prueba:

En la línea 2 se explicita la inclusión de los artefactos de namespace Articulos.CSharp.MetodosExtension. Luego, sobre la línea 8, sobre la literal de cadena de caracteres "cadena" invocamos de forma encadenada los métodos Pluralizar y Capitalizar (orden de llamada).

Compilación:


1. Creación de la librería a partir del archivo AyudanteString.cs de la sección anterior:


  1. csc /target:library /out:AyudanteString_2.dll AyudanteString_2.cs

2. Compilación de la clase de prueba:


  1. csc /target:exe /r:AyudanteString_2.dll PruebaAyudanteString.cs

Ejecución assembly:


  1. .\PruebaAyudanteString.exe

> Prueba de ejecución:
Ejecución assembly PruebaAyudanteString.exe
Figura 1. Ejecución assembly PruebaAyudanteString.exe.

Incluir el nombre de espacio con una sentencia using es mandatario para poder utilizar los métodos Pluralizar y Capitalizar, de lo contrario se generará el error de compilación CS1061 [3]:
Intento de compilación PruebaAyudanteString.cs
Figura 2. Intento de compilación de PruebaAyudanteString.cs.

3.2 Métodos de extensión vs métodos de instancia

Los métodos miembro de instancia o estáticos de un tipo de dato tienen mayor precedencia que cualquier de los métodos de extensión definidos en clases estáticas. Si por ejemplo definimos la clase ClaseA la cual tiene definido el método de instancia MetodoA, cualquier método de extensión homónimo (sobrecargado) tendrá menor precedencia en invocaciones desde código cliente. Expresemos esto con código fuente C#:

class ClaseA
{
    public void MetodoA(object x)
    { 
        // Implementación...
    }
}

La clase estática ExtensionesClaseA con un método de extensión:

static class ExtensionesClaseA
{
    public static void MetodoA(this ClaseA param1, int x)
    {
        // Implementación...
    }
}


Tal como hemos dicho, el método MetodoA definido en la clase concreta ClaseA tendrá mayor precedencia sobre el definido en la clase static ExtensionesClaseA.


En [1] nos advierten que la única manera de acceder al método de extensión MetodoA es usar la siguiente sintaxis:

ExtensionesClaseA.MetodoA(...)

3.3 Métodos de extensión v.s. métodos de extensión

Asumamos que contamos con dos clases estáticas que contienen métodos de extensión homónimos, luego para desambiguar la invocación o llamadas a estos métodos desde código cliente se debe utilizar la sintaxis:

ClaseEstatica_1.MetodoHomonimo(...)

ClaseEstatica_2.MetodoHomonimo(...)


Sin embargo, hay que tener en consideración que sino usamos el método de desambiguación propuesto, el compilador ejercerá precedencia sobre el método con los argumentos más específicos.


Este ejemplo propuesto y adaptado de [1] nos permitirá observar mejor el comportamiento descrito:

public class AyudanteString
{
    public static bool EstaCapitalizada(this String s) {...}
}

public class AyudanteObject
{
    public static bool EstaCapitalizada(this object s) {...}
}


La invocación del método de extensión EstaCapitalizada se hace de forma implícita como cualquier otro método de la clase String:


bool prueba1 = "xCSw".EstaCapitalizada();


En contraste con el método EstaCapitalizada de la clase estática AyudanteObject, se debe explicitar el tipo (i.e., AyudanteObject) y la llamada al método correspondiente:


bool prueba2 = (AyudanteObject.EstaCapitalizada("xCSw"));


E inclusive [1]:
«Classes and structs are considered more specific than interfaces.»

4. LINQ y Métodos de Extensión

En la biblioteca base de clases de .NET los métodos de extensión se usan extensivamente sobre los operadores de consultas de LINQ [4]. Las clases que se ven beneficiadas por esta característica de adicionamiento funcional son las interfaces:
  • System.Collections.IEnumerable [5], y 
  • System.Collections.IEnumerable<T> [6]
Como ya mencionamos, la resolución de métodos de extensión se lleva a cabo con la inclusión del (o los) nombre de espacio con la directiva using; en este para poder hacer uso de los métodos de extensión que provee el nombre de espacio de LINQ tendremos que hacer obligatoriamente esto:

using System.Linq;

Veamos un ejemplo en donde ordenamos los elementos de un arreglo a través de la invocación del método de extensión OrderBy. El método OrderBy hace uso de una expresión lambda (Expresiones Lambda) para ordenar de mayor a menor los elementos contenidos en el arreglo:

En la línea 2 importamos los elementos de programa del nombre de espacio System.Linq. En la línea 11 declaramos e inicializamos el arreglo de enteros de 32 bits (int) con 7 elementos desordenados. Sobre la línea 14 invocamos al método de extensión OrderBy al que le pasamos como argumento el predicado a modo de una expresión lambda que simplemente se traduce en una iteración por lo elementos del arreglo para su ordenamiento. El resultado de la operación anterior lo localizamos en la variable anónima (var) arregloOrdenado. Con el ciclo foreach (líneas 18-21) mostramos en la salida estándar los elementos del arreglo ordenados de mayor a menor.

Compilación:


  1. csc /target:exe MetodoExtensionOrderBy.cs

Ejecución assembly:


  1. .\MetodoExtensionOrderBy.exe

> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):
Ejecución assembly MetodoExtensionOrderBy.exe
Figura 3. Ejecución assembly MetodoExtensionOrderBy.exe.

5. Enumeraciones y Métodos de Extensión

Podemos definir métodos de extensión para las enumeraciones (Enumeraciones) que son parte de la BCL o las propias definiciones hechas por el programador. Esto lo podemos demostrar a través del siguiente ejemplo en el que extenderemos la funcionalidad de la enumeración Nota:

En las líneas 6-13 definimos la enumeración Notas con las constantes que representan las notas de un curso. Con la clase static ExtensionesNotas (líneas 17-28) definimos el método de extensión Paso (líneas 24-27) para determinar si una nota de un examen dada igual o supera el mínimo para pasar el examen. En el código cliente de Main (líneas 32-38) definimos dos variables de la enumeración Notas y comprobamos a través del método de extensión Paso si las dos notas de exámenes de ejemplo ameritan superar el examen.

Compilación:


  1. csc /target:exe EnumeracionMetodosExtension.cs

Ejecución assembly:


  1. .\EnumeracionMetodosExtension.exe

> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):
Ejecución assembly EnumeracionMetodosExtension.exe
Figura 4. Ejecución assembly EnumeracionMetodosExtension.exe.

6. Métodos de Extensión en IntelliSense de Visual Studio co

En Visual Studio contamos con la funcionalidad en IntelliSense para detectar los métodos de extensión disponibles para un determinado tipo de dato. En el Figura 5 se muestra la detección automática por parte de IntelliSense para el método OrderBy (además de su descripción de firma) para el arreglo enteros (ver ejemplo de la sección 4).
IntelliSense - Reconocimiento de métodos de extensión
Figura 5. IntelliSense - Reconocimiento de métodos de extensión.

7. Conclusiones

El desarrollo de este artículo nos ha permitido conocer la gran utilidad que nos brinda los métodos de extensión para extender la funcionalidad de tipos definidos en la BCL de .NET Framework, y también para los tipos definidos por el propio programador. Escribimos varios ejemplos en C# que nos afianzó el conocimiento práctico sintáctico. En futuras entregas de artículos LINQ usaremos de manera intensiva cada uno de estos métodos, de este modo tendremos a disposición más herramientas programáticas para resolver problemas de forma más efectiva y elegante. En el próximo artículo C# estudiaremos los aspectos avanzados de los tipos anónimos.

8. Glosario

  • BCL
  • Clase estática
  • IDE
  • IntelliSense
  • LINQ
  • Método
  • Método de extensión
  • Namespace
  • Nombre de espacio
  • Visual Studio

9. Literatura & Enlaces

[1]: C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[2]: Extension Methods (C# Programming Guide) - http://msdn.microsoft.com/en-us/library/bb383977.aspx
[3]: Compiler Error CS1061 - http://msdn.microsoft.com/en-us/library/bb383961.aspx
[4]: LINQ (Language-Integrated Query) - http://msdn.microsoft.com/en-us/library/bb397926.aspx
[5]: IEnumerable Interface (System.Collections) - http://msdn.microsoft.com/en-us/library/system.collections.ienumerable(v=vs.110).aspx
[6]: IEnumerable(T) Interface (System.Collections.Generic) - http://msdn.microsoft.com/en-us/library/9eekhta0(v=vs.110).aspx
[7]: Expresiones Lambda en C# - Parte 1: Introducción a las Expresiones Lambda | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/06/expresiones-lambda-en-csharp-parte-1-introduccion-a-las-expresiones-lambda.html
[8]: OrtizOL - Experiencias Construcción Software (xCSw): Expresiones Lambda en C# - Parte 1: Introducción a las Expresiones Lambda - http://ortizol.blogspot.com/2013/09/var-variables-locales-de-tipo-deducido.html
[9]: Enumeraciones en C# - Parte 1 | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/05/enumeraciones-en-c-parte-1.html


J