EF Core 表达式树

什么是表达式树

  • 表达式树(Expression Tree):树形数据结构表示代码以表示逻辑运算,以便可以在运行时访问逻辑运算的结构。
  • Expression <TDelegate>类型
  • 从Lambda表达式来生成表达式树:
 Expression<Func<Book,bool>> expression = b=>.Price > 5;

Expression和Func的区别

Expression<Func<Book,bool>> expression = b=>.Price > 5;
 Console.WriteLine(expression);//e => (e.Price > 5) 打印结果
 //把Expression<Func<Book,bool>>换成Func<Book,bool>如下
     
 Func<Book,bool> func = b=>.Price > 5;
 Console.WriteLine(func);

通过运行发现,两者执行效果一样

查看生成的SQL语句

 -- Expression<Func<Book,bool>> expression = b=>.Price > 5;    
      SELECT t."Id", t."AuthorName", t."Price", t."PubTime", t."Title"
       FROM "T_Books" AS t
       WHERE t."Price" > 5.0
       
 --Func<Book,bool> func = b=>.Price > 5;
       SELECT t."Id", t."AuthorName", t."Price", t."PubTime", t."Title"
       FROM "T_Books" AS t

结论

Expression表达树是编译器可运行时从内存中分析出表达式的结构,并把它翻译成SQL语句,Expression表达树在数据库就已经筛选了数据。

Func是把所有数据都查询出来,在内存中调用委托,来进行对应的数据查询,如果数据量大,这种处理是不合适的。

如何查看表达式树的结构

Visual Studio中调试程序,然后用【快速监视】的方式查看变量expression的值,展开Raw View.

Expression<Func<Book, bool>> e = b => b.Price > 5 || b.AuthorName.Contains("杨");

2.整个表达式树是一个“或”(OrElse)类型的节点,左节点(Left)是b.Price>5表达式,右节点(Right)是b.Authorname.Contains(“杨”)表达式。而b.Price>5这个表达式又是一个“大于”(GreaterThan)类型的节点,左节点(Left)是b.Price,右节点(Right)是5.

3.AST:抽象语法树

1、通过zspitz开发的ExpressionTreeVisualizer

GitHub - zspitz/ExpressionTreeVisualizer: Debugging visualizer for expression trees

安装VS插件(由于这个插件对版本要求很严格,所以不推荐使用)

2、通过代码查看表达式树

推荐使用代码查看结构,简单方便

安装ExpressionTreeToString Nuget包

//使用方式如下
 Expression<Func<Book, bool>> e = b => b.AuthorName.Contains("杨") || b.Price > 30;
 Console.WriteLine(e.ToString("Object notation", "C#"));

生成表达式树的结构代码

var b = new ParameterExpression {//参数
     Type = typeof(Book),//Book类型
     IsByRef = false,
     Name = "b"   //参数名字为b
 };
 ​
 new Expression<Func<Book, bool>> {
     NodeType = ExpressionType.Lambda,//根节点是一个Lambda
     Type = typeof(Func<Book, bool>),
     Parameters = new ReadOnlyCollection<ParameterExpression> {
         b
     },//整个Lambda表达式有一个参数 b
     Body = new BinaryExpression {//Lambda的体(Body)是一个二元表达式
         NodeType = ExpressionType.GreaterThan,//二元表达式的节点类型是 大于 >
         Type = typeof(bool),
         Left = new MemberExpression {//左节点是 访问成员表达式
             Type = typeof(double),
             Expression = b,
             Member = typeof(Book).GetProperty("Price")
         },
         Right = new ConstantExpression {//右节点是 常量表达式
             Type = typeof(double),//double类型
             Value = 5 //值为5 我们写代码的时候 double v= 5;编译器帮我们转化了
         }
     },
     ReturnType = typeof(bool)
 }

通过代码动态构造表达式树

注:比较麻烦,而且容易报错

思想:先创建树叶,再通过一片片树叶组成大树

//动态创建和Expression<Func<Book, bool>> e = b =>b.Price > 5一样的代码
 ​
 ParameterExpression paramB = Expression.Parameter(typeof(Book), "b");//参数类型与名字
 MemberExpression exprLeft = Expression.MakeMemberAccess(paramB, typeof(Book).GetProperty("Price"));//成员访问表达式左节点
 //右节点是 常量表达式
 ConstantExpression exprRight = Expression.Constant(5.0, typeof(double));
 //二元表达式的节点类型是 大于 >
 BinaryExpression exprBody = Expression.MakeBinary(ExpressionType.GreaterThan, exprLeft, exprRight);
 Expression<Func<Book, bool>> expr1 = Expression.Lambda<Func<Book, bool>>(exprBody, paramB);
 ctx.Books.Where(expr1).ToList();
 Console.WriteLine(expr1.ToString("Object notation", "C#"));

