Diez Mitos LINQ
Autor: Joseph Albahari & Ben Albahari.
Traducción: Yuneisy Rodriguez.
Presentamos diez de las principales causas de los malentendidos
más comunes destilados de muchos cientos de preguntas en los fórums
sobre LINQ.
Mito #1
Todas las consultas LINQ deben comenzar con la palabra reservada
var . De hecho, el propósito de la palabra reservada
var es iniciar una consulta LINQ.
La palabra reservada var y las consultas LINQ, son conceptos diferentes.
El propósito del var es dejar que el compilador adivine de
qué tipo desea declarar la variable local (tipos implícitos).
Por ejemplo, lo siguiente:
var s = "Hello";
es exactamente equivalente a:
string s = "Hello";
porque el compilador infiere que s es un string.
Así mismo, la siguiente consulta:
string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = people.Where (p => p.Length > 3);
es exactamente equivalente a:
string[] people = new [] { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredPeople = people.Where (p => p.Length > 3);
Aquí puede ver, que todo lo que estamos logrando con var
es abreviar IEnumerable<string>. A algunas personas les gusta
esto porque quita desorden; otros argumentan que los tipos implícitos pueden
dar una menor idea de qué es lo que está pasando
Ahora, hay ocasiones en que una consulta LINQ exige el uso de la
palabra reservada var. Esto es cuando se proyecta un tipo anónimo:
string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = people.Select (p => new { Name = p, p.Length });
Aquí está un ejemplo del uso de un tipo anónimo fuera
del contexto de una consulta LINQ:
var person = new { Name="Foo", Length=3 };
Mito #2
Todas las consultas LINQ deben utilizar la sintaxis de consulta.
Existen dos tipos de sintaxis para las consultas: sintaxis lambda
y sintaxis de consulta (o sintaxis de consulta de comprensión).
Aquí está un ejemplo de sintaxis lambda :
string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = people.Where (p => p.Length > 3);
Aquí está lo mismo expresado en sintaxis de consulta:
string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = from p in people where p.Length > 3 select p;
Lógicamente, el compilador convierte la sintaxis de consulta a una
sintaxis de tipo lambda. Esto significa que todo lo que puede expresarse
en sintaxis de consulta, también puede expresarse en sintaxis lambda.
La sintaxis de consulta puede ser mucho más sencilla, mucho más
con consultas que involucran a más de una variable de rango.
(En este ejemplo, usamos una única variable de rango p, así
que las dos sintaxis fueron igual de sencillas)
No todos los operadores admiten sintaxis de consulta, por lo que los dos
estilos de sintaxis son complementarios. Tomando lo mejor de ambos, usted
puede mezclar los estilos de consulta en un único comando (vea el
ejemplo del Mito #5)
Mito #3
Para recuperar todos los clientes de la tabla de clientes, debe
realizar una consulta similar a la siguiente:
var query = from c in db.Customers select
c;
La expresión:
from c in db.Customers select c
es una consulta frívola. Usted puede simplemente usar:
db.Customers
Asimismo, la siguiente consulta LINQ to XML:
var xe = from e in myXDocument.Descendants ("phone") select e;
puede ser simplificada a:
var xe = myXDocument.Descendants ("phone");
Y esto:
Customer customer = (from c in db.Customers where c.ID == 123 select c)
.Single();
puede ser simplificado a:
Customer customer = db.Customers.Single (c => c.ID == 123);
Mito #4
Para reproducir una consulta SQL en LINQ, debe realizar la consulta
LINQ tan similar como sea posible a la consulta SQL.
LINQ y SQL son lenguajes diferentes, que emplean
conceptos muy diferentes.
Posiblemente la mayor barrera para hacerse productivo con LINQ es el síndrome
de "pensando en SQL": mentalmente formular sus consultas en SQL
y, a continuación, transliterarlas dentro de LINQ. El resultado es
que usted está constantemente luchando contra la API.
Una vez que comience a pensar
directamente en LINQ, a menudo sus consultas tendrán poca semejanza
con sus contrapartes en SQL. En muchos casos, también podrá
ser radicalmente más sencillo.
Mito #5
Para realizar los joins eficientemente en LINQ, usted debe
utilizar la palabra reservada join.
Esto es cierto, pero sólo al consultar colecciones locales. Al consultar
una base de datos, la palabra reservada join es totalmente innecesaria:
todos las uniones pueden ser realizadas utilizando múltiples cláusulas
from y subconsultas. El uso de múltiples cláusulas
from y subconsultas es más versátil
también y nos permiten realizar non-equi-joins.
Mejor aún, en LINQ to SQL, se pueden consultar propiedades de
asociación, sin la necesidad de utilizar los join. Por ejemplo,
le explicamos cómo recuperar los nombres y el ID de todos los clientes
que no han hecho compras:
from c in db.Customers
where !c.Purchases.Any()
select new { c.ID, c.Name }
O, para recuperar los clientes que no han hecho compras por encima de $
1000:
from c in db.Customers
where !c.Purchases.Any (p => p.Price > 1000)
select new { c.ID, c.Name }
Observe que estamos mezclando la sintaxis de consulta y la sintaxis lambda.
Consulte LINQPad para más ejemplos de propiedades
de asociación, joins manuales y consultas con
sintaxis mezclada.
Mito #6
Porque SQL emite conjuntos de resultados planos, las consultas
LINQ deben estructurarse para emitir conjuntos de resultados planos, también.
Esto es una consecuencia del Mito # 4. Uno de los grandes beneficios de
LINQ es que puede:
- Consultar un gráfico de un objeto estructurado, a través
de las propiedades de asociación (en lugar
de tener que utilizar joins)
- Proyectar directamente dentro de las jerarquías de objetos.
Los dos beneficios son independientes, aunque 1 ayuda a 2.
Por ejemplo, si desea recuperar los nombres de clientes en el estado de
WA, junto con todas sus compras, puede simplemente hacer lo siguiente:
from c in db.Customers
where c.State == "WA"
select new
{
c.Name,
c.Purchases // An EntitySet (collection)
}
El resultado jerárquico de esta consulta es mucho más fácil
trabajar, que con un conjunto resultado plano.
Podemos lograr el mismo resultado sin las propiedades de asociación
de la siguiente manera:
from c in db.Customers
where c.State == "WA"
select new
{
c.Name,
Purchases = db.Purchases.Where (p => p.CustomerID == c.ID)
}
Myth #7
Para hacer joins externos en LINQ to SQL, debe utilizar siempre
DefaultIfEmpty().
Esto es cierto sólo si desea un conjunto resultado plano
. Los ejemplos en el mito anterior, por ejemplo, se convierten en un left
outer join en SQL y no requieren del operador DefaultIfEmpty.
Mito #8
Una consulta LINQ to SQL va a ser ejecutada en una sola ida al
servidor, solo si la consulta fue construida en un solo paso.
LINQ sigue un modelo de evaluación tardía, lo que significa
que las consultas se ejecutan no cuando se construyeron, sino cuando se
enumeraron. Esto significa que puede crear una consulta en tantos pasos
como desee, y en el proceso no consulta al servidor hasta que finalmente
inicie el consumo de los resultados.
Por ejemplo, la siguiente consulta recupera los nombres de todos los clientes
cuyo nombre empieza con la letra 'A', y que han hecho al menos dos compras.
Creamos esta consulta en tres pasos:
var query = db.Customers.Where (c => c.Name.StartsWith ("A"));
var query = query.Where (c => c.Purchases.Count() >= 2);
var result = query.Select (c => c.Name);
foreach (string name in result) // Solo en este momento la consulta es ejecutada!
Console.WriteLine (name);
Mito #9
Un método no puede devolver una consulta, si la consulta
termina con el operador 'new'
El truco está en proyectar en un tipo no anónimo ordinario
con un inicializador de objeto:
public IQueryable<NameDetails> GetCustomerNamesInState (string state)
{
return
from c in Customer
where c.State == state
select new NameDetails
{
FirstName = c.FirstName,
LastName = c.LastName
};
}
NameDetails es una clase que se podría definir como sigue:
public class NameDetails
{
public string FirstName, LastName;
}
Mito #10
La mejor manera de utilizar LINQ to SQL es
instancear un único DataContext a una propiedad estática
y utilizar esa instancia compartida para la vida de la aplicación.
Esta estrategia tendrá como resultado datos obsoletos, porque los
objetos registrados por una instancia DataContext no se actualizan simplemente
por reconsulta.
Utilizar una única instancia estática
del DataContext en la capa intermedia de una aplicación distribuida
provocará más problemas, porque las instancias del DataContext
no son multi hilos.
El enfoque correcto es crear nuevas instancias del
DataContext como sea necesario, manteniendo esas instancias con vida solo
el tiempo que sea necesario.