一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

打造自己的LINQ Provider(中):IQueryable和IQueryProvid...

 Wiley Library 2013-03-14

打造自己的LINQ Provider(中):IQueryable和IQueryProvider

概述

在.NET Framework 3.5中提供了LINQ 支持后,LINQ就以其強大而優(yōu)雅的編程方式贏得了開發(fā)人員的喜愛,而各種LINQ Provider更是滿天飛,如LINQ to NHibernate、LINQ to Google等,大有“一切皆LINQ”的趨勢。LINQ本身也提供了很好的擴展性,使得我們可以輕松的編寫屬于自己的LINQ Provider。
本文為打造自己的LINQ Provider系列文章第二篇,主要詳細介紹自定義LINQ Provider中兩個最重要的接口IQueryable和IQueryProvider。

IEnumerable<T>接口

在上一篇《打造自己的LINQ Provider(上):Expression Tree揭秘》一文的最后,我說到了這樣一句話:需要注意的是LINQ to Objects并不需要任何特定的LINQ Provider,因為它并不翻譯為表達式目錄樹,帶著這個問題,我們先來看下面這段代碼,查詢的結(jié)果query為IEnumerable<String>類型:

static void Main(string[] args)
{
    List<String> myList = new List<String>() { "a", "ab", "cd", "bd" };

    IEnumerable<String> query = from s in myList
                where s.StartsWith("a")
                select s;

    foreach (String s in query)
    {
        Console.WriteLine(s);
    }

    Console.Read();
}

這里將返回兩條結(jié)果,如下圖所示:

TerryLee_0170

這里就有一個問題,為什么在LINQ to Objects中返回的是IEnumerable<T>類型的數(shù)據(jù)而不是IQueryable<T>呢?答案就在本文的開始,在LINQ to Objects中查詢表達式或者Lambda表達式并不翻譯為表達式目錄樹,因為LINQ to Objects查詢的都是實現(xiàn)了IEnmerable<T>接口的數(shù)據(jù),所以查詢表達式或者Lambda表達式都可以直接轉(zhuǎn)換為.NET代碼來執(zhí)行,無需再經(jīng)過轉(zhuǎn)換為表達式目錄這一步,這也是LINQ to Objects比較特殊的地方,它不需要特定的LINQ Provider。我們可以看一下IEnumerable<T>接口的實現(xiàn),它里面并沒有Expression和Provider這樣的屬性,如下圖所示:

TerryLee_0171

至于LINQ to Objects中所有的標準查詢操作符都是通過擴展方法來實現(xiàn)的,它們在抽象類Enumerable中定義,如其中的Where擴展方法如下代碼所示:

public static class Enumerable
{
    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, bool> predicate)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (predicate == null)
        {
            throw Error.ArgumentNull("predicate");
        }
        return WhereIterator<TSource>(source, predicate);
    }

    public static IEnumerable<TSource> Where<TSource>(
        this IEnumerable<TSource> source, 
        Func<TSource, int, bool> predicate)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (predicate == null)
        {
            throw Error.ArgumentNull("predicate");
        }
        return WhereIterator<TSource>(source, predicate);
    }
}

注意到這里方法的參數(shù)Func<TSource>系列委托,而非Expression<Func<TSource>>,在本文的后面,你將看到,IQueryable接口的數(shù)據(jù),這些擴展方法的參數(shù)都是Expression<Func<TSource>>,關(guān)于它們的區(qū)別在上一篇文章我已經(jīng)說過了。同樣還有一點需要說明的是,在IEnumerable<T>中提供了一組擴展方法AsQueryable(),可以用來把一個IEnumerable<T>類型的數(shù)據(jù)轉(zhuǎn)換為IQueryable<T>類型,如下代碼所示:

static void Main(string[] args)
{
    var myList = new List<String>() 
                { "a", "ab", "cd", "bd" }.AsQueryable<String>();

    IQueryable<String> query = from s in myList
                where s.StartsWith("a")
                select s;

    foreach (String s in query)
    {
        Console.WriteLine(s);
    }

    Console.Read();
} 

運行這段代碼,雖然它的輸出結(jié)果與上面的示例完全相同,但它們查詢的機制卻完全不同:

TerryLee_0170

IQueryable<T>接口

