Myth # 1
All LINQ queries must begin with the 'var' keyword. In essence, the main purpose of the keyword 'var' is to start a LINQ query!
The
var keyword and LINQ are distinct concepts. The
var keyword allows the compiler to infer the type of a local variable based on the initial assignment
(implicit typing) . For example, the following code:
var s = "Hello";
exact equivalent for:
string s = "Hello";
because the compiler outputs the type of the variable
s as
string .
Similarly, the following query:
')
string[] people = new [] { "Tom", "Dick", "Harry" }; var filteredPeople = people.Where (p => p.Length > 3);
exact equivalent for:
string[] people = new [] { "Tom", "Dick", "Harry" }; IEnumerable<string> filteredPeople = people.Where (p => p.Length > 3);
You can see that all we achieved using the
var keyword is to create an abbreviation for
IEnumerable <string> . Many people like this record because it is shorter; others believe that implicit typing can make code less understandable.
There are situations in which LINQ queries
require the var keyword. This happens when projected into
an anonymous type :
string[] people = new [] { "Tom", "Dick", "Harry" }; var filteredPeople = people.Select (p => new { Name = p, p.Length });
The following example shows how to use an anonymous type outside the LINQ context:
var person = new { Name = "Foo", Length = 3 };
Myth # 2
All LINQ queries must use query syntax.
There are two ways to write LINQ queries:
lambda syntax and
query syntax .
Example of
lambda syntax :
string[] people = new [] { "Tom", "Dick", "Harry" }; var filteredPeople = people.Where (p => p.Length > 3);
An example similar to the previous one but using the
query syntax :
string[] people = new [] { "Tom", "Dick", "Harry" }; var filteredPeople = from p in people where p.Length > 3 select p;
Logically, the compiler translates the query syntax into lambda syntax. This means that everything that can be expressed using query syntax can also be expressed in lambda syntax. Query syntax can be much simpler with queries involving
more than one range variable . (In this example, we used only one variable of the
p range, so that both queries look the same simple).
Not all operators are available in query syntax, so these two types of syntax are rather complementary. To get the best from everyone, you can mix query styles in one expression (see myth # 5).
Myth # 3
To extract all clients from a table, you must use a query like this:
var query = from in db.Customers select c.
Expression
from c in db.Customers select c
is too verbose! You can simply use:
db.Customers
Similarly, the following LINQ to XML request:
var xe = from e in myXDocument.Descendants ("phone") select e;
can be simplified to:
var xe = myXDocument.Descendants ("phone");
And this request:
Customer customer = (from c in db.Customers where c.ID == 123 select c) .Single();
can be simplified to:
Customer customer = db.Customers.Single (c => c.ID == 123);
Myth # 4
To reproduce the SQL query in LINQ, you must make the LINQ query as similar to the SQL query as possible.
LINQ and SQL are different languages using different concepts.
Perhaps the main barrier to productive use of LINQ is the “think in terms of SQL” syndrome: mentally present a query in SQL, and then translate it to LINQ. The result will be a constant struggle with the API!
Once you start thinking exclusively in
LINQ terms , your queries will have very little in common with their SQL counterparts. In many cases, they will also be significantly easier.
Myth # 5
To effectively join in LINQ, you must use the join keyword.
This is true, but only for querying local collections. When you create a database query, the join keyword is not at all necessary: the join operation can be replaced by using several froms and subqueries. Several froms and subqueries are more versatile: you can also implement non-equi-join
connections .
Moreover, in LINQ to SQL and the Entity Framework, you can query for
association properties that reduce the need for join-ah! For example, the following code shows how to retrieve the names and identifiers of all customers who have not made a single purchase:
from c in db.Customers where !c.Purchases.Any() select new { c.ID, c.Name }
Or extract customers who have not made a purchase in excess of $ 1000:
from c in db.Customers where !c.Purchases.Any (p => p.Price > 1000) select new { c.ID, c.Name }
Notice, we have mixed lambda syntax and query syntax. For more examples of association properties, connection guidelines, and mixed syntax, see
LINQPad .
Myth # 6
Since the result of an SQL query is a flat data set, LINQ queries must be created to also return a flat data set.
This is a consequence of Myth # 4. One of the main advantages of LINQ is that you can:
- Query a structured object through the properties of the association (instead of manually connecting);
- Project directly into the object hierarchy.
In principle, 1 and 2 are independent, but 1 helps 2. For example, if you want to extract customer numbers in WA together with their purchases, you can use the following code:
from c in db.Customers where c.State == "WA" select new { c.Name, c.Purchases
The hierarchical result of this query is much easier to work with than a flat data set.
We can achieve the same result without using the
properties of associations :
from c in db.Customers where c.State == "WA" select new { c.Name, Purchases = db.Purchases.Where (p => p.CustomerID == c.ID) }
Myth # 7
To implement an external connection in LINQ to SQL, you must always use the DefaultIfEmpty () operator.
This is true if you need a
flat data set. The example in the previous myth is translated to the left outer join in SQL and does not require the
DefaultIfEmpty operator.
Myth # 8
LINQ to SQL or Entity Framework queries will be executed as a whole, only if they were built in one step.
LINQ uses the deferred execution model, that is, queries are executed not at the time of
creation , but at the time of
enumeration . This means you can construct your queries in as many steps as you want, and they will not get to the server until you start using the result.
For example, the following query retrieves the names of all customers who have made two purchases, whose name begins with the letter 'A'. We built this query in three steps:
var query = db.Customers.Where (c => c.Name.StartsWith ("A")); query = query.Where (c => c.Purchases.Count() >= 2); var result = query.Select (c => c.Name); foreach (string name in result)
Myth # 9
A method cannot return a query if it ends with the 'new' operator.
The trick is to project into a regular
named type using an object initializer:
public IQueryable<NameDetails> GetCustomerNamesInState (string state) { return from c in Customer where c.State == state select new NameDetails { FirstName = c.FirstName, LastName = c.LastName }; }
The
NameDetails class
is defined as follows:
public class NameDetails { public string FirstName, LastName; }
Myth # 10
The best way to use LINQ to SQL is to instantiate a single instance of the DataContext class in a static property and use this shared instance throughout the life of the application.
Such a strategy will lead to stale data, since objects monitored by the DataContext instance are not updated when resubmitted.
Using a single instance of the DataContext class will cause you a lot of trouble, since it is not thread safe.
The correct strategy is to create a new DataContext instance for each object request, making its life quite short. The same goes for the Entity Framework.
In addition to this article, I also translated LINQ Quiz (test) which I posted on my blog ( answers ). I think it will be very useful and interesting to discuss the answers to the questions asked by Joe Albahari!