本文介紹如何解析lambda表達式來獲取一個滿足條件的查詢語句。
通過設置實體對象Article_Content的查詢表達式,就可以獲取對應的參數化SQL語句,使用起來很方便,減少了代碼的書寫,同時提高了安全性。
本文需要了解的基礎知識有:
- lambda表達式
- Expression表達式樹
- 擴展方法
首先,我們應該有一個普通的實體對象和它的基類
//基類 class baseEntity { internal Expression whereFunc; }
//實體對象 class Article_Content : baseEntity { public int? Article_Id { get; set; } public string Article_Title { get; set; } public string Article_SourceUrl { get; set; } }
/// <summary> /// 設置匹配表達式 /// </summary> public static void SetWhereFunc<T>(this T entity, Expression<Func<T, bool>> func) where T : baseEntity { entity.whereFunc = func; }
好了。這個時候我們可以寫出類似下面的代碼了:
Article_Content art = new Article_Content(); art.SetWhereFunc(ar=>ar.Aritlce_Id == 4 && ar.Article_Title == "sss");
public static bool In<T>(this T obj, T[] array) { return true; } public static bool Like(this string str, string likeStr) { return true; }
這個時候很現在,我們的SetWhereFunc可以寫出如下的代碼了:
art.SetWhereFunc(ar=>ar.Aritlce_Id.In(new int?[]{4,6}) && ar.Article_Title.Like("%sss"));
到目前爲止,前臺編寫方式已經解決了。
剩下的就是如何來分析我們設置的這個lambda表達式了。
當然分析lambda表達式的時候,我們還需要考慮生成的SQL語句的參數化。這個時候還需要另一個數據操作對象:
public abstract class DbOperate { /// <summary> /// 獲取此數據類型的參數形式表現 /// </summary> /// <param name="fieldsName"></param> /// <returns></returns> public abstract string GetParamFlag(string fieldsName); //其他操作方法.....
}
internal static string Where<T>(this T entity, Expression func, DbOperate dbop, ref ArrayList alParamNames, ref ArrayList alParamValues) where T : baseEntity { return ExpressionRouter(func, dbop, ref alParamNames, ref alParamValues); }
lambda表達式也是表達式的一種。那麼我們可以通過分析表達式的類型計算對應的產生式。
現在主要遇到的表達式有以下幾種:
- BinaryExpression
- LambdaExpression
- MethodCallExpression
- MemberExpression
- ConstantExpression
- NewArrayExpression
- UnaryExpression
根據不同的類型,可以進行不同操作,爲了簡單起見,我們這裏演示的是直接拼接字符串的方式,各位可以自己嘗試使用參數的方式,函數原型在上面了。
static string BinarExpressionProvider(Expression left, Expression right, ExpressionType type)
{
string sb = "(";
//先處理左邊
sb += ExpressionRouter(left);
sb += ExpressionTypeCast(type);
//再處理右邊
string tmpStr = ExpressionRouter(right);
if (tmpStr == "null")
{
if (sb.EndsWith(" ="))
sb = sb.Substring(0, sb.Length - 2) + " is null";
else if (sb.EndsWith("<>"))
sb = sb.Substring(0, sb.Length - 2) + " is not null";
}
else
sb += tmpStr;
return sb += ")";
}
//表達式路由計算
static string ExpressionRouter(Expression exp)
{
string sb = string.Empty;
if (exp is BinaryExpression)
{
BinaryExpression be = ((BinaryExpression)exp);
return BinarExpressionProvider(be.Left, be.Right, be.NodeType);
}
else if (exp is MemberExpression)
{
MemberExpression me = ((MemberExpression)exp);
return me.Member.Name;
}
else if (exp is NewArrayExpression)
{
NewArrayExpression ae = ((NewArrayExpression)exp);
StringBuilder tmpstr = new StringBuilder();
foreach (Expression ex in ae.Expressions)
{
tmpstr.Append(ExpressionRouter(ex));
tmpstr.Append(",");
}
return tmpstr.ToString(0, tmpstr.Length - 1);
}
else if (exp is MethodCallExpression)
{
MethodCallExpression mce = (MethodCallExpression)exp;
if (mce.Method.Name == "Like")
return string.Format("({0} like {1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
else if (mce.Method.Name == "NotLike")
return string.Format("({0} Not like {1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
else if (mce.Method.Name == "In")
return string.Format("{0} In ({1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
else if (mce.Method.Name == "NotIn")
return string.Format("{0} Not In ({1})", ExpressionRouter(mce.Arguments[0]), ExpressionRouter(mce.Arguments[1]));
}
else if (exp is ConstantExpression)
{
ConstantExpression ce = ((ConstantExpression)exp);
if (ce.Value == null)
return "null";
else if (ce.Value is ValueType)
return ce.Value.ToString();
else if (ce.Value is string || ce.Value is DateTime || ce.Value is char)
return string.Format("'{0}'", ce.Value.ToString());
}
else if (exp is UnaryExpression)
{
UnaryExpression ue = ((UnaryExpression)exp);
return ExpressionRouter(ue.Operand);
}
return null;
}
static string ExpressionTypeCast(ExpressionType type)
{
switch (type)
{
case ExpressionType.And:
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:
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:
return null;
}
}
下面是文章一開始的那個lambda表達式分析出來的對應的where語句
對應的參數和參數值順序是一一對應的。
我這裏的對Expression表達式進行了計算,所以最後一個參數對應的
string.Concat(GetStr(7), "sdfsdf".Length, "sssss" + GetStr(6)).ToUpper()
這個表達式已經被計算完成得到"GOOD76SSSSSGOOD6",GetStr(int)函數見第一張截圖。
這裏主要體會的是用Expression Tree的一個實際用法。實際跑起來體會下就能明白。要說的話實在內容太多。而前面的都介紹都是概念性的.