SingleOrDefault () вызывает исключение более чем для одного элемента

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

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

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


person Fraz Sundal    schedule 06.07.2010    source источник


Ответы (7)


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);
person spender    schedule 06.07.2010

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

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);
    }
}
person Pieter van Ginkel    schedule 11.05.2012
comment
Спасибо за это, сейчас я использую это. Я переименовал его в ExclusiveOrDefault, а также создал метод расширения Exclusive, который выдает ошибку, когда есть ноль элементов, но возвращает null, когда есть 2 или более элементов. - person Rudey; 19.02.2016

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

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

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

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

person Jon Skeet    schedule 06.07.2010

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

person Sean Rose    schedule 15.08.2014
comment
Отлично. Однако я переименовал длинный SingleOrDefaultIfMultiple () в OnlyOrDefault (). Ваши комментарии даже предполагают это .... - person Marcel; 23.10.2014
comment
Или как насчет TheOneOrDefault (я недавно пересмотрел трилогию «Матрица») хе-хе .... - person Marcel; 23.10.2014
comment
@Marcel Мне нравится OnlyOrDefault(), но отчасти причина моего более длинного имени в том, что оно появилось бы как вариант intellisense, когда я (или другие члены команды) начну набирать .SingleOrDefault, и мы, надеюсь, в конечном итоге выберем тот, который подходит для ситуации . - person Sean Rose; 29.04.2016

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

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

person Geoff Appleford    schedule 06.07.2010

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

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

person DaveShaw    schedule 06.07.2010

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

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

person anishMarokey    schedule 06.07.2010