Запросы к базе данных

36

»» В ДАННОЙ СТАТЬЕ ИСПОЛЬЗУЕТСЯ ИСХОДНЫЙ КОД ДЛЯ ПРИМЕРОВ

Выполнение запросов 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>. Вот результат выполнения кода:

Простой запрос LINQ to SQL, возвращающий последовательность IQueryable<T>

Как было указано ранее, поскольку 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 догадается, что нужно делать с вызовом. Из-за этого неплохо бы знать, что может транслироваться, во что оно может быть транслировано, и что случится, когда трансляция невозможна.

Пройди тесты
Лучший чат для C# программистов