Код Linq, щоб вибрати один елемент


105

Мені здається, що я пишу багато такого коду, щоб вибрати один елемент, який відповідає

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

Чи є більш чистий спосіб зробити це чи це настільки стисло, як я збираюся отримати?

EDIT: Треба було сказати "Очищення способу використання синтаксису linq". Мені вже було відомо про синтаксис лямбда, і він починає виглядати, що це насправді єдиний спосіб. Хоча я отримав корисну інформацію, тому дякую всім, хто відповів.


5
Особисто я уникаю, Single()і SingleOrDefault()якщо я знаю, дані вже унікальні (наприклад, із бази даних, яка має таке обмеження тощо), оскільки Single()змушує її сканувати решту списку, щоб знайти можливий дублікат, але це я. Якщо вам потрібно на цей час нав'язати свою унікальність, використовуйте Single()сімейство, якщо ні - використовуйте First()сімейство.
Джеймс Майкл Заєць

Відповіді:


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);

який з них є найбільш ефективним щодо часу БД? Якби у мене був варчар, я хотів точної відповідності, але можливо, його не могло бути?
Пьотр Кула

2
Прийнята відповідь вирішує це повністю, але по суті FirstOrDefault зупиняється, як тільки він знайде збіг, але SingleOrDefault повинен вивчити весь список, щоб переконатися, що існує саме одна відповідність.
stuartd

12

Це кращі методи:

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

Або

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

Дякую - тож у цій ситуації немає позначення linq, і мені потрібно використовувати лямбда?
Майкі Хогарт

Це досить добре. Я не дуже багато використовував linq, тому не впевнений, що навіть знав про ці методи.
wageoghe

1
Один метод перевіряє, що повернене значення є унікальним. Тож якщо ваша колекція велика, це може зайняти багато разів. Перший метод просто повертає перший елемент, який відповідає присудку.
meziantou

@wageoghe У відповіді Джеймса не використовується linkq - методи 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 варті того, але лише якщо ви хочете повторити всю колекцію та переконатися, що збіг є унікальним на додаток до вибору відповідності. Перший / FirstOrDefault просто прийме перший матч і піде, незалежно від того, скільки дубліків насправді існує.


4

Ви можете використовувати синтаксис методу розширення:

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

Крім цього, я не впевнений, наскільки більш стислим ви можете отримати, не маючи написання власних спеціалізованих методів розширення "First" та "FirstOrDefault".


Я не думаю, що це призначена поведінка. Ця інструкція Select повертає (насправді ні, але вона вибирає для повернення) колекцію bool, по одному для кожного елемента елементів, перший із повернених як елемент, що стає просто булом. Використовуйте рішення Кріса Хеннона
Леонардо Дага

Можливо, ви маєте на увазі Where, на відміну від того Select, про що вже було сказано, але ця відповідь неправильна. Виберіть у c # зміни результатів на IEnumerable <bool>, тож ви отримаєте 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, потім я використав його для запиту, але цей запит повертає рядок, тож ви можете призначити отримані значення від запиту в текстове поле або будь-що інше , Мені довелося призначити їх до текстового поля.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.