来源: ORM开发之解析lambda实现group查询(附测试例子) – hubro – 博客园
目的:以编程方式实现group查询,在开发ORM时,需要达到这样的效果
先看一个简单的group语句
select BarCode,ProductName,COUNT(BarCode) as total from ProductData group by BarCode,ProductName
order by COUNT(BarCode) desc
结果
1
2
3
4
|
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 表示为
1
2
3
4
|
from p in ProductData group p.BarCode by p.ProductName into g select new { |
1
|
g.BarCode,<br>g.ProductName,<br>total=g.Count() |
1
|
} |
linq to SQL很容易表达,用lambda该如何表达呢
跟普通SQL查询不同,查询字段和排序可用聚合函数count,sum,抛开这,用lambda,同样的查询可表示为
这里用匿名对象来作选择器
设定 query=new LambdaQuery<Product>() 以下方法可能不实际存在,只作演示
1
2
3
4
5
|
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)这样的语法
没有直接的方法,但是有间接的方法,扩展方法 扩展方法真是个好东西,解决了很多问题 定义一个扩展方法,名称定义为大写,为避免冲突
1
2
3
4
|
public static int COUNT( this object origin) { return 0; } |
所有object对象将会有COUNT()方法
将上面语法进行改进为
1
|
query.Select(b=> new {b.BarCode,b.ProductName,total=b.BarCode.COUNT()}) |
以这样形式进行表示,这样在语法上是编译通过的,并且lambda支持这样的解析
完整表示改为
1
2
3
4
5
|
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语法了,剩下就需要进行解析了
定义参数
1
2
3
|
public List< string > queryFields = new List< string >(); public List< string > queryOrderBy = new List< string >(); public List< string > groupFields = new List< string >(); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//此方法解析方法调用表达式的属性名和方法名 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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,过程和上面差不多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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
1
2
3
4
5
6
7
8
9
|
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 ; } |
输出
1
2
3
4
|
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