如何将Linq Expression where子句翻译成hql where子句?

时间:2016-12-05 02:05:27

标签: linq nhibernate hql

出于某种原因,我需要结合Linq Expression(仅限where子句)&将HQL where子句放入一个查询中。

我发现session.Query<T>() API会将Linq Expression转换为HqlQuery对象(扩展HqlExpression)。

如何将Linq Expression where子句转换为HQL where子句queryString,然后我可以将另一个HQL where子句queryString组合成一个新查询?

1 个答案:

答案 0 :(得分:1)

似乎无法使用存在的NHibernate API将Linq表达式转换为HQL树。 从Linq表达式生成的HQL树不能与实际的HQL查询相反。

所以我必须通过self将Linq表达式翻译成HQL:

var expr = GetExpr<Ninja>(x =>
    x.Age > 1 && x.Country.Name == "中国"
    ||
    (x.Id > 10 && x.Country.Name == "中国")
);

var translator = new ExpressionToHqlTranslator("_this");
translator.Translate(expr);
Console.WriteLine(translator.WhereClause);
Console.WriteLine(translator.Patameters);

==============结果=============

WhereClause: (((_this.Age > ?) AND (_this.Country.Name = ?)) OR ((_this.Id > ?) AND (_this.Country.Name = ?)))

Patameters:4

===============关键代码=============

static Expression<Func<T, object>> GetExpr<T>(Expression<Func<T, object>> expr){
    eturn expr;
}


using System;
using System.Linq;
using NHibernate.Linq;
using NHibernate.Linq.Visitors;
using System.Linq.Expressions;
using NHibernate;
using System.Text;
using System.Collections.Generic;

namespace Rhythm.Linq
{
    public class ExpressionToHqlTranslator : System.Linq.Expressions.ExpressionVisitor
    {
        private StringBuilder sb;
        private string _orderBy = "";
        private int? _skip = null;
        private int? _take = null;
        private string _whereClause = "";
        List<object> patameters;

        public int? Skip
        {
            get
            {
                return _skip;
            }
        }

        public int? Take
        {
            get
            {
                return _take;
            }
        }

        public string OrderBy
        {
            get
            {
                return _orderBy;
            }
        }

        public string WhereClause
        {
            get
            {
                return _whereClause;
            }
        }

        public List<object> Patameters
        {
            get
            {
                return patameters;
            }

            set
            {
                patameters = value;
            }
        }

        string prefix;
        public ExpressionToHqlTranslator(string prefix = null)
        {
            this.prefix = string.IsNullOrEmpty(prefix) ? null : (prefix + ".");
        }

        public string Translate(Expression expression)
        {
            this.sb = new StringBuilder();
            this.patameters = new List<object>();
            this.Visit(expression);
            _whereClause = this.sb.ToString();
            return _whereClause;
        }

        private static Expression StripQuotes(Expression e)
        {
            while (e.NodeType == ExpressionType.Quote)
            {
                e = ((UnaryExpression)e).Operand;
            }
            return e;
        }

        protected override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method.DeclaringType == typeof(Queryable) && m.Method.Name == "Where")
            {
                this.Visit(m.Arguments[0]);
                LambdaExpression lambda = (LambdaExpression)StripQuotes(m.Arguments[1]);
                this.Visit(lambda.Body);
                return m;
            }
            else if (m.Method.Name == "Take")
            {
                if (this.ParseTakeExpression(m))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }
            else if (m.Method.Name == "Skip")
            {
                if (this.ParseSkipExpression(m))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }
            else if (m.Method.Name == "OrderBy")
            {
                if (this.ParseOrderByExpression(m, "ASC"))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }
            else if (m.Method.Name == "OrderByDescending")
            {
                if (this.ParseOrderByExpression(m, "DESC"))
                {
                    Expression nextExpression = m.Arguments[0];
                    return this.Visit(nextExpression);
                }
            }

