Extras
LINQ Quiz
Translations: English |
Spanish
Take the following short quiz and test your knowledge of LINQ!
All samples assume the following namespaces are imported:
using System;
using System.Linq;
using System.Data.Linq;
using System.Xml.Linq;
using System.Collections;
and that the following array is defined:
string[] colors = { "green", "brown", "blue", "red" };
OK, let's get started!
Q1. What does the following expression evaluate to?
colors.Max (c => c.Length)
(A) 5
(B) green
(C) brown
(D) Compile-time error
(E) Exception thrown
Answer
Answer: A
Max is one of the aggregation operators, which are
implemented as extension methods in System.Linq.Enumerable and
System.Linq.Queryable.
Max returns
the maximum value calculated by the lambda expression, not the element that had the maximum value.
Q2. What does this expression evaluate to?
colors.OrderBy (c => c.Length).Single()
(A) 3
(B) red
(C) Compile-time error
(D) Exception thrown
Answer
Answer: D
This throws an InvalidOperationException: the sequence emitted by
OrderBy contains four elements, and Single demands
exactly one element. To get the result "red", you must use the
First operator instead:
colors.OrderBy (c => c.Length).First()
Another option is to use FirstOrDefault (unlike First, FirstOrDefault doesn't throw an exception
there are zero elements in the sequence).
Single is good for retrieving a row from a table by primary key, in a LINQ
to SQL query:
Customer c =
dataContext.Customers.Single (c => c.ID == 123);
In this case, you'd want an exception to be thrown if there were two customers with that ID.
Q3. Given the following statement:
var query =
from c in colors
where c.Length > 3
orderby c.Length
select c;
what type is query?
(A) int
(B) string
(C) IEnumerable<int>
(D) IEnumerable<string>
(E) IQueryable<int>
(F) IQueryable<string>
Answer
Answer: D
The compiler translates this query to:
IEnumerable<string> query = colors
.Where (c => c.Length > 3)
.OrderBy (c => c.Length);
The Where and OrderBy methods, in this case, resolve to Enumerable.Where
and Enumerable.OrderBy, which both return IEnumerable<T>. T
is inferred to be string because we haven't transformed or projected
the input sequence elements in any way.
Q4. What's the output from the following?
var query =
from c in colors
where c.Length == colors.Max (c => c.Length)
select c;
foreach (var element in query)
Console.WriteLine (element);
That colors array, again, is { "green", "brown", "blue", "red"
}
(A) green followed by brown
(B) 5 followed by 5
(C) Compile-time error
(D) Exception is thrown
Answer
Answer: C
The variable, c, inside the subquery, conflicts with the outer query's
iteration variable, so the compiler complains. Here's how to fix it:
var query =
from c in colors
where c.Length == colors.Max (c2 => c2.Length)
select c;
The answer is then (A).
Q5. Assuming we make it syntactically correct, how many times does
the
Max subquery execute in the preceding example, when query is enumerated?
(A) once
(B) twice
(C) 3 times
(D) 4 times
Answer
Answer: D
With local queries, subqueries re-evaluate for each element in the outer
sequence. This is somewhat inefficient: we could avoid this by factoring out the subquery as follows:
int maxLength = colors.Max (c => c.Length);
var query
from c in colors
where c.Length == maxLength
select c;
The Max query then executes just once.
Q6. What does the following example output?
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) Exception thrown
Answer
Answer: A
Queries execute not when constructed, but when enumerated. This
is called lazy or deferred evaluation. Our query doesn't start executing until
it encounters the aggregation operator, Count, which forces immediate
enumeration. By that stage, red has been removed from the collection.
Any query operator that returns a scalar value or single element (such as
Single or First) forces immediate execution.
Q7. What's the output from the following?
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
Answer
Answer: A
We've chained one Where operator after another, so
that our final query considers only strings containing both the letters e
and n (only green). Our query is compositionally identical to
this:
var query = colors
.Where (c => c.Contains ("e"))
.Where (c => c.Contains ("n"));
and gives the same result as this:
var query = colors
.Where (c => c.Contains ("e")
&& c.Contains ("n"));
Q8. What's the output from the following?
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
Answer
Answer: B
This query looks very much like the last one, but the result is very
different! Because the variable s is referenced from within a lambda
expression, it's captured by the compiler and becomes an outer
variable.
With an outer variable, what matters is its value at the time the query
executes—not its value at the time the query was constructed. In our example, both
Where operators end up using "n" in their predicates, because this is the
value of s when the query was actually enumerated.
Q9. How does the compiler resolve the let clause, in the
following query?
from c in colors
let middle = c.Substring (1, c.Length - 2)
where middle.Contains ("e")
select middle
(A) By translating it into a call to Enumerable.Let
(B) By expanding middle into c.Substring (1, c.Length - 2) in
the where and select clauses
(C) By projecting it into a temporary anonymous type
Answer
Answer: C
Here's how the compiler translates this query:
colors.Select (
c => new
{
c = c,
middle = c.Substring (1, (c.Length - 2))
}
)
.Where (temp0 => temp0.middle.Contains ("e"))
.Select (temp0 => temp0.middle)
To see how the compiler translates comprehension queries, replace colors with
colors.AsQueryable(), and then run the query in
LINQPad. The translation
displays in the lambda tab.
Q10. The compiler translates queries containing multiple generators (i.e., multiple
from clauses) into:
(A) Multiple Selects
(B) SelectMany
(C) Join
Answer
Answer: B
Queries containing multiple generators translate to SelectMany.
LINQ to SQL, in turn, translates SelectMany into a
variety of SQL constructs, including inner and outer SQL joins.
Q11.
A LINQ to SQL query that uses multiple from clauses (or SelectMany)
can perform the equivalent of which of the following kind(s) of SQL JOIN:
I. Inner joins
II. Left outer joins
III. Full outer joins
IV. Non-equi inner joins
V. Non-equi outer joins
Answer
Answer: I and IV
Answer: II and V (in conjunction with DefaultIfEmpty)
With LINQ to SQL, SelectMany-based joins are the most flexible, and can
perform both equi and non-equi joins. Throw in DefaultIfEmpty, and you
can do left outer joins as well!
Here's an example
of a simple inner join:
from c in dataContext.Customers
from i in dataContext.Invoices
where c.CustomerID == i.CustomerID
select ...
Or, using an association property:
from c in dataContext.Customers
from i in c.Invoices
select ...
A left outer join:
from c in dataContext.Customers
from i in dataContext.Invoices
.Where (i => c.CustomerID == i.CustomerID)
.DefaultIfEmpty()
select ...
Or, using an association property:
from c in dataContext.Customers
from i in c.Invoices.DefaultIfEmpty()
select ...
Here's an non-equi inner join that queries post codes that cross states:
from t1 in dataContext.Towns
from t2 in dataContext.Towns
where t1.PostCode == t2.PostCode
&& t1.State.CompareTo (t2.State) < 0
select ...
We can turn this into an non-equi outer join as follows:
from t1 in dataContext.Towns
from t2 in dataContext.Towns
.Where (t => t.PostCode == t1.PostCode
&& t.State.CompareTo (t1.State) < 0)
.DefaultIfEmpty()
select ...
Q12.
A LINQ to SQL query that uses join clauses can perform the equivalent of
which of the following kind(s) of SQL JOIN
I. Inner joins
II. Left outer joins
III. Full outer joins
IV. Non-equi inner joins
V. Non-equi outer joins
Answer
Answer: I
Answer: II (in conjunction with DefaultIfEmpty)
Join is the not as powerful than SelectMany in LINQ to SQL (it
cannot perform non-equi-joins.)
The big advantage of Join over SelectMany is not in LINQ to
SQL, but in local queries: Join is much faster with large collections because it preloads the inner
sequence into a keyed lookup.
Q13. A LINQ to SQL query can include calls to your own local
methods:
A. In Where clauses only
B. In the final projection only
C. Anywhere in the query
D. Not at all
Answer
Answer: B
In a LINQ to SQL query, you cannot include calls to your own local methods (or unsupported Framework
methods) in any place other than the final projection. If you try to do
otherwise,
one of two things will happen:
- LINQ to SQL will throw an exception when the query executes ("method XYZ
has no translation to SQL")
- the query will be executed locally from that point on.
You can force the second scenario by calling
AsEnumerable on the query. For instance, the following uses a LINQ to SQL
query to retrieve all articles on influenza, and then uses a local query to
narrow the results down to only those articles whose abstract is less than 100
words:
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
);
We have to run the regular expressions filter locally, because Regex has no
translation in SQL.
Q14. In LINQ to SQL, to request that an entity’s association
properties be populated along with the entity (in a single round trip), you would:
(A) set DelayLoaded to false in the Association attribute
(B) set EagerLoad to true in the Association attribute
(C) use a DataLoadOptions object, and call AssociateWith
(D) use a DataLoadOptions object, and call LoadWith
Answer
Answer: D
Here's an example:
var options = new DataLoadOptions();
options.LoadWith <Customer> (c => c.Orders);
dataContext.LoadOptions = options;
This ensures that when a customer is retrieved, so are the customer's orders.
Q15. What does the following example print?
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) An exception is thrown
Answer
Answer: A
An XElement can have only one parent. When you try and make city
also the child of customer2, LINQ to XML automatically makes a deep clone
of city, rather than throwing an exception.
Q16. In the following example, what’s the namespace of the inner
element 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"
Answer
Answer: A
Child XElements do not implicitly inherit a namespace from
their parent. Here's what the XML serialization of our DOM would look like:
<customer xmlns="http://albahari.com/linqquiz">
<name xmlns="">Bloggs</name>
</customer>
If we wanted answer (B), we'd have to construct the DOM in the following
manner instead:
var x =
new XElement (ns + "customer",
new XElement (ns + "name", "Bloggs")
);
This would translate to the following XML:
<customer xmlns="http://albahari.com/linqquiz">
<name>Bloggs</name>
</customer>
Q17. When you call Save on an XElement
or XDocument to write its content to a file, an XML declaration is included in the output:
(A) Always
(B) Never
(C) Only if an XDeclaration object is present
Answer
Answer: A
An XML declaration is always written, unless you instead provide an
XmlWriter and instruct the writer explicitly not to emit the declaration.
With an XDocument, the presence of an XDeclaration has no
effect on whether an XML declaration is written. An XDeclaration merely hints
the writer as to what encoding to use, and what to put in the standalone
attribute.