为什么这个linq扩展方法会在数据库中命中两次?

时间:2010-02-13 13:52:46

标签: c# .net linq

我有一个名为ToListIfNotNullOrEmpty()的扩展方法,它会两次击中数据库,而不是一次。第一次返回一个结果时,第二次返回所有正确的结果。

我很确定它是第一次到达数据库,是在调用.Any()方法的时候。

这是代码。

public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
    if (value.IsNullOrEmpty())
    {
        return null;
    }
    if (value is IList<T>)
    {
        return (value as IList<T>);
    }
    return new List<T>(value);
}

public static bool IsNullOrEmpty<T>(this IEnumerable<T> value)
{
    if (value != null)
    {
        return !value.Any();
    }
    return true;
}

我希望重构它,以便在调用.Any()方法之前,它实际上枚举整个列表。

如果我执行以下操作,则只进行一次DB调用,因为列表已经枚举。

var pewPew = (from x in whatever
              select x)
             .ToList()   // This enumerates.
             .ToListIsNotNullOrEmpty();  // This checks the enumerated result.

我真的不想调用ToList()然后我的扩展方法。 任何想法,伙计们?

4 个答案:

答案 0 :(得分:3)

我承认我认为这种方法没什么意义。当然,如果你只是做一个ToList(),检查列表是否为空也是足够的。当你期望一个列表时,处理null结果可能会更难,因为那时你总是必须在迭代它之前检查null。

我认为:

 var query = (from ...).ToList();
 if (query.Count == 0) {
     ...
 }

同样有效,并且比

更轻松
var query = (from ...).ToListIfNotNullOrEmpty();
if (query == null) {
   ...
}

并且您不必实现(和维护)任何代码。

答案 1 :(得分:1)

这样的事情怎么样?

public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
    if (value == null)
        return null;

    var list = value.ToList();
    return (list.Count > 0) ? list : null;
}

答案 2 :(得分:1)

要真正回答你的问题:

此方法两次访问数据库,因为System.Linq.Enumerable类提供的扩展方法展示了所谓的延迟执行。实质上,这是为了消除为查询的每个部分构造一串临时缓存集合的需要。要理解这一点,请考虑以下示例:

var firstMaleTom = people
    .Where(p => p.Gender = Gender.Male)
    .Where(p => p.FirstName == "Tom")
    .FirstOrDefault();

如果没有延迟执行,上面的代码可能要求枚举整个集合people,填充一个临时缓冲区数组,其中包含GenderMale的所有个体。然后需要在上再次枚举,用第一个缓冲区中的所有个体填充另一个缓冲区数组,其名字为{{ 1}}。完成所有工作后,最后一部分将返回结果数组中的第一个项目。

这是一项毫无意义的工作。延迟执行的想法是,上面的代码实际上只是设置了Tom变量,其中包含了以最少的工作量返回所请求内容所需的信息。

现在,有一个反面:在查询数据库的情况下,延迟执行意味着在评估返回值时查询数据库。因此,在您的firstMaleTom方法中,当您调用IsNullOrEmpty时,Any参数实际上正在进行评估,因此会进行数据库查询。在此之后,在value方法中,行ToListIfNotNullOrEmpty 评估return new List<T>(value)参数 - 因为它会枚举值并将它们添加到新创建的value

答案 3 :(得分:0)

您可以将.ToList()调用粘贴到扩展程序中,效果略有不同,但是在您拥有的情况下这仍然有用吗?

public static IList<T> ToListIfNotNullOrEmpty<T>(this IEnumerable<T> value)
{
    if(value == null)
    {
        return null;
    }
    var result = value.ToList();        
    return result.IsNullOrEmpty() ? null : result;
}