好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

使用EF构建企业级应用(三)

使用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/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于使用EF构建企业级应用(三)的详细内容...

  阅读:48次