我有一个名为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()
然后我的扩展方法。
任何想法,伙计们?
答案 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
,填充一个临时缓冲区数组,其中包含Gender
为Male
的所有个体。然后需要在上再次枚举,用第一个缓冲区中的所有个体填充另一个缓冲区数组,其名字为{{ 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;
}