[英]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.