Часто задаваемые вопросы о 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 приложен в отдельном файле.