Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 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