目的:以編程方式實現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