C# 构建动态Lambda表达式

做CURD开发的过程中,通常都会需要GetList,然而查询条件是一个可能变化的需求,如何从容对应需求变化呢?

首先,我们来设计一个套路,尝试以最小的工作量完成一次查询条件的需求变更

1.UI收集查询数据

2.UI将查询数据传递给Service

3.Service从查询配置(数据库、JSON、XML)中匹配出查询条件,并赋予UI取得的值

4.Service根据查询配置(已赋值)构建查询表达式。

5.执行查询返回数据。

大概流程如下图所示:

下面上代码,希望有人能看懂 ><

查询保存设置

    public interface IEntity
    {
        int Id { get; set; }
    }
    public class QueryCondition : IEntity
    {
        [Key]
        public int Id { get; set; }
        /// <summary>
        /// 条件分组:以此做为查询条件
        /// </summary>
        public string Group { get; set; }
        /// <summary>
        /// 字段名称
        /// </summary>
        public string FieldName { get; set; }
        public int CompareType { get; set; }
        public int CompareDataType { get; set; }
        public string Value { get; set; }
    }

查询条件DTO模型

    /// <summary>
    /// 查询结构
    /// </summary>
    public class QueryConditionModel
    {
        public string FieldName { get; set; }
        public CompareType Type { get; set; }
        public CompareDataType DataType { get; set; }
        public string Value { get; set; }
    }
    public enum CompareType
    {
        Equal = 1,
        GreaterThan = 2,
        GreaterThanOrEqual = 3,
        LessThan = 4,
        LessThanOrEqual = 5,
        Include = 6,
    }
    public enum CompareDataType
    {
        Int = 1,
        String = 2,
        Double = 3,
        Decimal = 4,
        Float = 5,
        DateTime = 6
    }

查询条件DTO转换配置

    public class QueryConditionProfile : Profile
    {

        [Obsolete("")]
        protected override void Configure()
        {
            CreateMap<QueryCondition, QueryConditionModel>()
                .ForMember(p => p.Type, opt =>
                {
                    opt.MapFrom(k => (CompareType)k.CompareType);
                })
                .ForMember(p => p.DataType, opt =>
                {
                    opt.MapFrom(k => (CompareDataType)k.CompareDataType);
                })
                ;
        }
    }

查询条件构建

    public class ServiceBase 
    {
        protected XXXDbContext Ctx;
        /// <summary>
        /// 动态构建Lambda查询表达式
        /// </summary>
        /// <param name="searchItems"></param>
        /// <returns></returns>
        protected Expression<Func<T, bool>> BuildExpression<T>(IList<QueryConditionModel> searchItems)
        {
            var where = PredicateExtensionses.True<T>();
            if (!searchItems.Any()) return @where;
            foreach (var subitem in searchItems)
            {
                try
                {
                    var field = subitem.FieldName;
                    var compare = subitem.Type;
                    var type = subitem.DataType;
                    var value = subitem.Value;
                    if (string.IsNullOrEmpty(field)) continue;                 
                    if (string.IsNullOrEmpty(value)) continue;
                    //构建Lambda表达式
                    var parameter = Expression.Parameter(typeof(T), "p");
                    Expression constant;
                    //表达式左侧 like: p.Name
                    var left = Expression.PropertyOrField(parameter, field);
                    //表达式右侧,比较值, like '张三'
                    var right = Expression.Constant(value);
                    //比较表达式
                    switch (compare)
                    {
                        case CompareType.GreaterThan:
                            constant = Expression.GreaterThan(left, right);
                            break;
                        case CompareType.GreaterThanOrEqual:
                            constant = Expression.GreaterThanOrEqual(left, right);
                            break;
                        case CompareType.LessThan:
                            constant = Expression.LessThan(left, right);
                            break;
                        case CompareType.LessThanOrEqual:
                            constant = Expression.LessThanOrEqual(left, right);
                            break;
                        case CompareType.Include:
                            //like 查询,需要调用外部int或string的Contains方法
                            var method = type == CompareDataType.Int
                                ? typeof(int).GetMethod("Contains", new Type[] { typeof(int) })
                                : typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                            constant = Expression.Call(left, method, right);
                            break;
                        case CompareType.Equal:
                        default:
                            constant = Expression.Equal(left, right);
                            break;
                    }
                    var lambda = Expression.Lambda<Func<T, Boolean>>(constant, parameter);
                    @where = @where.And(lambda);
                }
                catch (Exception ex)
                {
                    //OnMethodExecuted(JsonConvert.SerializeObject(searchItems), JsonConvert.SerializeObject(ex), "",
                        LogType.Error);
                }


            }
            return @where;
        }
        protected Expression<Func<T, bool>> GenerateConditions<T>(Dictionary<string, string> conditions, string fieldGroup)
        {
            //read query condition define
            var fields = Ctx.QueryConditions.Where(p => p.Group == fieldGroup).ToList();

            //read value from client conditions
            foreach (var condition in conditions)
            {
                SetValue(fields, condition.Key, condition.Value);
            }
            var businessCondigions = fields.Select(Mapper.Map<EntityFramework.QueryCondition, QueryConditionModel>).ToList();
            return BuildExpression<T>(businessCondigions);

        }
        private void SetValue(IList<EntityFramework.QueryCondition> conditions, string name, string value)
        {
            var field = conditions.FirstOrDefault(p => p.FieldName == name);
            if (field == null) return;
            field.Value = value;
        }
    }

调用示例:

        public IList<CustomerListModel> GetList(Dictionary<string,string> conditions, Pager pager)
        {
            try
            {
                var skip = (pager.PageIndex - 1) * pager.PageSize;

                var where = GenerateConditions<EntityFramework.Customer>(conditions, "CustomerQueryModel");
                var query = Ctx.Customers.Include("MemberCard").WhereIf<EntityFramework.Customer>(where, pager);
                var list = query.Skip(skip).Take(pager.PageSize).ToList();
                var ret = new List<CustomerListModel>();
                foreach (var customer in list)
                {
                    ret.Add(Mapper.Map<EntityFramework.Customer, CustomerListModel>(customer));
                }
                //OnMethodExecuted("GetList", "", "", LogType.Operate);
                return ret;
            }
            catch (Exception ex)
            {
                //OnErrorThrow(JsonConvert.SerializeObject(conditions), JsonConvert.SerializeObject(ex), ex.Message);
                throw ex;
            }
        }
原文地址:https://www.cnblogs.com/blackice/p/5798486.html