在.NET中,IQueryable<T>繼承于IEnumerable<T>和IQueryable接口,如下圖所示:

TerryLee_0172

這里有兩個很重要的屬性Expression和Provider,分別表示獲取與IQueryable 的實例關(guān)聯(lián)的表達式目錄樹和獲取與此數(shù)據(jù)源關(guān)聯(lián)的查詢提供程序,我們所有定義在查詢表達式中方法調(diào)用或者Lambda表達式都將由該Expression屬性表示,而最終會由Provider表示的提供程序翻譯為它所對應(yīng)的數(shù)據(jù)源的查詢語言,這個數(shù)據(jù)源可能是數(shù)據(jù)庫,XML文件或者是WebService等。該接口非常重要,在我們自定義LINQ Provider中必須要實現(xiàn)這個接口。同樣對于IQueryable的標準查詢操作都是由Queryable中的擴展方法來實現(xiàn)的,如下代碼所示:

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(new Type[] { typeof(TSource) }), 
            new Expression[] { source.Expression, Expression.Quote(predicate) }));
    }

    public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source,
        Expression<Func<TSource, int, 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(new Type[] { typeof(TSource) }), 
            new Expression[] { source.Expression, Expression.Quote(predicate) }));
    }
}

最后還有一點,如果我們定義的查詢需要支持Orderby等操作,還必須實現(xiàn)IOrderedQueryable<T> 接口,它繼承自IQueryable<T>,如下圖所示:

TerryLee_0173 

IQueryProvider接口

在認識了IQueryable接口之后,我們再來看看在自定義LINQ Provider中另一個非常重要的接口IQueryProvider。它的定義如下圖所示:

TerryLee_0174

看到這里兩組方法的參數(shù),其實大家已經(jīng)可以知道,Provider負責執(zhí)行表達式目錄樹并返回結(jié)果。如果是LINQ to SQL的Provider,則它會負責把表達式目錄樹翻譯為T-SQL語句并并傳遞給數(shù)據(jù)庫服務(wù)器,并返回最后的執(zhí)行的結(jié)果;如果是一個Web Service的Provider,則它會負責翻譯表達式目錄樹并調(diào)用Web Service,最終返回結(jié)果。

這里四個方法其實就兩個操作CreateQuery和Execute(分別有泛型和非泛型),CreateQuery方法用于構(gòu)造一個 IQueryable<T> 對象,該對象可計算指定表達式目錄樹所表示的查詢,返回的結(jié)果是一個可枚舉的類型,;而Execute執(zhí)行指定表達式目錄樹所表示的查詢,返回的結(jié)果是一個單一值。自定義一個最簡單的LINQ Provider,至少需要實現(xiàn)IQueryable<T>和IQueryProvider兩個接口,在下篇文章中,你將看到一個綜合的實例。

擴展LINQ的兩種方式

通過前面的講解,我們可以想到,對于LINQ的擴展有兩種方式,一是借助于LINQ to Objects,如果我們所做的查詢直接在.NET代碼中執(zhí)行,就可以實現(xiàn)IEnumerable<T>接口,而無須再去實現(xiàn)IQueryable并編寫自定義的LINQ Provider,如.NET中內(nèi)置的List<T>等。如我們可以編寫一段簡單自定義代碼:

public class MyData<T> : IEnumerable<T>
                where T : class
{
    public IEnumerator<T> GetEnumerator()
    {
        return null;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return null;
    }

    // 其它成員
}

第二種擴展LINQ的方式當然就是自定義LINQ Provider了,我們需要實現(xiàn)IQueryable<T>和IQueryProvider兩個接口,下面先給出一段簡單的示意代碼,在下一篇中我們將完整的來實現(xiàn)一個LINQ Provider。如下代碼所示:

public class QueryableData<TData> : IQueryable<TData>
{
    public QueryableData()
    {
        Provider = new TerryQueryProvider();
        Expression = Expression.Constant(this);
    }

    public QueryableData(TerryQueryProvider provider, 
        Expression expression)
    {
        if (provider == null)
        {
            throw new ArgumentNullException("provider");
        }

        if (expression == null)
        {
            throw new ArgumentNullException("expression");
        }

        if (!typeof(IQueryable<TData>).IsAssignableFrom(expression.Type))
        {
            throw new ArgumentOutOfRangeException("expression");
        }

        Provider = provider;
        Expression = expression;
    }

