上次講解了怎么解析匿名對象(ORM開發之解析lambda實現group查詢),這次來實現解析二元運算,完成基本條件語法
先看一個表達式
query.Where(b => b.Number == 10&&b.Id<20);
表達式結構
一個運算符表示一個表達式,因此,此表達式實際上包含兩個子表達式 b.Number==10 和b.Id<20 他們的關系為And
看一個子表達式 b.Number==10
按運算符為位置,左邊為左操作數,右邊為右操作數
以And操作符來看,b.Number==10也為左操作數,b.Id<20為右操作數
再增加其它條件時,也是同樣的道理
那么我們解析將一個子表達式 b.Number==10 轉換為SQL邏輯,則需要:
- 取出左表達式對應的字段名稱 Number
- 取出運算符 =
- 取出右表達式的值 10
表達式類型
由上可以看出,表達式分左邊和右邊,左右兩邊也可是子表達式,它們形成一個表達式樹,基類型都為System.Linq.Expressions.Expression
具體類型大致按下面划分為:
- BinaryExpression 表示包含二元運算符的表達式。 可以理解為一個子表達式,如 b.Number>10
- MemberExpression 表示訪問字段或屬性。 如 b.Number
- NewArrayExpression 表示創建新數組並可能初始化該新數組的元素。
- MethodCallExpression 表示對靜態方法或實例方法的調用 如 b.Name.Contains("123")
- ConstantExpression 表示具有常量值的表達式 如 b.Name="hubro"
- UnaryExpression 表示包含一元運算符的表達式
因此,需要根據不同的類型解析不同的表達式
開始解析
拆分表達式樹
/// <summary>
/// 拆分表達式樹
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <param name="type"></param>
/// <returns></returns>
public string BinaryExpressionHandler(Expression left, Expression right, ExpressionType type)
{
StringBuilder sb = new StringBuilder();
sb.Append("(");
string needParKey = "=,>,<,>=,<=,<>";
string leftPar = RouteExpressionHandler(left);//獲取左邊
string typeStr = ExpressionTypeCast(type);//轉換運算符
var isRight = needParKey.IndexOf(typeStr) > -1;//用以區分是解析左邊的名稱還是右邊的值
string rightPar = RouteExpressionHandler(right, isRight);//獲取右邊
string appendLeft = leftPar;
sb.Append(appendLeft);//字段名稱
if (rightPar.ToUpper() == "NULL")
{
if (typeStr == "=")
rightPar = " IS NULL ";
else if (typeStr == "<>")
rightPar = " IS NOT NULL ";
}
else
{
sb.Append(typeStr);
}
sb.Append(rightPar);
sb.Append(")");
return sb.ToString();
}
解析表達式
表達式樹也會在這里處理,形成遞歸調用,當表達式是MemberExpression時,為了區分是左邊的屬性名還是右邊的屬性值,加了isRight進行區分
當是MethodCallExpression時,如果是左邊,則需要進行解析(這里沒有實現),右邊只需要執行方法結果即可
/// <summary>
/// 解析表達式
/// </summary>
/// <param name="exp"></param>
/// <param name="isRight"></param>
/// <returns></returns>
public string RouteExpressionHandler(Expression exp, bool isRight = false)
{
if (exp is BinaryExpression)
{
BinaryExpression be = (BinaryExpression)exp;
//重新拆分樹,形成遞歸
return BinaryExpressionHandler(be.Left, be.Right, be.NodeType);
}
else if (exp is MemberExpression)
{
MemberExpression mExp = (MemberExpression)exp;
if (isRight)//按表達式右邊值
{
var obj = Expression.Lambda(mExp).Compile().DynamicInvoke();
if (obj is Enum)
{
obj = (int)obj;
}
return obj + "";
}
return mExp.Member.Name;//按左邊的名稱
}
else if (exp is NewArrayExpression)
{
#region 數組
NewArrayExpression naExp = (NewArrayExpression)exp;
StringBuilder sb = new StringBuilder();
foreach (Expression expression in naExp.Expressions)
{
sb.AppendFormat(",{0}", RouteExpressionHandler(expression));
}
return sb.Length == 0 ? "" : sb.Remove(0, 1).ToString();
#endregion
}
else if (exp is MethodCallExpression)
{
if (isRight)
{
return Expression.Lambda(exp).Compile().DynamicInvoke() + "";
}
//在這里解析方法
throw new Exception("暫不支持");
}
else if (exp is ConstantExpression)
{
#region 常量
ConstantExpression cExp = (ConstantExpression)exp;
if (cExp.Value == null)
return "null";
else
{
return cExp.Value.ToString();
}
#endregion
}
else if (exp is UnaryExpression)
{
UnaryExpression ue = ((UnaryExpression)exp);
return RouteExpressionHandler(ue.Operand, isRight);
}
return null;
}
轉換運算符
public string ExpressionTypeCast(ExpressionType expType)
{
switch (expType)
{
case ExpressionType.And:
return "&";
case ExpressionType.AndAlso:
return " AND ";
case ExpressionType.Equal:
return "=";
case ExpressionType.GreaterThan:
return ">";
case ExpressionType.GreaterThanOrEqual:
return ">=";
case ExpressionType.LessThan:
return "<";
case ExpressionType.LessThanOrEqual:
return "<=";
case ExpressionType.NotEqual:
return "<>";
case ExpressionType.Or:
return "|";
case ExpressionType.OrElse:
return " OR ";
case ExpressionType.Add:
case ExpressionType.AddChecked:
return "+";
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
return "-";
case ExpressionType.Divide:
return "/";
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
return "*";
default:
throw new InvalidCastException("不支持的運算符");
}
}
獲取解析值
internal string FormatExpression(Expression<Func<T, bool>> expression)
{
string condition;
var visitor = new ExpressionVisitor();
if (expression == null)
return "";
condition = visitor.RouteExpressionHandler(expression.Body);
return condition;
}
拼接完整的SQL
public string GetQuery()
{
string where = Condition;
where = string.IsNullOrEmpty(where) ? " 1=1 " : where;
#region group判斷
if (groupFields.Count > 0)
{
where += " group by ";
foreach (var item in groupFields)
{
where += item + ",";
}
where = where.Substring(0, where.Length - 1);
}
#endregion
string tableName = typeof(T).Name;
string fileds = string.Join(",", queryFields);
var part = string.Format("select {0} from {1} where {2}", fileds, tableName, where);
return part;
}
運行輸出
var query = new LambdaQuery<Product>();
query.Select(b => new { b.BarCode, b.ProductName, total = b.BarCode.COUNT() });
query.GroupBy(b => new { b.BarCode, b.ProductName });
query.Where(b => b.ProductName == "ddd");
query.Where(b => b.Number == 10);
query.Where(b => b.Number == new aa().bb);//測試獲取對象參數
query.OrderBy(b => b.BarCode.COUNT(), true);
Console.Write(query.GetQuery());
這樣,一般查詢就能用lambda來表示了,但是一些SQL函數,是沒法表示的,和之前說的一樣,可以用擴展方法解決
上面上解析方法調用表達式里,解析即可,解析方法比較復雜,就不在這里寫了
else if (exp is MethodCallExpression)
{
if (isRight)
{
return Expression.Lambda(exp).Compile().DynamicInvoke() + "";
}
//在這里解析方法
throw new Exception("暫不支持");
}
測試例子下載 http://files.cnblogs.com/files/hubro/LambdaQueryTest2.rar
