在前两篇文章中,我们已经实现了基于EF的数据库基本操作基类的构建,以及简单的介绍了如何方便的动态构建排序表达式,在第二篇文章结尾,我们遗漏下来了一个问题:如何方便的构建查询参数(即类似于这样的Expression<TEntity, bool> expression查询表达式)
在往常的经验中,我们知道在和数据库交互的过程中,查询可能是最复杂的,做过数据持久化封装的同学们可能对这个认识尤为突出,其他原因我们就不细说了, 如何丰富的,易用的构建查询条件这个就有点让人迷惑.
我们来分析一个常见的查询条件(a>4 and b<3)Or(d>5 and c in(3,4,5)),
- 设定:var exp1=a>4 and b<3; var exp2=d>5 and c in(3,4,5),则上述条件可以表示为 var exp=exp1 or exp2;
- 进一步分解exp1:var exp1_1=a>4; var exp1_2=b<3; 则exp1=exp1_1 and var exp_2
- 进一步分解exp2:var exp2_1=d>5; var exp2_2=c in (3,4,5); 则exp2=exp2_1 and exp2_2
有了上面的分解,我们发现其实构建这个查询条件非常符合我们常见的递归算法.下面我们尝试一下用递归的思想来实现这个.我们先大致的画个UML草图
为了不和系统下面的Expression 命名混淆,我们这里采用EFExpression作为条件表达式基类名称,
- EFExpression<T> 为一个抽象基类,里面主要有一个返回类型为 Expression<Func<T,bool>> GetExpression()的一个抽象方法,其具体实现,我们放在了每一个具体的Expression中去定义.
- EmptyEFExpression<T> 表示一个空的查询表达式
- BinaryEFExpression<T> 表示一个基于二元条件的查询表达式,如大于,小于,等于 ……
- LikeEFExpression<T> 表示一个用于创建”类似于”条件的查询表达式
- LogicEFExpression<T> 表示一个逻辑运算的查询表达式,如and or
- ….可能还会有其他子类
1.首先我们试着来编写一下抽象基类EFExpression<T>的实现
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293/// <summary>
/// 查询表达式基类
/// </summary>
public
abstract
class
EFExpression<T> where T :
class
{
/// <summary>
/// 获取查询表达式
/// </summary>
/// <returns></returns>
public
virtual
Expression<Func<T,
bool
>> GetExpression()
{
if
(Expression !=
null
)
{
var candidateExp = Expression.Parameter(
typeof
(T),
"x"
);
var exp = Expression.Lambda<Func<T,
bool
>>(Expression, candidateExp);
return
exp;
}
else
{
return
null
;
}
}
/// <summary>
/// 查询条件对应表达式
/// </summary>
protected
Expression _Expression {
get
;
set
; }
/// <summary>
/// 参数表达式
/// </summary>
public
ParameterExpression[] Parameters {
get
;
set
; }
/// <summary>
/// 获取对应的表达式
/// </summary>
internal
abstract
Expression Expression {
get
; }
/// <summary>
/// 获取表达式主题
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="P"></typeparam>
/// <param name="old"></param>
/// <param name="property"></param>
/// <returns></returns>
protected
static
Expression GetMemberExpression<T, P>(EFExpression<T> old, Expression<Func<T, P>> property)
where T :
class
{
if
(old.Parameters ==
null
|| old.Parameters.Length == 0)
{
old.Parameters = property.Parameters.ToArray();
return
property.Body;
}
ParameterExpressionVisitor visitor =
new
ParameterExpressionVisitor(old.Parameters[0]);
Expression memberExpr = visitor.ChangeParameter(property.Body);
return
memberExpr;
}
/// <summary>
/// 获取一个空的表达式
/// </summary>
/// <returns></returns>
public
Expression<Func<T,
bool
>> GetEmptyExpression()
{
return
(Expression<Func<T,
bool
>>)(f =>
true
);
}
/// <summary>
/// 两个条件进行And运算
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns></returns>
public
static
EFExpression<T>
operator
&(EFExpression<T> left, EFExpression<T> right)
{
return
new
LogicEFExpression<T>(left, ELogicType.And, right);
}
/// <summary>
/// 两个条件进行Or运行
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
/// <returns></returns>
public
static
EFExpression<T>
operator
|(EFExpression<T> left, EFExpression<T> right)
{
return
new
LogicEFExpression<T>(left, ELogicType.Or, right);
}
}
2. 接着我们来看一下如何实现这个BinaryEFExpression<T>
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586/// <summary>
/// 二元运算查询条件
/// </summary>
/// <typeparam name="T">查询条件实体类型</typeparam>
/// <typeparam name="TVal">需要比较的属性类型</typeparam>
internal
class
BinanryEFExpression<T, TVal> : EFExpression<T>
where T :
class
where TVal : IComparable
{
/// <summary>
/// 定义条件的实体属性
/// </summary>
private
Expression<Func<T, TVal>> property;
/// <summary>
/// 比较的值
/// </summary>
private
TVal val;
/// <summary>
/// 二元运算符
/// </summary>
private
EBinaryType binaryType;
/// <summary>
/// 实例化新的二元查询表达式
/// </summary>
/// <param name="property">定义条件的实体属性</param>
/// <param name="binaryType">二元运算符</param>
/// <param name="val">比较的值</param>
public
BinanryEFExpression(Expression<Func<T, TVal>> property, EBinaryType binaryType, TVal val)
{
if
(property ==
null
)
throw
new
ArgumentNullException(
"property"
);
//if (val == null && binaryType != EBinaryType.Like)
// throw new ArgumentNullException("val");
this
.property = property;
this
.val = val;
this
.binaryType = binaryType;
}
internal
override
Expression Expression
{
get
{
if
(_Expression ==
null
)
{
var propertyBody = GetMemberExpression(
this
, property);
Type type =
typeof
(TVal);
Expression compareVal = Expression.Constant(val);
//如果是Nullable类型,则把value转化成Nullable类型
if
(type.IsNullableType())
{
compareVal = Expression.Convert(compareVal, type);
}
Expression tempExp =
null
;
switch
(binaryType)
{
case
EBinaryType.Equal:
tempExp = Expression.Equal(propertyBody, compareVal);
break
;
case
EBinaryType.GreaterThan:
tempExp = Expression.GreaterThan(propertyBody, compareVal);
break
;
case
EBinaryType.GreaterThanOrEqual:
tempExp = Expression.GreaterThanOrEqual(propertyBody, compareVal);
break
;
case
EBinaryType.LessThan:
tempExp = Expression.LessThan(propertyBody, compareVal);
break
;
case
EBinaryType.LessThanOrEqual:
tempExp = Expression.LessThanOrEqual(propertyBody, compareVal);
break
;
case
EBinaryType.NotEqual:
tempExp = Expression.NotEqual(propertyBody, compareVal);
break
;
default
:
break
;
}
_Expression = tempExp;
}
return
_Expression;
}
}
}
3.我们在来看一下关于表达式逻辑运算的LogicEFExpression<T> 类又是如何实现的
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980/// <summary>
/// 带有逻辑运算的查询表达式
/// </summary>
/// <typeparam name="T"></typeparam>
public
class
LogicEFExpression<T> : EFExpression<T> where T :
class
{
private
EFExpression<T> left;
private
EFExpression<T> right;
private
ELogicType logicType;
/// <summary>
/// 实例化新的逻辑运算查询表达式
/// </summary>
/// <param name="left"></param>
/// <param name="logicType">逻辑运算类型</param>
/// <param name="right"></param>
public
LogicEFExpression(EFExpression<T> left, ELogicType logicType, EFExpression<T> right)
{
if
(left ==
null
|| right ==
null
)
throw
new
ArgumentNullException(
"left 和 right 不能同时为空"
);
this
.left = left;
this
.right = right;
this
.logicType = logicType;
}
public
override
Expression<Func<T,
bool
>> GetExpression()
{
if
(left ==
null
)
return
right.GetExpression();
else
if
(right ==
null
)
return
left.GetExpression();
else
{
//判断进行运算的两个条件是否为空
if
(left
is
EmptyEFExpression<T> && right
is
EmptyEFExpression<T>)
return
left.GetExpression();
else
if
(left
is
EmptyEFExpression<T>)
return
right.GetExpression();
else
if
(right
is
EmptyEFExpression<T>)
return
left.GetExpression();
var leftExp = left.GetExpression();
var rightExp = right.GetExpression();
Expression<Func<T,
bool
>> exp =
null
;
if
(leftExp ==
null
&& rightExp ==
null
)
return
new
EmptyEFExpression<T>().GetExpression();
else
{
if
(leftExp ==
null
)
return
rightExp;
else
if
(rightExp ==
null
)
return
leftExp;
else
{
switch
(logicType)
{
case
ELogicType.And:
exp = leftExp.And(rightExp);
break
;
case
ELogicType.Or:
exp = leftExp.Or(rightExp);
break
;
default
:
break
;
}
}
}
return
exp;
}
}
internal
override
Expression Expression
{
get
{
return
null
;
}
}
}
4.为了使用方便,我们在基类中再添加个静态方法
12345678910111213/// <summary>
/// 构建一个二元查询表达式
/// </summary>
/// <typeparam name="TVal">比较值的类型</typeparam>
/// <param name="property">定义条件的实体属性</param>
/// <param name="binaryType">运算符类型</param>
/// <param name="val">比较的值</param>
/// <returns></returns>
public
static
EFExpression<T> CreateBinaryExpression<TVal>(Expression<Func<T, TVal>> property,
EBinaryType binaryType, TVal val) where TVal : IComparable
{
return
new
BinanryEFExpression<T, TVal>(property, binaryType, val);
}
5.有了上面的实现,我们来测试下我们的想法.为了让我们更容易理解,我们假设在一个订单管理环境中,有如下一个需求:请查询满足以下任意一个条件中的订单记录
- 订单状态还未送货且金额>50W的
- 订单状态为送货中,且客户地址在深圳片区的
为了便于理解,我们在这里先定义两个实体类
12345678910111213141516171819202122/// <summary>
/// 订单主表
/// </summary>
public
class
OrderMain
{
public
Guid Id {
get
;
set
; }
public
string
Code {
get
;
set
; }
public
DateTime BillDate {
get
;
set
; }
public
Guid CustomerId {
get
;
set
; }
public
virtual
Customer Customer {
get
;
set
; }
public
decimal
TotalAmount {
get
;
set
; }
public
int
Status {
get
;
set
; }
}
/// <summary>
/// 客户信息
/// </summary>
public
class
Customer
{
public
Guid Id {
get
;
set
; }
public
string
Name {
get
;
set
; }
public
string
Area {
get
;
set
; }
}
那么我们这里使用的查询条件可能就会写成如下形式:
1
2
3
4
5
6
7
8
9
10
11
|
var exp=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.Status,EBinaryType.Equal,0);
//还没有送货
exp&=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.TotalAmount ,EBinaryType.LessThan,500000);
//小于50W
var exp2=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.Status,EBinaryType.Equal,1);
//送货中
exp2&=EFExpression<OrderMain>.CreateBinaryExpression(o=>o.Customer.Area,EBinaryType.Equal,”0755”);
//地址在深圳片区
exp|=exp2;
var queryExpression=exp.GetExpression();
|
测试通过.,至于其他的子类在此就不一一列举了,当然我们可以通过扩展一些写法使之更容易使用,比如
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152/// <summary>
/// 在当前条件基础上,构建一个like条件,并且和当前条件产生And运算
/// </summary>
/// <param name="property"></param>
/// <param name="val"></param>
/// <returns></returns>
public
static
EFExpression<T> AndLike<T>(
this
EFExpression<T> old, Expression<Func<T,
string
>> property,
string
val) where T :
class
{
var temp =
new
LikeEFExpression<T>(property, val);
if
(old ==
null
)
return
temp;
else
return
new
LogicEFExpression<T>(old, ELogicType.And, temp);
}
/// <summary>
/// 在当前条件基础上,构建一个等于条件,并且和当前条件产生And运算
/// </summary>
/// <typeparam name="TVal"></typeparam>
/// <param name="old"></param>
/// <param name="property"></param>
/// <param name="val"></param>
/// <returns></returns>
public
static
EFExpression<T> AndEqual<T, TVal>(
this
EFExpression<T> old, Expression<Func<T, TVal>> property, TVal val)
where TVal : IComparable
where T :
class
{
var temp =
new
BinanryEFExpression<T, TVal>(property, EBinaryType.Equal, val);
if
(old ==
null
)
return
temp;
else
return
new
LogicEFExpression<T>(old, ELogicType.And, temp);
}
/// <summary>
/// 在当前条件基础上,构建一个大于条件,并且和当前条件产生And运算
/// </summary>
/// <typeparam name="TVal"></typeparam>
/// <param name="old"></param>
/// <param name="property"></param>
/// <param name="val"></param>
/// <returns></returns>
public
static
EFExpression<T> AndGreaterThan<T, TVal>(
this
EFExpression<T> old, Expression<Func<T, TVal>> property, TVal val)
where TVal : IComparable
where T :
class
{
var temp =
new
BinanryEFExpression<T, TVal>(property, EBinaryType.GreaterThan, val);
if
(old ==
null
)
return
temp;
else
return
new
LogicEFExpression<T>(old, ELogicType.And, temp);
}
那么我们在使用的时候就可以方便的写成下面这种形式
var exp=EFExpression<OrderMain>.Create().AndEqual(o=>o.Status,0).AndGreaterThan(o=>o.TotalAmount ,500000)…..
在此不再赘述,有兴趣的朋友们可以自己去写出类似的东东,或问我要源码包...
http://www.cnblogs.com/xie-zhonglai/archive/2012/04/10/2440569.html