Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Linq - Contenido Lenguaje Integrado de Consultas PDF
Linq - Contenido Lenguaje Integrado de Consultas PDF
1. INTRODUCCION
En la actualidad las empresas que anhelan tener éxito en el mercado deben implementar
Sistemas Informáticos que les permitan intercambiar información con el mundo externo.
De ahí comenzaron a surgir ideas para que las aplicaciones puedan comunicarse entres sí.
Empezaron a aparecer los compiladores que tenían un lenguaje intermedio como .NET
que disponía del MSIL, JAVA también tenía sus propios proyectos, etc. Y finalmente se
desarrollaron estándares para el envío y recepción de los datos que necesitaban los
programas.
Sin embargo estos estándares también requieren tener una forma de conexión para
consultar y modificar los datos que en ellos se encuentran, lo que ocasionaba que los
tenía que interactuar con fuentes de datos de disímiles tipos; el excesivo uso de variables
familiarizados con el manejo de objetos, clases, etc. Pero las formas más utilizadas para
a los datos que se encontraban en las BD y otra manera diferente para acceder a las
ARQUITECTURA LINQ
Microsoft que brinda consultas semejantes a las nativas de SQL. Define operadores de
consulta estándar permitiendo a los lenguajes habilitados con LINQ filtrar y crear
pueden incluir arreglos, bases de datos relacionales de SQL Server, archivos XML y bases
LINQ a SQL es una extensión de ORM (Mapeador de Objetos Relacionales) que nos
permite modelar bases de datos relacionales con Visual Studio .NET 2008, permitiendo
consultar, agregar, modificar y eliminar registros de las bases de datos y tratarlas como si
Los programadores pueden utilizar LINQ con cualquier fuente de datos inclusive
EJEMPLO: Para hacer las declaraciones de los impuestos fiscales se podría escribir
sentencias LINQ que saquen información de las bases de datos relacionales y por medio
del mismo Lenguaje Integrado de Consultas generar los archivos XML que deberán ser
origen de datos que cumpla con una sola condición: “EL ORIGEN DE DATOS DEBE SER
Esto debido a que al escribir la sentencia LINQ no significa que los datos ya estén en
memoria, sino que estos son traídos al momento de ejecutar las instrucciones en un bucle
EJERCICIO 1 El siguiente ejemplo, presenta la lista de números que sean menores al rango
Nota: El hecho de crear la consulta no significa que ya dispongamos de los datos, sino es al
Antes de que aparezca este lenguaje todas las sentencias SQL que se escribían en las
incorrecta la palabra reservada “from”, al ser una cadena esto no será reconocido como
un error, mientras que con las bondades de este lenguaje que pone al servicio del
1.2 HISTORIA
generación de tecnologías actual y siguiente, se hace evidente que el siguiente gran reto
objetos.
y no solo a los datos relacionales o XML. Esta facilidad se llama “Consultas integradas en
los lenguajes”.
Utilizamos el término consultas integradas en los lenguajes para indicar que las consultas
principal del desarrollador (por ejemplo C# 3.0, Visual Basic 9). Las consultas embebidas
en los lenguajes permiten que las expresiones de consulta se beneficien de los metadatos
IntelliSense que antes estaban disponibles solo para el código imperativo. Las consultas
integradas en el framework que utilizan los lenguajes también hacen posible aplicar una
Las consultas integradas en el framework 3.0 para los lenguajes de Visual Studio .NET
hacen posible que las operaciones de recorrido, filtro y proyección sean expresadas de
importante aún es que terceros fabricantes también pueden reemplazar los operadores
de consulta estándar con sus propias implementaciones que ofrezcan servicios adicionales
LINQ para ofrecer implementaciones que operan sobre datos XML y SQL. Los operadores
de consulta sobre XML (XLinq) utilizan una facilidad de XML en memoria interna eficiente
tipos del CLR. Esta integración ofrece un fuerte control de tipos sobre los datos
subyacente.
Visual Basic .NET 9, pero también se puede adicionar esta funcionalidad aunque no de
manera completa a estos dos lenguajes en la versión de Visual Studio 2005 haciendo una
autoría.
La funcionalidad que nos brinda LINQ radica en la posibilidad de conectarse con diferentes
eliminar los datos que se encuentren en ellas. Esto permite que los programadores que
aprendan este lenguaje no necesiten conocer diferentes tecnologías para hacer que sus
disímiles formatos. Al analizar detenidamente esta ventaja nos damos cuenta que el
políticas del negocio, también podrá distribuir la forma de registrar los datos que utilizan y
que escriba.
clientes, cuentas contables, productos, etc; que por lo general se encuentran en una base
de datos relacional y todas las transacciones que la empresa realice durante el periodo
Pero si la empresa requiere compartir información con otras instituciones que utilizan los
mismos datos pero diferentes aplicaciones e inclusive diferentes bases de datos entonces
lo debe hacer a través de algún estándar como XML. LINQ, hace que todo este proceso
que antes era muy laborioso para los desarrolladores se simplifique considerablemente ya
que con una misma sintaxis se conecta a su base de datos, obtiene la información que
La tecnología LINQ puede ser utilizada tanto para ambientes de oficina como para
desarrollar aplicaciones Web, es decir que el programador no solo que podrá hacer uso de
los procedimientos almacenados, sino que también lo puede combinar con los beneficios
que nos brinda los servicios web e incluso una arquitectura SOA que este bien
implementada. Aclarando que SOA no son Servicios Web, por el contrario los Servicios
Nosotros podemos construir instrucciones LINQ de la misma forma que se hace con SQL,
podemos utilizar los operadores “AND” y “OR”, con la única diferencia que:
OR Se representa con ||
Las consultas combinadas en LINQ se realizan a través de las claves externas que se
representa en el modelo como propiedades que tienen una colección de elementos, pero
también aunque no muy a menudo se puede utilizar el operador JOIN siempre y cuando se
esté trabajando con colecciones de objetos, que a diferencia de SQL se lo hace con tablas.
Es tan extenso las cláusulas SELECT, GROUP, ORDER, etc. Que para poder explotar todo el
potencial que estas nos ofrecen deberíamos realizar un estudio a fondo de cada una de
ellas pudiendo ser solo esta cláusula tema de una monografía completa.
Esta es una nueva característica que nos ofrece C# 2008, una variable con tipo implícito se
consultas LINQ se declaran de esta manera sin embargo esto no significa que “var” este
orientado únicamente a LINQ sino mejor es una funcionalidad del lenguaje que nosotros la
Qué es un delegado?
Los métodos anónimos aparecen con la versión 2.0 del Framework, la intención es
conocimiento básico sobre el funcionamiento de los delegados, es por esto que este tema
esta forma podemos ahorrar líneas de código ya que evitamos tener que crear funciones
para resolver tareas simples. Sin embargo la lectura del código también se complica un
poco.
“Los métodos anónimos aparecieron desde C# 2005 con el Framework 2.0 y estaban
Aunque el ejemplo que hemos expuesto cuando definimos los métodos anónimos es
Las expresiones lambda son propias de C# 3 y nos permiten hacer lo mismo que los
métodos anónimos pero de una forma más sencilla. Una de las características de las
Esta forma de programar abre la posibilidad de que nuestros programas llamen a otros
bucle foreach.
tipo de datos que la implemente puede servir directamente como fuente para expresiones
// System.Collections.Generic
public interface IEnumerable<T> : IEnumerable{IEnumerator<T> GetEnumerator();}
// System.Collections
public interface IEnumerable{IEnumerator GetEnumerator();}
// System.Collections.Generic
public interface IEnumerator<T> : IDisposable, IEnumerator
{
T Current { get; }
}
// System.Collections
public interface IEnumerator
{
object Current { get; }
void Reset();
bool MoveNext();
}
niveles).
al enumerador.
nivel más alto, mientras que IEnumerator<T> se encarga del “trabajo sucio”? ¿Por qué no
que ver con la necesidad de permitir la ejecución de iteraciones anidadas sobre una
implementar bucles anidados sobre una misma secuencia, como por ejemplo los que se
llamado.
Toda consulta LINQ que escribamos contiene el código que se va a ejecutar para consultar
o modificar los datos que tenemos en las diferentes fuentes. Pero la consulta solo se
Son clases que implementa .Net para realizar la lectura y escritura de archivos XML, como
sus nombres lo indican XMLReader se encargará de los procesos de lectura mientras que
XMLReader permite leer archivos XML sin realizar la carga en caché y evitando la lectura
System.Xml.
Estas clases ofrecen un alto potencial para trabajar con información contenida en
código potente y ágil para realizar consultas, modificar documentos y a través de este
El lenguaje de esquema nos permite definir la estructura y las restricciones que tienen los
archivos XML, fue desarrollado por WC3, este lenguaje permite tener un alto nivel de
XSD, no tiene una definición común para sus siglas pero respecto al significado de su
funcionalidad todos concuerdan que es un lenguaje que nos da una mejor visión de la
También desarrollado por WC3. Es un estándar que permite transformar los documentos
XML en otros incluso a formatos que nos sean xml, para esto se basa en reglas de
Es otra de las nuevas funcionalidades que nos brinda el Visual Studio 2008, consiste en la
posibilidad de agregar nuestros propios métodos a las clases del .NET o cualquier clase
EJEMPLO: Cuando solicitamos al usuario que ingrese la cédula la recibimos como una
implementar este método como una extensión de la clase string cada vez que declaremos
una variable de este tipo aparecerá nuestro método como uno más de la clase.
Los orígenes de datos representan los datos con los que ya se sabe vamos a trabajar en la
Visual Studio proporciona herramientas en tiempo de diseño para crear y editar los
orígenes de datos que va a utilizar en su aplicación, en los proyectos de Visual Studio los
- LINQ a SQL.- Permite la conexión con bases de datos SQL Server, es como si este
- LINQ a Entidades.- Permite que la aplicación se conecte con bases de datos que
- LINQ a DataSet.- también conocido como LINQ a objetos, que admite realizar las
mismas operaciones que normalmente se hacen con las bases de datos con
Nota: Debido a lo extenso que es la tecnología LINQ, en esta investigación haremos una
introducción a las dos primeras formas de conexión. Explicando cómo LINQ las define y
trabaja con cada una de ellas y en los siguientes capítulos se harán ejemplos de consultas
desde LINQ.
2.1.1.1 INTRODUCCION
En la actualidad todas las empresas han comenzando a construir aplicaciones que estén
formato que ha quedado definido como el estándar para realizar esta tarea.
“LINQ a XML” no es otra cosa que proporcionar toda la potencialidad del “Lenguaje
escribir para obtener los mismos resultados, adicionando la ventaja que con esta nueva
tecnología podemos conectarnos y trabajar con otras fuentes de datos que a diferencia de
las clases que se utilizaban en versiones anteriores para trabajar con XML son única y
Visión simplificada de los archivos XML.- Ya no tenemos que acceder a los nodos de los
Proporciona una identificación a los nodos de los archivos XML.- Podemos dar un nombre
a cada elemento de nuestro árbol XML a través de la propiedad XName, esta utilidad será
Creación de estructuras XML.- El Ejercicio 1 expone la gran facilidad con la que se puede
Acceso a datos.- Permite leer y modificar los nodos de los archivos XML con sentencias
muy similares a SQL y tomando en cuenta que LINQ también implementa conectividad con
bases de datos los desarrolladores pueden consultar las dos fuentes con una misma
sintaxis.
elementos y atributos respetando sus tipos con tan solo hacer un cast.
Elementos principales.- Los elementos básicos para trabajar con formatos XML a nuestro
XName, nosotros hemos preferido no hacerlo. Como ya hemos explicado antes XName es
una gran innovación pero respaldados en los ejercicios que se han desarrollado en esta
EJERCICIO 1
Crear un archivo XML, que contenga la información básica de una persona. Para poder
probar el ejercicio deberá crear un formulario con dos botones el uno para la forma
XElement Persona =
new XElement("Persona",
new XElement("Contacto1",
new XElement("Cédula", "0128928212"),
new XElement("Nombre", "Juan Carlos Pérez Maldonado"),
new XElement("Dirección", "Av. De las Américas y Batán"),
new XElement("Teléfonos",
new XElement("Convencional1",
new XAttribute("Hogar","2890654")),
new XElement("Convencional2",
new XAttribute("Oficina","2847241"),
new XAttribute("Desde","09H00AM"),
new XAttribute("Hasta","19H00pM")),
new XElement("Celular",
new XAttribute("Celular","098213125"))
)
),
);
Persona.Save("d:\\ConLINQ.xml");
Código: Este ejercicio tiene como finalidad introducir al lector a la funcionalidad que implementa LINQ a
XML, para esto hemos creado dos veces el mismo archivo, la diferencia en la cantidad y la complejidad del
código resalta a la vista. Esto se profundizará cuando estudiemos la clase XElement y XAttribute que son dos
de los pilares de la funcionalidad XML.
ejecutamos los dós codigos podremos comprobar que ambos crean un archivo XML
Es muy importante recalcar que LINQ también incorpora Intellisence, lo que significa que
consultas.
la cantidad de líneas de código que son necesarias escribir para poder crear árboles,
documentos, atributos, leer archivos XML o modificarlos. Está claro que esta es una
característica muy importante pero también hay otras mas que serán de mucha utilidad
estudiarlas.
continuar con la costrucción de árboles XML, en cambio cuando hicimos uso de las
bondades que nos brinda la nueva herramienta de Microsoft pudimos hacer lo mismo
(Documento XML).
Compatibilidad con espacios en blanco.- Los espacios en blanco siempre han significado un
problema al momento de desarrollar código para leer, cargar o editar archivos XML, con
“LINQ a XML” podemos tratar los espacios através de nodos texto conocidos como xText.
Control simplificado de nombres y espacios de nombres.- Evita tener que manejar espacios
Así mismo para cambiar el nombre de un nodo XML, se debía crear un nodo con diferente
nombre y copiar todo el contenido del nodo original. En cambio LINQ soluciona este
Diferencias entre “LINQ a XML” y XML Reader.- XML Reader nos permite realizar lecturas
de documentos XML, pero será el programador quien deba analizar y decidir que va a
utilizar.
A lo que queremos hacer referencia es que en muchas ocasiones no es suficiente solo leer
los orígenes de datos, es más, tal vez ni siquiera haya un único origen de datos entonces
los que nos toca es tomar los datos de las diferentes fuentes luego darles el tratamiento
Integrado de Consultas, o podría también ser una combinación entre XMLReader y las
Mediante esta clase se puede realizar varias operaciones con los elementos o nodos de los
archivos XML, así también se dispone de la clase XAttribute que tiene la misma función
Los elementos difieren de los atributos en el aspecto que los últimos no son nodos, más
bien podríamos decir que estos son parte de los nodos y que están compuestos por dos
valores, uno que representa el nombre del atributo y el segundo que indica el contenido
del atributo.
Por otra parte la clase XDocument si bien forma parte de LINQ, lo mejor es saber
analizarlo de manera correcta, ya que en muchas ocasiones resulta más facil manejar
Cuando utilizamos la clase XDocument debemos tomar en cuenta que un documento solo
puede tener un elemento secundario, siendo este el nodo ráiz. No cambia mucho o quizá
nada, ya que todo documento XML requiere que el nodo principal sea único.
Así mismo hay otros elementos que puede tener un documento XML, como son
El aprendizaje de estas tres clases es muy sencillo ya que la mayoría de métodos que
implementan cada una de ellas son muy similares entre sí. Ejemplo:
Código: Este es uno de los paso fundamentales para poder crear estructuras XML, en este ejemplo hemos
creado un nodo XElement y le asignado un solo atributo, al leer el código podemos darnos cuenta que
instanciamos un objeto XElement, el primer parámetro es el nombre y el segundo es vector que puede tener
uno o varios atributos o incluso subnodos. Lo único que debemos tomar en cuenta es anteponer la palabra
new a cada elemento o atributo que se crea.
En el ejemplo podemos ver que tanto la clase XElement como XAttribute son similares,
XElement recibe de parámetros el nombre del elemento en este caso MiElemento y como
Para el caso de los Documentos XDocument funciona de manera muy similar a las dos
Podemos cargar archivos XML a través de su path, con el método XElement.Load(), este
método tiene instancias adicionales que nos permite dar opciones de carga como es
Código: La primera línea carga el documento XML en un objeto XElement y la segunda carga el mismo
documento pero en un objeto XDocument.
la diferencia radica; Cuando lo hacemos con un XElement el nodo primario del documento
pasa a ser el documento, mientras que cuando lo hacemos a través de XDocument se crea
un nodo para el documento y el nodo raíz queda como nodo secundario. Sin dejar este de
Cuando deseamos realizar la carga de un documento XML con algunas restricciones como
por ejemplo preservar los espacios en blanco, podemos enviar el parámetro LoadOptions
Código: El segundo parámetro del metodo Load() indica que debe preservar los espacios en blanco
EJERCICIO 2
En este ejercicio vamos a cargar el archivo XML ConLINQ.xml que creamos al principio de
Código: La instrucción de consulta carga todos los subnodos del nodo Contactos (NodoRaiz) y luego a través
del bucle foreach los va presentando de uno en uno, anteponiendo la palabra nombre
Nota: Recomendamos ampliar el archivo XML por los menos a tres contactos para
entender mejor el código.
LINQ a SQL, es otra funcionalidad que nos brinda el Lenguaje Integrado de Consultas.
Como su nombre lo indica nos permite conectar con datos que se encuentren
Basado en ORM LINQ, permite realizar un mapeo de la base de datos con la que se desea
trabajar cargando dentro de este mapa tanto las tablas como los procedimientos
clase.
Una vez que hemos explicado cual es la funcionalidad de LINQ a SQL comenzaremos a
realizar algunos ejemplos que no permitirán entender mejor como se puede implementar
Lo primero que debemos hacer es agregar un nuevo elemento de la misma forma que si
presenta para que seleccionemos el elemento que vamos a adicionar a nuestro proyecto
debemos escoger la opcion LINQ a SQL clase que tiene una extension “.dbml” como se ve
en la siguiente figura:
Ahora debemos crear una conexión a la base de datos con la que deseamos trabajar para
nuestra aplicación.
Muchas veces los desarrolladores nos preguntamos que hace más eficiente nuestro
código, los procedimientos almacenados, utilizar consultas LINQ o combinar las dos
Como se podrá observar cuando hagamos el mapeo de nuestra base de datos. LINQ no fue
creado para reemplazar a los Procedimientos Almacenados sino mas bien para
formas de acceso a datos. Ya que si hacemos excesivo uso de LINQ podría ser que
ralenticen. Por el contrario si decidimos obviarlo por completo también podemos perder
¿cómo funciona? Y más aun si es nueva. Para entender mejor ¿cómo?, y en ¿qué? manera
LINQ, toma las sentencias que escribimos en nuestros programas las interpreta y
convierte a una consulta SQL (En lo que respecta a LINQ a SQL) y lo ejecuta contra la base
de datos.
cliente, consumo de memoria y eso está muy bien pues de un análisis correcto de estos
agentes tendremos aplicaciones más livianas y eficientes. Como LINQ se construyó sobre
el lenguaje C# 3.0 obviamente tendrá un mejor rendimiento que en Visual Basic 9.0;
También si ejecutamos un programa que contenga código LINQ paso a paso, veremos que
después de creada la consulta esta es una instrucción SQL, por lo que se convierte en un
proceso similar a escribir sentencias SQL dentro del código de la aplicación. ¿Y los
En conclusión podríamos decir que LINQ funciona como un intérprete de las transacciones
XQuery, etc. Según cuál sea la fuente de datos a la que se esté conectando.
Es importante aclarar que LINQ no es un ORM, sino mejor se apoya en los ORM para
implementar sus funcionalidades conocidas como LINQ a SQL y LINQ a Entidades que
bases de datos que se encuentran almacenadas en un motor de SQL Server mientras que
la segunda hace la misma función que la anterior pero con bases de datos que no se
Cuando mapeamos la base de datos podemos acceder a todas las tablas y procedimientos
almacenados que se hayan incluido en el mapa como si fueran clases del software que se
está construyendo. Así también estas clases podrán ser instanciadas y por lo tanto
Con la llegada de LINQ, tenemos finalmente entre otras cosas el tan ansiado ORM creado
base de datos en objetos .NET, sino que nos da la posibilidad de todo tipo de querys
contra el modelo relacional sin tener nunca que utilizar sentencias SQL, ya que las mismas
El ORM al que nos referimos se conoce específicamente como LINQ a SQL. Esta es solo
una de las variantes de LINQ, puesto que el mismo puede utilizarse con otros orígenes de
datos, como por ejemplo XML y objetos. En sí, LINQ brinda el poder de hacer todo tipo
de querys en el lenguaje de nuestra preferencia (Visual Studio .Net 2008), contra cualquier
origen de datos, transformar (opcionalmente) los datos a la forma que uno desee y luego
“El Mapeo de la Base de Datos, es un procedimiento que consiste en cargar todas las
subirlas a nuestra clase LINQ a SQL para poderlos utilizar en nuestros programas a través
En el gráfico anterior podemos observar como hemos creado la conexión a nuestra base
de datos através del explorador de servidores (parte izquierda del grafico), en el centro se
encuentran las tablas que hemos agregado a nuestra clase LINQ a SQL, en esta misma
zona podemos colocar las vistas en caso de haberlas y en la parte derecha colocamos los
Guardamos los cambios realizados en nuestra aplicación. Para terminar debemos hacer
una instancia de nuestro ORM que tenemos creado. Esto se realiza de la siguiente forma:
Absolutamente todas las entidades de la base de datos que deseamos utilizar con
consultas LINQ deberán estar cargadas en el esquema de Base de datos. Podemos seguir
agregando más entidades después de que hayamos creado el esquema con tan solo
Es importante aclarar que para conectarse con otros motores de base de datos a través
La siguiente instrucción permite cargar todos los clientes de nuestra base de datos y
Cuando hacemos el ejercicio 3 y pedimos que nos muestre el valor del elemento XML, nos
podremos dar cuenta que nos presenta toda la información de los nodos hijos de cada
nodo contacto eso se conoce con el nombre de valor profundo. El valor superficial hace
referencia al valor que tiene un nodo específico independiente del contenido que tengan
Ejercicio 3
Este ejemplo ha sido tomado de MSDN, lo que debemos hacer es crear el archivo xml en la
Código: Creamos una sentencia LINQ en donde cargue todos los nodos descendientes del documento que
llamamos en donde el valor del método ValorPresentar() que se construirá a continuación empiece con la
letra “C”.
Después de la llave que cierra la clase del formulario creamos una clase estática con el
siguiente código:
Código: El código anterior extrae los nodos del elemento que estamos procesando que sean de tipo texto a
través del método OfType(). Y estructura la cadena resultado a través de método Agregate que recibe tres
parámetros:
- El primero es un string builder en donde construiremo el valor resultante de todos los nodos hijos
que nos devuelva la sentencia.
- El segundo es una expresion lambda que concatena cada valor extraido a partir del método
Append() a la cadena que creamos en el paso anterior.
- El tercero es una cadena que necesita el método agregate para presentar el resultado, la expresion
lambda hace la asignacion
El siguiente ejercicio nos muestra una forma sencilla de buscar descendientes con un
que nos evitan las iteraciones cuando queremos encontrar elementos determinados en
los DataViews.
EJERCICIO 4
Código: Cargamos nuestro documento XML, luego escribimos la consulta LINQ, indicando que nos devolverá
los nodos hijos a través del método Descendants() y pasando como parámetro el nombre del nodo padre. En
el select hacemos un cast a string del elemento que nos devuelve la consulta.
Agregate.- al valor que nos devolvió la instrucción le ejecutamos el método Agregate() que recibe tres
parámteros:
- El primero es un string builder en donde construiremos el valor resultante de todos los nodos hijos
que nos devuelva la sentencia.
- El segundo es una expresion lambda que concatena cada valor extraído a partir del método
Append() a la cadena que creamos en el paso anterior.
- El tercero es una cadena que utiliza el método Agregate() para presentar el resultado, la expresión
lambda hace la asignacion.
Finalmente se presenta el resultado al usuario a través de un cuadro de diálogo.
El siguiente ejemplo esta tomado de MSDN y nos muestra como realizar la búsqueda de
un elemento dentro de un árbol a partir de un atributo. El árbol XML que debemos tener
EJERCICIO 5
<Tests>
<Test TestId="0001" TestType="CMD">
<Name>Convert number to string</Name>
<CommandLine>Examp1.EXE</CommandLine>
<Input>1</Input>
<Output>One</Output>
</Test>
<Test TestId="0002" TestType="CMD">
<Name>Find succeeding characters</Name>
<CommandLine>Examp2.EXE</CommandLine>
<Input>abc</Input>
<Output>def</Output>
</Test>
<Test TestId="0003" TestType="GUI">
<Name>Convert multiple numbers to strings</Name>
<CommandLine>Examp2.EXE /Verbose</CommandLine>
<Input>123</Input>
<Output>One Two Three</Output>
</Test>
IEnumerable<XElement> tests =
from el in root.Elements("Test")
where (string)el.Element("CommandLine") == "Examp2.EXE"
select el;
Código: Cargamos nuestro documento XML, Hacemos una consulta que filtre solo los nodos que tengan el
atributo CommandLine=”Examp2.EXE” (ver la estructura del archivo XML creado para entender mejor el
atributo CommandLine). Presentamos los resultados de la consulta através del bucle foreach.
Comenzaremos haciendo unos ejercicios que nos permitan cargar información de nuestra
base de datos a través de la cláusula Select, de la misma forma que hicimos con los
archivos XML, realizaremos varios ejemplos que realicen consultas de diferentes formas y
al final de cada ejemplo se hará una pequeña explicación del código fuente.
Para empezar haremos una consulta sencilla que nos presente todos los registros que
Ejercicio 6
Código: La consulta obtiene todos los registros de la entidad de clientes, en el bucle foreach asignamos el
nombre de cada elemento al texto que pasamos como parámetro, al ejecutar el programa quedará
visualizando el último registro en el texto, también se devuelve la consulta para asignar como DataSource de
la grilla.
Ejercicio 7
var Consulta = from cliente in MiEsquema.Cliente
where cliente.NomCli.StartsWith("D") ||
cliente.NomCli.StartsWith("J")
select cliente;
foreach (var elemento in Consulta)
{
texto.Text = elemento.NomCli;
}
return Consulta;
Código: Se diferencia del ejercicio anterior porque la consulta implementa una condición where para que
visualice los registros en los que el nombre del cliente empiece con las letras “C” o “J”.
Toda la información que presenten nuestras aplicaciones deberá estar ordenada para
simplificar al usuario su análisis. Esta operación se hace posible a través del operador
Ejercicio 8
return Consulta;
}
return Consulta;
}
Código: Tenemos 2 métodos el primero presenta el listado de clientes que tenemos ordenado en forma
ascendente de acuerdo al nombre, y el segundo presenta ordenado de forma descendente, lo único que
cambia es la palabra clave ascending o descending, en caso de que no se indique la forma de ordenamiento
el compilador por defecto asumirá que es ascendente.
Continuamente es necesario realizar cálculos a través de consultas para evitar tener que
ejemplos haremos ejercicios que nos darán como resultado el número de registros de la
tabla productos, el promedio del precio que tienen los produtos y la suma de los precios
Ejercicio 9
MessageBox.Show(Informe,"Informe:",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
Código: El código es muy facil de comprender, consiste en crear una cadena y concatenar el texto que se va a
presentar para indicar al usuario el resultado del proceso y la parte LINQ es solo la instancia del nuestro
contexto con el nombre de la entidad y llamamos al método Count() para que nos devuelva el número de
registros.
Para facilitar el entendimiento del código vamos a crear los siguientes dos métodos de la
entidad producto en nuestra clase cliente, para obtener el costo promedio de los precios
EJERCICIO 10
Código: Construimos la consulta LINQ entre parentesis y después de la palabra clave select seleccionamos la
propiedad precio (prom.PrePrd) despues cerramos el parentesis y llamamos al método Average()
que calcula el promedio y devuelve un valor decimal. Finalmente presentamos el resultado en un
cuadro de diálogo.
Ejercicio 11
Código: Es el mismo proceso que el cálculo del promedio, unicamente cambiamos la llamada al método
Average() por el método Sum().
Ejercicio 12
MessageBox.Show(cadena,"Informe:");
}
Código: La consulta de este ejercicio agrupa, ordena y seleciona los campos que vamos a presentar en el
resultado.
1- Indicamos la variable de iteracion (prod) y el origen de datos (MiEsquema.Productos)
2- Escribimos la palabra clave group seguido de la variable de iteracion que le informa al compilador
que se va a agrupar, después digitamos la palabra reservada by seguido de los campos por los que
vamos a agrupar. En el caso de ser más de un campo se debera hacer (new {campo1, campo2,
campo3…}.
3- A esto concatenamos la palabra into con un nombre de grupo (g).
4- Ordenamos los resultados.
5- Para terminar indicamos que campos deseamos visualizar através de la sentencia select, con la
diferencia que ahora en lugar de la variable de iteracion ponemos el nombre del grupo. Todos los
campos que se seleccionen deberán estar agrupados o ser parte de una funcion de agrupación.
Los resultados se presentan através del bucle foreach en donde preguntamos si el estado es true el grupo
esta activo caso contrario está descontinuado. Se forma la cadena y lo presentamos en la cuadro de diálogo.
El código del formulario para llamar a los métodos que hemos creado en nuestra clase
Código: Solo son llamadas a los métodos que hemos creado en la clase cliente. En el botón ordenar podemos
ver una línea en comentario, esto para que se ejecute el orden ascendente, luego comentamos el llamado al
orden ascedente y ejecutamos el llamado descendente.
Hasta aquí hemos hecho pequeños ejemplos sobre el funcionamiento de la cláusula Select
que nos permite consultar y cargar los datos de archivos XML, pero como hemos
también nos sirven para editar el contenido de los archivos con este formato.
Consecuentemente nos faltaría hacer el ejercicio que haga un AGE (Actualizar, Guardar y
Eliminar) a los datos de estos orígenes XML utilizando el potencial de “LINQ a XML ” para
EJERCICIO 13
En el evento click del botón de crear archivo escribiremos el siguiente código a través del
cual creamos nuestra estructua XML con la que desarrollaremos todo nuestro ejemplo:
try
{
XNamespace EN = "Espacio de Nombre";
XDocument MiDocumento =
new XDocument(
new XComment("Archivo XML que va a ser modificado"),
new XElement("Listado",
new XElement("Registro",
new XElement("Codigo", 1),
new XElement("Nombre", "Francisco José Ochoa Castillo"),
new XElement("Telefono", "2800600")
),
new XElement("Registro",
new XElement("Codigo", 2),
new XElement("Nombre", "Martha Florinda Iñiguez Santillán"),
new XElement("Telefono", "2800601")
)
)
);
MiDocumento.Save("d:\\EjemploAGE.xml");
MostarArchivo("Archivo creado:");
}
catch
{ MessageBox.Show("No se ha podido crear el archivo. Por favor vuelva a
intentar", "Crear archivo", MessageBoxButtons.OK, MessageBoxIcon.Error); }
Código: Creamos el archivo XML para poder realizar las operaciones de agregar, modificar y eliminar.
Podemos observar que en la primera linea hemos utilizado la clase XNameSpace, esta clase permite asignar
un espacio de nombre al archivo XML que estamos creando. También podemos notar que después de hacer
la instancia del documento utilizamos la clase XComment que agrega un comentario al documento XML.
El siguiente paso consiste en crear una enumeración y luego dos métodos el primero que
CargarNodo.ElementAt(0).ReplaceAll(
new XElement("Codigo", txtCodigo.Text),
new XElement("Nombre", txtNombre.Text),
new XElement("Telefono", txtTelefono.Text));
DocXML.Save("d:\\EjemploAGE.xml");
break;
case opciones.Eliminar:
EliminarNodo.Remove();
DocXML.Save("d:\\EjemploAGE.xml");
break;
}
MostarArchivo("Transaccion compleatada. El nuevo archivo es:");
}
catch
{
MostarArchivo("No se pudo completar la transaccion. El archivo actualmente se
encuentra de la siguiente forma:");
}
}
Código: Instanciamos y cargamos nuestro archivo con el método Load(). Creamos un switch() que clasificrá
las opciones de AGREGAR, MODIFICAR o ELIMINAR.
- Agregar.- Creamos la estructura que se va a adicionar en nuestro caso a través de la clase
XElement instanciada con el nombre de Nuevo Registro. Terminada de crear la nueva estructura la
implementamos a través del método Add() y finalmente guardamos el documento con el método
Save().
- Modificar.- A través de una consulta LINQ, colocamos en la variable CargarNodo el elemento
XElement que va a ser modificado, después con el método ReplaceAll(), reemplazamos los nuevos
valores del nodo y guardamos el documento con el método Save().
- Eliminar.- Así mismo con una consulta LINQ cargamos el nodo que va a ser eliminado. En nuestro
caso se llama EliminarNodo. El proceso de eliminar se realiza a través del método Remove(), y de
igual forma que en guardar y actualizar guardamos el documento con el método Save().
Finalmente solo nos falta hacer la llamada en los botones de Nuevo, Agregar y Eliminar
Tenemos cuatro cajas de texto en las que podemos ingresar el código, nombre, dirección y
teléfono del cliente; tres botones de edición: guardar, modificar y eliminar. Además
disponemos de una grilla en la parte inferior que nos permita visualizar el resultado de las
En los ejercicios de consulta agregamos una clase cliente a nuestro proyecto, en esa
Ejercicio 14
public void Guardar(string Codigo, string Nombre, string Direccion, string Telefono)
{
Cliente objCliente = new Cliente();
objCliente.CodCli = short.Parse(Codigo);
objCliente.NomCli = Nombre;
objCliente.DirCli = Direccion;
objCliente.TelCli = Telefono;
MiEsquema.Cliente.InsertOnSubmit(objCliente);
MiEsquema.SubmitChanges();
}
Código: Instanciamos la clase cliente, luego asignamos a las propiedades los valores de los parámetros que
recibe el método. Finalmente através de la instancia (MiEsquema) de nuestro ORM seleccionamos la entidad
que va a ser cambiada (Cliente) y llamamos al metodo InsertOnSubmit, le pasamos de parámetro nuestro
objeto cliente (objCliente) y el método SubmitChanges() lo utilizaremos en las tres transacciones para
confirmar los cambios en la base de datos.
public void Modificar(string Codigo, string Nombre, string Direccion, string Telefono)
{
var objActualiza = (from cliente in MiEsquema.Cliente
where cliente.CodCli == short.Parse(Codigo)
select cliente).First();
objActualiza.NomCli = Nombre;
objActualiza.DirCli = Direccion;
objActualiza.TelCli = Telefono;
MiEsquema.SubmitChanges();
}
Código: Hacemos una consulta LINQ que nos devuelva un registro de la entidad cliente. Para poder acceder
a las propiedades del objeto que cargamos notese que al final de la instrucción hemos utilizado el método
First(); que indica que el resultado es solo un registro. A continuación asignamos los nuevos valores y
enviamos a guardar através del método SubmitChanges().
Código: Igual que al momento de actualizar cargamos el registro que vamos a eliminar. Y ejecutamos la
acción a través del método DeleteOnSubmit pasando como parámetro objElimina. Y el método
SubmitChanges() confirma los cambios en la BD.
{
objCliente.Eliminar(txtCodigo.Text);
Cargar();
}
#region CargarDatosCliente
private void Cargar()
{
dtgListado.DataSource=objCliente.ListadoCliente();
}
#endregion
Código: En cada uno de los botones hacemos el llamado a los métodos de la clase cliente y pasamos de
parámetros los valores de las cajas de texto. El método cargar nos presentará los cambios que se van
realizando en la base de datos.
La mejor forma de entender como funcionan los procedimientos almacenados con LINQ,
es haciendo un ejemplo que ejecute estas sentencias SQL que tenemos guardadas en
nuestras bases de datos. Para el ejemplo que haremos a continuación vamos a utilizar el
mismo formulario del ejercicio anterior y solamente incrementaremos tres métodos más a
la clase clsClientes
Antes de comenzar a escribir código para utlizar procedimientos almacenados con LINQ,
debemos asegurarnos de que estos esten agregados en nuestro contexto. En caso de que
no hayamos agregado se lo puede hacer con tan solo arrastrar los PA a nuestro mapa
Como LINQ, trata todas las entidades de la base de datos como clases del .NET o de
cualquier lenguaje orientado a objetos que implemente el Framework 3.0 que abarca a
solo realizamos el llamado a los métodos de nuestra clase. Esto con la finalidad de tener
una sola clase a la que iremos agregando más metodos conforme avancemos en el estudio
de este tema.
Ejercicio 15
Código: Para ejecutar procedimientos almacenados desde LINQ utilizamos la instancia de nuestro contexto
(MiEsquema), luego seleccionamos el procedimiento almacenado que se va a ejecutar (CliGuarda,
CliActualiza o CliElimina) y finalmente enviamos los parámetros ().
Para ejecutar los procedimientos almacenados desde LINQ, debemos utilizar la instancia
del contexto en nuestro caso lo llamamos MiEsquema, luego al poner (.) nos presenta los
objetos de nuestro ORM y nosotros deberemos seleccionar el nombre del PA que vamos a
Cargar();
}
private void btnModificar_Click(object sender, EventArgs e)
{
objCliente.ModificarPA(txtCodigo.Text,txtNombre.Text,txtDireccion.Text, txtTelefono.Text);
Cargar();
}
private void btnEliminar_Click(object sender, EventArgs e)
{
objCliente.EliminarPA(txtCodigo.Text);
Cargar();
}
#endregion
#region CargarDatosCliente
private void Cargar()
{
dtgListado.DataSource=objCliente.ListadoCliente();
}
#endregion
Código: En el formulario se hace el mismo procedimiento que cuando hicimos las transacciones con
sentencias LINQ, solamente cambiando el nombre de los métodos de la clase.
como bases de datos relacionales. Por lo que hemos creído conveniente terminar el
de una factura de venta y cree un archivo XML con el contenido de la transacción que
realizamos.
1.- Búsquedas de clientes a través del botón que se encuentra a lado del código.
4.- Y en el menú tenemos las opciones para crear y guardar las facturas. Cuando
archivo XML.
Una vez que hemos explicado como va a funcionar el programa es hora de comenzar a
escribir el código. Vamos a agregar una clase factura (clsFactura) a nuestro proyecto que
Código: Librerías utilizadas ,instancia del contexto, declaración el DataTable que será el detalle de la factura
y creación del constructor por defecto de la clase factura.
GenerarDetalle
dtDetalle.Columns.Add("Codigo");
dtDetalle.Columns.Add("Descripcion");
dtDetalle.Columns.Add("Cantidad");
dtDetalle.Columns.Add("Precio");
dtDetalle.Columns.Add("Total");
dtDetalle.Columns["Precio"].DataType = System.Type.GetType("System.Double");
dtDetalle.Columns["Cantidad"].DataType = System.Type.GetType("System.Double");
dtDetalle.Columns["Total"].DataType = System.Type.GetType("System.Double");
dtDetalle.Columns["Precio"].DefaultValue = 0;
dtDetalle.Columns["Cantidad"].DefaultValue = 0;
dtDetalle.Columns["Total"].Expression=
"("+dtDetalle.Columns["Cantidad"]+")*("+dtDetalle.Columns["Precio"]+")";
return dtDetalle;
}
Código: Genera el DataTable de la factura, instancia y agrega las columnas que va a tener, en el segundo
bloque definimos el tipo de dato que van a tener las columnas: precio, cantidad, total. El tercer bloque asigna
el valor por defecto de precio y cantidad. Terminamos asignando una expresión de cálculo al DataTable con
el propósito que se calcule automáticamente cada línea de la factura.
Mensaje
Código: Este método será el encargado de presentar los mensajes de información de la clase factura
Número de Factura
Código: Propiedad que devuelve el número de factura, La instrucción LINQ busca en la cabecera de factura el
máximo valor que tenga el campo NumFac y le suma 1.
Actualizar Detalle
Código: Actualiza todos los cambios que se hayan hecho en el DataTable dtDetalle. Es como realizar una
confirmación y aceptación a las modificaciones realizadas en este.
Deshacer Cambios
Código: Deshace todos los cambios que se hayan realizado en el DataTable dtDetalle. Siempre y cuando no
se haya hecho antes un AcceptChanges.
Validar
return false;
}
else if (Nombre.Trim() == "")
{
Mensaje("No ha ingresado el nombre del cliente", "Datos del cliente");
return false;
}
else if (Direccion.Trim() == "")
{
Mensaje("No ha ingresado la dirección del cliente", "Datos del cliente");
return false;
}
else if (Nombre.Trim() == "")
{
Mensaje("No ha indicado la fecha de facturación", "Fecha Factura");
return false;
}
else if (dtDetalle.Rows.Count == 0)
{
Mensaje("Debe ingresar al menos un item paa facturar", "Facturar en cero");
return false;
}
return true;
}
catch
{
throw;
}
}
Código: Método de validación que devuelve verdadero si todos los datos están correctos, caso contrario a
través del método mensaje informa al usuario que campos le falta llenar.
Guardar Factura
public void Guardar(string CodCliente, string NomCliente, string Direccion, string telefono,
string Fecha, string total)
{
try
{
ActualizarDetalle();
int numero = pNumeroFactura;
sfdArchivo.ShowDialog();
if (sfdArchivo.FileName.Trim() == "")
{
if (MessageBox.Show("Debe indicar una ruta para generar el archvivo. Desea
volver a intentar", "Ruta archivo XML", MessageBoxButtons.YesNo,
MessageBoxIcon.Question) == DialogResult.No)
{
Preguntar = false;
}
}
else
{
Preguntar = false;
}
}
}
}
catch (Exception ex)
{
Mensaje(ex.Message, "Guardar");
}
}
Código: Este es el método más grande de nuestra clase. Pero no es nada complicado.
1.- En las dos primeras líneas actualizamos el detalle de la factura para que se confirmen los
cambios y después calculamos nuevamente el número de documento.
2.- Llamamos al método validar que devuelve un booleano para verificar que todos los campos
indispensables estén llenados. Cualquier error lo reportrá el propio método.
3.- Ejecutamos nuestro procedimiento almacenado para guardar la cabecera de la factura
(FacCabGuarda) enviando los parámetros que necesita.
4.- A continuación construimos un bucle para enviar a guardar el detalle. También con
Procedimiento almacenado(FacDetGuarda) enviando los parámetros que necesita.
5.- Pregunta al usuario a través de una caja de dialogo si quiere generar un archivo XML, de la
factura que generó.
6.- Si la respuesta del usuario fue SI, entonces declaramos un componente SaveFileDialog y una
variable booleana Preguntar que se inicia en false.
7.- Creamos un bucle que le presente al usuario el SaveFileDialog cuantas veces necesite para
guardar el archivo. Si el usuario no da una ubicación del archivo entonces le advierte que no se
generará el archivo y le preguntará si quiere asignar una ubicación o prefiere cancelar la creación
del archivo.
8.- Llama al método generar el archivo XML y pasa los parámetros que necesita.
9.- Solo si en alguno de los pasos anteriores ocurre un error, entraría al Catch y envía el contenido de
la variable de Excepción ex.
if (ruta.Trim() != "")
{
XElement Factura =
new XElement("Factura",
new XElement("Cabecera",
new XElement("Numero", numero),
new XElement("Cod_Cliente", CodCliente),
new XElement("Cliente", NomCliente),
new XElement("Dirección", DirCliente),
Código: Crea el archvio XML de la factura que se guardó. Comienza haciendo una validación que el
parámetro “ruta” no esté en blanco. Esto porque en el bucle que hay en el método que guarda la factura, sí
se da la opción al usuario de que no seleccione ningún lugar para guardar. En este caso no se genera el
archivo XML. Si pasa esta condición empieza a realizar los giguientes pasos:
1.- Declara un XElement Factura y dentro de esta instancia crea el nodo cabecera.
2.- En la misma instancia del nodo cabecera, crea los nodos hijos que tendrán la información de la
cabecera de la factura. Estos elementos son:
- Número de factura
- Código del cliente.
- Nombre del cliente.
- Dirección del cliente.
- Teléfono del cliente.
3.- Creamos un nodo detalle que contendrá todos los productos vendidos.
4.- Dentro del nodo detalle creamos elementos que contengan los valores:
- Item que es el número de línea del detalle
- Codido del producto
- Descripción del producto
- Cantidad vendida
- Precio de venta
- Total de línea
5.- Agregamos todos los subnodos al nodo item.
6.- Creamos el elemento Total y asignamos el valor de la venta.
7.- Agregamos el nodo item al nodo detalle.
8.- Agregamos el nodo detalle al nodo factura.
9.- Agregamos el nodo total a la factura.
10.- Guardamos el archivo xml en la ruta especificada por el usuario.
METODOS
Código: Creamos dos métodos. El primero que calculará el total de la venta y el segundo que habilitará e
inhabilitará los controles de acuerdo al parámetro que reciba.
BOTONES
Código: En los tres botones del formulario escribimos el Código correspondiente a cada uno de ellos.
Acuérdese el botón buscar cliente está representado por “…”, lo que hacemos en cada uno de los botones es
llamar al respectivo formulario.
1.- Botón Buscar Cliente.- Llamamos al formulario de buscar clientes que nos devolverá un vector
con toda la información del cliente que seleccionemos. Como se verá al momento que construyamos
dicho formulario.
2.- Botón Agregar Productos.- Llama al formulario que agrega los productos. Este formulario recibe
de parámetro un DataTable que será el que almacene los items. Después de cargar cada item llama
al método que calcula el Total().
3.- Llama al formulario que crea, modifica y elimina los clientes.
EVENTOS
Esto se debe a que al crear el DataTable nosotros asignamos el tipo de datos entero.
información de los mismos, con sus: códigos, nombres, precios y existencia actual. En este
la figura de abajo.
public frmProductos() { }
Código: Las primeras líneas declaran las instancias del DataTable y del contexto. Después podemos ver que
tenemos dos constructores el uno sin parámetros que controla la existencia de productos cuando se lo llama
desde la factura en el evento CellValueChanged y el otro que recibe de parámetro un DataTable que se utiliza
cuando se está agregando productos. Ver botón Productos en el formulario de Factura.
LOAD
dtgFacturados.DataSource = dtFacturado;
}
Código: En el Load() del formulario, escribimos la instrucción LINQ que cargue los registros de la entidad
producto en los cuales el estado del producto sea activo y ordenado por código. En el select podemos
observar que cada campo de la entidad producto esta precedido por un identificador y el signo igual. Esto es
el equivalente a la palabra clave “AS” de las sentencias SQL.
METODOS
EVENTOS
Código: Este es el único evento que tiene el formulario de agregar productos. Cuando hace click en la
cabecera de las filas. Agrega una fila al DataTable que pasamos de parámetro y se reflejan los cambios en el
detalle de la factura inmediatamente.
Ahora nos toca hacer una pantalla que nos permita buscar nuestros clientes. Este
En la figura anterior podemos notar que hay una pantalla encerrada. Esa es la que nos
Código: De igual forma que en las ventanas anteriores, debemos instanciar nuestro contexto. También
tenemos un vector object que nos devolverá el cliente seleccionado.
LOAD
Código: Instrucción LINQ que carga todos los clientes que estén activos y el resultado se asigna a una grilla.
EVENTOS
Código: Toma la fila seleccionada y la asigna al vector que declaramos en la parte superior.
PROPIEDADES
Para terminar haremos la implementación del formulario crear cliente. Que será llamado
desde el botón cliente. El formulario para realizar transacciones con clientes ha sdido
public clsCliente() { }
public void ModificarPA(string Codigo, string Nombre, string Direccion, string Telefono)
{
MiEsquema.CliActualiza(short.Parse(Codigo), Nombre, Direccion, Telefono);
}
Código: Cada método llama al respectivo procedimiento almacenado, salvo el método de ListadoCliente()
que ejecuta la consulta LINQ que nos devolverá todos los registros de clientes.
Para mejorar el aprendizaje proponemos a los lectores realizar los siguientes ejercicios.
registros de productos.
- Crear las tablas necesarias en la base de datos y una pantalla que registre los
- Construir las tablas necesarias en la base de datos y una ventana que permita
CONCLUSIONES
LINQ, es una utilidad que inicialmente ha sido implementada en el framework 3.0 para los
lenguajes de c# 3.0 y Visual Basic 9.0 pero que por la funcionalidad y versatilidad que
ofrece para la conexión con diferentes orígenes de datos. En poco tiempo se irá
incorporando a los demás lenguajes para que pueda ser usado por los desarrolladores de
También podemos decir que los desarrolladores que se dediquen a estudiar y aplicar esta
tecnología unicamente necesitarán conocer dos lenguajes para acceder a cualquier fuente
de datos. LINQ como el lenguaje principal y SQL que se convertirá en una herramienta
Para concluir acotamos que en la actualidad con la demanda que tienen las aplicaciones
web, formatos XML, etc. Se necesita que los desarrolladores de software sean cada vez
más eficientes y esta tecnología es una de las utensilios que nos facilitarán cumplir con
esta meta.
RECOMENDACIONES
Las consultas embebidas en los lenguajes de programación son muy extensas por lo que
para realizar un estudio a fondo que nos permita explotar todo el potencial que estas nos
En esta monografía hemos realizado una introducción a las prerrogativas básicas que nos
brinda LINQ en las áreas de “LINQ a SQL” y “LINQ a XML”. Pero le recordamos a nuestros
profesionales que lean este proyecto para que profundicen en el tema y de la misma
manera lo hagan público. Para que los programadores que vengan detrás de nosotros
puedan explotar cada vez más estas técnicas de desarrollo y que las combinen con las
herramientas que estén en auge en los momentos que ellos estén construyendo sus
BIBLIOGRAFIA
- www.msdn.com.es
- www.thingking.net
- www.elguille.com
- www.developer.mozilla.org
- www.wikipedia.org
- http://geeks.ms
- www.netdeveloper.com
- www.onglases.net
- www.wikipedia.org