解析表达式

.net中的条件查询需要支持表达式有两个理由:

1 更为灵活,也更为直观;

2 结合c#的强类型检查,代码可靠性可以得到增强。

要在条件查询中支持表达式,有几个要求要能得到满足:

1 能支持多表联合查询;

2 子类能自动关联父类;

3 对表达式的支持要尽可能的少限制;

4 不能要求一次性输入所有的表达式,而是要能根据条件逐步的拼入表达式。

在条件查询中支持表达式,说起来好像很玄奥,但仔细想下,不就是将表达式转换为sql语句吗?所以,最简单的考虑,就是解析表达式,然后来构造sql语句的from和where字句而已。

而from字句比较简单,其实就是识别表达式中用的类,没有用过的类,就添加表名列表中,最后在构造时按顺序逐一添加到from字句即可,当然,需要为其按序号分配一个别名。

所以核心的工作就是解析表达式来构造where字句。

首先就是表达式的解析:

        private string Visit(Expression exp)
        {
            if (exp != null)
                switch (exp.NodeType)
                {
                    case ExpressionType.Negate:
                    case ExpressionType.NegateChecked:
                    case ExpressionType.Not:
                    case ExpressionType.Convert:
                    case ExpressionType.ConvertChecked:
                        return VisitUnary((UnaryExpression)exp);
                    case ExpressionType.Add:
                    case ExpressionType.AddChecked:
                    case ExpressionType.Subtract:
                    case ExpressionType.SubtractChecked:
                    case ExpressionType.Multiply:
                    case ExpressionType.MultiplyChecked:
                    case ExpressionType.Divide:
                    case ExpressionType.Modulo:
                    case ExpressionType.And:
                    case ExpressionType.AndAlso:
                    case ExpressionType.Or:
                    case ExpressionType.OrElse:
                    case ExpressionType.LessThan:
                    case ExpressionType.LessThanOrEqual:
                    case ExpressionType.GreaterThan:
                    case ExpressionType.GreaterThanOrEqual:
                    case ExpressionType.Equal:
                    case ExpressionType.NotEqual:
                        return VisitBinary((BinaryExpression)exp);
                    case ExpressionType.Constant:
                        return VisitConstant((ConstantExpression)exp);
                    case ExpressionType.Parameter:
                        return VisitParameter((ParameterExpression)exp);
                    case ExpressionType.MemberAccess:
                        return VisitMemberAccess((MemberExpression)exp);
                    case ExpressionType.Call:
                        return this.VisitMethodCall((MethodCallExpression)exp);
                    default:
                        throw new Exception(string.Format("Unhandled expression type: '{0}'", exp.NodeType));
                }
            return null;
        }

一元表达式和两元表达式的实现都是将表达式类型转换为一个操作,然后将该操作和进一步的解析出的表达式拼接到一起,如一元表达式的解析是:

        private string VisitUnary(UnaryExpression u)
        {
            if (u == null) return null;
            string rs = null;
            string op = null;
            switch (u.NodeType)
            {
                case ExpressionType.Negate:
                case ExpressionType.NegateChecked:
                    op = "-";
                    break;
                case ExpressionType.Not:
                    op = "Not ";
                    break;
                case ExpressionType.Convert:
                case ExpressionType.ConvertChecked:
                    return Visit(u.Operand);
            }
            rs = String.Format("({0}{1})", op, Visit(u.Operand));
            return rs;
        }

常量的解析首先看能否常量的类型是否能直接转换,如果不能则将该表达式动态执行取其结果:

        private string VisitConstant(ConstantExpression c)
        {
            if (c == null) return null;
            string rs = Common.TransObjToSqlValue(c.Value);
            if (rs == null && !Common.CanTrans(c.Value))
            {
                LambdaExpression lambda = Expression.Lambda(c);
                Delegate fn = lambda.Compile();
                var obj = fn.DynamicInvoke(null);
                rs = Common.TransObjToSqlValue(obj);
            }
            return rs;
        }

