ORM開發之解析lambda實現group查詢(附測試例子)


目的:以編程方式實現group查詢,在開發ORM時,需要達到這樣的效果

先看一個簡單的group語句

select BarCode,ProductName,COUNT(BarCode) as total from ProductData  group by BarCode,ProductName
order by COUNT(BarCode) desc

  結果

BarCode                        ProductName                    total
------------------------------ ------------------------------ -----------
1212122                        product2                       4
21312313                       product3                       2

  group語法分解為

  • 查詢哪些字段 BarCode,ProductName,COUNT(BarCode) as total 
  • 按哪些字段進行分組 group by BarCode,ProductName
  • 按什么排序  order by COUNT(BarCode) desc

linq to sql 表示為

from p in  ProductData
group p.BarCode by p.ProductName into g
select  new 
{
g.BarCode,
g.ProductName,
total=g.Count()
}

linq to sql很容易表達,用lambda該如何表達呢 

跟普通SQL查詢不同,查詢字段和排序可用聚合函數count,sum,拋開這,用lambda,同樣的查詢可表示為

這里用匿名對象來作選擇器

設定 query=new LambdaQuery<Product>() 以下方法可能不實際存在,只作演示

query.Select(b=>new{b.BarCode,b.ProductName})

.GroupBy(b=>new{b.BarCode,b.ProductName})

.OrderBy(b=>b.BarCode,true);

query.Select(b=>new{b.BarCode,b.ProductName}) 能表示 select BarCode,ProductName 

但匿名對象可沒Count(b.ProductName)這樣的語法,所以沒法生成select count(BarCode)這樣的語法

沒有直接的方法,但是有間接的方法,擴展方法 擴展方法真是個好東西,解決了很多問題
定義一個擴展方法,名稱定義為大寫,為避免沖突
public static int COUNT(this object origin)
        {
            return 0;
        }

所有object對象將會有COUNT()方法

將上面語法進行改進為

query.Select(b=>new{b.BarCode,b.ProductName,total=b.BarCode.COUNT()})

  以這樣形式進行表示,這樣在語法上是編譯通過的,並且lambda支持這樣的解析

完整表示改為

query.Select(b=>new{b.BarCode,b.ProductName,total=b.BarCode.COUNT()})

.GroupBy(b=>new{b.BarCode,b.ProductName})

.OrderBy(b=>b.BarCode.COUNT(),true);

  這樣,完整的group表示語法就完成了,致少在邏輯上,是能實現SQL的group語法了,剩下就需要進行解析了

定義參數

public List<string> queryFields = new List<string>();
        public List<string> queryOrderBy = new List<string>();
        public List<string> groupFields = new List<string>();

  

//此方法解析方法調用表達式的屬性名和方法名
string GetPropertyMethod(Expression item,out string methodName)
        { //轉換為方法表達式
            var method = item as MethodCallExpression;
            MemberExpression memberExpression;            //獲取訪問屬性表達式
            if (method.Arguments[0] is UnaryExpression)
            {
                memberExpression = (method.Arguments[0] as UnaryExpression).Operand as MemberExpression;
            }
            else
            {
                memberExpression = method.Arguments[0] as MemberExpression;
            }
            methodName = method.Method.Name;//調用的方法名
            return memberExpression.Member.Name;//返回訪問的屬性名
        }

解析Select

public LambdaQuery<T> Select<TResult>(Expression<Func<T, TResult>> resultSelector)
        {
            string queryFullName = "";
            var newExpression = resultSelector.Body as NewExpression;//轉換為匿名對象表達式
            int i = 0;
            foreach (var item in newExpression.Arguments)//遍歷所有參數
            {
                var memberName = newExpression.Members[i].Name;//獲取構造的屬性名
                if (item is MethodCallExpression)//如果是方法
                {
                    string methodName;
                    string propertyName = GetPropertyMethod(item, out methodName);//獲取方法名和屬性名
                    queryFullName = string.Format("{0}({1}) as {2}", methodName, propertyName, memberName);
                }
                else//直接屬性
                {
                    var memberExpression = item as MemberExpression;//轉換為屬性訪問表達式
                    queryFullName = memberExpression.Member.Name;//返回屬性名
                }
                queryFields.Add(queryFullName);
                i += 1;
            }
            
            return this;
        }

  

 解析OrderBy,過程和上面差不多

public LambdaQuery<T> OrderBy<TKey>(Expression<Func<T, TKey>> expression, bool desc = true)
        {
            string orderBy="";
            string name;
            if (expression.Body is MethodCallExpression)//如果是方法
            {
                string methodName;
                string propertyName = GetPropertyMethod(expression.Body, out methodName);
                name = string.Format("{1}({0})", propertyName, methodName);
                orderBy = string.Format(" {0} {1}", name, desc ? "desc" : "asc");
            }
            else
            {
                MemberExpression mExp = (MemberExpression)expression.Body;
                if (!string.IsNullOrEmpty(orderBy))
                {
                    orderBy += ",";
                }
                name = mExp.Member.Name;
                orderBy = string.Format(" {0} {1}", name, desc ? "desc" : "asc");
            }
            queryOrderBy.Add(orderBy);
            return this;
        }

  解析GroupBy

public LambdaQuery<T> GroupBy<TResult>(Expression<Func<T, TResult>> resultSelector)
        {
            foreach (var item in (resultSelector.Body as NewExpression).Arguments)
            {
                var memberExpression = item as MemberExpression;//轉換為屬性訪問表達式
                groupFields.Add(memberExpression.Member.Name);
            }
            return this;
        }

  輸出

string fileds=string.Join(",",query.queryFields);
            string groupFields = string.Join(",", query.groupFields);
            string queryOrderBy = string.Join(",", query.queryOrderBy);
            Console.Write(string.Format("select {0} from Product group by {1} order by {2}", fileds, groupFields, queryOrderBy));

結果截圖

上面只實現了匿名對象簡單的解析,ORM查詢復雜的的是二元運算解析,如:

query.Where(b=>b.Id>10&&b.Name="hubro");

這樣就需要解析表達式樹了,情況比較復雜,回頭整理一下

實現表達式樹解析后就能實現having語法了,linq實現的group用lambda也能完整實現了

 

此示例只是實現lambda到SQL語句之間的轉換,實際應用需要考慮參數化,結果集映射,路還很長,有興趣的歡迎關注CRL框架

TO:管理員 現在達到放在首頁的要求了吧???

例子下載地址:http://files.cnblogs.com/files/hubro/LambdaQueryTest.rar


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM