Language Integrated Query ( LINQ , pronunciado "enlace") es un componente de Microsoft .NET Framework que agrega capacidades nativas de consulta de datos a los lenguajes .NET , lanzado originalmente como una parte importante de .NET Framework 3.5 en 2007.
LINQ amplía el lenguaje mediante la adición de expresiones de consulta , que son similares a declaraciones SQL , y se pueden utilizar para extraer y procesar datos de forma conveniente desde matrices , clases enumerables , documentos XML , bases de datos relacionales y fuentes de datos de terceros. Otros usos, que utilizan expresiones de consulta como marco general para componer cálculos arbitrarios de forma legible, incluyen la construcción de controladores de eventos [1] o analizadores monádicos . [2] También define un conjunto de nombres de métodos (llamados operadores de consulta estándar u operadores de secuencia estándar ), junto con las reglas de traducción utilizadas por el compilador para traducir expresiones de sintaxis de consulta en expresiones que utilizan un estilo fluido (llamado sintaxis de método por Microsoft) con estos nombres de métodos, expresiones lambda y tipos anónimos .
Existen puertos de LINQ para PHP (PHPLinq), JavaScript (linq.js), TypeScript (linq.ts) y ActionScript (ActionLinq), aunque ninguno es estrictamente equivalente a LINQ en los lenguajes inspirados en .NET C#, F# y VB.NET. (donde es parte del lenguaje, no una biblioteca externa, y donde a menudo aborda una gama más amplia de necesidades). [ cita necesaria ]
A continuación, las descripciones de los operadores se basan en la aplicación del trabajo con colecciones. Muchos de los operadores toman otras funciones como argumentos. Estas funciones pueden proporcionarse en forma de método con nombre o función anónima.
El conjunto de operadores de consulta definidos por LINQ se expone al usuario como API del operador de consulta estándar (SQO) . Los operadores de consulta soportados por la API son: [3]
El operador Select realiza una proyección sobre la colección para seleccionar aspectos interesantes de los elementos. El usuario proporciona una función arbitraria, en forma de expresión lambda o con nombre , que proyecta los miembros de datos. La función se pasa al operador como delegado .
El operador Where permite la definición de un conjunto de reglas de predicado que se evalúan para cada objeto de la colección, mientras que los objetos que no coinciden con la regla se filtran. El predicado se proporciona al operador como delegado.
Para una asignación proporcionada por el usuario de elementos de colección a colecciones, semánticamente se realizan dos pasos. Primero, cada elemento se asigna a su colección correspondiente. En segundo lugar, el resultado del primer paso se aplana en un nivel. Nota: Tanto Select como Where se pueden implementar en términos de SelectMany, siempre que haya colecciones únicas y vacías disponibles. Las reglas de traducción mencionadas anteriormente aún hacen obligatorio que un proveedor de LINQ proporcione los otros dos operadores.
Opcionalmente, estos operadores toman una función que recupera un determinado valor numérico de cada elemento de la colección y lo utiliza para encontrar los valores suma, mínimo, máximo o promedio de todos los elementos de la colección, respectivamente. Las versiones sobrecargadas no tienen ninguna función y actúan como si la identidad se proporcionara como lambda.
Una suma generalizada / mín / máx. Este operador toma una función que especifica cómo se combinan dos valores para formar un resultado intermedio o final. Opcionalmente, se puede proporcionar un valor inicial, lo que permite que el tipo de resultado de la agregación sea arbitrario. Además, se puede proporcionar una función de finalización que lleve el resultado de la agregación a otro valor más.
IGrouping<Key, Values>
objetos, para cada valor clave distinto. Luego, los IGrouping
objetos se pueden usar para enumerar todos los objetos para un valor clave particular.La API del operador de consulta estándar también especifica ciertos operadores que convierten una colección en otro tipo: [3]
IEnumerable<T>
. [4]IQueryable<T>
.T[]
a partir de la colección.List<T>
a partir de la colección.Dictionary<K, T>
a partir de la colección, indexado por la clave K. Una función de proyección proporcionada por el usuario extrae una clave de cada elemento.Lookup<K, T>
a partir de la colección, indexado por la clave K. Una función de proyección proporcionada por el usuario extrae una clave de cada elemento.IEnumerable
en una IEnumerable<T>
emitiendo cada elemento al tipo T
. Alternativamente, convierte un genérico IEnumerable<T>
en otro genérico IEnumerable<R>
al convertir cada elemento de un tipo T
a otro R
. Lanza una excepción en cualquier elemento que no se puede convertir al tipo indicado.IEnumerable
en una de IEnumerable<T>
. Alternativamente, convierte un genérico IEnumerable<T>
en otro genérico IEnumerable<R>
intentando convertir cada elemento de un tipo T
a otro R
. En ambos casos, solo se incluye el subconjunto de elementos convertidos correctamente al tipo de destino. No se lanzan excepciones.Si bien LINQ se implementa principalmente como una biblioteca para .NET Framework 3.5, también define extensiones de lenguaje opcionales que hacen de las consultas una construcción de lenguaje de primera clase y proporcionan azúcar sintáctico para escribir consultas. Estas extensiones de lenguaje se implementaron inicialmente en C# 3.0, [5] : 75 VB 9.0 , F# [6] y Oxygene , y otros lenguajes como Nemerle han anunciado soporte preliminar. Las extensiones de idioma incluyen: [7]
var
palabra clave. En VB9.0, la Dim
palabra clave sin declaración de tipo logra lo mismo. Estos objetos todavía están fuertemente tipados ; para estos objetos el compilador infiere los tipos de variables mediante inferencia de tipos , lo que permite especificar y definir los resultados de las consultas sin declarar el tipo de las variables intermedias.Por ejemplo, en la consulta para seleccionar todos los objetos de una colección con SomeProperty
menos de 10,
var resultados = de c en SomeCollection donde c . AlgunaPropiedad < 10 seleccione nuevo { c . Alguna propiedad , c . Otra propiedad }; foreach ( var resultado en resultados ) { Console . WriteLine ( resultado ); }
El compilador infiere todos los tipos de variables result , c y results de acuerdo con las firmas de los métodos finalmente utilizados. La base para elegir los métodos está formada por el resultado de la traducción sin expresión de la consulta.
var resultados = AlgunaColección . Donde ( c => c . AlgunaPropiedad < 10 ) . Seleccione ( c => nuevo { c . AlgunaPropiedad , c . OtraPropiedad }); resultados . ForEach ( x => { Consola . WriteLine ( x . ToString ());})
La especificación C#3.0 define un patrón de expresión de consulta junto con reglas de traducción de una expresión LINQ a una expresión en un subconjunto de C# 3.0 sin expresiones LINQ. La traducción así definida en realidad no tiene tipo, lo que, además de que las expresiones lambda se pueden interpretar como delegados o árboles de expresión, permite un gran grado de flexibilidad para las bibliotecas que deseen exponer partes de su interfaz como cláusulas de expresión LINQ. Por ejemplo, LINQ to Objects funciona con IEnumerable<T>
s y con delegados, mientras que LINQ to SQL utiliza árboles de expresión.
Los árboles de expresión son el núcleo del mecanismo de extensibilidad de LINQ, mediante el cual LINQ se puede adaptar a muchas fuentes de datos. Los árboles de expresión se entregan a los proveedores LINQ, que son implementaciones específicas de la fuente de datos que adaptan las consultas LINQ para usarse con la fuente de datos. Si así lo desean, los proveedores LINQ analizan los árboles de expresión contenidos en una consulta para generar piezas esenciales necesarias para la ejecución de una consulta. Pueden ser fragmentos de SQL o cualquier otra representación de código completamente diferente como datos manipulables adicionales. LINQ viene con proveedores LINQ para colecciones de objetos en memoria, bases de datos de Microsoft SQL Server , conjuntos de datos ADO.NET y documentos XML. Estos diferentes proveedores definen las diferentes versiones de LINQ:
El proveedor LINQ to Objects se utiliza para colecciones en memoria, utilizando el motor de ejecución de consultas local de LINQ. El código generado por este proveedor hace referencia a la implementación de los operadores de consulta estándar definidos en el Sequence
patrón y permite IEnumerable<T>
consultar las colecciones localmente. La implementación actual de LINQ to Objects realiza comprobaciones de implementación de interfaz para permitir pruebas de membresía, recuentos y operaciones de búsqueda indexadas rápidas cuando son compatibles con el tipo de tiempo de ejecución de IEnumerable. [8] [9] [10]
El proveedor LINQ to XML convierte un documento XML en una colección de XElement
objetos, que luego se consultan utilizando el motor de ejecución local que se proporciona como parte de la implementación del operador de consulta estándar. [11]
El proveedor LINQ to SQL permite utilizar LINQ para consultar bases de datos de Microsoft SQL Server , incluidas las bases de datos de SQL Server Compact . Dado que los datos de SQL Server pueden residir en un servidor remoto y que SQL Server tiene su propio motor de consultas, LINQ to SQL no utiliza el motor de consultas de LINQ. En su lugar, convierte una consulta LINQ en una consulta SQL que luego se envía a SQL Server para su procesamiento. [12] Sin embargo, dado que SQL Server almacena los datos como datos relacionales y LINQ trabaja con datos encapsulados en objetos, las dos representaciones deben asignarse entre sí. Por este motivo, LINQ to SQL también define un marco de mapeo. El mapeo se realiza definiendo clases que corresponden a las tablas de la base de datos y que contienen todas o un subconjunto de las columnas de la tabla como miembros de datos. [13] La correspondencia, junto con otros atributos del modelo relacional , como las claves primarias , se especifican mediante atributos definidos por LINQ to SQL . Por ejemplo,
[Table(Name="Clientes")] public class Cliente { [Column(IsPrimaryKey = true)] public int CustID ; [Columna] cadena pública CustName ; }
Esta definición de clase se asigna a una tabla denominada Customers
y los dos miembros de datos corresponden a dos columnas. Las clases deben definirse antes de poder utilizar LINQ to SQL. Visual Studio 2008 incluye un diseñador de asignaciones que se puede utilizar para crear la asignación entre los esquemas de datos en el objeto y el dominio relacional. Puede crear automáticamente las clases correspondientes a partir de un esquema de base de datos , así como permitir la edición manual para crear una vista diferente utilizando solo un subconjunto de las tablas o columnas de una tabla. [13]
El mapeo se implementa mediante el DataContext
que toma una cadena de conexión al servidor y se puede usar para generar un Table<T>
donde T es el tipo al que se asignará la tabla de la base de datos. Encapsula Table<T>
los datos en la tabla e implementa la IQueryable<T>
interfaz, de modo que se crea el árbol de expresión, que maneja el proveedor LINQ to SQL. Convierte la consulta a T-SQL y recupera el conjunto de resultados del servidor de la base de datos. Dado que el procesamiento se realiza en el servidor de la base de datos, no se pueden utilizar métodos locales que no estén definidos como parte de las expresiones lambda que representan los predicados. Sin embargo, puede utilizar los procedimientos almacenados en el servidor. Se realiza un seguimiento de cualquier cambio en el conjunto de resultados y se puede enviar nuevamente al servidor de la base de datos. [13]
Dado que el proveedor LINQ to SQL (arriba) solo funciona con bases de datos de Microsoft SQL Server , para admitir cualquier base de datos genérica, LINQ también incluye LINQ to DataSets. Utiliza ADO.NET para manejar la comunicación con la base de datos. Una vez que los datos están en conjuntos de datos ADO.NET, LINQ to DataSets ejecuta consultas en estos conjuntos de datos. [14]
Los usuarios no profesionales pueden tener dificultades con las sutilezas de las funciones y la sintaxis de LINQ to Objects . Los patrones ingenuos de implementación de LINQ pueden provocar una degradación catastrófica del rendimiento. [15] [16]
El rendimiento de LINQ to XML y LINQ to SQL en comparación con ADO.NET depende del caso de uso. [17] [18]
La versión 4 del marco .NET incluye PLINQ , o Parallel LINQ , un motor de ejecución paralela para consultas LINQ. Define la ParallelQuery<T>
clase. Cualquier implementación de la IEnumerable<T>
interfaz puede aprovechar el motor PLINQ llamando al AsParallel<T>(this IEnumerable<T>)
método de extensión definido por la clase ParallelEnumerable en el espacio de nombres System.Linq del marco .NET. [19] El motor PLINQ puede ejecutar partes de una consulta simultáneamente en múltiples subprocesos, proporcionando resultados más rápidos. [20]
Muchos de los conceptos que introdujo LINQ se probaron originalmente en el proyecto de investigación Cω de Microsoft , anteriormente conocido con los nombres en clave X# (X Sharp) y Xen . Pasó a llamarse Cω después de que se integrara Polyphonic C# , otro lenguaje de investigación basado en el cálculo de unión .
Cω intenta hacer que los almacenes de datos (como bases de datos y documentos XML ) sean accesibles con la misma facilidad y seguridad de tipos que los tipos tradicionales como cadenas y matrices . Muchas de estas ideas fueron heredadas de un proyecto de incubación anterior dentro del equipo XML de WebData llamado X# y Xen. Cω también incluye nuevas construcciones para soportar la programación concurrente ; Estas características se derivaron en gran medida del proyecto anterior Polyphonic C#. [21]
Disponible por primera vez en 2004 como una vista previa del compilador, las características de Cω fueron utilizadas posteriormente por Microsoft en la creación de las características de LINQ lanzadas en 2007 en .NET versión 3.5 [22]. Las construcciones de concurrencia también se han lanzado en una forma ligeramente modificada como una biblioteca. denominado Joins Concurrency Library , para C# y otros lenguajes .NET por Microsoft Research . [23]
Si bien es cierto que LINQ es potente y muy eficiente, grandes conjuntos de datos aún pueden causar problemas de rendimiento inesperados.
Al llamar a una consulta varias veces con Entity Framework, el enfoque recomendado es utilizar consultas LINQ compiladas. La compilación de una consulta da como resultado un impacto en el rendimiento la primera vez que usa la consulta, pero las llamadas posteriores se ejecutan mucho más rápido.