    public IQueryProvider Provider { get; private set; }
    public Expression Expression { get; private set; }

    public Type ElementType
    {
        get { return typeof(TData); }
    }

    public IEnumerator<TData> GetEnumerator()
    {
        return (Provider.Execute<IEnumerable<TData>>(Expression)).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return (Provider.Execute<IEnumerable>(Expression)).GetEnumerator();
    }
}

public class TerryQueryProvider : IQueryProvider
{
    public IQueryable CreateQuery(Expression expression)
    {
        Type elementType = TypeSystem.GetElementType(expression.Type);
        try
        {
            return (IQueryable)Activator.CreateInstance(
                typeof(QueryableData<>).MakeGenericType(elementType),
                new object[] { this, expression });
        }
        catch
        {
            throw new Exception();
        }
    }

    public IQueryable<TResult> CreateQuery<TResult>(Expression expression)
    {
        return new QueryableData<TResult>(this, expression);
    }

    public object Execute(Expression expression)
    {
        // ......
    }

    public TResult Execute<TResult>(Expression expression)
    {
        // ......
    }
}

上面這兩個接口都沒有完成,這里只是示意性的代碼,如果實現(xiàn)了這兩個接口,我們就可以像下面這樣使用了(當然這樣的使用是沒有意義的,這里只是為了演示):

static void Main(string[] args)
{
    QueryableData<String> mydata = new QueryableData<String> { 
        "TerryLee",
        "Cnblogs",
        "Dingxue"
    };

    var result = from d in mydata
                 select d;
    foreach (String item in result)
    {
        Console.WriteLine(item);
    }
}

現(xiàn)在再來分析一下這個執(zhí)行過程,首先是實例化QueryableData<String>,同時也會實例化TerryQueryProvider;當執(zhí)行查詢表達式的時候,會調(diào)用TerryQueryProvider中的CreateQuery方法,來構(gòu)造表達式目錄樹,此時查詢并不會被真正執(zhí)行(即延遲加載),只有當我們調(diào)用GetEnumerator方法,上例中的foreach,此時會調(diào)用TerryQueryProvider中的Execute方法,此時查詢才會被真正執(zhí)行,如下圖所示:

TerryLee_0178 

總結(jié)

本文介紹了在自定義LINQ Provider中兩個最重要的接口IQueryable和IQueryProvider,希望對大家有所幫助,下一篇我我們將開發(fā)一個完整的自定義LINQ Provider。

相關(guān)文章:打造自己的LINQ Provider(上):Expression Tree揭秘

作者:TerryLee
出處:http://terrylee.cnblogs.com
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權(quán)利。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    欧美区一区二在线播放| 国产精品香蕉在线的人| 色婷婷国产熟妇人妻露脸| 亚洲av首页免费在线观看| 久久99这里只精品热在线| 日本午夜免费福利视频| 国产高清一区二区白浆| 国产av精品一区二区| 激情中文字幕在线观看| 亚洲最新av在线观看| 成人日韩视频中文字幕| 东京热加勒比一区二区| 91国内视频一区二区三区| 人妻乱近亲奸中文字幕| 亚洲乱妇熟女爽的高潮片| 欧美有码黄片免费在线视频| 精品日韩欧美一区久久| 伊人久久青草地婷婷综合| 精品国产一区二区欧美| 91亚洲精品综合久久| 女人高潮被爽到呻吟在线观看| 在线免费视频你懂的观看| 国产精品美女午夜福利| 好吊日视频这里都是精品| 国产午夜免费在线视频| 91久久精品国产一区蜜臀| 国产美女精品人人做人人爽| 视频一区二区 国产精品| 五月综合激情婷婷丁香| 欧美乱视频一区二区三区| 日韩综合国产欧美一区| 欧美人妻少妇精品久久性色 | 大尺度剧情国产在线视频| 久久精品欧美一区二区三不卡| 高跟丝袜av在线一区二区三区| 精品国产丝袜一区二区| 欧美熟妇喷浆一区二区| 国产高清视频一区不卡| 亚洲二区欧美一区二区| 一级欧美一级欧美在线播| 久久午夜福利精品日韩|