Будет ли полезна эта оптимизация SingleOrDefault () или она излишня / вредна? - PullRequest
3 голосов
/ 06 мая 2009

Я возился с LinqToSQL и LINQPad и заметил, что SingleOrDefault () не выполняет никакой фильтрации или ограничения в сгенерированном SQL (я почти ожидал эквивалент Take (1)).

Итак, если вы хотите защитить себя от случайного возвращения больших количеств, будет ли полезен следующий фрагмент или это плохая идея?


// SingleType is my LinqToSQL generated type 
// Singles is the table that contains many SingleType's
// context is my datacontext
public SingleType getSingle(int id)
{
     var query = from s in context.Singles where s.ID == id select s;
     var result = query.Take(2).SingleOrDefault();
     return result;
}

В отличие от обычного способа, которым я бы это сделал (уведомление №. Возьмите (2))


public SingleType getSingle(int id)
{
     var query = from s in Singles where s.ID == id select s;
     var result = query.SingleOrDefault();
     return result;
}

Я решил, что с Take (2) я все равно получу функциональность SingleOrDefault () с дополнительным преимуществом - никогда не придется беспокоиться о случайном возвращении {n} строк, но я не уверен, стоит ли это того если только я не собираюсь случайно вернуть {n} строк с моим запросом.

Итак, стоит ли это? Это вредно? Есть ли какие-то плюсы / минусы, которых я не вижу?

Edit:

SQL, сгенерированный без Take (2)


SELECT [t0].[blah], (...) 
FROM [dbo].[Single] AS [t0]
WHERE [t0].[ID] = @p0

SQL, созданный с помощью Take (2)


SELECT TOP 2 [t0].[blah], (...) 
FROM [dbo].[Single] AS [t0]
WHERE [t0].[ID] = @p0

Кроме того, когда я говорю о функциональности SingleOrDefault, я специально хочу, чтобы он генерировал исключение, если возвращается 2 или более, поэтому я делаю "Take (2)". Разница в том, что без .Take (2) он будет возвращать {n} строк из базы данных, когда ему действительно нужно будет вернуть только 2 (этого достаточно, чтобы его выбросило).

Ответы [ 4 ]

3 голосов
/ 06 мая 2009

Single - это скорее удобный метод для получения одного элемента запроса, чем способ ограничения количества результатов. Используя Single, вы фактически говорите: «Я знаю, что в этом запросе может быть только один элемент, поэтому просто дайте его мне», точно так же, как при выполнении someArray[0], когда вы знаете, что будет только один элемент. SingleOrDefault добавляет возможность возвращать null вместо выдачи исключения при работе с последовательностями длины 0. Вы не должны использовать Single или SingleOrDefault с запросами, которые могут возвращать более 1 результата: InvalidOperationException будет брошен.

Если ID в вашем запросе является первичным ключом таблицы или столбцом UNIQUE, база данных обеспечит, чтобы результирующий набор содержал 1 строку или не содержал ее без необходимости в предложении TOP.

Однако, если вы выбираете для неуникального / неключевого столбца и хотите получить первый или последний результат (обратите внимание, что они не имеют значения, если вы также не введете OrderBy), тогда вы можете использовать First или Last (оба имеют OrDefault дубликаты), чтобы получить желаемый SQL:

var query = from s in context.Singles 
            where s.Id == id
            orderby s.someOtherColumn
            select s;

var item = query.FirstOrDefault();

В дополнение к этому, вы можете сэкономить некоторую печать, если вы действительно делаете запрос для одного элемента:

var query = from s in context.Singles where s.Id == id select s;
var item = query.SingleOrDefault();

может стать:

var item = context.Singles.SingleOrDefault(s => s.Id == id);
2 голосов
/ 06 мая 2009

Вы уже упомянули точку. Если вы ожидаете, что запрос будет возвращать огромное количество строк довольно часто, это может дать вам выигрыш в производительности. Но если это исключительный случай - и SingleOrDefault() ясно указывает на это - это не стоит усилий. Он просто загрязняет ваш код, и вы должны документировать его, если решите его впустить.

UPDATE

Только что заметил, что вы запрашиваете идентификатор. Я предполагаю, что это первичный ключ, и в результате вы получите одну или ноль строк. Таким образом, в теории вам не нужно беспокоиться об использовании Single(), First(), Take(1) или чего-либо еще. Но я бы все же счел целесообразным использовать Single(), чтобы явно указать, что вы ожидаете ровно одну строку. Несколько недель назад группа рассказала мне, что у них даже был проект, в котором что-то пошло не так, и первичный ключ больше не был уникальным из-за сбоя базы данных мэра. Так лучше, чем потом сожалеть.

2 голосов
/ 06 мая 2009

SingleOrDefault IEnumerable .SingleOrDefault () ) оба вызывают InvalidOperationException, если последовательность имеет более одного элемента.

Ваш случай выше никогда не может произойти - он выдаст исключение.


Edit:

Мое предложение здесь будет зависеть от вашего сценария использования. Если вы думаете, что будут случаи, когда вы будете регулярно возвращать больше, чем несколько строк из этого запроса, добавьте строку .Take (2). Это даст вам то же поведение и то же исключение, но исключит возможность возврата многих записей из БД.

Тем не менее, использование SingleOrDefault () предполагает, что никогда не должно быть возвращено> 1 строки. Если это действительно так, я бы оставил это и воспринял это как исключение. По моему мнению, вы снижаете читабельность кода, предлагая, чтобы при добавлении .Take (2) было бы нормально иметь> 2 записи, и в этом случае я не верю, что это правда. Я бы взял Perf. ударил в исключительном случае для простоты оставить его.

0 голосов
/ 06 мая 2009

Аллен, ID - это первичный ключ для таблицы Singles? Если это так, я не до конца понимаю вашу проблему, так как ваш второй запрос вернет одну запись или ноль. И SQL будет там, где ID = ### ... Использование Take (2) .SingleOrDefault () отменяет цель SingleOrDefault.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...