SingleOrDefault () создает исключение для нескольких элементов - PullRequest
15 голосов
/ 06 июля 2010

Я получаю исключение всякий раз, когда получаю вот так

Feature f = o.Features.SingleOrDefault(e => e.LinkName == PageLink);

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

Ответы [ 7 ]

29 голосов
/ 06 июля 2010

Single и 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);
20 голосов
/ 11 мая 2012

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

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);
    }
}
13 голосов
/ 06 июля 2010

Если вам нужен только первый элемент , используйте FirstOrDefault.

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

  • Точно один: Single
  • Один или ноль: SingleOrDefault
  • Один или несколько: First
  • Ноль или больше: FirstOrDefault

(ElementAt и ElementAtOrDefault, Last и LastOrDefault также доступны.)

5 голосов
/ 15 августа 2014

Я обнаружил, что мне нужно поведение возврата значения по умолчанию, если нет точно одного элемента (то есть ноль, два или более) чаще, чем мне нужно нормальное поведение 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 для генерации исключений, когда аргументы нулевые, но вы можете почувствовать иначе.

4 голосов
/ 06 июля 2010

SingleOrDefault предполагает, что вы ожидаете 0 или 1 результатов по вашему запросу.Если у вас больше 1, значит что-то не так с вашими данными или запросом.

Если вы ожидаете более 1 результата и хотите получить только первый, тогда следует использовать FirstOrDefault.

2 голосов
/ 06 июля 2010

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

Если вы ищете только один, используйте First (или FirstOrDefault), как указано выше, но убедитесь, что вы правильно упорядочили данные.

1 голос
/ 06 июля 2010

Если вы используете SingleOrDefault , если условие удовлетворяет больше, чем результат, оно выдаст ошибку.

вы можете достичь своего результата, используя FirstOrDefault

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