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;
            }
        }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM