Pull to refresh

10 мифов о LINQ

Reading time 5 min
Views 78K
Original author: Joseph Albahari

Миф #1


Все LINQ запросы должны начинаться с ключевого слова 'var'. По сути основная цель ключевого слова 'var' — начать LINQ запрос!


Ключевое слово var и LINQ — это самостоятельные концепции. Ключевое слово var позволяет компилятору вывести тип локальной переменной на основании начального присваивания(неявная типизация). К примеру, следующий код:

var s = "Hello"; 

точный эквивалент для:

string s = "Hello"; 

потому что компилятор выводит тип переменной s как string.
Аналогично, следующий запрос:

string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = people.Where (p => p.Length > 3); 

точный эквивалент для:

string[] people = new [] { "Tom", "Dick", "Harry" };
IEnumerable<string> filteredPeople = people.Where (p => p.Length > 3); 

Можно заметить, всё чего мы добились с помощью ключевого слова var — это создали сокращение для IEnumerable<string>. Многие люди любят такую запись, поскольку она короче; другие же считают, что неявная типизация способна сделать код менее понятным.

Бывают ситуации, в которых для LINQ запросов необходимо ключевое слово var. Это происходит при проекции в анонимный тип:

string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = people.Select (p => new { Name = p, p.Length }); 

Следующий пример показывает, как использовать анонимный тип вне LINQ контекста:

var person = new { Name = "Foo", Length = 3 };

Миф #2


Все LINQ запросы должны использовать синтаксис запросов.


Существует два способа записи LINQ запросов: лямбда синтаксис и синтаксис запросов.

Пример лямбда синтаксиса:

string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = people.Where (p => p.Length > 3); 

Пример, аналогичный предыдущему но использующий синтаксис запросов:

string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = from p in people where p.Length > 3 select p; 

Логически, компилятор транслирует синтаксис запросов в лямбда синтаксис. Это означает, что всё, что может быть выражено с помощью синтаксиса запросов, так же может быть выражено в лямбда синтаксисе. Синтаксис запросов может быть намного проще с запросами включающими более одной переменной диапазона. (В данном примере, мы использовали только одну переменную диапазона p, так что оба запроса выглядят одинаково просто).

