表达式组装器代码分享
起因
某些业务,组装的表达式比较长,且、或、括号混杂一块难以立即理解:
public async Task<List<PatientMedicineScheduleInfo>> GetPatientMedicineSchedule(string inpatientID, string hospitalID, DateTime startDate, TimeSpan startTime, DateTime endDate, TimeSpan endTime)
{
return await _medicalDbContext.PatientMedicineScheduleInfos.Where(m =>
(
(m.ScheduleDate == startDate.Date && m.ScheduleTime >= startTime) || m.ScheduleDate > startDate.Date)
&& (m.ScheduleDate < endDate.Date || (m.ScheduleDate == endDate.Date && m.ScheduleTime <= endTime)
)
&& m.InpatientID == inpatientID && m.HospitalID == hospitalID
&& m.DeleteFlag != "*")
.OrderBy(m => m.ScheduleDate).ThenBy(m => m.ScheduleTime)
.ThenBy(m => m.GroupID).ThenBy(m => m.HISOrderSort)
.ToListAsync();
}
我们可以使用表达式树,来对表达式进行分割,提高可读性。通过封装表达式树,实现了表达式组装器ExpBuilder,先看使用:
使用方法
And、Or函数都是静态函数,可以直接通过ExpBuilder.xx来调用。
public async Task<List<PatientMedicineScheduleInfo>> GetPatientMedicineSchedule(string inpatientID, string hospitalID, DateTime startDate, TimeSpan startTime, DateTime endDate, TimeSpan endTime)
{
// 将表达式拆分为四个孙表达式
Expression<Func<PatientMedicineScheduleInfo, bool>> onDateAfterTime = (PatientMedicineScheduleInfo m) => m.ScheduleDate == startDate.Date && m.ScheduleTime >= startTime;
Expression<Func<PatientMedicineScheduleInfo, bool>> afterDate = (PatientMedicineScheduleInfo m) => m.ScheduleDate > startDate.Date;
Expression<Func<PatientMedicineScheduleInfo, bool>> onDateBeforeTime = (PatientMedicineScheduleInfo m) => m.ScheduleDate == endDate.Date && m.ScheduleTime <= endTime;
Expression<Func<PatientMedicineScheduleInfo, bool>> beforeDate = (PatientMedicineScheduleInfo m) => m.ScheduleDate < endDate.Date;
// 使用or函数,组装出两个子表达式
var onDateAfter = ExpBuilder.Or(onDateAfterTime, afterDate);
var onDateBefore = ExpBuilder.Or(onDateBeforeTime, beforeDate);
// 最后使用And组装两个子表达式
var dateBetween = ExpBuilder.And(onDateAfter, onDateBefore);
return await _medicalDbContext.PatientMedicineScheduleInfos
// 使用
.Where(dateBetween)
.Where(m => m.InpatientID == inpatientID && m.HospitalID == hospitalID && m.DeleteFlag != "*")
.OrderBy(m => m.ScheduleDate).ThenBy(m => m.ScheduleTime)
.ThenBy(m => m.GroupID).ThenBy(m => m.HISOrderSort)
.ToListAsync();
}
条件拼接
如果表达式本身是有某些条件才拼接的,可以使用IfAnd、IfOr
// 表达式后跟两个条件表达式,若均为false,则表达式恒返回true
var apInterventionPredicate = ExpBuilder.True<QuarterPlanWorkInfo>()
// 条件一:若excludeApInterventionIDs不为空,则根据其排除部分数据
.IfAnd(excludeApInterventionIDs.Length > 0, m => !excludeApInterventionIDs.Contains(m.APInterventionID))
// 条件二:若includeApInterventionID有值,则根据其筛选对应数据
.IfAnd(includeApInterventionID.HasValue, m => m.APInterventionID == includeApInterventionID.Value);
源代码
ExpBuilder.cs
public static class ExpBuilder
{
public static Expression<Func<T, bool>> True<T>()
{
return f => true;
}
public static Expression<Func<T, bool>> False<T>()
{
return f => false;
}
/// <summary>
/// 组合表达式
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="first">原表达式</param>
/// <param name="second">拼接表达式</param>
/// <param name="merge">合并方式函数</param>
/// <returns></returns>
private static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second,
Func<Expression, Expression, Expression> merge)
{
// build parameter map (from parameters of second to parameters of first)
var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with parameters from the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// apply composition of lambda expression bodies to parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
/// <summary>
/// 表达式且
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="first">原表达式</param>
/// <param name="second">拼接表达式</param>
/// <returns></returns>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
/// <summary>
/// 条件表达式且
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="first">原表达式</param>
/// <param name="condition">条件</param>
/// <param name="second">拼接表达式</param>
/// <returns></returns>
public static Expression<Func<T, bool>> IfAnd<T>(this Expression<Func<T, bool>> first, bool condition, Expression<Func<T, bool>> second)
{
return condition ? first.Compose(second, Expression.AndAlso) : first;
}
/// <summary>
/// 表达式或
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="first">原表达式</param>
/// <param name="second">拼接表达式</param>
/// <returns></returns>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
/// <summary>
/// 条件表达式或
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <param name="first">原表达式</param>
/// <param name="condition">条件</param>
/// <param name="second">拼接表达式</param>
/// <returns></returns>
public static Expression<Func<T, bool>> IfOr<T>(this Expression<Func<T, bool>> first, bool condition, Expression<Func<T, bool>> second)
{
return condition ? first.Compose(second, Expression.OrElse) : first;
}
}
ParameterRebinder.cs
public class ParameterRebinder : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> map;
public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
internal static Expression ReplaceParameters(
Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
if (map.TryGetValue(p, out ParameterExpression replacement)) p = replacement;
return base.VisitParameter(p);
}
}
单元测试
public class ExBuilderTest
{
/// <summary>
/// 且表达式
/// </summary>
[Fact]
public void AndTest()
{
Expression<Func<int, bool>> exp1 = (_) => true;
Expression<Func<int, bool>> exp2 = (_) => false;
var combined = exp1.And(exp2); // exp1 && exp2
Assert.False(combined.Compile()(0));
}
/// <summary>
/// 或表达式
/// </summary>
/// <example>
/// exp1 || exp2
/// </example>
[Fact]
public void OrTest()
{
Expression<Func<int, bool>> exp1 = (_) => true;
Expression<Func<int, bool>> exp2 = (_) => false;
var combined = exp1.Or(exp2); // exp1 || exp2
Assert.True(combined.Compile()(0));
}
/// <summary>
/// 条件且表达式
/// </summary>
/// <example>
/// if (condition) exp1 && exp2
/// </example>
[Fact]
public void IfAndTest()
{
Expression<Func<int, bool>> exp1 = (_) => true;
Expression<Func<int, bool>> exp2 = (_) => false;
var combined1 = exp1.IfAnd(true, exp2); // true, exp1 && exp2
Assert.False(combined1.Compile()(0));
var combined2 = exp1.IfAnd(false, exp2); // false, exp1
Assert.True(combined2.Compile()(0));
}
/// <summary>
/// 条件或表达式
/// </summary>
/// <example>
/// if (condition) exp1 || exp2
/// </example>
[Fact]
public void IfOrTest()
{
Expression<Func<int, bool>> exp1 = (_) => false;
Expression<Func<int, bool>> exp2 = (_) => true;
var combined1 = exp1.IfOr(true, exp2); // true, exp1 || exp2
Assert.True(combined1.Compile()(0));
var combined2 = exp1.IfOr(false, exp2); // false, exp1
Assert.False(combined2.Compile()(0));
}
/// <summary>
/// 且表达式与或表达式组合
/// </summary>
[Fact]
public void AndCombineOr()
{
Expression<Func<int, bool>> exp1 = (_) => false;
Expression<Func<int, bool>> exp2 = (_) => true;
Expression<Func<int, bool>> exp3 = (_) => true;
var combined1 = exp1.And(exp2).Or(exp3);
Assert.True(combined1.Compile()(0)); // (exp1 && exp2) || exp3
var combined2 = exp1.And(exp2.Or(exp3));
Assert.False(combined2.Compile()(0)); // exp1 && (exp2 || exp3)
}
}

浙公网安备 33010602011771号