杨中科B站视频链接:.NET 6教程,.Net Core 2022视频教程,杨中科主讲_哔哩哔哩_bilibili
1、表达式树(Expression Tree):树形数据结构表示代码,以表示逻辑运算,以便可以在运行时访问逻辑运算的结构
2、Expression
3、从Lambda表达式来生成表达式树:
Expression>e1=b=>b.Price>5
1、把Expression
Funce=b=>b.price>5;
ctx.Books.where(e).ToList();
查看生成的SQL 会把全部的数据捞过来放到内存中再进行过滤。
2、Expression对象储存了运算逻辑,它把运算逻辑保存成抽象语法树(AST),可以在运行时动态获取运算逻辑。而普通委托则没有
1、Visual Studio中调式程序,然后用【快速监视】的方式查看变量e的值,展开Raw View
2、整个表达式树是一个“或”(OrElse)类型的节点,左节点(Left)是b.Price>5表达式,右节点(Right)是b.AuthorName.Contains("严值")表达式。而b.Price>5这个表达式又是一个“大于”(GreaterThen)类型的节点,左节点(Left)是b.Price,右节点(Right)是5。
3、AST:抽象语法树
1、zspit开发的ExpressionTreeVisualizer:GitHub - zspitz/ExpressionTreeVisualizer: Debugging visualizer for expression trees 安装VS插件 2、目前:如果项目的.NET Core版本是5,那么可视化查看就会报BadImageFormatException异常,只能把项目降级到.NET Core3.1才可以
1、Install-Packge ExpressionTreeToString
2、
Expression> e = b => b.AuthorName.Contains("杨中科")||b.Price>30;
Console.WriteLine(e.ToString("Object notation", "C#"));
1、只有通过代码动态构建表达式树才能更好的发挥表达式树的能力
2、ParameterExpression、BinaryExpression、MethodCallExpression、ConstantExpression等类几乎都没有提供构造方法,而且所有属性也几乎都是只读的,因此我们一般不会直接创建这些类的实例,而是调用Expression类的Parameter、MakeBinary、Call、Constant等静态方法来生成、这些静态方法我们一般称作创建表达式树的工厂方法,而属性则通过方法参数类设置
目标
生成和如下硬编码的C#代码一样的表达式树:
Expression> 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> expr1 = Expression.Lambda>(exprBody, paramB);
工厂方法
Add:加法;
AndAlso:短路与运算;
ArrayAccess:数组元素访问;
Call:方法访问;
Condition:三元条件运算符;
Constant:常量表达式;
Convert:类型转换;
GreaterThan:大于或等于运算符;
MakeBinary:创建二元运算;
NotEqual:不等于运算;
OrElse:短路或运算;
Parameter:表达式的参数;
1、通过代码来动态构造表达式树要求开发者精通表达式树的结构,甚至还需要了解CLR底层的机制。不过可以用ExpressionTressToString来简化动态构造表达式树的代码,
2、可以用ExpressionTreeToString的Tostring("Factory methods","C#")输出类似于工厂方法生成这个表达式树的代码
3、输出的所有代码都是对于工厂方法的调用,不过调用工厂方法的时候都省略了Expression类。手动添加Expression或者用using static
4、可能需要微调生成的代码
1、动态构建表达式树最有价值的地方就是运行时根据条件不同生成不同的表达式树。
2、编写:static IEnumerable
3、先编写
Expression> expr1 = b => b.Price == 5;
Expression> expr2 = b => b.Title == "零基础趣学C语言";
查看代码再写代码e1.ToString("Factory methods","C#")
4、判断是否基本数据类型:if(propType.IsPrimitive)
5、测试调用:QueryBooks("Price",18.0);QueryBooks("AuthorName","严值")
1、Select(b=>new{b.Id,b.Name})
2、运行时动态设定Select查询出来的属性,需要使用Emit技术来采用动态生成IL的技术来运行时创建一个类。难度大!
3、简单的再运行时动态设定Select查询出来的属性方法
1、Select参数中传递一个数组:Select(b=>new object[]{b.Id,b.Title})
2、把列对应的属性的访问表达式放到一个Expression数组中,然后使用Expression,NewArrayInit构建一个代表数组的NewArrayExpression表达式对象,然后就可以用这个NewArrayExpression对象类供Select调用来执行了
代码
ParameterExpression exParameter = Expression.Parameter(typeof(TEntity));
List exProps = new List();
foreach(string propName in propNames)
{
Expression exProp = Expression.Convert(Expression.MakeMemberAccess(exParameter, typeof(TEntity).GetProperty(propName)), typeof(object));
exProps.Add(exProp);
}
Expression[] initializers = exProps.ToArray();
var newArrayExp = Expression.NewArrayInit(typeof(object), initializers);
var selectExpression = Expression.Lambda>(newArrayExp, exParameter);
IQueryable
1、动态构建表达式树的代码仍然复杂,而且易读性差,维护起来是一个让人头疼的事情。
2、一般只有在编写不特定于某个实体类的通用框架的时候,由于无法在编译器确定要操作的类名、属性等,所以才需要编写动态构建表达式树的代码。否则为了提高代码的可读性和可维护性,要尽量避免动态构建表达式树。而是用IQueryable的延迟执行特性来动态构造
编写如下一个方法Book[] QueryBooks(string title,double?lowerPrice,double?upperPrice,int orderByType),用于根据指定的参数条件进行数据的复合查询:title如果不为空,则进行标题的匹配;如果lowerPrice不为空,则还要匹配价格不小于lowerPrice的树;如果upperPrice不为空,则还要匹配价格不大于upperPrice的书;orderByrType为排序规则,取值为1代表按照价格降序排列,取值为2代表按照价格升序排列,取值为3代表按照发表日期降序排列,取值为4代表按照发表日期升序排列
IQueryable source = ctx.Books;
if (!string.IsNullOrEmpty(title))
{
source = source.Where(b => b.Title.Contains(title));
}
if (lowerPrice != null)
{
source = source.Where(b => b.Price >= lowerPrice);
}
if (upperPrice != null)
{
source = source.Where(b => b.Price <= upperPrice);
}
if (orderByType == 1)
{
source = source.OrderByDescending(b => b.Price);
}
else if (orderByType == 2)
{
source = source.OrderBy(b => b.Price);
}
else if (orderByType == 3)
{
source = source.OrderByDescending(b => b.PubTime);
}
else if (orderByType == 4)
{
source = source.OrderBy(b => b.PubTime);
}
return source.ToArray();
System.Linq.Dynamic.Core
1、System.Linq.Dynamic.Core
2、使用字符串格式的语法来进行数据操作
var query = db.Customers
.Where("City == @0 and Orders.Count >= @1", "London", 10)
.OrderBy("CompanyName")
.Select("new(CompanyName as Name, Phone)");