Linq to “everything you want” 深入浅出(一) 实现IQueryable的基类

Linq to “everything you want” 深入浅出(一) 实现IQueryable的基类

  为了节省大家的时间,希望先了解Expression Tree然后再看本系列的文章,关于这些方面的介绍,建议查看TerryLee的打造自己的LINQ Provider:Expression Tree揭秘

  前言:之所以要发出这个系列,主要是之前在发出的.NET 业务框架开发实战之八 业务层Mapping的选择策略 一文中,提到了查询对象的实现。在文章发出之后,园子里的朋友给出很多非常不错评论反馈,其中园友mywork提出了非常好的建议:提议用Expression Tree来实现。同时Harold Shen也提出了这个建议。在博客园里 Linq Provider的文章很多,那就当给博客园里面的Linq主题多添加一点资料吧,分享一下,同时也为.NET业务框架实战系列中条件对象(查询对象只是条件对象的一种)的实现做铺路。 J.

  现在有很多的linq to XXX(Linq to sql,Linq to javascript等),所以文章的标题意思大家就已经明白了。

   本篇议题如下:

  1. 条件对象怎样实现

  2. IQueryable介绍

  3. IQueryProvider介绍

  4. IQueryable的基类实现

 

1.  条件对象怎样实现

 

开始的时候,条件对象的实现如下:

 

ICriteria condition = CriteriaFactory.Create( typeof (ProductBL).Where( " ProductName " , Operation.Equal, " book " );

  

采用这种方式固然灵活,但是最大的问题就是Where后面的条件:ProductName是基于字符串的,缺少编译器的智能感应,而且如果ProductBL的ProductName改变为了Name,,有些地方可能仍然采用的ProductName,且这样的错误很难发现,只有在运行时才知道,再者Where条件的构造也显得有点复杂(和现在的linq相比)。如果换成如下的方式,可能就更好一些: 

 

ICriteria < T >  condition = CriteriaFactory.Create < T > (Expression < Func < T, bool >>  func);

    使用时

ICriteria < ProductBL >  condition = CriteriaFactory.Create < ProductBL > (o  =>  o.ProductName  ==   " book " );

  

惟一很问题就是解析Expression树。

而且上面的代码是在UI代码中被调用,然后,ICriteria 对象最后会被传到BLL,最后传到DAL,解析为SQL语句进行执行。 

趁实现条件对象的机会,把IQueryable Provider具体的讲述一下,一举两得。  

 

2. IQueryable介绍 

我们从IQueryable接口入手,首先来看看接口的定义: 

public   interface  IQueryable : IEnumerable
 {
      Type ElementType { 
get ; }
      Expression Expression { 
get ; }
      IQueryProvider Provider { 
get ; }

 }

  

           接口中定义了三个只读的属性,简单的理解:ElementType表示查询返回对象的类型;Expression就是查询条件集合(Expression可以组成一棵树),Provider就是用来解析Expression的对象。下面的接口是上面接口的泛型版:

 

public   interface  IQueryable < T >  : IEnumerable < T > , IQueryable, IEnumerable
{

}

 

            IQueryable对象的本质就是一个表达式,这个表达式体现了在linq查询时方法的调用。

怎么讲:首先在这里不得不提及System.Linq.Queryable类,这个类在实现Linq的查询中起了很大的作用,代码如下:

  

ExpandedBlockStart.gif 代码
public   static   class  Queryable
{    
        ….
        
public   static  IQueryable < TSource >  Where < TSource > ( this  IQueryable < TSource >  source, Expression < Func < TSource,  bool >>  predicate);
        
public   static  IQueryable < TSource >  Where < TSource > ( this  IQueryable < TSource >  source, Expression < Func < TSource,  int bool >>  predicate);
        ….
}

       

  这个类的方法很多,而且这个类是专门用来对IQueryable添加扩展方法的。其中,当我们使用Queryable.Where的方法时在IQueryable对象上进行过滤时,Where方法在IQueryableExpression的表达式属性的顶层添加了一个表示方法调用的表达式节点(也是Expression类型的),这个节点表示你已经调用了Queryable.Where方法。QueryableIQueryable添加表达式树的节点。

  所以说IQueryable对象的本质就是一个体现了其上的查询方法调用的一个记录树。这样当这个IQueryable在被IQueryProvider解析的时候,IQueryProvider就一步步的解析这颗记录树。

3.  IQueryProvider介绍

正如我们上面介绍的,IQueryProvider才是真正用来解析Linq查询的:

 

ExpandedBlockStart.gif 代码
public   interface  IQueryProvider
 {
        IQueryable CreateQuery(Expression expression);

        IQueryable
< TElement >  CreateQuery < TElement > (Expression expression);

        
object  Execute(Expression expression);

        TResult Execute
< TResult > (Expression expression);


   

  其实这个接口说到底就只有两个方法,CreateQuery,Execute.

  CreateQuery方法一看就知道是干什么:这个方法就让Provider基于传入的Expression创建一个新的IQueryable实例,随后Provider就处理这个IQueryable实例的Expression.

  Execute就是真正的来解析表达式(expression)的。实际就是遍历Expression,然后一个个的处理。

  下面我们就来看一个例子:

ExpandedBlockStart.gif 代码
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);
        }
    }

 

 

  上面的代码看起来有点多,有点吓人,其实上面的代码就只是简单的实现了那些接口。而且我们之前也说过:实现IQueryable的对象基本不做什么事情,而是把事情都交给Provider来处理,而Provider处理过程也是很简单的:遍历Expression,调用Execute方法解析:

      

ExpandedBlockStart.gif 代码
   public  IEnumerator < T >  GetEnumerator() {
            
return  ((IEnumerable < T > ) this .provider.Execute( this .expression)).GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() {
            
return  ((IEnumerable) this .provider.Execute( this .expression)).GetEnumerator();
        }

  

       所以在实现的过程中,最重要的方法就是ProviderExecute下面看看一个IQueryProvider实现的例子: 

ExpandedBlockStart.gif 代码
public   abstract   class  QueryProvider : IQueryProvider {
         protected  QueryProvider() {
        }
        
//  正如之前所说的:CreateQuery方法就是根据传入的expression,创建一个新的Query对象。
        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);

}

 

  

  以上只是给出了一个基本实现IQueryProvider的抽象类,具体实现,我们后面讲解。

另外上面还有一个辅助类的实现:(感兴趣的可以看看,代码有点多,实际做的事情就是返回expression中指定的类型)。 

ExpandedBlockStart.gif 代码

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 ;
        }

    }

 

 

  今天就暂时写到这里,下篇接着写。

  谢谢大家!祝大家端午节快乐!呵呵

  版权为小洋和博客园所有,转载请标明出处给作者。

   http://www.cnblogs.com/yanyangtian

 

  本系列文章参考: The Wayward WebLog.

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值