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         }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM