Linq to “everything you want” 深入淺出(一) 實(shí)現(xiàn)IQueryable的基類 為了節(jié)省大家的時(shí)間,,希望先了解Expression Tree然后再看本系列的文章,關(guān)于這些方面的介紹,,建議查看TerryLee的打造自己的LINQ Provider:Expression Tree揭秘 前言:之所以要發(fā)出這個(gè)系列,,主要是之前在發(fā)出的.NET 業(yè)務(wù)框架開發(fā)實(shí)戰(zhàn)之八 業(yè)務(wù)層Mapping的選擇策略 一文中,,提到了查詢對(duì)象的實(shí)現(xiàn)。在文章發(fā)出之后,,園子里的朋友給出很多非常不錯(cuò)評(píng)論反饋,,其中園友mywork提出了非常好的建議:提議用Expression Tree來(lái)實(shí)現(xiàn)。同時(shí)Harold Shen也提出了這個(gè)建議,。在博客園里 Linq Provider的文章很多,,那就當(dāng)給博客園里面的Linq主題多添加一點(diǎn)資料吧,分享一下,,同時(shí)也為.NET業(yè)務(wù)框架實(shí)戰(zhàn)系列中條件對(duì)象(查詢對(duì)象只是條件對(duì)象的一種)的實(shí)現(xiàn)做鋪路,。 J. 現(xiàn)在有很多的linq to XXX(Linq to sql,Linq to javascript等),所以文章的標(biāo)題意思大家就已經(jīng)明白了。 本篇議題如下: 1. 條件對(duì)象怎樣實(shí)現(xiàn) 2. IQueryable介紹 3. IQueryProvider介紹 4. IQueryable的基類實(shí)現(xiàn)
1. 條件對(duì)象怎樣實(shí)現(xiàn)
開始的時(shí)候,,條件對(duì)象的實(shí)現(xiàn)如下:
ICriteria condition=CriteriaFactory.Create(typeof(ProductBL).Where("ProductName", Operation.Equal,"book");
采用這種方式固然靈活,,但是最大的問(wèn)題就是Where后面的條件:ProductName是基于字符串的,缺少編譯器的智能感應(yīng),,而且如果ProductBL的ProductName改變?yōu)榱?/span>Name,,,有些地方可能仍然采用的ProductName,而且這樣的錯(cuò)誤很難發(fā)現(xiàn),,只有在運(yùn)行時(shí)才知道,,再者Where條件的構(gòu)造也顯得有點(diǎn)復(fù)雜(和現(xiàn)在的linq相比)。如果換成如下的方式,,可能就更好一些:
ICriteria<T> condition=CriteriaFactory.Create<T>(Expression<Func<T,bool>> func); 使用時(shí) ICriteria<ProductBL> condition=CriteriaFactory.Create<ProductBL>(o => o.ProductName == "book");
惟一很問(wèn)題就是解析Expression樹,。 而且上面的代碼是在UI代碼中被調(diào)用,然后,,ICriteria 對(duì)象最后會(huì)被傳到BLL,,最后傳到DAL,解析為SQL語(yǔ)句進(jìn)行執(zhí)行,。 趁實(shí)現(xiàn)條件對(duì)象的機(jī)會(huì),,把IQueryable Provider具體的講述一下,一舉兩得。
2. IQueryable介紹 我們從IQueryable接口入手,,首先來(lái)看看接口的定義: public interface IQueryable : IEnumerable { Type ElementType { get; } Expression Expression { get; } IQueryProvider Provider { get; } }
接口中定義了三個(gè)只讀的屬性,,簡(jiǎn)單的理解:ElementType表示查詢返回對(duì)象的類型;Expression就是查詢條件集合(Expression可以組成一棵樹),,Provider就是用來(lái)解析Expression的對(duì)象,。下面的接口是上面接口的泛型版:
public interface IQueryable<T> : IEnumerable<T>, IQueryable, IEnumerable { }
IQueryable對(duì)象的本質(zhì)就是一個(gè)表達(dá)式,這個(gè)表達(dá)式體現(xiàn)了在linq查詢時(shí)方法的調(diào)用,。 怎么講:首先在這里不得不提及System.Linq.Queryable類,,這個(gè)類在實(shí)現(xiàn)Linq的查詢中起了很大的作用,代碼如下:
代碼
這個(gè)類的方法很多,而且這個(gè)類是專門用來(lái)對(duì)IQueryable添加擴(kuò)展方法的,。其中,,當(dāng)我們使用Queryable.Where的方法時(shí)在IQueryable對(duì)象上進(jìn)行過(guò)濾時(shí),Where方法在IQueryable的Expression的表達(dá)式屬性的頂層添加了一個(gè)表示方法調(diào)用的表達(dá)式節(jié)點(diǎn)(也是Expression類型的),,這個(gè)節(jié)點(diǎn)表示你已經(jīng)調(diào)用了Queryable.Where方法,。Queryable為IQueryable添加表達(dá)式樹的節(jié)點(diǎn)。 所以說(shuō)IQueryable對(duì)象的本質(zhì)就是一個(gè)體現(xiàn)了其上的查詢方法調(diào)用的一個(gè)記錄樹,。這樣當(dāng)這個(gè)IQueryable在被IQueryProvider解析的時(shí)候,,IQueryProvider就一步步的解析這顆記錄樹。 3. IQueryProvider介紹 正如我們上面介紹的,,IQueryProvider才是真正用來(lái)解析Linq查詢的:
其實(shí)這個(gè)接口說(shuō)到底就只有兩個(gè)方法,,CreateQuery,Execute. CreateQuery方法一看就知道是干什么:這個(gè)方法就讓Provider基于傳入的Expression創(chuàng)建一個(gè)新的IQueryable實(shí)例,隨后Provider就處理這個(gè)IQueryable實(shí)例的Expression. Execute就是真正的來(lái)解析表達(dá)式(expression)的,。實(shí)際就是遍歷Expression,,然后一個(gè)個(gè)的處理,。 下面我們就來(lái)看一個(gè)例子:
public class Query<T> : IQueryable<T>, IQueryable, IEnumerable<T>, IEnumerable, IOrderedQueryable<T>, IOrderedQueryable { QueryProvider provider; Expression expression; public Query(QueryProvider provider) { if (provider == null) { throw new ArgumentNullException("provider"); } this.provider = provider; this.expression = Expression.Constant(this); } public Query(QueryProvider provider, Expression expression) { if (provider == null) { throw new ArgumentNullException("provider"); } if (expression == null) { throw new ArgumentNullException("expression"); } if (!typeof(IQueryable<T>).IsAssignableFrom(expression.Type)) { throw new ArgumentOutOfRangeException("expression"); } this.provider = provider; this.expression = expression; } Expression IQueryable.Expression { get { return this.expression; } } Type IQueryable.ElementType { get { return typeof(T); } } IQueryProvider IQueryable.Provider { get { return this.provider; } } public IEnumerator<T> GetEnumerator() { return ((IEnumerable<T>)this.provider.Execute(this.expression)).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this.provider.Execute(this.expression)).GetEnumerator(); } public override string ToString() { return this.provider.GetQueryText(this.expression); } }
上面的代碼看起來(lái)有點(diǎn)多,,有點(diǎn)嚇人,其實(shí)上面的代碼就只是簡(jiǎn)單的實(shí)現(xiàn)了那些接口,。而且我們之前也說(shuō)過(guò):實(shí)現(xiàn)IQueryable的對(duì)象基本不做什么事情,,而是把事情都交給Provider來(lái)處理,而Provider處理過(guò)程也是很簡(jiǎn)單的:遍歷Expression,調(diào)用Execute方法解析:
所以在實(shí)現(xiàn)的過(guò)程中,,最重要的方法就是Provider的Execute,。下面看看一個(gè)IQueryProvider實(shí)現(xiàn)的例子: public abstract class QueryProvider : IQueryProvider {
protected QueryProvider() {
} // 正如之前所說(shuō)的:CreateQuery方法就是根據(jù)傳入的expression,創(chuàng)建一個(gè)新的Query對(duì)象,。 IQueryable<S> IQueryProvider.CreateQuery<S>(Expression expression) { return new Query<S>(this, expression); } IQueryable IQueryProvider.CreateQuery(Expression expression) { Type elementType = TypeSystem.GetElementType(expression.Type); try { return (IQueryable)Activator.CreateInstance(typeof(Query<>).MakeGenericType(elementType), new object[] { this, expression }); } catch (TargetInvocationException tie) { throw tie.InnerException; } } S IQueryProvider.Execute<S>(Expression expression) { return (S)this.Execute(expression); } object IQueryProvider.Execute(Expression expression) { return this.Execute(expression); } public abstract string GetQueryText(Expression expression); public abstract object Execute(Expression expression); }
以上只是給出了一個(gè)基本實(shí)現(xiàn)IQueryProvider的抽象類,,具體實(shí)現(xiàn),我們后面講解,。 另外上面還有一個(gè)輔助類的實(shí)現(xiàn):(感興趣的可以看看,,代碼有點(diǎn)多,實(shí)際做的事情就是返回expression中指定的類型),。 internal static class TypeSystem { internal static Type GetElementType(Type seqType) { Type ienum = FindIEnumerable(seqType);
if (ienum == null) return seqType; return ienum.GetGenericArguments()[0]; } private static Type FindIEnumerable(Type seqType) { if (seqType == null || seqType == typeof(string)) return null; if (seqType.IsArray) return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType()); if (seqType.IsGenericType) { foreach (Type arg in seqType.GetGenericArguments()) { Type ienum = typeof(IEnumerable<>).MakeGenericType(arg); if (ienum.IsAssignableFrom(seqType)) { return ienum; } } } Type[] ifaces = seqType.GetInterfaces(); if (ifaces != null && ifaces.Length > 0) { foreach (Type iface in ifaces) { Type ienum = FindIEnumerable(iface); if (ienum != null) return ienum; } } if (seqType.BaseType != null && seqType.BaseType != typeof(object)) { return FindIEnumerable(seqType.BaseType); } return null; } }
今天就暫時(shí)寫到這里,,下篇接著寫。 謝謝大家,!祝大家端午節(jié)快樂(lè),!呵呵 版權(quán)為小洋和博客園所有,轉(zhuǎn)載請(qǐng)標(biāo)明出處給作者,。 http://www.cnblogs.com/yanyangtian
本系列文章參考: The Wayward WebLog.
|
|