• Microsoft .NET
  • LINQ
  • LINQtoSQL: Модификация в соответствии с требованиями код, генерируемый конструктором

Часто задаваемые вопросы о LINQ - часть вторая - Как происходят полные обходы в LINQ

ОГЛАВЛЕНИЕ

Как происходят полные обходы в LINQ

Сначала разберем, как на самом деле работают запросы LINQ, а затем узнаем, как происходят полные обходы. Рассмотрим следующую структуру базы данных с 3 таблицами – customer(клиент), addresses(адреса) и phone(телефон). Есть отношение один-к-многим между клиентом и адресами, а между таблицами адресов и телефонов есть отношение один-к-одному.

Были созданы три сущности согласно структуре таблицы, т.е. ‘ClsCustomerWithAddresses’,’ClsAddresses’ и ‘ClsPhone’. Были определены отношения между ними с помощью ‘EntitySet’ и ‘EntityRef’.

Заполнение объектов-сущностей данными из таблицы – процесс из 5 шагов. На первом шаге создается подключение datacontext с помощью строки подключения, создается запрос LINQ, а затем начинается просмотр клиента, адреса и телефонов.

Анализ полных обходов LINQ SQL

Было разобрано 5 шагов выполнения запроса LINQ. Выясним, на каком шаге запрос LINQ фактически запускает SQL в базе данных. Запустим вышеуказанный код LINQ и проанализируем его с помощью профилировщика SQL.

Чтобы не поймать кучу шума сервера SQL, были включены только групповые события RPC и SQL.

Теперь при выполнении запроса выясняется следующее:
• Выполнение реального SQL происходит, когда инструкция foreach повторяется над объектами LINQ.
• Для каждой сущности запускается отдельный запрос к серверу SQL. Например, запускается один запрос для клиента, и затем запускаются отдельные запросы для адреса и телефонов, чтобы вырастить объект сущности. То есть много полных обходов.

Как избежать лишних полных обходов

Можно приказать механизму LINQ загружать все объекты с помощью ‘DataLoadOptions’. Ниже описаны шаги по включению ‘DataLoadOptions’.

Первый шаг – создать класс контекста данных.

DataContext objContext = new DataContext(strConnectionString);

Второй шаг – создать объект ‘DataLoadOption’.

DataLoadOptions objDataLoadOption = new DataLoadOptions();

С помощью метода LoadWith надо определить загрузку клиента с адресом в одном SQL.

objDataLoadOption.LoadWith<clsCustomerWithAddresses>(clsCustomerWithAddresses => clsCustomerWithAddresses.Addresses);

Каждый объект адреса имеет объект телефона, поэтому была определена опция, говорящая, что объекты телефона должны загружаться для каждого объекта адреса в одном SQL.

objDataLoadOption.LoadWith<clsAddresses>(clsAddresses => clsAddresses.Phone);

Какая бы опция загрузки ни была определена, надо установить то же самое в объекте контекста данных с помощью свойства ‘LoadOptions’.

objContext.LoadOptions = objDataLoadOption;

Наконец, готовится запрос.

var MyQuery = from objCustomer in objContext.GetTable<clsCustomerWithAddresses>()
select objCustomer;

Начинается обход объектов в цикле.

foreach (clsCustomerWithAddresses objCustomer in MyQuery)
{

Response.Write(objCustomer.CustomerName + "<br>");

foreach (clsAddresses objAddress in objCustomer.Addresses)
{
Response.Write("===Address:- " + objAddress.Address1 + "<br>");
Response.Write("========Mobile:- " + objAddress.Phone.MobilePhone + "<br>");
Response.Write("========LandLine:- " + objAddress.Phone.LandLine + "<br>");
}
}

Ниже приведен полный исходный код для того же самого.

DataContext objContext = new DataContext(strConnectionString);
DataLoadOptions objDataLoadOption = new DataLoadOptions();
objDataLoadOption.LoadWith<clsCustomerWithAddresses>(clsCustomerWithAddresses => clsCustomerWithAddresses.Addresses);
objDataLoadOption.LoadWith<clsAddresses>(clsAddresses => clsAddresses.Phone);
objContext.LoadOptions = objDataLoadOption;
var MyQuery = from objCustomer in objContext.GetTable<clsCustomerWithAddresses>()
select objCustomer;

foreach (clsCustomerWithAddresses objCustomer in MyQuery)
{

Response.Write(objCustomer.CustomerName + "<br>");

foreach (clsAddresses objAddress in objCustomer.Addresses)
{
Response.Write("===Address:- " + objAddress.Address1 + "<br>");
Response.Write("========Mobile:- " + objAddress.Phone.MobilePhone + "<br>");
Response.Write("========LandLine:- " + objAddress.Phone.LandLine + "<br>");
}
}

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

Исходный код

К данной статье был приложен исходный код. Запустите проект и посмотрите, как профилировщик показывает разное выполнение SQL. Сначала запустите пример ‘EntitySet’ и посмотрите, как профилировщик SQL реагирует на то же самое, а затем запустите пример с ‘DataLoadOptions’. Скрипт SQL приложен в отдельном файле.