手工把LINQ转成表达式(二) 准备工作

来源:互联网 发布:安装示意图制作软件 编辑:程序博客网 时间:2024/05/01 11:01

先来看一句简单的LINQ语句:

from item in Source

select item

这里如果写成方法调用的话就是

Source.Select(item => item)

这意味着in后面的是作为方法调用的发起方(invoker) ,from是定义表达式参数,select才是真正的方法本身。

再来看个多语句的例子:

from a in A

where a != null

select a

不考虑优化的情况下写成方法应该是

A.Where(a => a != null).Select(a => a)

也就是说可以认为前一句的输出是后一句的输入并且后一句还需要用到前一句的参数别名,在解析的时候需要一种机制能满足这种要求,为了简化处理不再采用递归方式分析,而是用了这么一个结构保存解析LINQ时所有要用到的上下文场景:

 

    internal class LINQContext    {        public ParameterExpression Parameter { get; set; }        public string Name { get; set; }        public Expression Self { get; set; }        private ParameterExpression nextParameter;        public ParameterExpression NextParameter        {            get            {                if (nextParameter != null) return nextParameter;                nextParameter = IsFirst ? Parameter : Expression.Parameter(Self.GetElementType(), Name);                return nextParameter;            }            set            {                nextParameter = value;            }        }        private bool isFirst = false;        public bool IsFirst        {            get { return isFirst; }            set { isFirst = value; }        }    }


Parameter是当前的输入参数,Name代表参数名,Self是当前语句的表达式,NextParameter是表示为下一句LINQ的输入参数,IsFirst标志位表示是否是第一个from语句(在介绍from时详细说)。

这里顺便说一下,grammar又更新了,这次把所有赋值运算符全部移出二元运算符,目前的解析器暂时不支持带赋值运算符的表达式解析。

有了这个结构我们就可以轻松把一句LINQ中的每个操作语句的上下文给保存下来便于转换了:

private Stack<LINQContext> _linqVariables;

这里用了栈来保存linq语句的上下文,实际实现下来感觉也并非必要,不过用栈的话可以逆推整个操作过程,对代码调试还是有点帮助的。

先通过grammar对LINQ语句解析后得到的是每一句linq的数组,所以转换入口就这么写了:

        private Expression ProcessLINQ(ParseTreeNode expNode)        {            _linqVariables = new Stack<LINQContext>();            Expression linq = Expression.Empty();            foreach (var child in expNode.ChildNodes)            {                linq = ProcessQueryExpression(child);            }            return linq;        }


可以注意到,其实最后一句linq被转换后返回的才是完整的表达式,其余中间产生的表达式都入栈了返回值直接被忽略,这里用递归的话可以节省那个栈但是调试起来就麻烦了。

linq语句对应的方法定义为:

            #region ExtensionMethods            extensionMethods["select"] = typeof(Queryable).GetExtensionMethod("Select", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));            extensionMethods["let"] = extensionMethods["select"];            extensionMethods["from"] = typeof(Queryable).GetExtensionMethod("SelectMany", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,,>)));            extensionMethods["group"] = typeof(Queryable).GetExtensionMethod("GroupBy", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));            extensionMethods["join"] = typeof(Queryable).GetExtensionMethod("Join", typeof(IQueryable<>), typeof(IEnumerable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,,>)));            extensionMethods["groupjoin"] = typeof(Queryable).GetExtensionMethod("GroupJoin", typeof(IQueryable<>), typeof(IEnumerable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,>)), typeof(Expression<>).MakeGenericType(typeof(Func<,,>)));            extensionMethods["orderby"] = typeof(Queryable).GetExtensionMethod("OrderBy", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));            extensionMethods["orderbydescending"] = typeof(Queryable).GetExtensionMethod("OrderByDescending", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));            extensionMethods["thenby"] = typeof(Queryable).GetExtensionMethod("ThenBy", typeof(IOrderedQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));            extensionMethods["thenbydescending"] = typeof(Queryable).GetExtensionMethod("ThenByDescending", typeof(IOrderedQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));            extensionMethods["where"] = typeof(Queryable).GetExtensionMethod("Where", typeof(IQueryable<>), typeof(Expression<>).MakeGenericType(typeof(Func<,>)));            extensionMethods["DefaultIfEmpty"] = typeof(Enumerable).GetExtensionMethod("DefaultIfEmpty", typeof(IEnumerable<>));            #endregion


这里只把DefaultIfEmpty这个扩展单独拿出来是为了支持left join语句,其他扩展方法就先不支持了。

在接下来的分析过程中,都是通过模仿C#编译LINQ产生的表达式来生成我们自己的表达式的,并且忽略了优化。

最新代码下载http://tinynetevent.googlecode.com/files/ExpressionParser0815.zip