            throw new NotSupportedException(string.Format("The method '{0}' is not supported", m.Method.Name));
        }

        protected override Expression VisitUnary(UnaryExpression u)
        {
            switch (u.NodeType)
            {
                case ExpressionType.Not:
                    sb.Append(" NOT ");
                    this.Visit(u.Operand);
                    break;
                case ExpressionType.Convert:
                    this.Visit(u.Operand);
                    break;
                default:
                    throw new NotSupportedException(string.Format("The unary operator '{0}' is not supported", u.NodeType));
            }
            return u;
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="b"></param>
        /// <returns></returns>
        protected override Expression VisitBinary(BinaryExpression b)
        {
            sb.Append("(");
            this.Visit(b.Left);

            switch (b.NodeType)
            {
                case ExpressionType.And:
                    sb.Append(" AND ");
                    break;

                case ExpressionType.AndAlso:
                    sb.Append(" AND ");
                    break;

                case ExpressionType.Or:
                    sb.Append(" OR ");
                    break;

                case ExpressionType.OrElse:
                    sb.Append(" OR ");
                    break;

                case ExpressionType.Equal:
                    if (IsNullConstant(b.Right))
                    {
                        sb.Append(" IS ");
                    }
                    else
                    {
                        sb.Append(" = ");
                    }
                    break;

                case ExpressionType.NotEqual:
                    if (IsNullConstant(b.Right))
                    {
                        sb.Append(" IS NOT ");
                    }
                    else
                    {
                        sb.Append(" <> ");
                    }
                    break;

                case ExpressionType.LessThan:
                    sb.Append(" < ");
                    break;

                case ExpressionType.LessThanOrEqual:
                    sb.Append(" <= ");
                    break;

                case ExpressionType.GreaterThan:
                    sb.Append(" > ");
                    break;

                case ExpressionType.GreaterThanOrEqual:
                    sb.Append(" >= ");
                    break;

                default:
                    throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", b.NodeType));

            }

            this.Visit(b.Right);
            sb.Append(")");
            return b;
        }


        protected override Expression VisitConstant(ConstantExpression c)
        {
            this.patameters.Add(c.Value);
            sb.Append('?');
            //IQueryable q = c.Value as IQueryable;

            //if (q == null && c.Value == null)
            //{
            //    sb.Append("NULL");
            //}
            //else if (q == null)
            //{
            //    switch (Type.GetTypeCode(c.Value.GetType()))
            //    {
            //        case TypeCode.Boolean:
            //            sb.Append(((bool)c.Value) ? 1 : 0);
            //            break;

            //        case TypeCode.String:
            //            sb.Append("'");
            //            sb.Append(c.Value);
            //            sb.Append("'");
            //            break;

            //        case TypeCode.DateTime:
            //            sb.Append("'");
            //            sb.Append(c.Value);
            //            sb.Append("'");
            //            break;

            //        case TypeCode.Object:
            //            throw new NotSupportedException(string.Format("The constant for '{0}' is not supported", c.Value));

            //        default:
            //            sb.Append(c.Value);
            //            break;
            //    }
            //}

            return c;
        }

        protected override Expression VisitMember(MemberExpression m)
        {
            if (this.prefix != null)
            {
                sb.Append(this.prefix);
            }
            sb.Append(ContactModelPropertyVistHierarchyExpression(m, m.Member.DeclaringType));
            //if (m.Expression != null && m.Expression.NodeType == ExpressionType.Parameter)
            //{
            //    sb.Append(m.Member.Name);
            //    return m;
            //}
            return m;

            //throw new NotSupportedException(string.Format("The member '{0}' is not supported", m.Member.Name));
        }

        protected bool IsNullConstant(Expression exp)
        {
            return (exp.NodeType == ExpressionType.Constant && ((ConstantExpression)exp).Value == null);
        }

        private bool ParseOrderByExpression(MethodCallExpression expression, string order)
        {
            UnaryExpression unary = (UnaryExpression)expression.Arguments[1];
            LambdaExpression lambdaExpression = (LambdaExpression)unary.Operand;

            lambdaExpression = (LambdaExpression)NHibernate.Linq.Visitors.Evaluator.PartialEval(lambdaExpression);

            MemberExpression body = lambdaExpression.Body as MemberExpression;
            if (body != null)
            {
                if (string.IsNullOrEmpty(_orderBy))
                {
                    _orderBy = string.Format("{0} {1}", body.Member.Name, order);
                }
                else
                {
                    _orderBy = string.Format("{0}, {1} {2}", _orderBy, body.Member.Name, order);
                }

                return true;
            }

            return false;
        }

        private bool ParseTakeExpression(MethodCallExpression expression)
        {
            ConstantExpression sizeExpression = (ConstantExpression)expression.Arguments[1];

            int size;
            if (int.TryParse(sizeExpression.Value.ToString(), out size))
            {
                _take = size;
                return true;
            }

            return false;
        }

        private bool ParseSkipExpression(MethodCallExpression expression)
        {
            ConstantExpression sizeExpression = (ConstantExpression)expression.Arguments[1];

            int size;
            if (int.TryParse(sizeExpression.Value.ToString(), out size))
            {
                _skip = size;
                return true;
            }

            return false;
        }
    }


    public static string ContactModelPropertyVistHierarchyExpression(Expression expr, Type modelType)
    {
            StringBuilder sb = new StringBuilder();
            Expression curr = expr;
            // TypedParameterExpression
            while (curr != null)
            {
                if (curr is MemberExpression)
                {
                    var x = curr as MemberExpression;
                    sb.Insert(0, x.Member.Name);
                    curr = x.Expression;
                }
                else if (curr is MethodCallExpression)
                {
                    var x = curr as MethodCallExpression;
                    sb.Insert(0, x.Method.Name);
                    curr = x.Object;
                }
                else if (curr is ParameterExpression)
                {
                    break;
                }
                else
                {
                    throw new ArgumentException("Unsupported Expression type " + curr.GetType().FullName + " for expression " + expr.ToString(), "expr");
                }
                sb.Insert(0, '.');
            }
            return sb.Length > 1 ? sb.Remove(0, 1).ToString() : sb.ToString();
    }
}

添加dll引用NHibernate.Linq.dll