2016-12-05 93 views
0

出于某种原因,我需要将Linq Expression(仅限where子句)& HQL where子句组合到一个查询中。如何将Linq Expression where子句转换为hql where子句?

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

如何将Linq表达式where子句转换为HQL where子句queryString,然后我可以将另一个HQL where子句queryString组合到新查询中?

+1

你如何将LINQ表达式转换为'HqlExpression'?我能找到的最好的是:来自FluentNHibernate的'ExpressionToSql.Convert (e => e.Id> 0)'。 –

+0

谢谢。最后,我自己实施它,你可以在我的答案中看到。 – shine

回答

1

似乎不可能使用存在的NHibernate API将Linq表达式转换为HQL树。 从Linq表达式生成的HQL树对于实际的HQL查询是不可逆的。

所以我必须通过自我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

+0

什么是评估者课程? –

+1

NHibernate.Linq.dll中的NHibernate.Linq.Visitors.Evaluator – shine

+0

NHibernate.Linq.dll在很久以前就被弃用了。 –