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 = ,
GreaterThan = ,
GreaterThanOrEqual = ,
LessThan = ,
LessThanOrEqual = ,
Include = ,
}
public enum CompareDataType
{
Int = ,
String = ,
Double = ,
Decimal = ,
Float = ,
DateTime =
}
查询条件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 - ) * 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;
}
}
C# 构建动态Lambda表达式的更多相关文章
- SqlDataReader生成动态Lambda表达式
上一扁使用动态lambda表达式来将DataTable转换成实体,比直接用反射快了不少.主要是首行转换的时候动态生成了委托. 后面的转换都是直接调用委托,省去了多次用反射带来的性能损失. 今天在对Sq ...
- 生成动态Lambda表达式1
SqlDataReader生成动态Lambda表达式 上一扁使用动态lambda表达式来将DataTable转换成实体,比直接用反射快了不少.主要是首行转换的时候动态生成了委托. 后面的转换都是直接调 ...
- c# ef 排序字段动态,构建动态Lambda和扩展方法OrderBy
1.动态构建排序 Lambda /// <summary> /// 获取排序Lambda(如果动态排序,类型不同会导致转换失败) /// </summary> /// < ...
- EntityFramework使用动态Lambda表达式筛选数据
public static class PredicateBuilder { public static Expression<Func<T, bool>> True<T ...
- 动态LINQ(Lambda表达式)构建
using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; us ...
- 动态生成C# Lambda表达式
转载:http://www.educity.cn/develop/1407905.html,并整理! 对于C# Lambda的理解我们在之前的文章中已经讲述过了,那么作为Delegate的进化使用,为 ...
- Lambda表达式树构建(上)
概述 Lambda是C#常用的语句,采用委托等方式,来封装真实的代码块.Lambda其实就是语法糖,是一个匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量.它可 ...
- 动态Lambda表达式打印HelloWorld
最近在用C#与数据库打交道.开发过程中采用了ORM模型(以前是纯sql玩法,复杂的逻辑用存储过程做). 为了能通过配置文件动态地查询字段,也就是说需要能这样写: db.AsQuery<T> ...
- C# Lambda 表达式学习之(三):动态构建类似于 c => c.Age == null || c.Age > 18 的表达式
可能你还感兴趣: 1. C# Lambda 表达式学习之(一):得到一个类的字段(Field)或属性(Property)名,强类型得到 2. C# Lambda 表达式学习之(二):LambdaExp ...
随机推荐
- 53. Reverse Words in a String【easy】
Given an input string, reverse the string word by word. For example, Given s = "the sky is blue ...
- mysql 索引 大于等于 走不走索引 最左前缀
你可以认为联合索引是闯关游戏的设计 例如你这个联合索引是state/city/zipCode 那么state就是第一关 city是第二关, zipCode就是第三关 你必须匹配了第一关,才能匹配第二关 ...
- python模块之codecs: 自然语言编码转换
python对多国语言的处理是支持的很好的,它可以处理现在任意编码的字符,这里深入的研究一下python对多种不同语言的处理. 有一点需要清楚的是,当python要做编码转换的时候,会借助于内部 ...
- eclipse导入svn中的web工程,部署到tomcat时候,只有WEB-INF目录问题
eclipse版本不同,上传工程svn中的settings文件,容易导致别的版本的eclipse,tomcat启动失败
- Bootstrap学习笔记(4)--导航栏
相关类: nav, nav-pills, nav-tags, nav-stacked ul里使用,导航格胶囊,方片外观,堆叠外观 navbar, navbar-header, navbar-brand ...
- 幸好会java
转做android的可能性又往前增加了一分.
- [Linux内核]软中断、tasklet、工作队列
转自:http://www.cnblogs.com/li-hao/archive/2012/01/12/2321084.html 软中断.tasklet和工作队列并不是Linux内核中一直存在的机制, ...
- C++中类所占的存储空间
#include <iostream> using namespace std; class A { int m_a; int get() { return m_a; } virtual ...
- RSYNC在zabbix中的检查
RSYNC在zabbix中的检查 作者:高波 归档:学习笔记 2017/08/21 快捷键: Ctrl + 1 标题1 Ctrl + 2 标题2 Ctrl + 3 标题3 Ctr ...
- Enlish相关术语
APM 自动编程机(Automatic Programming Machine) 高级电源管理(Advanced Power Management) OSD 屏幕显示(On Screen Displa ...