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

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

1.UI收集查询数据

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

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

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

5.执行查询返回数据。

大概流程如下图所示:

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

查询保存设置

  1. public interface IEntity
  2. {
  3. int Id { get; set; }
  4. }
  5. public class QueryCondition : IEntity
  6. {
  7. [Key]
  8. public int Id { get; set; }
  9. /// <summary>
  10. /// 条件分组:以此做为查询条件
  11. /// </summary>
  12. public string Group { get; set; }
  13. /// <summary>
  14. /// 字段名称
  15. /// </summary>
  16. public string FieldName { get; set; }
  17. public int CompareType { get; set; }
  18. public int CompareDataType { get; set; }
  19. public string Value { get; set; }
  20. }

查询条件DTO模型

  1. /// <summary>
  2. /// 查询结构
  3. /// </summary>
  4. public class QueryConditionModel
  5. {
  6. public string FieldName { get; set; }
  7. public CompareType Type { get; set; }
  8. public CompareDataType DataType { get; set; }
  9. public string Value { get; set; }
  10. }
  11. public enum CompareType
  12. {
  13. Equal = ,
  14. GreaterThan = ,
  15. GreaterThanOrEqual = ,
  16. LessThan = ,
  17. LessThanOrEqual = ,
  18. Include = ,
  19. }
  20. public enum CompareDataType
  21. {
  22. Int = ,
  23. String = ,
  24. Double = ,
  25. Decimal = ,
  26. Float = ,
  27. DateTime =
  28. }

查询条件DTO转换配置

  1. public class QueryConditionProfile : Profile
  2. {
  3.  
  4. [Obsolete("")]
  5. protected override void Configure()
  6. {
  7. CreateMap<QueryCondition, QueryConditionModel>()
  8. .ForMember(p => p.Type, opt =>
  9. {
  10. opt.MapFrom(k => (CompareType)k.CompareType);
  11. })
  12. .ForMember(p => p.DataType, opt =>
  13. {
  14. opt.MapFrom(k => (CompareDataType)k.CompareDataType);
  15. })
  16. ;
  17. }
  18. }

查询条件构建

  1. public class ServiceBase
  2. {
  3. protected XXXDbContext Ctx;
  4. /// <summary>
  5. /// 动态构建Lambda查询表达式
  6. /// </summary>
  7. /// <param name="searchItems"></param>
  8. /// <returns></returns>
  9. protected Expression<Func<T, bool>> BuildExpression<T>(IList<QueryConditionModel> searchItems)
  10. {
  11. var where = PredicateExtensionses.True<T>();
  12. if (!searchItems.Any()) return @where;
  13. foreach (var subitem in searchItems)
  14. {
  15. try
  16. {
  17. var field = subitem.FieldName;
  18. var compare = subitem.Type;
  19. var type = subitem.DataType;
  20. var value = subitem.Value;
  21. if (string.IsNullOrEmpty(field)) continue;
  22. if (string.IsNullOrEmpty(value)) continue;
  23. //构建Lambda表达式
  24. var parameter = Expression.Parameter(typeof(T), "p");
  25. Expression constant;
  26. //表达式左侧 like: p.Name
  27. var left = Expression.PropertyOrField(parameter, field);
  28. //表达式右侧,比较值, like '张三'
  29. var right = Expression.Constant(value);
  30. //比较表达式
  31. switch (compare)
  32. {
  33. case CompareType.GreaterThan:
  34. constant = Expression.GreaterThan(left, right);
  35. break;
  36. case CompareType.GreaterThanOrEqual:
  37. constant = Expression.GreaterThanOrEqual(left, right);
  38. break;
  39. case CompareType.LessThan:
  40. constant = Expression.LessThan(left, right);
  41. break;
  42. case CompareType.LessThanOrEqual:
  43. constant = Expression.LessThanOrEqual(left, right);
  44. break;
  45. case CompareType.Include:
  46. //like 查询,需要调用外部int或string的Contains方法
  47. var method = type == CompareDataType.Int
  48. ? typeof(int).GetMethod("Contains", new Type[] { typeof(int) })
  49. : typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
  50. constant = Expression.Call(left, method, right);
  51. break;
  52. case CompareType.Equal:
  53. default:
  54. constant = Expression.Equal(left, right);
  55. break;
  56. }
  57. var lambda = Expression.Lambda<Func<T, Boolean>>(constant, parameter);
  58. @where = @where.And(lambda);
  59. }
  60. catch (Exception ex)
  61. {
  62. //OnMethodExecuted(JsonConvert.SerializeObject(searchItems), JsonConvert.SerializeObject(ex), "",
  63. LogType.Error);
  64. }
  65.  
  66. }
  67. return @where;
  68. }
  69. protected Expression<Func<T, bool>> GenerateConditions<T>(Dictionary<string, string> conditions, string fieldGroup)
  70. {
  71. //read query condition define
  72. var fields = Ctx.QueryConditions.Where(p => p.Group == fieldGroup).ToList();
  73.  
  74. //read value from client conditions
  75. foreach (var condition in conditions)
  76. {
  77. SetValue(fields, condition.Key, condition.Value);
  78. }
  79. var businessCondigions = fields.Select(Mapper.Map<EntityFramework.QueryCondition, QueryConditionModel>).ToList();
  80. return BuildExpression<T>(businessCondigions);
  81.  
  82. }
  83. private void SetValue(IList<EntityFramework.QueryCondition> conditions, string name, string value)
  84. {
  85. var field = conditions.FirstOrDefault(p => p.FieldName == name);
  86. if (field == null) return;
  87. field.Value = value;
  88. }
  89. }

