簡體   English   中英

如何維護LINQ延遲執行?

[英]How to maintain LINQ deferred execution?

假設我有一個IQueryable<T>表達式,我想封裝它的定義,存儲它並重用它,或者稍后將它嵌入到更大的查詢中。 例如:

IQueryable<Foo> myQuery =
    from foo in blah.Foos
    where foo.Bar == bar
    select foo;

現在我相信我可以保持myQuery對象並像我描述的那樣使用它。 但有些事情我不確定:

  1. 如何最好地參數化? 最初我在方法中定義了這個,然后返回IQueryable<T>作為方法的結果。 這樣我就可以將blahbar定義為方法參數,我想每次只創建一個新的IQueryable<T> 這是封裝IQueryable<T>邏輯的最佳方法嗎? 還有其他方法嗎?

  2. 如果我的查詢解析為標量而不是IQueryable怎么辦? 例如,如果我希望此查詢完全如所示,但附加.Any() ,只是讓我知道是否有任何匹配的結果? 如果我添加(...).Any()然后結果是bool並立即執行,對吧? 有沒有辦法利用這些Queryable運算符( AnySindleOrDefault等)而不立即執行? 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()返回標量值,因此它聽起來會在構建其余查詢之前立即執行。

  1. 當你使用DataContext時,你必須非常小心地傳遞IQueryables,因為一旦上下文被處理掉,你就不能再在那個IQueryable上執行了。 如果你沒有使用上下文,那么你可能沒問題,但要注意這一點。

  2. .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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM