[英]How to maintain LINQ deferred execution?
假设我有一个IQueryable<T>
表达式,我想封装它的定义,存储它并重用它,或者稍后将它嵌入到更大的查询中。 例如:
IQueryable<Foo> myQuery =
from foo in blah.Foos
where foo.Bar == bar
select foo;
现在我相信我可以保持myQuery对象并像我描述的那样使用它。 但有些事情我不确定:
如何最好地参数化? 最初我在方法中定义了这个,然后返回IQueryable<T>
作为方法的结果。 这样我就可以将blah
和bar
定义为方法参数,我想每次只创建一个新的IQueryable<T>
。 这是封装IQueryable<T>
逻辑的最佳方法吗? 还有其他方法吗?
如果我的查询解析为标量而不是IQueryable
怎么办? 例如,如果我希望此查询完全如所示,但附加.Any()
,只是让我知道是否有任何匹配的结果? 如果我添加(...).Any()
然后结果是bool
并立即执行,对吧? 有没有办法利用这些Queryable
运算符( Any
, SindleOrDefault
等)而不立即执行? LINQ-to-SQL如何处理这个问题?
编辑:第2部分更多的是试图了解IQueryable<T>.Where(Expression<Func<T, bool>>)
与IQueryable<T>.Any(Expression<Func<T, bool>>)
之间的限制差异IQueryable<T>.Any(Expression<Func<T, bool>>)
。 在创建要延迟执行的大型查询时,似乎后者并不灵活。 可以附加Where()
,然后可以添加其他构造,然后最终执行。 由于Any()
返回标量值,因此它听起来会在构建其余查询之前立即执行。
当你使用DataContext时,你必须非常小心地传递IQueryables,因为一旦上下文被处理掉,你就不能再在那个IQueryable上执行了。 如果你没有使用上下文,那么你可能没问题,但要注意这一点。
.Any()和.FirstOrDefault() 不会延期。 当你调用它们时,它们将导致执行。 但是,这可能不符合您的想法。 例如,在LINQ to SQL中,如果在IQueryable上执行.Any(),它基本上充当IF EXISTS(SQL HERE)。
如果您愿意,可以像这样链接IQueryable:
var firstQuery = from f in context.Foos
where f.Bar == bar
select f;
var secondQuery = from f in firstQuery
where f.Bar == anotherBar
orderby f.SomeDate
select f;
if (secondQuery.Any()) //immediately executes IF EXISTS( second query in SQL )
{
//causes execution on second query
//and allows you to enumerate through the results
foreach (var foo in secondQuery)
{
//do something
}
//or
//immediately executes second query in SQL with a TOP 1
//or something like that
var foo = secondQuery.FirstOrDefault();
}
比缓存IQueryable对象更好的选择是缓存表达式树。 所有IQueryable对象都有一个名为Expression(我相信)的属性,它表示该查询的当前表达式树。
在以后的某个时间点,您可以通过调用queryable.Provider.CreateQuery(表达式)重新创建查询,或者直接在提供者的任何内容(在您的情况下是Linq2Sql数据上下文)中重新创建查询。
然而,参数化这些表达式树稍微有些困难,因为它们使用ConstantExpressions来构建值。 为了参数化这些查询,您每次需要不同的参数时都必须重建查询。
使用这种方式的Any()
都是延迟的。
var q = dc.Customers.Where(c => c.Orders.Any());
使用这种方式的Any()
不会延迟,但仍会转换为SQL(整个customers表不会加载到内存中)。
bool result = dc.Customers.Any();
如果你想要一个延迟的Any(),那就这样做:
public static class QueryableExtensions
{
public static Func<bool> DeferredAny<T>(this IQueryable<T> source)
{
return () => source.Any();
}
}
这被称为:
Func<bool> f = dc.Customers.DeferredAny();
bool result = f();
缺点是这种技术不允许进行子查询。
在表达式中创建查询的部分应用程序
Func[Bar,IQueryable[Blah],IQueryable[Foo]] queryMaker =
(criteria, queryable) => from foo in queryable.Foos
where foo.Bar == criteria
select foo;
然后你可以用......
IQueryable[Blah] blah = context.Blah;
Bar someCriteria = new Bar();
IQueryable[Foo] someFoosQuery = queryMaker(blah, someCriteria);
如果要使查询更具可移植性/可重用性,则可以将查询封装在类中。
public class FooBarQuery
{
public Bar Criteria { get; set; }
public IQueryable[Foo] GetQuery( IQueryable[Blah] queryable )
{
return from foo in queryable.Foos
where foo.Bar == Criteria
select foo;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.