动态构建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); } }
原文地址:http://www.zkea.net/codesnippet/detail/entity-framework-dynamic-search.html