Cuestionario LINQ
Autor: Joseph Albahari & Ben Albahari.
Traducción: Yuneisy Rodriguez.
¡Tome este pequeño questionario y pruebe sus conocimientos de
LINQ!
Todos los ejemplos asumen que los siguientes espacios de nombres son importados:
using System;
using System.Linq;
using System.Data.Linq;
using System.Xml.Linq;
using System.Collections;
y que se define el siguiente arreglo:
string[] colors = { "green", "brown", "blue", "red" };
¡OK, vamos a comenzar!
P1. ¿Cuál es el resultado de la siguiente expresión?
colors.Max (c => c.Length)
(A) 5
(B) green
(C) brown
(D) Error en tiempo de Compilación
(E) Se lanza una excepción
Respuesta
Respuesta: A
Max es uno de los operadores de agregación, los cuáles son
implementados como métodos extendidos en System.Linq.Enumerable y
System.Linq.Queryable.
Max retorna el máximo valor calculado por la expresión
lambda, no el elemento que tenía el valor máximo.
P2. ¿Cuál es el resultado de la siguiente expresión?
colors.OrderBy (c => c.Length).Single()
(A) 3
(B) red
(C) Error en tiempo de Compilación
(D)
Se lanza una excepción
Respuesta
Respuesta: D
Esto lanza un InvalidOperationException: la secuencia emitida por
OrderBy contiene cuatro elementos y Single exige exactamente
un elemento. Para obtener el resultado "red", debe utilizar el
operador First:
colors.OrderBy (c => c.Length).First()
Otra opción es utilizar FirstOrDefault ( a diferencia de
First, FirstOrDefault no lanza una excepción si la
secuencia contiene cero elementos).
Single es bueno para recuperar una fila de una tabla con la clave
primaria, en una consulta LINQ to SQL:
Customer c =
dataContext.Customers.Single (c => c.ID == 123);
En este caso, deseará que se lance una excepción si hubieran
dos clientes con ese ID
P3. Dada la siguiente declaración:
var query =
from c in colors
where c.Length > 3
orderby c.Length
select c;
¿De qué tipo es la consulta?
(A) int
(B) string
(C)
IEnumerable<int>
(D)
IEnumerable<string>
(E)
IQueryable<int>
(F) IQueryable<string>
Respuesta
Respuesta: D
El compilador traduce esta consulta en:
IEnumerable<string> query = colors
.Where (c => c.Length > 3)
.OrderBy (c => c.Length);
Los métodos Where y OrderBy, en este caso se convierten
en invocaciones a Enumerable.Where y Enumerable.OrderBy, ambos
retornan IEnumerable<T>. T es infierido de tipo cadena porque
no hemos transformado o proyectado la entrada de los elementos de
secuencia de ninguna manera.
P4. ¿Cuál es el resultado del siguiente segmento de código?
var query =
from c in colors
where c.Length == colors.Max (c => c.Length)
select c;
foreach (var element in query)
Console.WriteLine (element);
El arreglo de colores una vez más es { "green", "brown", "blue", "red"
}
(A) green followed by brown
(B) 5
followed by 5
(C) Error en tiempo de Compilación
(D) Se lanza una excepción
Respuesta
Respuesta: C
La variable, c, dentro de la subconsulta, entra en conflictos con la variable
de iteración de la consulta externa, por lo que el compilador se
queja. Le explicamos cómo solucionarlo:
var query =
from c in colors
where c.Length == colors.Max (c2 => c2.Length)
select c;
La respuesta entonces es (A).
P5. Asumiendo que hacemos que esa expresion esté sintácticamente
correcta ¿Cuántas veces la subconsulta Max se ejecuta en el
ejemplo anterior, cuando la variable query es enumerada?
(A) once
(B) twice
(C) 3 times
(D) 4
times
Respuesta
Respuesta: D
Con las consultas locales, las subconsultas se reevalúan para cada elemento
en la secuencia exterior. Esto es algo ineficaz: lo podríamos evitar
dividiendo la subconsulta de la siguiente manera:
int maxLength = colors.Max (c => c.Length);
var query
from c in colors
where c.Length == maxLength
select c;
La consulta Max entonces se ejecuta una sola vez.
P6. ¿Cuál es el resultado del siguiente ejemplo?
var list = new List<string> (colors);
IEnumerable<string> query = list.Where (c => c.Length == 3);
list.Remove ("red");
Console.WriteLine (query.Count());
(A) 0
(B) 1
(C) 2
(D) Se lanza una excepción
Respuesta
Respuesta: A
Las consultas no se ejecutan cuando se construyen, sino cuando se
enumeran. Esto es llamado evaluación diferida o tardía.
Nuestra consulta no empieza a ejecutarse hasta que encuentra el operador
de agregación, Count, que obliga a la enumeración inmediata.
Cuando se llega a ese paso, red ya ha sido removido de la colección.
Cualquier operador de consulta que devuelva un valor escalar o único
elemento (como Single o First) obliga a la ejecución
inmediata.
P7. ¿Cuál es el resultado del siguiente segmento de código?
string[ ] colors = { "green", "brown", "blue", "red" };
var query = colors.Where (c => c.Contains ("e"));
query = query.Where (c => c.Contains ("n"));
Console.WriteLine (query.Count());
(A) 1
(B) 2
(C) 3
(D) 4
Respuesta
Respuesta: A
Hemos encadenado un operador Where detrás de otro, por lo
que nuestra consulta considera solamente cadenas que contengan tanto la
letra e y n (solamente green). Nuestra consulta es
identica a esto:
var query = colors
.Where (c => c.Contains ("e"))
.Where (c => c.Contains ("n"));
y da el mismo resultado que esta:
var query = colors
.Where (c => c.Contains ("e")
&& c.Contains ("n"));
P8. ¿Cuál es el resultado del siguiente segmento de codigo?
string s = "e";
var query = colors.Where (c => c.Contains (s));
s = "n"
query = query.Where (c => c.Contains (s));
Console.WriteLine (query.Count());
(A) 1
(B) 2
(C) 3
(D) 4
Respuesta
Respuesta: B
Esta consulta se parece mucho a la anterior, pero el resultado es muy distinto.
Porque la variable es referenciada desde dentro de una expresión
lambda, es capturada por el compilador y se convierte en una variable
exterior.
Con una variable exterior, lo que importa es su valor en el momento en
que la consulta se ejecuta- no su valor en el momento en que la consulta
fue construida. En nuestro ejemplo, ambos operadores Where terminan
usando "n" en sus predicados, porque éste es el valor de
s cuando la consulta fue realmente enumerada.
P9. ¿Cómo el compilador resuelve la cláusula
let, en la siguiente consulta?
from c in colors
let middle = c.Substring (1, c.Length - 2)
where middle.Contains ("e")
select middle
(A) Convirtiendo esto a una llamada al método Enumerable.Let
(B) Sustituyendo la cadena middle por la cadena c.Substring
(1, c.Length - 2) en las cláusulas where y select
(C) Proyectando dentro de un tipo anónimo temporal
Respuesta
Respuesta: C
Aquí está cómo el compilador convierte esta consulta:
colors.Select (
c => new
{
c = c,
middle = c.Substring (1, (c.Length - 2))
}
)
.Where (temp0 => temp0.middle.Contains ("e"))
.Select (temp0 => temp0.middle)
Para ver cómo el compilador traduce consultas de comprensión,
reemplace colors con colors.AsQueryable(), y entonces corra
la consulta en LINQPad. La conversión se muestra en
la ficha de lambda
P10. El compilador convierte las consultas que contengan varios
generadores (múltiples cláusulas from) a:
(A) Múltiples Selects
(B) SelectMany
(C)
Join
Respuesta
Respuesta: B
Las consultas que contengan varios generadores son convertidas a SelectMany.
LINQ to SQL, a su vez, convierte SelectMany en una variedad de construcciones
SQL, incluyendo inner y outer SQL joins
P11. Una consulta LINQ to SQL que usa la cláusula múltiple from
(o SelectMany) puede realizar el equivalente de cuál de los
siguientes tipo(s) de SQL JOIN:
I. Inner joins
II. Left outer joins
III. Full outer joins
IV. Non-equi inner joins
V. Non-equi outer joins
Respuesta
Respuesta: I y IV
Respuestas: II y V (en combinación con DefaultIfEmpty)
Con LINQ to SQL, los joins basados en SelectMany, son los más
flexibles, y se pueden comportar tanto como equi y non-equi joins. A través
del DefaultIfEmpty se pueden hacer left outer joins.
Aqui está un ejemplo de un inner join simple:
from c in dataContext.Customers
from i in dataContext.Invoices
where c.CustomerID == i.CustomerID
select ...
O utilizando una propiedad de asociación:
from c in dataContext.Customers
from i in c.Invoices
select ...
Un left outer join:
from c in dataContext.Customers
from i in dataContext.Invoices
.Where (i => c.CustomerID == i.CustomerID)
.DefaultIfEmpty()
select ...
O utilizando una propiedad de asociación:
from c in dataContext.Customers
from i in c.Invoices.DefaultIfEmpty()
select ...
Aqui está un non-equi inner join de una consulta de códigos postales
que cruzan los estados:
from t1 in dataContext.Towns
from t2 in dataContext.Towns
where t1.PostCode == t2.PostCode
&& t1.State.CompareTo (t2.State) < 0
select ...
Podemos convertir esto a un non-equi outer join de la siguiente
manera:
from t1 in dataContext.Towns
from t2 in dataContext.Towns
.Where (t => t.PostCode == t1.PostCode
&& t.State.CompareTo (t1.State) < 0)
.DefaultIfEmpty()
select ...
P12. Una consulta LINQ to SQL que usa la cláusula join,
puede realizar el equivalente de cuál de los siguientes tipo(s) de
SQL JOIN:
I. Inner joins
II. Left outer
joins
III. Full outer joins
IV. Non-equi inner
joins
V. Non-equi outer joins
Respuesta
Respuesta: I
Respuesta: II (en combinación con DefaultIfEmpty)
Join no es tan potente como el SelectMany en LINQ to SQL
(no puede realizar non-equi joins.)
La gran ventaja de Join sobre SelectMany
no es en LINQ to SQL, sino en las consultas locales: Join es mucho
más rápido con grandes colecciones debido a que él
precarga la secuencia interior dentro de una búsqueda por llaves
P13. Una consulta LINQ to SQL puede incluir llamadas a tus propios
métodos locales:
A. En las cláusulas Where solamente
B. En la proyección final solamente
C. En cualquier parte de la consulta
D. De ninguna forma
Respuesta
Respuesta: B
En una consulta LINQ to SQL , no puede incluir llamadas a sus propios métodos
locales (o métodos Framework no compatibles) en ningún lugar
excepto en la proyección final. Si intenta lo contrario, una de estás
dos cosas ocurrirá :
- LINQ to SQL lanzará una excepción cuando la consulta se
ejecute ("el método XYZ no se puede traducir
a SQL")
- La consulta será ejecutada localmente desde ese punto.
Puede forzar el segundo escenario llamando a AsEnumerable en la
consulta. Por ejemplo, en el siguiente caso se utiliza una consulta LINQ
to SQL para recuperar todos los artículos sobre influenza, y a continuación,
utiliza una consulta local para restringir los resultados a sólo
los artículos cuyo resumen es menor de 100 palabras:
var wordCounter =
new System.Text.RegularExpressions.RegEx (
@"\b(\w|[-'])+\b");
var query = dataContext.Articles
.Where (a => a.Topic == "influenza")
.AsEnumerable()
.Where (a =>
wordCounter.Matches (a.Abstract).Count < 100
);
Tenemos que ejecutar localmente el filtrado de las
expresiones regulares, porque Regex no se puede convertir a SQL
P14. En LINQ to SQL, para solicitar que unas propiedades de asociación
de una entidad sean pobladas junto con la entidad (en una sola ida), usted
haría lo siguiente:
(A) Establecer DelayLoaded como falso en el atributo Association
(B) Establecer EagerLoad como verdadero en el atributo Association
(C) Utilizar un objeto DataLoadOptions , y llamar al AssociateWith
(D) Utilizar un objeto DataLoadOptions , y llamar al LoadWith
Respuesta
Respuesta: D
Aqui está un ejemplo:
var options = new DataLoadOptions();
options.LoadWith <Customer> (c => c.Orders);
dataContext.LoadOptions = options;
Esto garantiza que cuando un cliente se recupera, también lo hacen
las órdenes del cliente.
P15. ¿Qué imprime el siguiente ejemplo?
var city = new XElement ("city", "Seattle");
var customer1 = new XElement ("customer", city);
var customer2 = new XElement ("customer", city);
city.SetValue ("London");
Console.WriteLine (customer2.Element ("city").Value);
(A) Seattle
(B) London
(C) Se lanza una excepción
Respuesta
Respuesta: A
Un XElement puede tener solo un padre. Cuando usted trata de hacer
a city también hijo de customer2, LINQ to XML hace
automáticamente un clon de city, en lugar de lanzar una excepción.
P16. En el siguiente ejemplo, ¿Cuál
es el namespace del elemento interior name?
XNamespace ns = "http://albahari.com/linqquiz";
var x =
new XElement (ns + "customer",
new XElement ("name", "Bloggs")
);
(A) "" (empty string)
(B)
"http://albahari.com/linqquiz"
(C) "name"
Respuesta
Respuesta: A
Los XElement hijos no heredan implícitamente un namespace de sus
padres. Aquí está cómo luciría la serialización
XML de nuestro DOM:
<customer xmlns="http://albahari.com/linqquiz">
<name xmlns="">Bloggs</name>
</customer>
Si deseáramos la respuesta (B), tendríamos que construir
el DOM de la siguiente manera:
var x =
new XElement (ns + "customer",
new XElement (ns + "name", "Bloggs")
);
Esto se convertiría al siguiente XML:
<customer xmlns="http://albahari.com/linqquiz">
<name>Bloggs</name>
</customer>
P17. Cuando llame a Save en un XElement o en un
XDocument para escribir su contenido en un archivo, una declaración
XML se incluye en la salida:
(A) Siempre
(B) Nunca
(C) Solo si un objeto XDeclaration está presente
Respuesta
Respuesta: A
Una declaración XML siempre será escrita, a menos que en
su lugar proporcione un XmlWriter e indique al escritor explícitamente
no emitir la declaración.
Con un XDocument, la presencia de un XDeclaration no tiene
efecto sobre si será o no escrita una declaración XML. Un
XDeclaration simplemente indica al escritor qué codificación
utilizar , y qué poner en el atributo standalone.