动态构建Lambda表达式实现EF动态查询
在使用entity framework做数据查询的时候,查询条件往往不是固定的,需要动态查询。可以通过动态构建lamda表达式来实现动态查询。
lamda表达式
使用lamda表达式可以很方便的按条件过滤数据。entity framework也是将lamda表达式转换成对应的sql语句执行。
比如下列代码,输出年龄大于1的人的名字:
namespace consoleapp { public class person { public string name { get; set; } public int age { get; set; } } class mydbcontext : dbcontext { public dbset<person> people { get; set; } protected override void onconfiguring(dbcontextoptionsbuilder optionsbuilder) { optionsbuilder.usesqlserver("server=(local);database=testdb;user id=sa;password=sa;"); } } class program { static void main(string[] args) { mydbcontext dbcontext = new mydbcontext(); foreach (var item in dbcontext.people.where(m => m.age > 1)) { console.writeline(item.name); } } } }
peoples.where(m => m.age > 1)
这个在代码里面写死了,如果要换条件就一定要改代码。
expression
先看一下where里面是什么,where里面是表达式的主体,分为参数m,左边参数m的属性和右边的值,通过中间的大于运算符进行比较运算。所以我们在构建表达式的时候,也需要构建这四个部分:
- 参数
- 参数的属性
- 值
- 运算符
参数
参数有类型和名字:
type type= typeof(person); var parameter = expression.parameter(type, "m");
属性
我们需要知道属性的名称和类型,可通过反射来得到对应的类型并和刚刚的参数关联起来:
propertyinfo property = type.getproperty("age"); expression expproperty = expression.property(parameter, property.name);
值
我们还需构建一个值的表达式:
expression<func<object>> valuelamda = () => 1; expression expvalue = expression.convert(valuelamda.body, property.propertytype);
因为值委托的返回类型是object,所以需要使用expression.convert来转换成正确的类型。
如果值是简单类型,如int, string等,也可以直接使用expression.constant
,简单一些:
expression expvalue = expression.constant(1);
运算符
现在已经有了属性表达式,值表达式,接下来就是要使用运算符表达式把它们连接起来:
expression expression = expression.greaterthan(expproperty, expvalue);
将表达式转换成对应的类型即可以使用了:
expression<func<person, bool>> filter = ((expression<func<person, bool>>)expression.lambda(expression, parameter)); foreach (var item in dbcontext.people.where(filter)) { console.writeline(item.name); }
下面是完整代码
namespace consoleapp { public class person { public string name { get; set; } public int age { get; set; } } class mydbcontext : dbcontext { public dbset<person> people { get; set; } protected override void onconfiguring(dbcontextoptionsbuilder optionsbuilder) { optionsbuilder.usesqlserver("server=(local);database=testdb;user id=sa;password=sa;"); } } class program { static void main(string[] args) { mydbcontext dbcontext = new mydbcontext(); type type = typeof(person); var parameter = expression.parameter(type, "m"); propertyinfo property = type.getproperty("age"); expression expproperty = expression.property(parameter, property.name); expression<func<object>> valuelamda = () => 1; expression expvalue = expression.convert(valuelamda.body, property.propertytype); expression expression = expression.greaterthan(expproperty, expvalue); expression<func<person, bool>> filter = ((expression<func<person, bool>>)expression.lambda(expression, parameter)); foreach (var item in dbcontext.people.where(filter)) { console.writeline(item.name); } } } }
这样就可以通过动态传入属性名和值来进行动态查询了。
封装和使用
我们做了一些简单的封装,更方便使用,代码:
https://github.com/seriawei/zkeacms/blob/master/src/easyframework/linq/query.cs
使用querycollection来添加条件,最后转成表达式:
static void main(string[] args) { mydbcontext dbcontext = new mydbcontext(); querycollection queries = new querycollection(); queries.add(new query { name = "age", operator = query.operators.greaterthan, value = 1 }); foreach (var item in dbcontext.people.where(queries.asexpression<person>())) { console.writeline(item.name); } }
原文地址:
上一篇: 零基础写python爬虫之爬虫编写全记录