用lambda構建ORM查詢語句

本文介紹如何解析lambda表達式來獲取一個滿足條件的查詢語句。

先看個截圖 

 

通過設置實體對象Article_Content的查詢表達式,就可以獲取對應的參數化SQL語句,使用起來很方便,減少了代碼的書寫,同時提高了安全性。

 

本文需要了解的基礎知識有:

  1. lambda表達式
  2. Expression表達式樹
  3. 擴展方法

 

首先,我們應該有一個普通的實體對象和它的基類

//基類
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; }
}
這個時候我們需要定義一些擴展方法以便對實體的查詢表達式進行保存。我們的SQL語句的需求是要參數化的。所以,在實體對象這裏還不知道數據庫類型,沒有辦法獲取對應的參數化符號。
/// <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");
但是SQL語句中很常見的Like查詢似乎還沒有辦法實現。
 
我們通過分析Where子句,發現where語句的做事然篩選出來的紀錄要滿足where子句的條件,那麼where子句返回的就是一個bool,where子句其實是一個邏輯表達式。所以,就有了剛纔SetWhereFunc函數裏面的第二個參數中Func的返回值是bool。
 
既然where子句要求返回的是一個bool表達式同理:Like、In、Not等表達方法我們也只能讓它返回的是bool值;
那麼就有了下面的擴展函數:
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);
    //其他操作方法.....
}
這個對象還有其他方法,不過在這裏,我們主要使用的就是這樣一個參數標識,如果它在MSSQL裏,它返回的是@+fieldName,Orcale則返回的是:fieldName。
在構建SQL語句的時候,我們同樣也使用擴展方法來解析表達式。
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表達式也是表達式的一種。那麼我們可以通過分析表達式的類型計算對應的產生式。
現在主要遇到的表達式有以下幾種:
  1. BinaryExpression
  2. LambdaExpression
  3. MethodCallExpression
  4. MemberExpression
  5. ConstantExpression
  6. NewArrayExpression
  7. 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的一個實際用法。實際跑起來體會下就能明白。要說的話實在內容太多。而前面的都介紹都是概念性的.

代碼下載地址:http://download.csdn.net/detail/ysq5202121/4263117

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章