调用示例:

  1. public IList<CustomerListModel> GetList(Dictionary<string,string> conditions, Pager pager)
  2. {
  3. try
  4. {
  5. var skip = (pager.PageIndex - ) * pager.PageSize;
  6.  
  7. var where = GenerateConditions<EntityFramework.Customer>(conditions, "CustomerQueryModel");
  8. var query = Ctx.Customers.Include("MemberCard").WhereIf<EntityFramework.Customer>(where, pager);
  9. var list = query.Skip(skip).Take(pager.PageSize).ToList();
  10. var ret = new List<CustomerListModel>();
  11. foreach (var customer in list)
  12. {
  13. ret.Add(Mapper.Map<EntityFramework.Customer, CustomerListModel>(customer));
  14. }
  15. //OnMethodExecuted("GetList", "", "", LogType.Operate);
  16. return ret;
  17. }
  18. catch (Exception ex)
  19. {
  20. //OnErrorThrow(JsonConvert.SerializeObject(conditions), JsonConvert.SerializeObject(ex), ex.Message);
  21. throw ex;
  22. }
  23. }

C# 构建动态Lambda表达式的更多相关文章

  1. SqlDataReader生成动态Lambda表达式

    上一扁使用动态lambda表达式来将DataTable转换成实体,比直接用反射快了不少.主要是首行转换的时候动态生成了委托. 后面的转换都是直接调用委托,省去了多次用反射带来的性能损失. 今天在对Sq ...

  2. 生成动态Lambda表达式1

    SqlDataReader生成动态Lambda表达式 上一扁使用动态lambda表达式来将DataTable转换成实体,比直接用反射快了不少.主要是首行转换的时候动态生成了委托. 后面的转换都是直接调 ...

  3. c# ef 排序字段动态,构建动态Lambda和扩展方法OrderBy

    1.动态构建排序 Lambda /// <summary> /// 获取排序Lambda(如果动态排序,类型不同会导致转换失败) /// </summary> /// < ...

  4. EntityFramework使用动态Lambda表达式筛选数据

    public static class PredicateBuilder { public static Expression<Func<T, bool>> True<T ...

  5. 动态LINQ(Lambda表达式)构建

    using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; us ...

  6. 动态生成C# Lambda表达式

    转载:http://www.educity.cn/develop/1407905.html,并整理! 对于C# Lambda的理解我们在之前的文章中已经讲述过了,那么作为Delegate的进化使用,为 ...

  7. Lambda表达式树构建(上)

    概述 Lambda是C#常用的语句,采用委托等方式,来封装真实的代码块.Lambda其实就是语法糖,是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量.它可 ...

  8. 动态Lambda表达式打印HelloWorld

    最近在用C#与数据库打交道.开发过程中采用了ORM模型(以前是纯sql玩法,复杂的逻辑用存储过程做). 为了能通过配置文件动态地查询字段,也就是说需要能这样写: db.AsQuery<T> ...

  9. C# Lambda 表达式学习之(三):动态构建类似于 c => c.Age == null || c.Age > 18 的表达式

    可能你还感兴趣: 1. C# Lambda 表达式学习之(一):得到一个类的字段(Field)或属性(Property)名,强类型得到 2. C# Lambda 表达式学习之(二):LambdaExp ...

随机推荐

  1. action(三)

    CCSize boxSize = CCSizeMake(100.0f, 100.0f); CCLayerColor *box = CCLayerColor::create(ccc4(, , , )); ...

  2. 使用API Gateway

    http://dockone.io/article/482 [编者的话]本系列的第一篇介绍了微服务架构模式.它讨论了采用微服务的优点和缺点,除了一些复杂的微服务,这种模式还是复杂应用的理想选择. Do ...

  3. iptables安装失败后-------------firewalld回归

    yum install firewalld systemctl stop iptables; systemctl mask iptables; systemctl unmask firewalld s ...

  4. kafka启动及查看topic命令【已用过的,待更新】

    以下均为开发测试环境下: 启动Zookeeperbin/zookeeper-server-start.sh config/zookeeper.properties &启动kafkabin/ka ...

  5. oracle 远程登录

    打开命令终端,输入命令:sqlplus /nolog 输入命令:conn sys/sys@dba as sysdba

  6. 基于HTML5 Canvas生成粒子效果的人物头像

    前面我们分享过一个HTML5 Canvas实现的图像马赛克模糊效果,HTML5处理图片真的非常简单.今天我们要再利用HTML5 Canvas实现一个粒子效果的人物头像,你可以任意选择一张头像图片,接下 ...

  7. C语言 · 排列数

    算法提高 排列数   时间限制:1.0s   内存限制:256.0MB      问题描述 0.1.2三个数字的全排列有六种,按照字母序排列如下: 012.021.102.120.201.210 输入 ...

  8. LT和ET模式

    #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include &l ...

  9. Am335x 应用层之SPI操作

    SPI接口有四种不同的数据传输时序,取决于CPOL和CPHL这两位的组合.图1中表现了这四种时序, 时序与CPOL.CPHL的关系也可以从图中看出. 图1 CPOL是用来决定SCK时钟信号空闲时的电平 ...

  10. Redis学习笔记——数据类型及操作

    数据操作 redis是key-value的数据,所以每个数据都是一个键值对 键的类型是字符串 值的类型分为五种: 字符串string 哈希hash 列表list 集合set 有序集合zset 数据操作 ...