欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

动态构建Lambda表达式实现EF动态查询

程序员文章站 2023-01-01 20:34:52
在使用Entity Framework做数据查询的时候,查询条件往往不是固定的,需要动态查询。可以通过动态构建Lamda表达式来实现动态查询。 Lamda表达式 使用Lamda表达式可以很方便的按条件过滤数据。Entity Framework也是将Lamda表达式转换成对应的SQL语句执行。 ......

在使用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);
    }
}

原文地址: