首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

打造專屬 LINQ Provider (中):深度解析 IQueryable 與 IQueryProvider 核心介面

     編輯:關於.NET

.NET Framework 3.5 提供 LINQ 支援後,各式 LINQ Provider 百花齊放 (LINQ to NHibernate、LINQ to Google 等)。LINQ 擴展性優異,可輕鬆開發自訂 Provider。本系列第二篇,詳細剖析自訂 Provider 兩大核心介面:IQueryable 與 IQueryProvider。

IEnumerable<T> 與 LINQ to Objects 特殊性

LINQ to Objects 查詢回傳 IEnumerable<T>,因查詢的是實作 IEnumerable<T> 的記憶體內集合,Lambda 運算式直接轉 .NET 程式碼執行,無需轉換為表達式樹,故不需特定 Provider。

IEnumerable<T> 介面無 Expression 與 Provider 屬性。標準查詢運算子定義於 Enumerable 靜態類別擴展方法,參數為 Func<TSource, bool> 委托而非 Expression<Func<TSource, bool>>。

可透過 AsQueryable() 將 IEnumerable<T> 轉為 IQueryable<T>,查詢機制隨之改變 (雖結果相同)。

IQueryable<T> 介面詳解

繼承 IEnumerable<T> 與 IQueryable:

IQueryable 介面繼承關係圖
IQueryable<T> 繼承關係

兩大關鍵屬性

  • Expression:關聯實例的表達式樹
  • Provider:關聯資料來源的查詢提供者

所有查詢運算子方法呼叫/ Lambda 運算式均由 Expression 表示,最終由 Provider 翻譯為對應資料源查詢語言 (SQL、XML、Web Service 等)。自訂 Provider 必須實作此介面。

Queryable 標準查詢運算子實作

public static class Queryable
{
    public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,
        Expression<Func<TSource, bool>> predicate)
    {
        if (source == null) throw Error.ArgumentNull("source");
        if (predicate == null) throw Error.ArgumentNull("predicate");
        return source.Provider.CreateQuery<TSource>(
            Expression.Call(null,
                ((MethodInfo)MethodBase.GetCurrentMethod()).MakeGenericMethod(typeof(TSource)),
                new Expression[] { source.Expression, Expression.Quote(predicate) }));
    }
    // 另一個 Where 多載略...
}

參數為 Expression<Func<TSource, bool>>,透過 Provider.CreateQuery 建構新查詢。

支援 OrderBy 需實作 IOrderedQueryable<T>

IOrderedQueryable 繼承關係
IOrderedQueryable<T> 繼承自 IQueryable<T>

IQueryProvider 介面詳解

IQueryProvider 介面定義
IQueryProvider 四大核心方法

兩組方法對應兩個操作:

  • CreateQuery (泛型/非泛型):建構可計算指定表達式樹的 IQueryable<T>,回傳可列舉類型 (延遲執行)
  • Execute (泛型/非泛型):執行指定表達式樹查詢,回傳單一值 (立即執行)

LINQ to SQL Provider:翻譯表達式樹為 T-SQL 傳資料庫執行回結果。Web Service Provider:翻譯表達式樹呼叫 Web Service 回結果。

LINQ 擴展兩大路徑

  1. 依賴 LINQ to Objects:查詢在 .NET 程式碼中直接執行,實作 IEnumerable<T> 即可,無需 IQueryable 與自訂 Provider。如 List<T> 等內建類型。
  2. 自訂 LINQ Provider:實作 IQueryable<T> 與 IQueryProvider 兩大介面。

自訂 Provider 骨架程式碼示意

public class QueryableData<TData> : IQueryable<TData>
{
    public QueryableData() { Provider = new TerryQueryProvider(); Expression = Expression.Constant(this); }
    public QueryableData(TerryQueryProvider provider, Expression expression) { ...驗證參數... Provider = provider; Expression = expression; }
    public IQueryProvider Provider { get; private set; }
    public Expression Expression { get; private set; }
    public Type ElementType => typeof(TData);
    public IEnumerator<TData> GetEnumerator() => (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() => (Provider.Execute<IEnumerable>(Expression)).GetEnumerator();
}

public class TerryQueryProvider : IQueryProvider
{
    public IQueryable CreateQuery(Expression expression) { ...動態建立 QueryableData<T>... }
    public IQueryable<TResult> CreateQuery<TResult>(Expression expression) => new QueryableData<TResult>(this, expression);
    public object Execute(Expression expression) { ...執行邏輯... }
    public TResult Execute<TResult>(Expression expression) { ...執行邏輯... }
}

執行流程分析

查詢執行流程圖
實例化 → CreateQuery 建構表達式樹 (延遲) → GetEnumerator/Execute 觸發真實執行
  1. 實例化 QueryableData<String>,同步實例化 TerryQueryProvider
  2. 查詢運算式觸發 TerryQueryProvider.CreateQuery 建構表達式樹,**查詢不真正執行** (延遲載入)
  3. 呼叫 GetEnumerator (如 foreach) 觸發 TerryQueryProvider.Execute,**查詢真正執行**

總結與預告

掌握 IQueryable (表達式樹容器) 與 IQueryProvider (執行引擎) 兩大核心,奠定自訂 LINQ Provider 基石。下篇將完整實作一個自訂 LINQ Provider。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved