Код Linq для выбора одного элемента

105

Я пишу много такого кода, чтобы выбрать один элемент, который соответствует

var item = (from x in Items where x.Id == 123 select x).First();

Есть ли более чистый способ сделать это или это настолько кратко, насколько я собираюсь получить?

РЕДАКТИРОВАТЬ: следовало бы сказать «Более чистый способ с использованием синтаксиса linq». Я уже знал о синтаксисе лямбда, и мне стало казаться, что это единственный способ. Тем не менее, я получил некоторую полезную информацию, поэтому спасибо всем, кто ответил.

Майки Хогарт
источник
5
Лично я избегаю этого, Single()и SingleOrDefault()ЕСЛИ я знаю, что данные уже уникальны (например, из базы данных, которая имеет это ограничение и т.д.), поскольку Single()заставляет его сканировать оставшуюся часть списка, чтобы найти возможный дубликат, но это я. Если вам нужно усилить свою уникальность на этом этапе, используйте Single()family, если нет, используйте First()family.
Джеймс Майкл Хэйр

Ответы:

176

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

var item = Items.First(i => i.Id == 123);

А если вы не хотите выдавать ошибку, если список пуст, используйте, FirstOrDefaultкоторый возвращает значение по умолчанию для типа элемента ( nullдля ссылочных типов):

var item = Items.FirstOrDefault(i => i.Id == 123);

if (item != null)
{
    // found it
}

Single()и SingleOrDefault()также может быть использован, но если вы читаете из базы данных или чего-то, что уже гарантирует уникальность, я бы не стал беспокоиться, поскольку он должен сканировать список, чтобы увидеть, есть ли какие-либо дубликаты и выбросы. First()и FirstOrDefault()останавливаются на первом совпадении, чтобы они были более эффективными.

Из First()и Single()семьи, вот где они бросают:

  • First() - бросает, если пуст / не найден, не бросает, если дублируется
  • FirstOrDefault() - возвращает значение по умолчанию, если пусто / не найдено, не выбрасывает, если дублируется
  • Single() - выбрасывает, если пусто / не найдено, выбрасывает, если существует дубликат
  • SingleOrDefault() - возвращает значение по умолчанию, если пусто / не найдено, выбрасывает, если существует дубликат
Джеймс Майкл Хэйр
источник
1
Я думаю, вам не хватает двух знаков равенства. Должно бытьi.Id == 123
davehale23
18

FirstOrDefault или SingleOrDefault могут быть полезны, в зависимости от вашего сценария и от того, хотите ли вы обрабатывать ноль или более одного совпадения:

FirstOrDefault: возвращает первый элемент последовательности или значение по умолчанию, если элемент не найден.

SingleOrDefault: возвращает единственный элемент последовательности или значение по умолчанию, если последовательность пуста; этот метод выдает исключение, если в последовательности более одного элемента

Я не знаю, как это работает в запросе linq 'from', но в синтаксисе лямбда это выглядит так:

var item1 = Items.FirstOrDefault(x => x.Id == 123);
var item2 = Items.SingleOrDefault(x => x.Id == 123);
Стюарт
источник
какой из них наиболее эффективен с точки зрения времени БД? Если бы у меня был varchar, я хотел бы точное совпадение, но возможно, его не могло быть?
Петр Кула
2
В принятом ответе это полностью рассматривается, но по существу FirstOrDefault останавливается, как только находит совпадение, но SingleOrDefault должен проверить весь список, чтобы убедиться, что есть ровно одно совпадение.
Стюарт
12

Это предпочтительные методы:

var item = Items.SingleOrDefault(x => x.Id == 123);

Или

var item = Items.Single(x => x.Id == 123);
Джеймс Хилл
источник
Спасибо - значит, в этой ситуации нет нотации linq, и мне нужно использовать лямбды?
Майки Хогарт
Это очень хорошо. На самом деле я нечасто использовал linq, поэтому не уверен, что знал об этих методах.
wageog
1
Единый метод проверяет уникальность возвращаемого значения. Так что, если ваша коллекция большая, это может занять много времени. Первый метод просто возвращает первый элемент, соответствующий предикату.
meziantou
Ответ @wageoghe Джеймса не использует linq - методы Single и SingleOrDefault являются частью реализации IEnumerable.
Майки Хогарт
12

Чтобы облегчить кому-то жизнь, запрос linq с лямбда-выражением

(from x in Items where x.Id == 123 select x).FirstOrDefault();

приводит к SQL-запросу с select top (1)в нем.

Амаль
источник
9

Это можно лучше свести к следующему.

var item = Items.First(x => x.Id == 123);

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

Single / SingleOrDefault имеет смысл, но только если вы хотите перебрать всю коллекцию и убедиться, что совпадение уникально, в дополнение к выбору этого совпадения. First / FirstOrDefault просто возьмет первое совпадение и уйдет, независимо от того, сколько дубликатов действительно существует.

Крис Хэннон
источник
4

Вы можете использовать синтаксис метода расширения:

var item = Items.Select(x => x.Id == 123).FirstOrDefault();

В остальном я не уверен, насколько более кратким можно было бы стать, если бы, возможно, не написали свои собственные специализированные методы расширения «First» и «FirstOrDefault».

заработная плата
источник
Я не думаю, что это предполагаемое поведение. Эта инструкция Select возвращает (на самом деле нет, но она выбирает для возврата) набор bool, по одному для каждого элемента Items, первого из того, что возвращается как item, который становится просто bool. Воспользуйтесь решением Криса Хэннона
Леонардо Дага
Возможно, вы имеете Whereв виду противоположное Select, которое уже было сказано, но этот ответ неверен. Select в C # изменяет результаты на IEnumerable <bool>, поэтому вы получаете a boolдля первого элементаx.Id == 123
bradlis7
2

Я расскажу, что у меня сработало:

int id = int.Parse(insertItem.OwnerTableView.DataKeyValues[insertItem.ItemIndex]["id_usuario"].ToString());

var query = user.First(x => x.id_usuario == id);
tbUsername.Text = query.username;
tbEmail.Text = query.email;
tbPassword.Text = query.password;

Мой идентификатор - это строка, которую я хочу запросить, в этом случае я получил ее из radGrid, затем я использовал ее для запроса, но этот запрос возвращает строку, затем вы можете присвоить значения, полученные из запроса, в текстовое поле или что-то еще , Мне пришлось назначить их текстовому полю.

Джени Рамирес
источник