Запросы к базе данных
36LINQ --- LINQ to DataSet и SQL --- Запросы к базе данных
»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ
Выполнение запросов LINQ to SQL похоже на выполнение любого другого запроса LINQ, но с несколькими исключениями. Рассмотрим их очень кратко.
Чтобы выполнить запрос LINQ to SQL, сначала понадобится создать DataContext. Затем можно выполнять запрос в таблицу внутри этого DataContext:
Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS;
AttachDbFilename=C:\Northwind.mdf;
Integrated Security=True");
Customer cust = (from c in db.Customers
where c.CustomerID == "EASTC"
select c).Single<Customer>();
Когда выполняется этот код, заказчик, CustomerID которого равен "EASTC", извлекается в переменную cust. Однако следует иметь в виду, что, стандартная операция запроса Single сгенерирует исключение, если последовательность, на которой онa вызванa, не содержит подходящих элементов. Поэтому в данном случае нужно точно знать, что заказчик "EASTC" существует. В действительности стандартная операция запроса SingleOrDefault обеспечивает лучшую защиту на случай, если не найдется записи, соответствующей конструкции where.
В этом примере необходимо отметить еще пару моментов. Во-первых, обратите внимание, что в запросе при сравнении CustomerID с "EASTC" используется синтаксис C#. Об этом говорит применение двойных кавычек вместо одинарных, как того требует синтаксис SQL.
К тому же используется операция проверки эквивалентности C# == вместо операции проверки эквивалентности SQL =. Это демонстрирует тот факт, что запрос на самом деле интегрирован в язык: в конце концов, это следует из самого названия LINQ — язык интегрированных запросов.
Во-вторых, обратите внимание, что в этом запросе синтаксис выражений запроса смешивается со стандартным синтаксисом точечной нотации. Часть, представленная в синтаксисе выражений запросов, заключена в скобки, а операция Single вызывается с применением стандартной точечной нотации.
А теперь вопрос. Вызовет ли запуск приведенного кода немедленное выполнение запроса? Обдумывая ответ, не забудьте об отложенном выполнении запросов. Ответ: да, стандартная операция запроса Single вызовет немедленное выполнение запроса. Если исключить вызов этой операции и немедленно вернуть результат запроса, то запрос не будет выполнен немедленно.
Код выше не выдает никакого экранного вывода, поэтому просто чтобы убедиться, что код действительно извлечет соответствующего заказчика, в следующем примере приведен тот же код, но с добавлением вывода на экран, отображающего извлеченную запись о заказчике:
// Используйте свою строку подключения
Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS;
AttachDbFilename=C:\Northwind.mdf;
Integrated Security=True");
Customer cust = (from c in db.Customers
where c.CustomerID == "EASTC"
select c).Single();
MessageBox.Show("Имя: " + cust.ContactName + " \nКомпания: " + cust.CompanyName);
Ниже показан вывод:
Ранее упоминалось, что запросы LINQ to SQL подобны обычным запросам LINQ, но с некоторыми исключениями. Давайте обсудим эти исключения:
- Запросы LINQ to SQL возвращают IQueryable<T>
В то время как запросы LINQ, выполненные на массивах и коллекциях, возвращают последовательности IEnumerable<T>, запросы LINQ to SQL, запрашивающие такую последовательность, возвращают последовательность типа IQueryable<T>. Ниже приведен пример запроса, возвращающего последовательность типа IQueryable<T>:
Northwind db = new Northwind(@"Data Source=MICROSOF-1EA29E\SQLEXPRESS; AttachDbFilename=C:\Northwind.mdf; Integrated Security=True"); IQueryable<Customer> custs = from c in db.Customers where c.City == "London" select c; string s = ""; foreach (Customer cust in custs) { s += "\nЗаказчик: "+ cust.CompanyName; } MessageBox.Show("Заказчики из Лондона: \n" + s);
Как видите, типом возврата этого запроса является IQueryable<Customer>. Вот результат выполнения кода:
Как было указано ранее, поскольку IQueryable<T> реализует IEnumerable<T>, обычно можно трактовать последовательность типа IQueryable<T>, как если бы это была последовательность IEnumerable<T>. Если при этом возникнут проблемы — не забудьте об операции AsEnumerable.
- Запросы LINQ to SQL выполняются на объектах Таble<Т>
В то время как большинство обычных запросов LINQ выполняются на массивах и коллекциях, реализующих интерфейсы IEnumerable<T> или IEnumerable, запрос LINQ to SQL выполняется на классах, реализующих интерфейс IQueryable<T>, таком как Table<Т>.
Это значит, что запросам LINQ to SQL доступны дополнительные операции запросов, наряду со стандартными операциями запросов, поскольку IQueryable<T> реализует IEnumerable<T>.
- Запросы LINQ to SQL транслируются в SQL
Поскольку запросы LINQ to SQL возвращают последовательность типа IQueryable<T>, они не компилируются в код промежуточного языка .NET, как это делают обычные запросы LINQ. Вместо этого они преобразуются в деревья выражений, что позволяет им вычисляться как единое целое и транслироваться в соответствующие и оптимальные конструкции SQL. Прочтите статью Трансляция SQL, чтобы узнать больше о трансляции SQL, которая происходит в запросах LINQ to SQL.
- Запросы LINQ to SQL выполняются в базе данных
В отличие от запросов LINQ, которые выполняются в памяти локальной машины, запросы LINQ to SQL транслируются в вызовы SQL, которые в действительности выполняются в базе данных. Из-за этого происходит некоторое расхождение, такое как способ обработки проекции, которая не может в действительности случаться в базе данных, поскольку база ничего не знает о сущностных классах, как и о любых других классах.
К тому же, поскольку запрос на самом деле выполняется в базе данных, и база не имеет доступа к коду вашего приложения, то, что можно сделать в запросе, должно транслироваться, что ограничивает его некоторым образом в зависимости от возможностей транслятора. Нельзя просто встроить вызов написанного метода в лямбда-выражение и ожидать, что SQL Server догадается, что нужно делать с вызовом. Из-за этого неплохо бы знать, что может транслироваться, во что оно может быть транслировано, и что случится, когда трансляция невозможна.