【发布时间】:2011-03-12 05:37:30
【问题描述】:
每当我像这样获取时都会遇到异常
Feature f = o.Features.SingleOrDefault(e => e.LinkName == PageLink);
因为这可以返回一个或多个元素。我可以使用什么替代方法来解决此问题?
【问题讨论】:
标签: linq linq-to-sql collections
每当我像这样获取时都会遇到异常
Feature f = o.Features.SingleOrDefault(e => e.LinkName == PageLink);
因为这可以返回一个或多个元素。我可以使用什么替代方法来解决此问题?
【问题讨论】:
标签: linq linq-to-sql collections
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);
【讨论】:
或者,如果您只在只有一个匹配项时想要该项目,而不想在有多个匹配项时扔掉,则可以轻松实现。我在我的项目中为此创建了一个扩展方法:
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 .
如果您只想要 first 元素,请改用 FirstOrDefault。
基本上,这里是关于有效结果的选项(即你不想扔的地方)和使用什么:
Single
SingleOrDefault
First
FirstOrDefault
(ElementAt 和 ElementAtOrDefault、Last 和 LastOrDefault 也可用。)
【讨论】:
我发现如果没有一个元素(即零、两个或更多)比我需要正常的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 时,我可以依赖 Take 和 Where 调用引发异常,但您可能会有其他感觉。
【讨论】:
OnlyOrDefault(),但我之所以使用更长的名称,部分原因是当我(或其他团队成员)开始输入.SingleOrDefault 和我们希望最终能挑选出适合这种情况的那个。
SingleOrDefault 表明您期望从查询中得到 0 或 1 个结果。如果您有超过 1 个,那么您的数据或查询有问题。
如果您期望超过 1 个结果并且只想要第一个结果,则应使用 FirstOrDefault。
【讨论】:
如果您使用SingleOrDefault,如果条件满足超过结果,则会抛出错误。
您可以使用FirstOrDefault实现您的结果
【讨论】:
Single 表示您希望成为序列中的一个元素。
SingleOrDefault 表示您希望序列中有一个或零个元素。
当您想知道有一个(或零个)并且您希望它在返回超过一个时崩溃时,应该使用它。
如果您只需要一个,请按照上面的建议使用First(或FirstOrDefault),但请确保您正确排序数据。
【讨论】: