zoukankan      html  css  js  c++  java
  • LINQ:创建IQueryable Provider<1>

    这是一个教你创建一个LINQ IQueryable Provider的系列教程,总共13篇,来源于The Wayward WebLog,翻译中除去了一部分作者的琐碎之语,如有翻译不当之处,请指出,谢谢!

    系列索引:

    Part I - Reusable IQueryable base classes

    Part II - Where and reusable Expression tree visitor

    Part II - Local variable references

    Part IV - Select

    Part V - Improved Column binding

    Part VI - Nested queries

    Part VII - Join and SelectMany

    Part VIII - OrderBy

    Part IX - Removing redundant subqueries

    Part X – GroupBy and Aggregates

    Part XI - More of everything

    Part XII - Relationships and the IQ Toolkit

    Part XIII - Updates and Batch Processing

    Part XIV - Mapping and Providers

    Part XV - Transactions, Sessions and Factories

    Part XVI - Performance and Caching

    Part XVII - Automatic Caching and Loading Policies

    现在你可以通过CodePlex上查看这个项目。

    http://www.codeplex.com/IQToolkit

    balabalbala。。。

    首先提示大家IQueryable接口在LINQ Beta2中发生了变化,从单接口IQueryable分解为两个:IQueryable和IQueryProvider。我们先粗略过一下这些知识。

    如果你使用Visual Studio “转到定义”功能,你就可以看到这些东西:

    public interface IQueryable : IEnumerable
        {        
            Type ElementType { get; }
            Expression Expression { get; }
            IQueryProvider Provider { get; }
        }
    public interface IQueryable<T> : IEnumerable<T>, IQueryable, IEnumerable 
        {
        } 

    (balabala:IQueryable本来很有料,但是现在里面的好东西全部被拆分到IQueryProvider中去了,想当年我开始研究LINQ的时候,这个类还是很值得一看的。)

    IQueryable接口中包含了三个只读的属性:

    • ElementType:指示元素类型(在IQueryable<T>中也是);

    注意:所有实现IQueryable接口的类都必须实现IQueryable<T>.IQueryable<T>将会使用的更多,非泛型实现只是用来提供一种创建dynamic query时的弱类型入口。如果你更深入的研究下IQueryable基础实现(System.Linq.Queryable),你会发现它只不过是是一种自动构建ExpressionTree的机制。

    • Expression:

    IQueryable接口典型的要素,包含着一个由LINQ查询操作符和函数调用组成的树形结构。

    使用Queryable.Where方法,就是在IQueryable的表达式顶端添加一个函数调用ExpressionNode,最终仍返回其本身。

    • Provider:  返回IQueryProvider的实例。
        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方法通过特定的ExpressionTree创建一个新的IQueryable查询实例。
    Execute方法是真正执行查询表达式的Provider的入口。
    

    通过IQueryable和IQueryProvider分离的方式,我们可以只需要实现一个IQueryable,并且在任何Provider中使用。我们先创建一个实现IQueryable<T>和其他必须接口的类Query<T>。

     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实现很浅显,仅仅是持有一个Expression Tree和Provider。
    接下来我们提供一些Provider的实现,我先实现一个抽象基类,真正的Provider都可以从这个类派生而来。
        public abstract class QueryProvider : IQueryProvider {
            protected QueryProvider() {
            }
     
    
            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);
        }
    还有一个辅助类:
       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;
            }
        }
    
     
    真正的动作在Execute中执行,只有在这里才会执行Expression Tree。我们在下一节中再说。
  • 相关阅读:
    Get distinct count of rows in the DataSet
    单引号双引号的html转义符
    PETS Public English Test System
    Code 39 basics (39条形码原理)
    Index was outside the bounds of the array ,LocalReport.Render
    Thread was being aborted Errors
    Reportviewer Error: ASP.NET session has expired
    ReportDataSource 值不在预期的范围内
    .NET/FCL 2.0在Serialization方面的增强
    Perl像C一样强大,像awk、sed等脚本描述语言一样方便。
  • 原文地址:https://www.cnblogs.com/tukzer/p/1805483.html
Copyright © 2011-2022 走看看