动态拼接表达式树
问题来源于我正在做的日志功能模块:界面如下
界面上的查询条件,根据用户的操作会有不同的状态
比如:用户名称这个文本框有值的话就作为查询条件,没有的话就不作为查询条件,一个查询条件还好;现在有七八个条件的动态组合
原来有一个条件的时候都是这样写的:
Expression<Func<Log, bool>> a = e => e.UserName.Contains("李行周");
Expression<Func<Log, bool>> a = e => e.UserName.Contains("李行周")&&e.CreateDateTime<="2018年1月10日 13:57:18";
当有多个条件的时候,每次都要把语句重新写就有问题,出现了重复
我记得之前用rafy的时候,rafy是在内部实现了
internal class EntityQueryable<TEntity> : IOrderedQueryable<TEntity>
用的时候就创建 一个可查询的接口对象
var q = this.CreateLinqQuery<External>(); q = q.Where(e => e.TransactionExternalId == transactionExternalId); return (ExternalList)this.QueryData(q);
但现在我要查的是Mongodb没有用rafy,在网上找了一些说是用动态表达式拼接 主要参考的地址是:https://www.cnblogs.com/wzxinchen/p/4611592.html
我的实现,一些代码直接用的转载博客的
public class SqlFilter { public static SqlFilter Create(string propertyName, Operation operation, object value) { return new SqlFilter() { Name = propertyName, Operation = operation, Value = value }; } /// <summary> /// 字段名 /// </summary> public string Name { get; set; } /// <summary> /// 搜索操作,大于小于等于 /// </summary> public Operation Operation { get; set; } /// <summary> /// 搜索参数值 /// </summary> public object Value { get; set; } }
public enum Operation { GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, NotEqual, Equal, Like, In }
public class ExpressionBuilder<T> where T : class { public ExpressionBuilder() { } /// <summary> /// 生成表达式 /// </summary> /// <param name="filters">要拼接的条件</param> /// <param name="filterNameMap">字段映射集合</param> /// <returns></returns> public Expression<Func<T, bool>> Build(IList<SqlFilter> filters, Dictionary<string, string> filterNameMap) { var parameterExp = Expression.Parameter(typeof(T), "x"); Expression twoBinaryExpressionMerge = null; for (int i = 0; i < filters.Count; i++) { var sqlFilter = filters[i]; if (i == 0) { twoBinaryExpressionMerge = this.CreateBinaryExpression(sqlFilter, parameterExp); } else { var rightBinaryExpression = this.CreateBinaryExpression(sqlFilter, parameterExp); twoBinaryExpressionMerge = Expression.And(twoBinaryExpressionMerge, rightBinaryExpression); } } //结果是:x=>x.Id==1,这个··还需要解释么,很简单,不是么。创建一个相等的表达式,然后传入左边和右边的表达式 //当然到这儿还不能用,还需要继续 var lambda = Expression.Lambda<Func<T, bool>>(twoBinaryExpressionMerge, parameterExp); return lambda; } public Expression CreateBinaryExpression(SqlFilter sqlFilter, ParameterExpression parameterExpression) { Expression binaryExpression = null; //结果是这样:x=>,x是变量名 var propertyExp = Expression.Property(parameterExpression, sqlFilter.Name); //结果是这样:x=>x.Id,这句是为了构建访问属性的表达式 //上面这句第一个参数是你要取属性的对象表达式。我们要拼的表达式是x=>x.Id==1,==1这块先不管,其实就是x=>x.Id,那么其实我们就是对x进行取属性值,而x是parameterExp,所以第一个参数是parameterExp,第二个参数好说,就是属性名 var constExp = Expression.Constant(sqlFilter.Value, sqlFilter.Value.GetType()); var method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); switch (sqlFilter.Operation) { case Operation.GreaterThan: binaryExpression = Expression.GreaterThan(propertyExp, constExp); break; case Operation.LessThan: binaryExpression = Expression.LessThan(propertyExp, constExp); break; case Operation.GreaterThanOrEqual: binaryExpression = Expression.GreaterThanOrEqual(propertyExp, constExp); break; case Operation.LessThanOrEqual: binaryExpression = Expression.LessThanOrEqual(propertyExp, constExp); break; case Operation.NotEqual: binaryExpression = Expression.NotEqual(propertyExp, constExp); break; case Operation.Equal: binaryExpression = Expression.Equal(propertyExp, constExp); break; case Operation.Like: constExp = Expression.Constant(sqlFilter.Value, typeof(string)); binaryExpression = Expression.Call(propertyExp, method, constExp); break; case Operation.In: binaryExpression = null; break; default: binaryExpression = null; break; } return binaryExpression; } }
用法如下:
var builder = new ExpressionBuilder<Log>();//实例化组件,User是什么下面说 var filters = new List<SqlFilter>(); if (!string.IsNullOrWhiteSpace(queryLogPar.UserName)) filters.Add(SqlFilter.Create("Username", Operation.Like, queryLogPar.UserName)); if (queryLogPar.StartDate != null) filters.Add(SqlFilter.Create("CreateDateTime", Operation.GreaterThanOrEqual, queryLogPar.StartDate)); if (queryLogPar.EndDate != null) filters.Add(SqlFilter.Create("CreateDateTime", Operation.LessThanOrEqual, queryLogPar.EndDate)); if (!string.IsNullOrWhiteSpace(queryLogPar.Exception)) filters.Add(SqlFilter.Create("Exception", Operation.Like, queryLogPar.Exception)); if (!string.IsNullOrWhiteSpace(queryLogPar.TenantId)) filters.Add(SqlFilter.Create("TenantId", Operation.Like, queryLogPar.TenantId)); if (!string.IsNullOrWhiteSpace(queryLogPar.RawUrl)) filters.Add(SqlFilter.Create("RawUrl", Operation.Like, queryLogPar.RawUrl)); if (!string.IsNullOrWhiteSpace(queryLogPar.ExtTenantId)) filters.Add(SqlFilter.Create("ExtTenantId", Operation.Like, queryLogPar.ExtTenantId)); if (queryLogPar.LogLevel != -1) filters.Add(SqlFilter.Create("LogLevel", Operation.Equal, (LogLevel)queryLogPar.LogLevel)); if (queryLogPar.LogType != -1) filters.Add(SqlFilter.Create("LogType", Operation.Equal, (LogType)queryLogPar.LogType)); var where = builder.Build(filters, new Dictionary<string, string>());//根据上面的条件,拼接出表达式树 return where;