如果我用这样的东西查询EF。。。
IEnumerable<FooBar> fooBars = db.FooBars.Where(o => o.SomeValue == something);
IIRC,这在后台创建了一个懒惰评估的、可迭代的状态机,它还不包含任何结果;相反,它包含一个"如何"在需要时获得结果的表达式。
如果我想强制集合包含结果,我必须调用.ToArray()
或.ToList()
有没有一种方法可以强制IEnumerable<T>
集合包含结果而不调用.ToArray()
或.ToList();
?
基本原理
我不知道CLR是否能够做到这一点,但本质上我想强制创建一个实现IEnumerable<T>
接口的求值集合,但它是由CLR在后台实现的,因此NOTList<T>
或Array<T>
这可能是不可能的,因为我不知道有任何CLR功能可以在内存中创建实现IEnumerable<T>
的已评估集合
建议书
举个例子,我可以写这样的东西:
var x = IEnumerable<FooBar> fooBars = db.FooBars
.Where(o => o.SomeValue == something)
.Evaluate(); // Does NOT return a "concrete" impl such as List<T> or Array<T>
Console.WriteLine(x.GetType().Name);
// eg. <EvaluatedEnumerable>e__123
有没有一种方法可以强制
IEnumerable<T>
集合包含结果而不调用.ToArray()
或.ToList();
?
是的,但这可能不是你想要的:
IEnumerable<T> source = …;
IEnumerable<T> cached = new List<T>(source);
问题是,IEnumerable<T>
不是一个具体的类型。它是一个表示项目序列的接口(合同)。可以有任何具体的类型"隐藏在"这个接口后面;有些可能只表示一个查询,而另一些则实际将查询项保存在内存中。
如果要强制计算序列,以便将结果实际存储在物理内存中,则需要确保IEnumerable<T>
后面的具体类型是内存中的集合,其中包含计算结果。上面的代码示例就是这样做的。
您可以使用foreach
循环:
foreach (var item in fooBars) { }
请注意,这会评估fooBars
中的所有项,但会立即丢弃结果。下次运行相同的foreach
循环或.ToArray()
、.ToList()
时,将再次计算枚举值。
我遇到的一个具体用例是,在将控制权返回给调用方法之前,需要确保包装DB查询的IEnumerable已经开始返回结果(表明查询没有超时)。但结果太大,无法完全评估,因此IEnumerable无法支持流媒体。
internal class EagerEvaluator<T>
{
private readonly T _first;
private readonly IEnumerator<T> _enumerator;
private readonly bool _hasFirst;
public EagerEvaluator(IEnumerable<T> enumerable)
{
_enumerator = enumerable.GetEnumerator();
if (_enumerator.MoveNext())
{
_hasFirst = true;
_first = _enumerator.Current;
}
}
public IEnumerable<T> ToEnumerable()
{
if (_hasFirst)
{
yield return _first;
while (_enumerator.MoveNext())
{
yield return _enumerator.Current;
}
}
}
}
用法非常直接:
IEnumerable<FooBar> fooBars = new EagerEvaluator(fooBars).ToEnumerable()
另一个选项是:
<linq expression>.All( x => true);
我使用Aggregate<T>()
来评估有副作用的IEnumerable<T>
:
private static IEnumerable<T> Evaluate<T>(IEnumerable<T> source)
=> source.Aggregate(Enumerable.Empty<T>(), (evaluated, s) => evaluated.Append(s));
在行动中看到它:https://dotnetfiddle.net/iya2l0