• SqlHelper简单实现(通过Expression和反射)5.Lambda表达式解析类


    这个ExpressionHelper类,是整个SqlHelper中,最核心的一个类,主要功能就是将Lambda表达式转换为Sql语句。同时这个转换过程比较复杂,所以下面详细讲解一下思路和这个类的作用。

    0x01基本

    在Sql语句中,主要由表名,字段,条件语句等元素组成,而这些元素中,表名,字段可以由实体表示出来,条件语句可以由Lambda表达式表现出来。

    在Sql语句中,条件语句可以说是Select中的核心,其中很多功能和Lambda中提供的方法很相似,比如:

    实体:

     1     [TableName("RA_MyBlog_Article")]
     2     public class ArticleEntity
     3     {
     4         [Primary]
     5         [Identity]
     6         //文章ID
     7         public int articleID { get; set; }
     8         //分类ID
     9         public int categoryID { get; set; }
    10         //文章标题
    11         public string articleTitle { get; set; }
    12         //文章版权
    13         public string articleCopyright { get; set; }
    14         //文章创建时间
    15         public DateTime articleDate { get; set; }
    16         //文章摘要
    17         public string articleAbstract { get; set; }
    18         //文章内容
    19         public string articleContain { get; set; }
    20         //文章所属User
    21         public int userID { get; set; }
    22     }

    Lambda表达式 Sql表达式
    GetList<ArticleEntity>(a=>a.articleID > 1 && a.userID = 1) Select * From RA_MyBlog_Article Where articleID > 1 And userID = 1
    GetList<ArticleEntity>(a=>list.Contains(a.articleID)) Select * From RA_MyBlog_Article Where articleID In [list中的元素]
    Join<UserEntity,ArticleEntity>((a,b)=>a.UserID == b.UserID) Select * From RA_MyBlog_Article,RA_MyBlog_User Join RA_MyBlog_User On (RA_MyBlog_Article.UserID = RA_MyBlog_User.UserID )

    从上表可以看出,由Lambda表达式转换为Sql表达式是完全可能的。

    0x02 Lambda表达式

    Lambda设计到的内容比较多,大家有兴趣可以去百度找一下这方面的介绍,这里只介绍一些涉及到的部分:

    以上面的表达式GetList<ArticleEntity>(a=>a.articleID > 1 && a.userID = 1)为例,其中a=>a.articleID > 1 && a.userID = 1这部分是我们需要的部分。在C#中,由Expression对象负责对Lambda表达式的解析和处理。Expression的子类有很多,分布在System.Linq.Expressions命名空间下,这里用到的有

    UnaryExpression:一元表达式,比如取反'!'

    ConstantExpression:常量表达式,比如1

    MemberExpression:成员表达式,一般为变量,比如a.articleID

    MethodCallExpression:函数表达式,比如Contains()

    BinaryExpression:二元表达式,比如a.articleID > 1

    对于这个例子:a.articleID > 1 && a.userID = 1,整体是一个与类型的二元表达式,左元素为a.articleID > 1,是一个Larger类型的二元表达式,其中左元素是成员表达式,右元素是常量表达式。右元素为a.userID = 1,是一个相等类型的二元表达式,其中左元素是成员表达式,右元素是常量表达式。

    2.用到的枚举,没啥可说的,包含了Lambda表达式中常见的元素类型。

     1 namespace RA.DataAccess.Enumerations
     2 {
     3     public enum EnumNodeType
     4     {
     5         [Description("二元运算符")]
     6         BinaryOperator = 1,
     7         [Description("一元运算符")]
     8         UndryOperator = 2,
     9         [Description("常量表达式")]
    10         Constant = 3,
    11         [Description("成员(变量)")]
    12         MemberAccess = 4,
    13         [Description("函数")]
    14         Call = 5,
    15         [Description("未知")]
    16         Unknown = -99,
    17         [Description("不支持")]
    18         NotSupported = -98
    19     }
    20 }

    0x03 功能部分

    1.判断表达式类型

     1         /// <summary>
     2         /// 判断表达式类型
     3         /// </summary>
     4         /// <param name="func">lambda表达式</param>
     5         /// <returns></returns>
     6         private static EnumNodeType CheckExpressionType(Expression func)
     7         {
     8             switch (func.NodeType)
     9             {
    10                 case ExpressionType.AndAlso:
    11                 case ExpressionType.OrElse:
    12                 case ExpressionType.Equal:
    13                 case ExpressionType.GreaterThanOrEqual:
    14                 case ExpressionType.LessThanOrEqual:
    15                 case ExpressionType.GreaterThan:
    16                 case ExpressionType.LessThan:
    17                 case ExpressionType.NotEqual:
    18                     return EnumNodeType.BinaryOperator;
    19                 case ExpressionType.Constant:
    20                     return EnumNodeType.Constant;
    21                 case ExpressionType.MemberAccess:
    22                     return EnumNodeType.MemberAccess;
    23                 case ExpressionType.Call:
    24                     return EnumNodeType.Call;
    25                 case ExpressionType.Not:
    26                 case ExpressionType.Convert:
    27                     return EnumNodeType.UndryOperator;
    28                 default:
    29                     return EnumNodeType.Unknown;
    30             }
    31         }

    2.判断一元表达式:

     1         /// <summary>
     2         /// 判断一元表达式
     3         /// </summary>
     4         /// <param name="func"></param>
     5         /// <returns></returns>
     6         private static string VisitUnaryExpression(UnaryExpression func)
     7         {
     8             var result = ExpressionTypeToString(func.NodeType);
     9             var funcType = CheckExpressionType(func.Operand);
    10             switch (funcType)
    11             {
    12                 case EnumNodeType.BinaryOperator:
    13                     return result + VisitBinaryExpression(func.Operand as BinaryExpression);
    14                 case EnumNodeType.Constant:
    15                     return result + VisitConstantExpression(func.Operand as ConstantExpression);
    16                 case EnumNodeType.Call:
    17                     return result + VisitMethodCallExpression(func.Operand as MethodCallExpression);
    18                 case EnumNodeType.UndryOperator:
    19                     return result + VisitUnaryExpression(func.Operand as UnaryExpression);
    20                 case EnumNodeType.MemberAccess:
    21                     return result + VisitMemberAccessExpression(func.Operand as MemberExpression);
    22                 default:
    23                     throw new NotSupportedException("不支持的操作在一元操作处理中:" + funcType.GetDescription());
    24             }
    25         }

    3.判断常量表达式:

     1         /// <summary>
     2         /// 判断常量表达式
     3         /// </summary>
     4         /// <param name="func"></param>
     5         /// <returns></returns>
     6         private static string VisitConstantExpression(ConstantExpression func)
     7         {
     8             if (func.Value.ToString() == "")
     9             {
    10                 return "\'\' ";
    11             }
    12             else if (func.Value.ToString() == "True")
    13             {
    14                 return "1 = 1 ";
    15             }
    16             else if (func.Value.ToString() == "False")
    17             {
    18                 return "0 = 1 ";
    19             }
    20             else
    21             {
    22                 return "'" + func.Value.ToString() + "' ";
    23                 
    24             }
    25         }

    4.判断变量表达式

     1         /// <summary>
     2         /// 判断包含变量的表达式
     3         /// </summary>
     4         /// <param name="func"></param>
     5         /// <returns></returns>
     6         private static string VisitMemberAccessExpression(MemberExpression func)
     7         {
     8             try
     9             {
    10                 var tablename = EntityHelper.GetTableName(func.Expression.Type);
    11                 return tablename + "." + func.Member.Name + " ";
    12             }catch
    13             {
    14                 object value;
    15                 switch (func.Type.Name)
    16                 {
    17                     case "Int32":
    18                     {
    19                         var getter = Expression.Lambda<Func<int>>(func).Compile();
    20                         value = getter();
    21                     }
    22                         break;
    23                     case "String":
    24                     {
    25                         var getter = Expression.Lambda<Func<string>>(func).Compile();
    26                         value = "'" + getter() + "'";
    27                     }
    28                         break;
    29                     case "DateTime":
    30                     {
    31                         var getter = Expression.Lambda<Func<DateTime>>(func).Compile();
    32                         value = "'" + getter() + "'";
    33                     }
    34                         break;
    35                     default:
    36                     {
    37                         var getter = Expression.Lambda<Func<object>>(func).Compile();
    38                         value = getter();
    39                     }
    40                         break;
    41                 }
    42                 return value.ToString();
    43             }
    44         }

    5.判断函数表达式:为了演示,此处这个表达式只支持Contains()函数,其他的函数可以按需添加。

     1         /// <summary>
     2         /// 判断包含函数的表达式
     3         /// </summary>
     4         /// <param name="func"></param>
     5         /// <returns></returns>
     6         private static String VisitMethodCallExpression(MethodCallExpression func)
     7         {
     8             if (func.Method.Name.Contains("Contains"))
     9             {
    10                 //获得调用者的内容元素
    11                 var getter = Expression.Lambda<Func<object>>(func.Object).Compile();
    12                 var data = getter() as IEnumerable;
    13                 //获得字段
    14                 var caller = func.Arguments[0];
    15                 while (caller.NodeType == ExpressionType.Call)
    16                 {
    17                     caller = (caller as MethodCallExpression).Object;
    18                 }
    19                 var field = VisitMemberAccessExpression(caller as MemberExpression);
    20                 var list = (from object i in data select "'" + i + "'").ToList();
    21                 return field + " IN (" + string.Join(",", list.Cast<string>().ToArray()) + ") ";
    22             }
    23             else
    24             {
    25                 throw new NotSupportedException("不支持的函数操作:" + func.Method.Name);
    26             }
    27         }

    6.判断二元表达式:二元表达式一般由其他表达式组成,有时还会有嵌套的情况,所以此处使用递归来解析。

     1         /// <summary> 
     2         /// 判断包含二元运算符的表达式
     3         /// </summary>
     4         /// <remarks>注意,这个函数使用了递归,修改时注意不要修改了代码顺序和逻辑</remarks>
     5         /// <param name="func"></param>
     6         private static string VisitBinaryExpression(BinaryExpression func)
     7         {
     8             var result = "(";
     9             var leftType = CheckExpressionType(func.Left);
    10             switch (leftType)
    11             {
    12                 case EnumNodeType.BinaryOperator:
    13                     result += VisitBinaryExpression(func.Left as BinaryExpression);break;
    14                 case EnumNodeType.Constant:
    15                     result += VisitConstantExpression(func.Left as ConstantExpression);break;
    16                 case EnumNodeType.MemberAccess:
    17                     result += VisitMemberAccessExpression(func.Left as MemberExpression);break;
    18                 case EnumNodeType.UndryOperator:
    19                     result += VisitUnaryExpression(func.Left as UnaryExpression);break;
    20                 case EnumNodeType.Call:
    21                     result += VisitMethodCallExpression(func.Left as MethodCallExpression);break;
    22                 default:
    23                     throw new NotSupportedException("不支持的操作在二元操作处理中:" + leftType.GetDescription());
    24             }
    25 
    26             result += ExpressionTypeToString(func.NodeType) + " ";
    27 
    28             var rightType = CheckExpressionType(func.Right);
    29             switch (rightType)
    30             {
    31                 case EnumNodeType.BinaryOperator:
    32                     result += VisitBinaryExpression(func.Right as BinaryExpression); break;
    33                 case EnumNodeType.Constant:
    34                     result += VisitConstantExpression(func.Right as ConstantExpression); break;
    35                 case EnumNodeType.MemberAccess:
    36                     result += VisitMemberAccessExpression(func.Right as MemberExpression); break;
    37                 case EnumNodeType.UndryOperator:
    38                     result += VisitUnaryExpression(func.Right as UnaryExpression); break;
    39                 case EnumNodeType.Call:
    40                     result += VisitMethodCallExpression(func.Right as MethodCallExpression); break;
    41                 default:
    42                     throw new NotSupportedException("不支持的操作在二元操作处理中:" + rightType.GetDescription());
    43             }
    44 
    45             result += ") ";
    46             return result;
    47         }

    7.将Lambda表达式转换为Sql语句。整个类的入口点:

     1         /// <summary>
     2         /// 通过Lambda解析为Sql
     3         /// </summary>
     4         /// <param name="func"></param>
     5         /// <returns></returns>
     6         public static string GetSqlByExpression(Expression func)
     7         {
     8             var funcType = CheckExpressionType(func);
     9             switch (funcType)
    10             {
    11                 case EnumNodeType.BinaryOperator:
    12                     return FormatSqlExpression(VisitBinaryExpression(func as BinaryExpression));
    13                 case EnumNodeType.Constant:
    14                     return FormatSqlExpression(VisitConstantExpression(func as ConstantExpression));
    15                 case EnumNodeType.Call:
    16                     return FormatSqlExpression(VisitMethodCallExpression(func as MethodCallExpression));
    17                 case EnumNodeType.UndryOperator:
    18                     return FormatSqlExpression(VisitUnaryExpression(func as UnaryExpression));
    19                 case EnumNodeType.MemberAccess:
    20                     return FormatSqlExpression(VisitMemberAccessExpression(func as MemberExpression));
    21                 default:
    22                     throw new NotSupportedException("不支持的操作在表达式处理中:" + funcType.GetDescription());
    23             }
    24         }
  • 相关阅读:
    QT删除QWidget或QLayout下的控件
    OSG HUD实现类似ArcGIS制图中图例效果(色块+标注)
    QT简易画板练习
    展望
    打字比赛日前
    学习的第一天
    SpringBoot之CommandLineRunner启动执行任务
    java根据ip获取城市
    springboot项目中, 同一浏览器JSESSIONID互相覆盖问题
    eclipse的spring项目中,alt+/无提示或按多次才正确提示
  • 原文地址:https://www.cnblogs.com/kakura/p/6108950.html
Copyright © 2020-2023  润新知