LINQ to SQL: создание одного запроса при вызове First () или Take () - PullRequest
1 голос
/ 27 октября 2010

У меня есть база данных со следующими таблицами:

create table Categories (
  Id int primary key,
  Name nvarchar(256) not null)

create table Products (
  Id int primary key,
  Name nvarchar(256) not null,
  CategoryId int not null,
  foreign key (CategoryId) references Categories (Id))

Используя DataLoadOptions, я могу написать это:

DataLoadOptions dlo = new DataLoadOptions();
dlo.LoadWith<Category>(c => c.Products);

и получить всю информацию о категориях и продуктах водин запрос.

Однако db.Categories.First(), db.Categories.FirstOrDefault() и db.Categories.Take(10) выполнят запрос, который извлекает только подмножество записей из таблицы Categories и ничего больше.По сути, это:

SELECT TOP (1) Id, Name FROM Categories
-- and
SELECT TOP (10) Id, Name FROM Categories

Доступ к свойству Products экземпляра Category приведет к выполнению другого запроса.

Я обнаружил несколько способов обойти это.

// For First():
var category = db.Categories.Single(c => c == db.Categories.First());

// For FirstOrDefault():
var category = db.Categories.SingleOrDefault(c => c == db.Categories.First());

// For Take(10):
var categories = db.Categories.Where(c => db.Categories.Take(10).Contains(c));

Все приведенные выше операторы LINQ будут возвращать всю информацию о категории и продукте для подмножества Categories записей в одном запросе.

Кто-нибудь знает, существуетлучший или более эффективный способ достижения этого?Спасибо.

1 Ответ

1 голос
/ 27 октября 2010

Вы должны прочитать о Skip, Take и загрузке связанных коллекций здесь:

LINQ to SQL Выполнить без пропуска Причины нескольких операторов SQL

Плохая новость заключается в том, что решение не будет работать для вас. CompiledQuery имеет аллергию на LoadOptions - вы получите ошибку во время выполнения.


Доступ к свойству Products Экземпляр категории приведет к другое выполнение запроса.

Nitty-пикап. Это не совсем так. К тому времени, как ваш звонок в First возвращается, Продукты будут получены. Контракт LoadsWith заключается в том, что ваши данные получаются. Проблема в том, что он не гарантирует, что наиболее эффективные запросы используются для выборки (в данном случае один запрос с ROWNUMBER).

Вот реализация метода, которая подтверждает:

        DataClasses1DataContext myDC = new DataClasses1DataContext();
        var myOptions = new System.Data.Linq.DataLoadOptions();
        myOptions.LoadWith<Customer>(c => c.Orders);
        myDC.LoadOptions = myOptions;

        myDC.Log = Console.Out;


        myDC.Customers.Take(10).ToList();

1 запрос выдается для клиентов, затем 10 запросов, по 1 для каждого заказа клиента. Эти 10 запросов выполняются, хотя к свойствам Orders экземпляров Customer не обращались.


Эта статья KWatkins демонстрирует, как использовать LoadOptions с CompiledQuery. Это открывает CompiledQuery как способ блокировки Skip (x), даже если x может быть 0.

...