参数的解析稍微复杂点,主要需要判断该参数是否是给的类的父类/祖类中的成员,如果是还需要判断该父类/祖类是否已经被添加到所有数据表中了,如果没有需要将其引用过来并和该子类自动进行关联。

成员引用的解析主要是将成员首先判断是否是参数,如果是那么则按参数进行解析,否则还是要视为一个动态的表达式,执行后取其值。

方法调用的解析最为复杂,因为,方法可以有多个参数、参数还有可能需要进一步的解析,后来考虑到为了进行查询而构造的表达式,其调用的方法应该不会如此复杂,所以对方法调用所支持的表达式进行了限制,如参数只能由一个,且为简单数值等等:

       private string VisitMethodCall(MethodCallExpression c)
        {
            if (c == null) return null;
            string rs = null;
            if (c.Object != null)
            {
                //实例对象的方法调用,没有参数调用
                if (c.Object.NodeType == ExpressionType.Constant)
                {
                    //实例对象是常量
                    LambdaExpression lambda = Expression.Lambda(c);
                    Delegate fn = lambda.Compile();
                    rs = Common.TransObjToSqlValue(fn.DynamicInvoke(null));
                }
                else if (c.Object.NodeType == ExpressionType.MemberAccess)
                {
                    LambdaExpression lambda = Expression.Lambda(c.Object);
                    Delegate fn = lambda.Compile();
                    var obj = fn.DynamicInvoke(null);
                    ParameterExpression paramObj = Expression.Parameter(typeof(object), "entity");
                    var paramEntityObj = Expression.Convert(paramObj, c.Object.Type);


                    var exp = Expression.Call(paramEntityObj, c.Method);
                    var l = Expression.Lambda<Func<object, object>>(exp, paramObj);
                    Func<object, object> d = l.Compile();
                    var o = d(obj);
                    rs = Common.TransObjToSqlValue(o);
                }
            }
            else
            {
                //静态方法调用,只有一个参数
                LambdaExpression lambda = Expression.Lambda(c.Arguments[0]);
                Delegate fn = lambda.Compile();
                var obj = fn.DynamicInvoke(null);
                var o = c.Method.Invoke(null, new object[] { obj });
                rs = Common.TransObjToSqlValue(o);
            }
            return rs;
        }

有了表达式的解析,我们就可以定义创建函数和按条件添加表达式的函数了:

        public static ExpressionVisitor_Sql Create<T>(Expression<Func<T, bool>> predicate)
        {
            var ev = new ExpressionVisitor_Sql();
            var ty = typeof(T);
            ev.m_TableList.Add(ty.Name, 1);
            if (predicate != null)
                ev.m_SqlWhere = " Where " + ev.Visit(predicate.Body);
            return ev;
        }

以及:

        public void Append<T1, T2>(bool AppendIf, Expression<Func<T1, T2, bool>> predicate)
        {
            var ty = typeof(T1);
            if (AppendIf && m_TableList.InList(ty.Name) && predicate != null)
            {
                int count = m_TableList.Count + m_TableList2.Count;
                ty = typeof(T2);
                if (m_TableList.InList(ty.Name))
                    m_TableList2.Add(ty.Name, count + 1);
                else
                    m_TableList.Add(ty.Name, count + 1);
                if (m_SqlWhere == null)
                    m_SqlWhere = " Where " + Visit(predicate.Body);
                else
                    m_SqlWhere += " And " + Visit(predicate.Body);
            }
        }

当然还可以定义支持更多泛型参数表达式的类似函数。

这里面有一个比较特殊的地方,就是字符串和其它类型有一个不同点在于,字符串有一个包含操作,而包含操作不像大于、等于之类的比较操作在表达式中有明确的对应关系可供解析,所以最后用一个单独的Append_Like函数进行单独的解析。

目前还存在一个问题难以解决,即如果需要反复用到某个类,由于表达式中无法确定到底是指的哪一个类,所以没法使用重名的类进行关联查询。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值