使用EF构建企业级应用(三)
使用EF构建企业级应用(三)
2012-04-10 14:42 by 谢中涞, 880 visits, 收藏 , 编辑
在前两篇文章中,我们已经实现了基于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>的实现
/// <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>
/// <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> 类又是如何实现的
/// <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.为了使用方便,我们在基类中再添加个静态方法
/// <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的 订单状态为送货中,且客户地址在深圳片区的为了便于理解,我们在这里先定义两个实体类
/// <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 ; }
}
那么我们这里使用的查询条件可能就会写成如下形式:
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();
测试通过. ,至于其他的子类在此就不一一列举了,当然我们可以通过扩展一些写法使之更容易使用,比如
/// <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
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://www.haodehen.cn/did49391