SingleOrDefault() выдает исключение из более чем одного элемента
Я получаю исключение всякий раз, когда я выбираю этот
Feature f = o.Features.SingleOrDefault(e => e.LinkName == PageLink);
потому что это может вернуть один или несколько элементов. Каков альтернативный подход, который я могу использовать для решения этой проблемы?
Ответы
Ответ 1
Одиночные и SingleOrDefault предназначены для выброса, если в последовательности существует больше одного совпадения. Следствием этого является то, что вся последовательность должна быть повторена до завершения. Не похоже, что это то, что вы хотите. Вместо этого попробуйте FirstOrDefault:
Feature f = o.Features
.FirstOrDefault(e => e.vcr_LinkName == PageLink && e.bit_Activate == true);
Это будет (как правило) работать лучше, потому что оно завершается, как только будет найдено совпадение.
Конечно, если вы действительно хотите сохранить более одного элемента, предложение Where будет более подходящим:
IEnumerable<Feature> fs = o.Features
.Where(e => e.vcr_LinkName == PageLink && e.bit_Activate == true);
Ответ 2
В качестве альтернативы, если вы хотите только элемент, когда есть ровно одно совпадение, и вы не хотите бросать его, когда их больше одного, это можно легко выполнить. Я создал метод расширения для этого в моем проекте:
public static class QueryableExtensions
{
public static TSource SingleWhenOnly<TSource>(this IQueryable<TSource> source)
{
if (source == null)
throw new ArgumentNullException("source");
var results = source.Take(2).ToArray();
return results.Length == 1 ? results[0] : default(TSource);
}
public static TSource SingleWhenOnly<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
if (source == null)
throw new ArgumentNullException("source");
if (predicate == null)
throw new ArgumentNullException("predicate");
var results = source.Where(predicate).Take(2).ToArray();
return results.Length == 1 ? results[0] : default(TSource);
}
}
Ответ 3
Если вам нужен только первый элемент, используйте FirstOrDefault
вместо этого.
В принципе, вот варианты с точки зрения допустимых результатов (т.е. где вы не хотите бросать) и что использовать:
- Точно одно:
Single
- Один или ноль:
SingleOrDefault
- Один или несколько:
First
- Нуль или больше:
FirstOrDefault
(ElementAt
и ElementAtOrDefault
, Last
и LastOrDefault
).
Ответ 4
SingleOrDefault
предполагает, что вы ожидаете 0 или 1 результат вашего запроса. Если у вас больше 1, то что-то не так с вашими данными или запросом.
Если вы ожидаете более одного результата и хотите только первого, тогда следует использовать FirstOrDefault
.
Ответ 5
Я нашел, что мне нужно поведение, возвращающее значение по умолчанию, если не существует ровно одного элемента (то есть, ноль, два или более) чаще, чем мне требуется нормальное поведение SingleOrDefault
, поэтому здесь моя адаптированная версия Ответ Питера ван Гинкеля:
public static class LinqExtensions
{
/// <summary>
/// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
/// </summary>
public static TSource SingleOrDefaultIfMultiple<TSource>(this IEnumerable<TSource> source)
{
var elements = source.Take(2).ToArray();
return (elements.Length == 1) ? elements[0] : default(TSource);
}
/// <summary>
/// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
/// </summary>
public static TSource SingleOrDefaultIfMultiple<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
return source.Where(predicate).SingleOrDefaultIfMultiple();
}
/// <summary>
/// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
/// </summary>
public static TSource SingleOrDefaultIfMultiple<TSource>(this IQueryable<TSource> source)
{
var elements = source.Take(2).ToArray();
return (elements.Length == 1) ? elements[0] : default(TSource);
}
/// <summary>
/// Returns the only element of a sequence, or a default value if the sequence is empty or contains more than one element.
/// </summary>
public static TSource SingleOrDefaultIfMultiple<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
return source.Where(predicate).SingleOrDefaultIfMultiple();
}
}
Я пропустил проверку нулевого аргумента, потому что я в порядке, полагаясь на вызовы Take
и Where
, чтобы генерировать исключения, когда аргументы равны нулю, но вы можете чувствовать себя иначе.
Ответ 6
Single
означает, что вы ожидаете быть одним элементом в последовательности.
SingleOrDefault
означает, что вы ожидаете, что в последовательности будет один или нулевой элемент.
Это должно использоваться, когда вы хотите знать, что есть один (или ноль), и вы хотите, чтобы он сработал, когда было возвращено более одного.
Если вы используете только один, используйте First
(или FirstOrDefault
), как было предложено выше, но убедитесь, что вы правильно заказываете данные.
Ответ 7
Если вы используете SingleOrDefault, если условие удовлетворяет больше, чем результат, оно будет вызывать ошибку.
вы можете достичь своего результата, используя FirstOrDefault