在回答this question时,它让我思考......
我经常使用这种模式:
collectionofsomestuff //here it's LinqToEntities
.Select(something=>new{something.Name,something.SomeGuid})
.ToArray() //From here on it's LinqToObjects
.Select(s=>new SelectListItem()
{
Text = s.Name,
Value = s.SomeGuid.ToString(),
Selected = false
})
也许我会把它拆分成两行,但基本上,在ToArray
点,我实际上是在枚举我的查询并存储结果序列,这样我就可以进一步处理它所有的善良完整的CLR。
由于我对中间列表的任何操作都不感兴趣,我使用ToArray
而不是ToList
,因为开销较少。
我一直这样做,但我想知道这种问题是否有更好的模式?
答案 0 :(得分:5)
如果你在LINQ查询的其余部分进行简单的赋值,那么Reed的答案确实是正确的。但是,如果您在查询的LinqToObjects部分中执行大量工作或计算,则如果考虑与基础数据源的连接,则他的解决方案会有一些小问题:
考虑:
collectionofsomestuff //here it's LinqToEntities
.Select(something=>new{something.Name,something.SomeGuid})
.AsEnumerable() //From here on it's LinqToObjects
.Select(s=>new SelectListItem()
{
Text = s.Name,
Value = s.SomeGuid.ToString(),
OtherValue = someCrazyComputationOnS(s)
})
如果您可以想象一下LinqToEntities选择函数的代码(高度简化,但您应该得到图片),它可能看起来像:
using(SqlConnection con = createConnection())
{
using(SqlCommand com = con.CreateCommand())
{
con.Open();
com.CommandText = createQuery(expression);
using(SqlDataReader reader = com.ExecuteReader())
{
while(reader.Read())
{
yield return createClrObjectFromReader(reader);
}
}
}
}
此方法支持传统的Linq延迟执行模式。这意味着无论何时从读取器读取结果,它都会“返回”给调用者,并且在调用者请求之前不会读取下一个值。
因此,在上面的代码中,5个记录的结果集的执行顺序是:
con.Open();
reader.Read();
createClrObjectFromReader(reader);
// at this point there is a yield back to the caller
someCrazyComputationOnS(s);
reader.Read();
createClrObjectFromReader(reader);
// at this point there is a yield back to the caller
someCrazyComputationOnS(s);
reader.Read();
createClrObjectFromReader(reader);
// at this point there is a yield back to the caller
someCrazyComputationOnS(s);
reader.Read();
createClrObjectFromReader(reader);
// at this point there is a yield back to the caller
someCrazyComputationOnS(s);
reader.Read();
createClrObjectFromReader(reader);
// at this point there is a yield back to the caller
someCrazyComputationOnS(s);
// ONLY here does the connection finally get closed:
con.Close();
虽然这确实保留了延迟执行模式。在这种情况下,这不是最佳选择。调用ToList()或ToArray()会导致整个原始查询结果被缓冲到数组或列表中,之后可以关闭SqlConnection。只有在关闭SqlConnection之后才会调用someCrazyComputationOnS(s)。
在大多数情况下,这不是一个问题,Reed的答案确实是正确的,但在极少数情况下,您正在对数据集进行大量工作,您肯定希望在继续进行大型LinqToObjects查询之前缓冲结果。
答案 1 :(得分:3)
有一个更好的选择:AsEnumerable
用法类似:
collectionofsomestuff //here it's LinqToEntities
.Select(something=>new{something.Name,something.SomeGuid})
.AsEnumerable() //From here on it's LinqToObjects
.Select(s=>new SelectListItem()
{
Text = s.Name,
Value = s.SomeGuid.ToString(),
Selected = false
})
但是,这并不会强制执行完整副本,如ToList()
或ToArray()
,并保留您的提供商的任何延迟执行。