Не все операторы доступны в синтаксисе запросов, так что эти два вида синтаксиса скорее дополняют друг друга. Чтобы получить лучшее от каждого, вы можете смешивать стили запросов в одном выражении (см. миф # 5).

Миф #3


Чтобы извлечь всех клиентов из таблицы, вы должны использовать запрос на подобии этого:
var query = from c in db.Customers select c.


Выражение

from c in db.Customers select c 

является слишком многословным! Вы можете просто использовать:

db.Customers

Аналогично, следующий LINQ to XML запрос:

var xe = from e in myXDocument.Descendants ("phone") select e;

может быть упрощен до:

var xe = myXDocument.Descendants ("phone");

И этот запрос:

Customer customer = (from c in db.Customers where c.ID == 123 select c)
                    .Single();

можно упростить до:

Customer customer = db.Customers.Single (c => c.ID == 123);

Миф #4


Чтобы воспроизвести SQL запрос в LINQ, вы должны сделать LINQ запрос максимально похожим на SQL запрос.


LINQ и SQL — разные языки, использующие разные концепции.

Возможно, главным барьером продуктивного использования LINQ является синдром «думать в терминах SQL»: мысленно представлять запрос на языке SQL, а затем переводить его на LINQ. Результатом будет постоянная борьба с API!

Однажды начав думать исключительно в терминах LINQ, ваши запросы будут иметь очень мало общего с их SQL-аналогами. Во многих случаях, они будут также значительно проще.

Миф #5


Для эффективного соединения в LINQ, вы должны использовать ключевое слово join.


Это правда, но только для запросов к локальным коллекциям. Когда вы создаете запрос к базе данных, ключевое слово join вовсе необязательно: операция соединения может быть заменена использованием нескольких from-ов и подзапросов. Несколько from-ов и подзапросы являются более универсальными: вы так же можете реализовать соединение не по равенству (non-equi-join).

Более того в LINQ to SQL и Entity Framework вы можете запрашивать свойства ассоциации, уменьшающие потребность в join-ах! К примеру, следующий код показывает, как можно извлечь имена и идентификаторы всех клиентов, не совершивших ни одной покупки:

from c in db.Customers
where !c.Purchases.Any()
select new { c.ID, c.Name } 

Или извлечь клиентов, не совершивших покупку на сумму свыше $1000:

from c in db.Customers
where !c.Purchases.Any (p => p.Price > 1000)
select new { c.ID, c.Name } 

Заметьте, мы смешали лямбда синтаксис и синтаксис запросов. Большее количество примеров свойств ассоциации, руководства по соединениям и смешанного синтаксиса смотри на LINQPad.

Миф #6


Поскольку результатом SQL запроса является плоский набор данных, то LINQ запросы должны создаваться так, чтобы возвращать также плоский набор данных.


Это следствие из Мифа #4. Одно из основных преимуществ LINQ заключается в том, что вы можете:
  1. Запрашивать структурированный объект через свойства ассоциации (вместо того чтобы соединяться вручную);
  2. Проецировать прямо в иерархию объектов.

В принципе, 1 и 2 независимы, однако 1 помогает 2. К примеру, если вы хотите извлечь номера клиентов в штате WA вместе с их покупками, вы можете использовать следующий код:

from c in db.Customers
where c.State == "WA"
select new
{
   c.Name,
   c.Purchases    // An EntitySet (collection)
}

Иерархический результат этого запроса намного проще в работе, чем плоский набор данных.

Мы можем достигнуть такого же результата, не используя свойства ассоциаций:

from c in db.Customers
where c.State == "WA"
select new
{
   c.Name,
   Purchases = db.Purchases.Where (p => p.CustomerID == c.ID)
}


Миф #7


Чтобы реализовать внешнее соединение в LINQ to SQL вы всегда должны использовать оператор DefaultIfEmpty().


Это правда, если вам нужен плоский набор данных. Пример в предыдущем мифе, транслируется в левое внешнее соединение (left outer join) в SQL и не требует оператора DefaultIfEmpty.

Миф #8


Запросы LINQ to SQL или Entity Framework будут выполняться как единое целое, только если они были построены в один шаг.


LINQ использует отложенную модель выполнения, то есть запросы выполняются не во время создания, а во время перечисления. Это означает, вы можете конструировать ваши запросы во столько шагов, во сколько захотите, и они не попадут на сервер до тех пор, пока вы не начнёте использовать результат.

К примеру, следующий запрос извлекает имена всех клиентов совершивших две покупки, чьё имя начинается с символа 'А'. Мы построили этот запрос в три шага:

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)   // Только сейчас запрос начинает выполняться!
   Console.WriteLine (name);


Миф #9


Метод не может вернуть запрос, если он заканчивается оператором 'new'.


Трюк заключается в проекции в обычный именованный тип, используя инициализатор объектов:

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 определен следующим образом:

public class NameDetails
{
   public string FirstName, LastName;
}

Миф #10


Лучший способ использования LINQ to SQL- это инстанциировать единственный экземпляр класса DataContext в статическом свойстве и использовать этот общий экземпляр на протяжении всей жизни приложения.


Такая стратегия приведет к устаревшим данным, поскольку объекты отслеживаемые экземпляром DataContext не обновляются при повторном запросе.

Используя единственный экземпляр класса DataContext вы получите много неприятностей, поскольку он не является потокобезопастным.

Правильной стратегией является создание нового экземпляра DataContext по каждому запросу объектов, делая его жизнь довольно короткой. То же самое касается Entity Framework.

Помимо этой статьи, я так же перевёл LINQ Quiz (тест) который разместил в своём блоге (ответы). Думаю будет весьма полезно и интересно обсудить ответы на вопросы заданные Джо Албахари!
Tags:
Hubs:
+5
Comments 20
Comments Comments 20

Articles