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.