【问题标题】:SingleOrDefault() throws an exception on more than one elementSingleOrDefault() 在多个元素上引发异常
【发布时间】:2011-03-12 05:37:30
【问题描述】:

每当我像这样获取时都会遇到异常

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

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

【问题讨论】:

    标签: linq linq-to-sql collections


    【解决方案1】:

    SingleSingleOrDefault 旨在在序列中存在多个匹配项时抛出。这样做的结果是整个序列必须在完成之前迭代。听起来这不是你想要的。改用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);
          }
      }
      

      【讨论】:

      • 谢谢你,我目前正在使用这个。我将它重命名为ExclusiveOrDefault,还做了一个扩展方法Exclusive,当有个元素时抛出错误,但有2个或更多个元素时返回null .
      【解决方案3】:

      如果您只想要 first 元素,请改用 FirstOrDefault

      基本上,这里是关于有效结果的选项(即你不想扔的地方)和使用什么:

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

      ElementAtElementAtOrDefaultLastLastOrDefault 也可用。)

      【讨论】:

        【解决方案4】:

        我发现如果没有一个元素(即零、两个或更多)比我需要正常的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 参数检查,因为当参数为 null 时,我可以依赖 TakeWhere 调用引发异常,但您可能会有其他感觉。

        【讨论】:

        • 不错。不过,我已将冗长的 SingleOrDefaultIfMultiple() 重命名为 OnlyOrDefault()。您的 cmets 甚至确实建议这样做....
        • 或者,那《TheOneOrDefault》呢(我最近重看了《黑客帝国》三部曲)呵呵....
        • @Marcel 我确实喜欢OnlyOrDefault(),但我之所以使用更长的名称,部分原因是当我(或其他团队成员)开始输入.SingleOrDefault 和我们希望最终能挑选出适合这种情况的那个。
        【解决方案5】:

        SingleOrDefault 表明您期望从查询中得到 0 或 1 个结果。如果您有超过 1 个,那么您的数据或查询有问题。

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

        【讨论】:

          【解决方案6】:

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

          您可以使用FirstOrDefault实现您的结果

          【讨论】:

            【解决方案7】:

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

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-07-03
              • 1970-01-01
              • 2015-09-15
              • 1970-01-01
              • 2010-09-21
              • 1970-01-01
              相关资源
              最近更新 更多