Programación con LINQ

Download Report

Transcript Programación con LINQ

LINQ
(Language-Integrated Query)
Gabriel Espinoza Erices
2012 – 03 – 15
INTRODUCCIÓN
• LINQ es una innovación para disminuir la
brecha entre el mundo de los objetos y el
mundo de los datos.
INTRODUCCIÓN
• En Visual Studio, se pueden escribir consultas
LINQ en VB o C# con:
– Bases de datos SQL Server (Linq to SQL)
– Documentos XML (Linq to XML)
– DataSets ADO.NET (Linq to DataSet)
– Colecciones de objetos que soporten IEnumerable
o IEnumerable<T>
• Requiere .NET Framework 3.5+
QUERYABLE TYPES
• Los tipos que soportan IEnumerable<T> o derivan
de interfaces como IQueryable<T> son llamados
Queryable Types
• Un objeto “queryable type” no requiere
modificación ni trato especial para servir como
datasource de LINQ.
• Si los datos orígenes no están en memoria como
“queryable type”, el proveedor de LINQ debe
representarlo como si estuviera.
LINQ TO SQL COMO QUERYABLE TYPE
Con LINQ to SQL,
primero se crea un
mapeo objeto-relacional
en tiempo de diseño, se
crean las querys contra
esos objetos. Luego en
tiempo de ejecución,
LINQ to SQL maneja la
comunicación con la
base de datos.
LA QUERY
• La query especifica qué información obtener de una o varias
fuentes. Opcionalmente la query también puede especificar
como esos datos deben ser ordenados, agrupados y formados
antes de ser retornada.
• Una query tiene 3 clausulas:
– from: Especifica el data source
– where: aplica el filtro
– select: especifica el tipo de los elementos retornados.
EJECUCIÓN DE LA QUERY
La query por si misma no ejecuta
ninguna acción ni retorna datos. Solo
almacena la información requerida para
producir los resultados cuando la
consulta sea ejecutada en un punto
posterior.
FORZAR EJECUCIÓN INMEDIATA
•
Las querys que realizan
funciones de agregado sobre
un rango de fuentes, primero
deben iterar sobre esos
elementos. Ej: Count, Max,
Average, First, etc. Notar que
estas consultas no retornan
una colección, sino que un
valor.
•
Para forzar la ejecución
inmediata de cualquier
query y almacenar en cache
sus resultados, se pueden
usar el ToList<T> o
ToArray<T>
SINTAXIS
Son equivalentes
OPERACIONES BÁSICAS
Range Variable
Delcaración del
DataSource
DataSource
Declaración de filtros
Da “forma” al resultado
de la query
ORDER BY & JOINS
Ordenamiento
Un join crea
asociaciones entre
secuencias que no
estén explicitamente
modeladas en los
DataSources. En LINQ
los join SIEMPRE
funcionan contra
objetos y no tablas de
bases de datos
AGRUPAR
Agrupar: Al agrupar,
se forma una lista
de listas.
La cláusula group permite
agrupar los resultados basados
en una llave que se especifique.
Ejemplo: Los resultados
agrupados por ciudad, de tal
manera que los clientes de
Londres o Paris son grupos
individuales. En este caso
cust.City es la llave.
Si se necesita interactuar
con el grupo, se puede
usar into para crear una
variable que lo referencie
CONSULTAS y TRANSFORMACIONES DE
TIPOS DE DATO
LINQ y CLASES GENÉRICAS
• IEnumerable<T> es la interfaz que habilita que las
colecciones de clases genéricas puedan ser
enumeradas usando la sentencia foreach. Las
colecciones de clases genéricas soportan
IEnumerable<T> tal como las colecciones no
genéricas (arraylists).
• Cuando veas que una query está escripta como
IEnumerable<Customer>, significa que la query,
cuando sea ejecutada, producirá una secuencia
de 0 o más objetos Customer.
LINQ y CLASES GENÉRICAS
Se puede dejar que el
compilador maneje la
declaración genérica.
POR QUÉ LINQ VENCE A SQL
Supongamos queremos
hacer una consulta simple
SELECT UPPER(Name)
FROM Customer
WHERE Name LIKE 'A%'
ORDER BY Name
Ahora supongamos que
estos resultados están
alimentando una página
web y queremos obtener
solo las filas 21-30. De la
nada, ahora necesitamos
una subquery
SELECT UPPER(Name) FROM
(
SELECT *, RN = row_number()
OVER (ORDER BY Name)
FROM Customer
WHERE Name LIKE 'A%'
)A
WHERE RN BETWEEN 21 AND 30
ORDER BY Name
POR QUÉ LINQ VENCE A SQL
Y si necesitamos agregar
soporte para bases de datos
anteriores a SQL SERVER
2005, se vuelve peor aún!!
No solo es complicado, sino
que viola el principio DRY
(Don’t Repeat Yourself)
SELECT TOP 10 UPPER (c1.Name)
FROM Customer c1
WHERE
c1.Name LIKE 'A%'
AND c1.ID NOT IN
(
SELECT TOP 20 c2.ID
FROM Customer c2
WHERE c2.Name LIKE 'A%'
ORDER BY c2.Name
)
ORDER BY c1.Name
POR QUÉ LINQ VENCE A SQL
SELECT TOP 10 UPPER (c1.Name)
FROM Customer c1
WHERE
c1.Name LIKE 'A%'
AND c1.ID NOT IN
(
SELECT TOP 20 c2.ID
FROM Customer c2
WHERE c2.Name LIKE 'A%'
ORDER BY c2.Name
)
ORDER BY c1.Name
var query = from c in db.Customers
where c.Name.StartsWith("A")
orderby c.Name
select c.Name.ToUpper();
var thirdPage = query.Skip(20).Take(10);
Aquí tenemos la misma
consulta pero en LINQ. La
ganancia en simplicidad es
clara.
Composability: Podemos
dividir la consulta y hacer
métodos genéricos
reutilizables
IQueryable<T> Paginate<T> (this IQueryable<T> query,
int skip, int take)
{
return query.Skip(skip).Take(take);
}
var thirdPage = query.Paginate (20, 10);
SELECT p.*
FROM Purchase p
LEFT OUTER JOIN
Customer c INNER JOIN Address a ON c.AddressID = a.ID
ON p.CustomerID = c.ID
WHERE
(a.State = 'WA' || p.CustomerID IS NULL)
AND p.ID in
(
SELECT PurchaseID FROM PurchaseItem
GROUP BY PurchaseID HAVING SUM (SaleAmount) > 1000
)
Otro beneficio de LINQ es que
puedes consultar a través de
relaciones sin tener que hacer
joins.
var purchases = from p in db.Purchases
where p.Customer.Address.State == "WA“ || p.Customer == null
where p.PurchaseItems.Sum(pi => pi.SaleAmount) > 1000
select p;
Parametrización: No hay que complicarse con los ataques de inyección de SQL pues la
parametrización de LINQ es inline, segura y altamente legible.
IQueryable<Customer> GetCustomers (string state, decimal? minPurchase)
{
var query = Customers.AsQueryable();
if (state != null)
query = query.Where (c => c.Address.State == "WA");
if (minPurchase != null)
query = query.Where (c => c.Purchases.Any (p => p.Price > minPurchase.Value));
return query;
}
SELECT [t0].[ID], [t0].[Name], [t0].[AddressID]
FROM [Customer] AS [t0]
Si pasamos state y minPurchase nulos
SELECT [t0].[ID], [t0].[Name], [t0].[AddressID]
FROM [Customer] AS [t0]
LEFT OUTER JOIN [Address] AS [t1] ON [t1].[ID] = [t0].[AddressID]
WHERE (EXISTS(
SELECT NULL AS [EMPTY]
FROM [Purchase] AS [t2]
WHERE ([t2].[Price] > @p0) AND ([t2].[CustomerID] = [t0].[ID])
)) AND ([t1].[State] = @p1)
Si pasamos ambos valores LINQ no solo
agregará los predicados, sino que
también los JOINS
Cuándo no usar LINQ
A pesar de su poder, LINQ no deja obsoleto a SQL.
Toma más del 95% de la funcionalidad de las
queries, pero se seguirá necesitando SQL para:
• Queries que busquen máxima optimización.
• Queries que involucren seleccionar en tablas
temporales y luego consultar esas tablas.
• Actualizaciones con predicados e inserciones
masivas (bulk)
• Triggers
Tener que conocer dos lenguajes para querys no
es realmente un problema, dado que de todas
maneras LINQ provee una forma común para
interactuar con distintas fuentes de datos.
Arreglos, Listas, XML, Bases de Datos, y en
general, de cualquier objeto que herede de
IQueryable y IEnumerable.
Fuentes
• http://msdn.microsoft.com/enus/library/bb397933.aspx
• http://msdn.microsoft.com/enus/library/bb397926.aspx
• http://www.codeproject.com/Articles/230380
/LINQ-to-SQL-Advanced-Concepts-andFeatures
• http://www.linqpad.net/WhyLINQBeatsSQL.as
px