目的
遇到一个场景需要接收一个表的列来进行动态排序,比如我想根据CreateTime进行正序排序,加上我使用的ORM框架是EFCore,那么我一下子就想到应该使用OrderBy,然后接收一个要排序的列
query.OrderBy("CreateTime")
但是这样子是不行的,所以寻找方案后找到了一个nuget包System.Linq.Dynamic.Core,该包的用法如下
//OrderBy("time asc")
query.OrderBy($"{orderContent.SortName} {orderContent.Sort}");
但是把这样子就会多引用一个nuget包,所以就在继续找其他方法,就找到了使用表达式树来实现,那么就简单介绍几种表达式树可能用到的例子吧。
是什么
定义一种树状的数据结构来描述c#中的代码,这种树状的数据结构就是表达式树。
还是直接放上原文的链接吧
和委托的关系
表达式树其实与委托已经没什么关系了,非要扯上关系,那就这么说吧,表达式树是存放委托的容器。
要用Lambda表达式的时候,直接从表达式中获取出来,Compile()就可以直接用了。如下代码:
static void Main(string[] args)
{Expression<Func<int, int, int>> exp = (x, y) => x + y;Func<int, int, int> fun = exp.Compile();int result = fun(2, 3);
}
使用场景
平常我是很少或者几乎不自己构建表达式树,就像上面文章那个老哥说的那样子,表达式树一般是给框架的作者用的。
操作
下文中UserDto.GetUserDtos()返回的是一个UserDto集合(算是一个伪代码),虽然本文示例没有直接查询的数据库,但是我已经在其他项目中测试过,在执行输出SQL中已经体现出来效果。
本文示例虽已经过验证,但是还未上生产。
简单筛选
public void SampleWhere()
{var user = UserDto.GetUserDtos().AsQueryable();//泛型写法Func<UserDto, bool> predicate = s => s.Deleted=false;var list = user.Where(predicate).ToList(); // 这个执行是在内存中执行的//表达式树写法Expression<Func<UserDto, bool>> lambdaExp = (s) => !s.Deleted;var list2 = user.Where(lambdaExp).ToList();
}
通过简单的筛选去学习如何创建简单的表达式树
/// <summary>
/// 通过简单的筛选去学习
/// </summary>
public void SampleWhereToStudy()
{// 实现效果 t => t.Name == "张三"var user = UserDto.GetUserDtos().AsQueryable();var result1 = user.Where(t => t.Name == "张三");ParameterExpression demo = Expression.Parameter(typeof(UserDto), "t");Console.WriteLine(demo);MemberExpression demo_name = Expression.Property(demo, "Name");Console.WriteLine(demo_name);// t.NameConstantExpression value = Expression.Constant("张三");Console.WriteLine(value);// 张三BinaryExpression greaterThen = Expression.Equal(demo_name, value);Console.WriteLine(greaterThen); // t.Name=="张三"var lambda = Expression.Lambda<Func<UserDto, bool>>(greaterThen, demo);Console.WriteLine(lambda);// t=>t.Name=="张三"var lamdbaFunc = lambda.Compile();// 编译表达式Console.WriteLine(lamdbaFunc);// System.Func`2[CSharpBasic.Model.UserDto,System.Boolean]var resultTrue = lamdbaFunc(new UserDto { Name = "张三" });Console.WriteLine(resultTrue);//True// 筛选张三var result = user.Where(lamdbaFunc).ToList();
}
资料来自:超超老师教程
动态筛选
public void DynamicWhere()
{//实现效果:已知一个表UserDto 包含属性Name、Address、Id等,需要实现通过属性进行动态过滤var user = UserDto.GetUserDtos().AsQueryable();var list2 = user.EqualWhere("Name", "张三").FirstOrDefault();// 执行SQL:SELECT u.id, u.account,u.name FROM test."user" AS u WHERE u.name = '张三'
}// 扩展方法
public static class ExpressExtensons
{/// <summary>/// 等于筛选/// </summary>/// <typeparam name="T"></typeparam>/// <param name="queryable"></param>/// <param name="whereField"></param>/// <param name="value"></param>/// <returns></returns>public static IQueryable<T> EqualWhere<T>(this IQueryable<T> queryable, string whereField, object value){return queryable.Where<T>(whereField, value);}/// <summary>/// 小于筛选/// </summary>/// <typeparam name="T"></typeparam>/// <param name="queryable"></param>/// <param name="whereField"></param>/// <param name="value"></param>/// <returns></returns>public static IQueryable<T> LessWhere<T>(this IQueryable<T> queryable, string whereField, object value){return queryable.Where<T>(whereField, value, 1);}/// <summary>/// 大于筛选/// </summary>/// <typeparam name="T"></typeparam>/// <param name="queryable"></param>/// <param name="whereField"></param>/// <param name="value"></param>/// <returns></returns>public static IQueryable<T> GreaterWhere<T>(this IQueryable<T> queryable, string whereField, object value){return queryable.Where<T>(whereField, value, 1);}private static IQueryable<T> Where<T>(this IQueryable<T> queryable, string whereField, object value, int type = 0){var paramExp = Expression.Parameter(typeof(T), "t");//因为这个Property里面已经包含属性校验的功能,所以不用再另外写了var memberExp = Expression.Property(paramExp, whereField);//值表达式var valueExp = Expression.Constant(value);var exp = type switch{//小于1 => Expression.LessThan(memberExp, valueExp),//小于等于2 => Expression.LessThanOrEqual(memberExp, valueExp),//大于3 => Expression.GreaterThan(memberExp, valueExp),//大于等于4 => Expression.GreaterThanOrEqual(memberExp, valueExp),//等于_ => Expression.Equal(memberExp, valueExp),};var lambda = Expression.Lambda<Func<T, bool>>(exp, paramExp);return queryable.Where(lambda);}
}
动态排序
public void DynamicOrderby()
{// 实现效果var userQueryable = UserDto.GetUserDtos().AsQueryable();var sortList = userQueryable.OrderBy(t => t.CreateTime).ToList();foreach (var item in sortList){Console.WriteLine(item.CreateTime);}Console.WriteLine("-----------------");// 通过表达式树去实现效果var list = userQueryable.OrderBy("CreateTime", false);// 执行SQL:SELECT u.id, u.account, u.create_time FROM test."user" AS u ORDER BY u.create_time DESCforeach (var item in list){Console.WriteLine(item.CreateTime);}
}public static class ExpressExtensons
{/// <summary>/// 根据字段排序处理/// </summary>/// <typeparam name="T">泛型列</typeparam>/// <param name="queryable">查询queryable</param>/// <param name="sortField">排序列</param>/// <param name="isAsc">true正序 false倒序</param>/// <returns></returns>public static IQueryable<T> OrderBy<T>(this IQueryable<T> queryable, string sortField, bool isAsc = true){var parameter = Expression.Parameter(typeof(T));var property = typeof(T).GetProperty(sortField);if (property == null)throw new ArgumentNullException($"无效的属性 {sortField}");var memberExpression = Expression.Property(parameter, property);var orderbeExpression = Expression.Lambda(memberExpression, new ParameterExpression[] { parameter });var orderMethod = isAsc ? "OrderBy" : "OrderByDescending";var resultExpression = Expression.Call(typeof(Queryable), orderMethod, new Type[] { queryable.ElementType, property.PropertyType },new Expression[] { queryable.Expression, Expression.Quote(orderbeExpression) });return queryable.Provider.CreateQuery<T>(resultExpression);}
}
动态查询指定属性
通过传递一个字符串,然后查询指定的列返回
var userQueryable = UserDto.GetUserDtos().AsQueryable();ParameterExpression parameter1 = Expression.Parameter(typeof(UserDto));
MemberExpression men = Expression.Property(parameter1, "Name");
var selectFieldExpression = Expression.Lambda<Func<UserDto, string>>(men, new ParameterExpression[] { parameter1 });
var result1 = userQueryable.Select(selectFieldExpression).ToList();
通过编写一个映射后的类实现简单的对应映射转换
public void DynamicSelect()
{var userQueryable = UserDto.GetUserDtos().AsQueryable();// 简单映射转换var result2 = userQueryable.SelectMapper<UserDto, UserTest>().ToList();// 执行SQL:SELECT u.id AS "Id", u.account AS "Account" FROM test."user" AS u
}public static class ExpressExtensons
{/// <summary>/// 查询映射/// </summary>/// <typeparam name="T"></typeparam>/// <typeparam name="M"></typeparam>/// <param name="queryable"></param>/// <returns></returns>public static IQueryable<M> SelectMapper<T, M>(this IQueryable<T> queryable){var parameter = Expression.Parameter(typeof(T), "t");var newExpression = Expression.New(typeof(M));var mapperType = typeof(T).GetProperties();var listBinding = new List<MemberBinding>();foreach (var item in typeof(M).GetProperties()){if (!mapperType.Any(t => t.Name == item.Name)){continue;}var mem = Expression.Property(parameter, item.Name);// t.namevar member = typeof(M).GetMember(item.Name)[0];MemberBinding memBinding = Expression.Bind(member, mem);// 这里传mem是用t.name给他赋值listBinding.Add(memBinding);}var memberExp = Expression.MemberInit(newExpression, listBinding);var selectExpression = Expression.Lambda<Func<T, M>>(memberExp, new ParameterExpression[] { parameter });return queryable.Select(selectExpression);}
}
资料
表达式树资料:https://www.cnblogs.com/li-peng/p/3154381.html