List<T>列表通用过滤
List<T>列表通用过滤模块设计
需求描述
数据列表如List<Customer> 加载到DataGridView后,如果记录比较多可能需要对其进行二次过滤,即客户端过滤
过滤条件做成可由用户设置的,如下图:
在数据源是DataTable时,使用DataView的RowFilter可以轻松按用户的配置拼接出过滤表达式字符串来,
设置RowFilter就可以实现过滤效果,但是当数据源是List<T>这样由EF,Linq to sql 等框架返回的集合时要实现上面的功能就需要费点力气了。
首先参考上面的截图,用户设置好过滤条件后会形成:" (工号 = 222 And 部门=人力) Or 性别=女" 这样的过滤表达式,可以表示成(Exp1 And Exp2) Or Exp3 这样的形式.针对"工号=222"这样的Exp求值我们会转变成针对Employe实体的EmpId属性是否等于222的判断(Employe.EmpId==222),这个可以通过反射方式来实现,将多个Exp求值的结果通过And或Or连接并运算得出最终结果,True表示这一行(Employe)符合.
不过考虑Exp1 Or Exp2 Or Exp3 这样的条件,如果第一个Exp1是True的话结果必定是True,这个时候还去计算Exp2,Exp3是完全多余的,如果List集合有几万条记录(当然超过几千行的列表对用户来说是没有多少意义的,一般人不会看那么多行,这个时候应该想想过滤条件设置是否合理)那么针对列表的每个实体的每个属性(字段)使用反射的方式计算一遍Exp将是一个比较大的开销,好在And与Or跟算术操作符(+,-,*,/)有所不同,And运算时只要两个操作数中有一个是False就没必要计算另外一个操作数(这里的是Exp)而Or在一个操作数是True时就可以忽略另一个操作数。不过当所的Exp都是false时针对上面"Exp1 Or Exp2 Or Exp3"这样的表达式计算每个Exp是不可避免的
到这里我们可以看到该问题的本质就是表达式求值,而操作符只限And与Or两个二元操作符,最后结果是True或False.
首先我们将用户设置的过滤表达式转变成逆波兰式(后缀表达式),接着传入每个要判断的实体,使用后缀表达式求出该实体是否符合过滤条件,
当然我们也可以将后缀表达式构建成Expression树,接着将该Expression编译成动态方法(委托),使用该委托对每个实体做出判断,下面的代码给出了这两种实现,
经过测试发现两种方法速度区别不大。
自己对逆波兰式求值时需要下面的判定表,如果构建Expression树则Expression.AndAlso或Expression.OrElse会自己判断是否对两个操作数都进行计算(参考下面的代码)
代码:
View Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
namespace FIStudio.WinUI.Core
{
public class ExpParser
{
public ExpParser(List<Elem> midfixList)
{
this .MidfixList = midfixList;
}
public List<Elem> PostfixList { get ; private set ; }
public List<Elem> MidfixList { get ; private set ; }
private Stack<Elem> CalcStack = new Stack<Elem> ();
private void GuardMidfixListExist()
{
if (MidfixList == null || MidfixList.Count <= 0 ) throw new Exception( " 中序列表为null或为空! " );
}
private void EnsurePostfixReady()
{
if (PostfixList == null )
{
PostfixList = DoParse(MidfixList);
if (PostfixList == null || PostfixList.Count <= 0 ) throw new Exception( " 后序列表为null或为空! " );
}
}
/// <summary>
/// 判断元素是否符合要求
/// </summary>
/// <param name="ent"></param>
/// <returns></returns>
public bool IsSatisfy( object ent)
{
GuardMidfixListExist();
EnsurePostfixReady();
CalcStack.Clear();
foreach ( var item in PostfixList)
{
if (item is ExpElem)
{
CalcStack.Push(item);
continue ;
}
#region And 运算
if (item is AndElem)
{
var op1 = CalcStack.Pop() as ExpElem;
var op2 = CalcStack.Pop() as ExpElem;
// 任意一个是false则直接压入false
if (op1.Result == false || op2.Result == false )
{
CalcStack.Push( new ExpElem() { Result = false });
continue ;
}
if (!op1.Result.HasValue && ! op2.Result.HasValue)
{
op1.Compare(ent);
if (op1.Result.Value == false )
{
CalcStack.Push( new ExpElem() { Result = false });
continue ;
}
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
continue ;
}
if (!op1.Result.HasValue && op2.Result == true )
{
op1.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op1.Result });
continue ;
}
if (op1.Result == true && ! op2.Result.HasValue)
{
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
continue ;
}
if (op1.Result == true && op2.Result == true )
{
CalcStack.Push( new ExpElem() { Result = true });
continue ;
}
}
#endregion
#region Or 运算
if (item is OrElem)
{
var op1 = CalcStack.Pop() as ExpElem;
var op2 = CalcStack.Pop() as ExpElem;
// 任意一个是true则直接压入true
if (op1.Result == true || op1.Result == true )
{
CalcStack.Push( new ExpElem() { Result = true });
continue ;
}
if (!op1.Result.HasValue && ! op2.Result.HasValue)
{
op1.Compare(ent);
if (!op1.Result == true )
{
CalcStack.Push( new ExpElem() { Result = true });
continue ;
}
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
}
if (!op1.Result.HasValue && op2.Result == false )
{
op1.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op1.Result });
continue ;
}
if (op1.Result == false && ! op2.Result.HasValue)
{
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
continue ;
}
if (op1.Result == false && op2.Result == false )
{
CalcStack.Push( new ExpElem() { Result = false });
}
}
#endregion
}
return (CalcStack.Pop() as ExpElem).Result.Value;
}
/// <summary>
/// 生成判断函数
/// </summary>
/// <returns></returns>
public Expression<Func<T, bool >> GenIsSatisfyFunc<T> ()
{
GuardMidfixListExist();
EnsurePostfixReady();
Stack < object > stack = new Stack< object > ();
ParameterExpression entExp = Expression.Parameter( typeof (T), " ent " );
foreach ( var elem in PostfixList)
{
if (elem is ExpElem)
{
stack.Push(elem);
continue ;
}
if (elem is AndElem)
{
var elem1 = stack.Pop();
var elem2 = stack.Pop();
var exp= Expression.AndAlso(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
stack.Push(exp);
continue ;
}
if (elem is OrElem)
{
var elem1 = stack.Pop();
var elem2 = stack.Pop();
var exp= Expression.OrElse(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
stack.Push(exp);
continue ;
}
}
LambdaExpression lambda = Expression.Lambda<Func<T, bool >>( stack.Pop() as Expression,entExp);
return lambda as Expression<Func<T, bool >> ;
}
private Expression GetCallExpression( object elem, ParameterExpression entExp)
{
if (elem is ExpElem)
{
return Expression.Call(Expression.Constant(elem), typeof (ExpElem).GetMethod( " Compare " ), entExp);
}
return elem as Expression;
}
/// <summary>
/// 中序表达式转后缀表达式
/// </summary>
/// <param name="midfix"></param>
/// <returns></returns>
private List<Elem> DoParse(List<Elem> midfix)
{
Stack <Elem> stack = new Stack<Elem> ();
var list= new List<Elem> ();
foreach ( var elem in midfix)
{
if (elem is ExpElem)
{
list.Add(elem);
continue ;
}
if (elem is LBElem)
{
stack.Push(elem);
continue ;
}
if (elem is RBElem)
{
var e = stack.Pop();
while (!(e is LBElem))
{
list.Add(e);
e = stack.Pop();
}
continue ;
}
if ((elem is AndElem) || (elem is OrElem))
{
if (stack.Count > 0 )
{
var e = stack.Peek();
while ( !(e is LBElem) && elem.Priority <= e.Priority)
{
list.Add(stack.Pop());
if (stack.Count <= 0 ) break ;
e = stack.Peek();
}
}
stack.Push(elem);
}
}
while (stack.Count > 0 )
{
list.Add(stack.Pop());
}
return list;
}
}
#region 节点定义
public class Elem
{
public virtual string Name { get ; set ; }
public virtual int Priority { get ; set ; }
public Object Data { get ; set ; }
}
/// <summary>
/// 左括号
/// 注意stack中只会压入'(','And','Or'
/// </summary>
public class LBElem : Elem
{
public override string Name
{
get
{
return " ( " ;
}
}
public override int Priority
{
get
{
return 59 ;
}
}
}
/// <summary>
/// 右括号
/// </summary>
public class RBElem : Elem
{
public override string Name
{
get
{
return " ) " ;
}
}
public override int Priority
{
get
{
return 99 ;
}
}
}
public class AndElem : Elem
{
public override string Name
{
get
{
return " And " ;
}
}
public override int Priority
{
get
{
return 88 ;
}
}
}
public class OrElem : Elem
{
public override string Name
{
get
{
return " Or " ;
}
}
public override int Priority
{
get
{
return 77 ;
}
}
}
public class ExpElem : Elem
{
public override int Priority
{
get
{
return 66 ;
}
}
public bool Compare( object ent)
{
Console.WriteLine( " 计算了: " + Name);
bool ? ret= null ;
if (AssertType == Core.CompareType.Equal)
{
ret = string .Compare(GetV(ent),Value, true )== 0 ;
}
if (AssertType == Core.CompareType.NotEqual)
{
ret = string .Compare(GetV(ent), Value, true ) != 0 ;
}
if (AssertType == Core.CompareType.Greate)
{
ret = string .Compare(GetV(ent), Value, true ) > 0 ;
}
if (AssertType == Core.CompareType.GreateOrEqual)
{
ret = string .Compare(GetV(ent), Value, true ) >= 0 ;
}
if (AssertType == Core.CompareType.Less)
{
ret = string .Compare(GetV(ent), Value, true ) < 0 ;
}
if (AssertType == Core.CompareType.LessOrEqual)
{
ret = string .Compare(GetV(ent), Value, true ) <= 0 ;
}
if (AssertType == Core.CompareType.Contains)
{
ret = GetV(ent).Contains(Value);
}
if (AssertType == Core.CompareType.NoContains)
{
ret =! GetV(ent).Contains(Value);
}
if (AssertType == Core.CompareType.StartWith)
{
ret = GetV(ent).StartsWith(Value);
}
if (AssertType == Core.CompareType.EndWith)
{
ret = GetV(ent).EndsWith(Value);
}
if (!ret.HasValue) throw new Exception( " 未知的CompareType! " );
Result = ret;
return ret.Value;
}
public bool ? Result { get ; set ; }
public PropertyInfo Property { get ; set ; }
public CompareType AssertType { get ; set ; }
public string Value { get ; set ; }
private string GetV( object ent)
{
var tmp= Property.GetValue(ent, null );
if (tmp == null ) tmp = string .Empty;
return tmp.ToString();
}
}
public enum CompareType { Equal, NotEqual, Less, LessOrEqual, Greate, GreateOrEqual, Contains, NoContains, StartWith, EndWith };
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Reflection;
namespace FIStudio.WinUI.Core
{
public class ExpParser
{
public ExpParser(List<Elem> midfixList)
{
this .MidfixList = midfixList;
}
public List<Elem> PostfixList { get ; private set ; }
public List<Elem> MidfixList { get ; private set ; }
private Stack<Elem> CalcStack = new Stack<Elem> ();
private void GuardMidfixListExist()
{
if (MidfixList == null || MidfixList.Count <= 0 ) throw new Exception( " 中序列表为null或为空! " );
}
private void EnsurePostfixReady()
{
if (PostfixList == null )
{
PostfixList = DoParse(MidfixList);
if (PostfixList == null || PostfixList.Count <= 0 ) throw new Exception( " 后序列表为null或为空! " );
}
}
/// <summary>
/// 判断元素是否符合要求
/// </summary>
/// <param name="ent"></param>
/// <returns></returns>
public bool IsSatisfy( object ent)
{
GuardMidfixListExist();
EnsurePostfixReady();
CalcStack.Clear();
foreach ( var item in PostfixList)
{
if (item is ExpElem)
{
CalcStack.Push(item);
continue ;
}
#region And 运算
if (item is AndElem)
{
var op1 = CalcStack.Pop() as ExpElem;
var op2 = CalcStack.Pop() as ExpElem;
// 任意一个是false则直接压入false
if (op1.Result == false || op2.Result == false )
{
CalcStack.Push( new ExpElem() { Result = false });
continue ;
}
if (!op1.Result.HasValue && ! op2.Result.HasValue)
{
op1.Compare(ent);
if (op1.Result.Value == false )
{
CalcStack.Push( new ExpElem() { Result = false });
continue ;
}
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
continue ;
}
if (!op1.Result.HasValue && op2.Result == true )
{
op1.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op1.Result });
continue ;
}
if (op1.Result == true && ! op2.Result.HasValue)
{
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
continue ;
}
if (op1.Result == true && op2.Result == true )
{
CalcStack.Push( new ExpElem() { Result = true });
continue ;
}
}
#endregion
#region Or 运算
if (item is OrElem)
{
var op1 = CalcStack.Pop() as ExpElem;
var op2 = CalcStack.Pop() as ExpElem;
// 任意一个是true则直接压入true
if (op1.Result == true || op1.Result == true )
{
CalcStack.Push( new ExpElem() { Result = true });
continue ;
}
if (!op1.Result.HasValue && ! op2.Result.HasValue)
{
op1.Compare(ent);
if (!op1.Result == true )
{
CalcStack.Push( new ExpElem() { Result = true });
continue ;
}
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
}
if (!op1.Result.HasValue && op2.Result == false )
{
op1.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op1.Result });
continue ;
}
if (op1.Result == false && ! op2.Result.HasValue)
{
op2.Compare(ent);
CalcStack.Push( new ExpElem() { Result = op2.Result });
continue ;
}
if (op1.Result == false && op2.Result == false )
{
CalcStack.Push( new ExpElem() { Result = false });
}
}
#endregion
}
return (CalcStack.Pop() as ExpElem).Result.Value;
}
/// <summary>
/// 生成判断函数
/// </summary>
/// <returns></returns>
public Expression<Func<T, bool >> GenIsSatisfyFunc<T> ()
{
GuardMidfixListExist();
EnsurePostfixReady();
Stack < object > stack = new Stack< object > ();
ParameterExpression entExp = Expression.Parameter( typeof (T), " ent " );
foreach ( var elem in PostfixList)
{
if (elem is ExpElem)
{
stack.Push(elem);
continue ;
}
if (elem is AndElem)
{
var elem1 = stack.Pop();
var elem2 = stack.Pop();
var exp= Expression.AndAlso(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
stack.Push(exp);
continue ;
}
if (elem is OrElem)
{
var elem1 = stack.Pop();
var elem2 = stack.Pop();
var exp= Expression.OrElse(GetCallExpression(elem1,entExp),GetCallExpression(elem2,entExp));
stack.Push(exp);
continue ;
}
}
LambdaExpression lambda = Expression.Lambda<Func<T, bool >>( stack.Pop() as Expression,entExp);
return lambda as Expression<Func<T, bool >> ;
}
private Expression GetCallExpression( object elem, ParameterExpression entExp)
{
if (elem is ExpElem)
{
return Expression.Call(Expression.Constant(elem), typeof (ExpElem).GetMethod( " Compare " ), entExp);
}
return elem as Expression;
}
/// <summary>
/// 中序表达式转后缀表达式
/// </summary>
/// <param name="midfix"></param>
/// <returns></returns>
private List<Elem> DoParse(List<Elem> midfix)
{
Stack <Elem> stack = new Stack<Elem> ();
var list= new List<Elem> ();
foreach ( var elem in midfix)
{
if (elem is ExpElem)
{
list.Add(elem);
continue ;
}
if (elem is LBElem)
{
stack.Push(elem);
continue ;
}
if (elem is RBElem)
{
var e = stack.Pop();
while (!(e is LBElem))
{
list.Add(e);
e = stack.Pop();
}
continue ;
}
if ((elem is AndElem) || (elem is OrElem))
{
if (stack.Count > 0 )
{
var e = stack.Peek();
while ( !(e is LBElem) && elem.Priority <= e.Priority)
{
list.Add(stack.Pop());
if (stack.Count <= 0 ) break ;
e = stack.Peek();
}
}
stack.Push(elem);
}
}
while (stack.Count > 0 )
{
list.Add(stack.Pop());
}
return list;
}
}
#region 节点定义
public class Elem
{
public virtual string Name { get ; set ; }
public virtual int Priority { get ; set ; }
public Object Data { get ; set ; }
}
/// <summary>
/// 左括号
/// 注意stack中只会压入'(','And','Or'
/// </summary>
public class LBElem : Elem
{
public override string Name
{
get
{
return " ( " ;
}
}
public override int Priority
{
get
{
return 59 ;
}
}
}
/// <summary>
/// 右括号
/// </summary>
public class RBElem : Elem
{
public override string Name
{
get
{
return " ) " ;
}
}
public override int Priority
{
get
{
return 99 ;
}
}
}
public class AndElem : Elem
{
public override string Name
{
get
{
return " And " ;
}
}
public override int Priority
{
get
{
return 88 ;
}
}
}
public class OrElem : Elem
{
public override string Name
{
get
{
return " Or " ;
}
}
public override int Priority
{
get
{
return 77 ;
}
}
}
public class ExpElem : Elem
{
public override int Priority
{
get
{
return 66 ;
}
}
public bool Compare( object ent)
{
Console.WriteLine( " 计算了: " + Name);
bool ? ret= null ;
if (AssertType == Core.CompareType.Equal)
{
ret = string .Compare(GetV(ent),Value, true )== 0 ;
}
if (AssertType == Core.CompareType.NotEqual)
{
ret = string .Compare(GetV(ent), Value, true ) != 0 ;
}
if (AssertType == Core.CompareType.Greate)
{
ret = string .Compare(GetV(ent), Value, true ) > 0 ;
}
if (AssertType == Core.CompareType.GreateOrEqual)
{
ret = string .Compare(GetV(ent), Value, true ) >= 0 ;
}
if (AssertType == Core.CompareType.Less)
{
ret = string .Compare(GetV(ent), Value, true ) < 0 ;
}
if (AssertType == Core.CompareType.LessOrEqual)
{
ret = string .Compare(GetV(ent), Value, true ) <= 0 ;
}
if (AssertType == Core.CompareType.Contains)
{
ret = GetV(ent).Contains(Value);
}
if (AssertType == Core.CompareType.NoContains)
{
ret =! GetV(ent).Contains(Value);
}
if (AssertType == Core.CompareType.StartWith)
{
ret = GetV(ent).StartsWith(Value);
}
if (AssertType == Core.CompareType.EndWith)
{
ret = GetV(ent).EndsWith(Value);
}
if (!ret.HasValue) throw new Exception( " 未知的CompareType! " );
Result = ret;
return ret.Value;
}
public bool ? Result { get ; set ; }
public PropertyInfo Property { get ; set ; }
public CompareType AssertType { get ; set ; }
public string Value { get ; set ; }
private string GetV( object ent)
{
var tmp= Property.GetValue(ent, null );
if (tmp == null ) tmp = string .Empty;
return tmp.ToString();
}
}
public enum CompareType { Equal, NotEqual, Less, LessOrEqual, Greate, GreateOrEqual, Contains, NoContains, StartWith, EndWith };
#endregion
}
参考:逆波兰式构建方法
1、从左至右扫描一中缀表达式。
2、若读取的是操作数,则判断该操作数的类型,并将该操作数存入操作数堆栈
3、若读取的是运算符
(1) 该运算符为左括号"(",则直接存入运算符堆栈。
(2) 该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止,此时抛弃该左括号。
(3) 该运算符为非括号运算符:
(a) 若运算符堆栈栈顶的运算符为左括号,则直接存入运算符堆栈。
(b) 若比运算符堆栈栈顶的运算符优先级高,则直接存入运算符堆栈。
(c) 若比运算符堆栈栈顶的运算符优先级低或相等,则输出栈顶运算符到操作数堆栈,
直至运算符栈栈顶运算符低于(不包括等于)该运算符优先级,或为左括号,并将当前运算符压入运算符堆栈。
4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。
1、循环扫描语法单元的项目。
2、如果扫描的项目是操作数,则将其压入操作数堆栈,并扫描下一个项目。
3、如果扫描的项目是一个二元运算符,则对栈的顶上两个操作数执行该运算。
4、如果扫描的项目是一个一元运算符,则对栈的最顶上操作数执行该运算。
5、将运算结果重新压入堆栈。
6、重复步骤2-5,堆栈中即为结果值。
字符串公式解析器——使用“逆波兰式算法”及C#实现
标签: EF , 布儿表达式解析
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息