- PVSM.RU - https://www.pvsm.ru -
Ключевое слово 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. Это происходит при проекции в анонимный тип [1]:
string[] people = new [] { "Tom", "Dick", "Harry" };
var filteredPeople = people.Select (p => new { Name = p, p.Length });
Следующий пример показывает, как использовать анонимный тип вне LINQ контекста:
var person = new { Name = "Foo", Length = 3 };
Существует два способа записи 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).
Выражение
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);
LINQ и SQL — разные языки, использующие разные концепции.
Возможно, главным барьером продуктивного использования LINQ является синдром «думать в терминах SQL»: мысленно представлять запрос на языке SQL, а затем переводить его на LINQ. Результатом будет постоянная борьба с API!
Однажды начав думать исключительно в терминах LINQ [2], ваши запросы будут иметь очень мало общего с их SQL-аналогами. Во многих случаях, они будут также значительно проще.
Это правда, но только для запросов к локальным коллекциям. Когда вы создаете запрос к базе данных, ключевое слово 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 [3].
Это следствие из Мифа #4. Одно из основных преимуществ LINQ заключается в том, что вы можете:
В принципе, 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)
}
Это правда, если вам нужен плоский набор данных. Пример в предыдущем мифе, транслируется в левое внешнее соединение (left outer join) в SQL и не требует оператора DefaultIfEmpty.
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);
Трюк заключается в проекции в обычный именованный тип, используя инициализатор объектов:
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;
}
Такая стратегия приведет к устаревшим данным, поскольку объекты отслеживаемые экземпляром DataContext не обновляются при повторном запросе.
Используя единственный экземпляр класса DataContext вы получите много неприятностей, поскольку он не является потокобезопастным.
Правильной стратегией является создание нового экземпляра DataContext по каждому запросу объектов, делая его жизнь довольно короткой. То же самое касается Entity Framework.
Помимо этой статьи, я так же перевёл LINQ Quiz (тест) который разместил в своём блоге [4]. Думаю будет весьма полезно и интересно обсудить ответы на вопросы заданные Джо Албахари!
Автор: timyrik20
Источник [5]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/51689
Ссылки в тексте:
[1] анонимный тип: http://www.albahari.com/nutshell/whatsnewcs30.aspx
[2] терминах LINQ: http://www.linqpad.net/challenge.aspx
[3] LINQPad: http://www.linqpad.net/
[4] в своём блоге: http://timyrguev.blogspot.ru/2013/12/linq-quiz.html
[5] Источник: http://habrahabr.ru/post/207578/
Нажмите здесь для печати.