• 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的查询中起了很大的作用,代码如下:

      

    代码
    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, intbool>> predicate);
            ….
    }

           

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

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

    3.  IQueryProvider介绍

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

     

    代码
    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,然后一个个的处理。

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

    代码
    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方法解析:

          

    代码
      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实现的例子: 

    代码
    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中指定的类型)。 

    代码

    internal static class TypeSystem {

            
    internal static Type GetElementType(Type seqType) {
                Type ienum = FindIEnumerable(seqType);
                
    if (ienum == nullreturn 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 != nullreturn ienum;
                    }
                }

                
    if (seqType.BaseType != null && seqType.BaseType != typeof(object)) {
                    
    return FindIEnumerable(seqType.BaseType);
                }
                
    return null;
            }

        }

     

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

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

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

       http://www.cnblogs.com/yanyangtian

     

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

     

     

  • 相关阅读:
    python基础——列表生成式
    python基础——迭代
    python基础——切片
    python基础——递归函数
    python基础——函数的参数
    python基础——使用dict和set
    python基础——使用list和tuple
    python基础——字符串和编码
    堆——神奇的优先队列(下)
    堆——神奇的优先队列(上)
  • 原文地址:https://www.cnblogs.com/yanyangtian/p/1757526.html
Copyright © 2020-2023  润新知