SingleOrDefault()在多个元素上抛出异常

时间:2010-07-06 09:36:06

标签: linq linq-to-sql collections

每当我像这样获取

时,我都会收到异常
Feature f = o.Features.SingleOrDefault(e => e.LinkName == PageLink);

因为这可以返回一个或多个元素。我可以用什么方法来解决这个问题?

7 个答案:

答案 0 :(得分:29)

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

答案 1 :(得分:20)

或者,如果你只想要一个匹配的项目,并且当有多个匹配时不想抛出,那么这很容易实现。我在我的项目中为此创建了一个扩展方法:

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

答案 2 :(得分:13)

如果您只想要第一个元素,请改用FirstOrDefault

基本上,以下是有效结果的选项(即您不想扔的地方)以及使用方法:

  • 正好一个:Single
  • 一个或零个:SingleOrDefault
  • 一个或多个:First
  • 零或更多:FirstOrDefault

ElementAtElementAtOrDefaultLastLastOrDefault也可用。)

答案 3 :(得分:5)

我发现如果没有一个元素(即零,两个或更多)比我需要正常SingleOrDefault行为更多,我需要返回默认值的行为,所以这里是我的改编版本Pieter van Ginkel's answer

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

我省略了null参数检查,因为我可以依赖TakeWhere调用在参数为null时抛出异常,但你可能会感觉不同。

答案 4 :(得分:4)

SingleOrDefault表示您希望查询的结果为0或1。如果您的数据超过1,那么您的数据或查询就会出现问题。

如果您期望获得超过1个结果而只想要第一个结果,则应使用FirstOrDefault

答案 5 :(得分:2)

Single表示您希望成为序列中的一个元素。 SingleOrDefault表示您希望序列中有一个或零个元素。 当你想知道有一个(或零)时,你应该使用它,并且当它返回多个时你想要它崩溃。

如果您只是一个,请按照上面的建议使用First(或FirstOrDefault),但请确保您正确订购数据。

答案 6 :(得分:1)

如果您使用SingleOrDefault,如果条件满足超过结果,则会抛出错误。

您可以使用FirstOrDefault

来达到结果