动态拼接表达式树

问题来源于我正在做的日志功能模块:界面如下

界面上的查询条件,根据用户的操作会有不同的状态

比如:用户名称这个文本框有值的话就作为查询条件,没有的话就不作为查询条件,一个查询条件还好;现在有七八个条件的动态组合

原来有一个条件的时候都是这样写的:

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;

 

posted on 2018-01-10 14:38  行周  阅读(935)  评论(0编辑  收藏  举报

导航