假设我有一个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>>)
之间的限制差异。在创建要延迟执行的大型查询时,似乎后者并不灵活。可以附加Where()
,然后可以添加其他构造,然后最终执行。由于Any()
返回标量值,因此它可能会在构建其余查询之前立即执行。
答案 0 :(得分:5)
当你使用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();
}
答案 1 :(得分:2)
比缓存IQueryable对象更好的选择是缓存表达式树。所有IQueryable对象都有一个名为Expression(我相信)的属性,它表示该查询的当前表达式树。
稍后,您可以通过调用queryable.Provider.CreateQuery(表达式)重新创建查询,或者直接在提供者身上重新创建查询(在您的情况下是Linq2Sql数据上下文)。
然而,参数化这些表达式树稍微有些困难,因为它们使用ConstantExpressions来构建值。为了参数化这些查询,您每次需要不同的参数时都必须重建查询。
答案 2 :(得分:1)
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();
缺点是这种技术不允许进行子查询。
答案 3 :(得分:0)
在表达式
中创建查询的部分应用程序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;
}
}