可以用ExpressionTreeToString的ToString("Factory methods", "C#")输出类似于工厂方法生成这个表达式树的代码。

使用Factory methods 生成的表达式树是以工厂模式生成的 比上述原生要简单许多

Expression<Func<Book, bool>> e = b => b.Price > 5;
 Console.WriteLine(e.ToString("Factory methods", "C#"));

工厂模式生成的表达式树

 var b = Parameter(
     typeof(Book),
     "b"
 );
 ​
 Lambda(
     GreaterThan(
         MakeMemberAccess(b,
             typeof(Book).GetProperty("Price")
         ),
         Constant(5)
     ),
     b
 )

注意:使用Using static引用调用静态方法不需要再加类名。

使用工厂模式构造表达式树:用此代码打印出来的与上述一样

 var b = Parameter(typeof(Book), "b");
 var expr1 = Lambda<Func<Book, bool>>(GreaterThan(MakeMemberAccess(b, typeof(Book).GetProperty("Price")),
             Constant(5.0)), b);
 using TestDbContext ctx = new TestDbContext();
 ctx.Books.Where(expr1).ToList();
 Console.WriteLine(expr1.ToString("Factory methods", "C#"));

尽量避免使用动态构建表达式树

  1. 动态构建表达式树的代码很复杂,而且易读性差,维护起来不方便
  2. 一般只有在编写不特定于某个实体类的通用框架的时候,由于无法在编译器确定要操作的类名,属性等,所以才需要编写动态构建表达式树的代码,否则为了提高代码的可读性和可维护性,要尽量避免构建表达式树。而是用IQueryable的延迟执行特性来动态构造。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Entity Framework CoreEF Core)提供了一种构建形结构的方法。在 EF Core 中,我们可以使用自引用关系(Self-Referencing Relationships)来构建形结构。自引用关系是指一个实体可以与同一实体类型的其他实体发生关系。在形结构中,每个节点都可以与其它节点发生关系,这些节点都是同一实体类型的实体。 为了演示如何使用 EF Core 构建形结构,我们可以考虑一个简单的示例:假设我们有一个 Department(部门)实体,每个部门可以有一个或多个子部门,每个子部门也可以有自己的子部门。我们可以使用自引用关系来构建这个形结构。具体来说,我们可以添加一个 ParentDepartment(父部门)导航属性和一个 ChildDepartments(子部门)集合导航属性到 Department 实体中。 在 EF Core 中,我们可以使用 Fluent API 来配置自引用关系。具体来说,我们可以使用 HasOne 和 WithMany 方法来定义父子关系。例如,下面的代码片段演示了如何定义 Department 实体的自引用关系: ``` public class Department { public int DepartmentId { get; set; } public string Name { get; set; } public int? ParentDepartmentId { get; set; } public Department ParentDepartment { get; set; } public ICollection<Department> ChildDepartments { get; set; } } public class MyDbContext : DbContext { public DbSet<Department> Departments { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Department>() .HasOne(d => d.ParentDepartment) .WithMany(d => d.ChildDepartments) .HasForeignKey(d => d.ParentDepartmentId); } } ``` 在这个示例中,Department 实体包含一个 ParentDepartmentId 属性和一个 ParentDepartment 导航属性,这两个属性定义了父子关系。同时,Department 实体还包含一个 ChildDepartments 导航属性,用于获取子部门列表。在 MyDbContext 类中,我们使用 HasOne 和 WithMany 方法来定义 Department 实体的自引用关系。 通过以上的配置,我们就可以使用 EF Core 来查询形结构了。例如,下面的代码演示了如何查询指定部门的所有子部门: ``` var department = dbContext.Departments.Include(d => d.ChildDepartments).FirstOrDefault(d => d.DepartmentId == 1); var childDepartments = department.ChildDepartments; ``` 在这个示例中,我们使用 Include 方法来加载 ChildDepartments 导航属性,以便在查询结果中包含子部门列表。然后,我们使用 FirstOrDefault 方法来查询指定的部门,并获取其子部门列表。 以上就是使用 EF Core 构建形结构的一个简单示例。当然,在实际开发中,我们可能需要更复杂的形结构,需要根据具体情况进行配置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值