说明

一个Orm自然不仅仅包含条件表达式,还会有如下的场景:

  1. OrderBy(s => s.StudentName)
  2. Select<StudentDto>(s => new StudentDto { s.Id, s.Name, SchoolName = s.School.Name})

而应用场景的不同,导致解析的方式也有所不同,在这里我们又定义了两个解析类:[ObjectMemberVisitor] 和 [SelectExpressionResolver]

[ObjectMemberVisitor] 主要用于从表达式中解析出参数的属性名,会自动忽略导航属性

[SelectExpressionResolver] 主要用于查询的Select方法,也是用于从表达式中解析出属性名,与ObjectMemberVisitor不同的是它不会忽略导航属性。

从下面的代码可以看出,两个类虽然功能类似,但是代码差异很大,主要是因为ObjectMemberVisitor的使用场景比较简单,只需要拿到表达式的Member(成员)就可以了,不必考虑太多。但SelectExpressionResolver不同,其解析结果需要反馈给查询工具更多信息,包括Member与Parameter的映射关系等。


对象表达式解析

  1. using System.Collections.Generic;
  2. using System.Linq.Expressions;
  3.  
  4. namespace MyOrm.Expressions
  5. {
  6. public class ObjectMemberVisitor : ExpressionVisitor
  7. {
  8. private readonly List<string> _propertyList;
  9.  
  10. public ObjectMemberVisitor()
  11. {
  12. _propertyList = new List<string>();
  13. }
  14.  
  15. public List<string> GetPropertyList()
  16. {
  17. return _propertyList;
  18. }
  19.  
  20. public void Clear()
  21. {
  22. _propertyList.Clear();
  23. }
  24.  
  25. protected override Expression VisitMember(MemberExpression node)
  26. {
  27. if (node.Expression != null && node.Expression.NodeType == ExpressionType.Parameter)
  28. {
  29. _propertyList.Add(node.Member.Name);
  30. }
  31. return node;
  32. }
  33.  
  34. protected override Expression VisitNew(NewExpression node)
  35. {
  36. foreach (var arg in node.Arguments)
  37. {
  38. if (arg.NodeType == ExpressionType.MemberAccess)
  39. {
  40. var member = (MemberExpression) arg;
  41. if (member.Expression != null && member.Expression.NodeType == ExpressionType.Parameter)
  42. {
  43. _propertyList.Add(member.Member.Name);
  44. }
  45. }
  46. }
  47. return node;
  48. }
  49. }
  50. }

Select表达式解析

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq.Expressions;
  4. using System.Text;
  5. using MyOrm.Reflections;
  6.  
  7. namespace MyOrm.Expressions
  8. {
  9. public class SelectExpressionResolver
  10. {
  11. private readonly List<string> _propertyList;
  12.  
  13. private readonly List<SelectResolveResult> _dict;
  14.  
  15. private Type _targetType;
  16.  
  17. public SelectExpressionResolver()
  18. {
  19. _propertyList = new List<string>();
  20. _dict = new List<SelectResolveResult>();
  21. }
  22.  
  23. public List<SelectResolveResult> GetPropertyList()
  24. {
  25. return _dict;
  26. }
  27.  
  28. public Type GetTargetType()
  29. {
  30. return _targetType;
  31. }
  32.  
  33. public void Clear()
  34. {
  35. _propertyList.Clear();
  36. }
  37.  
  38. public void Visit(LambdaExpression expression)
  39. {
  40. if (expression.Body.NodeType == ExpressionType.MemberAccess)
  41. {
  42. VisitMember((MemberExpression)expression.Body);
  43. }
  44. else if (expression.Body.NodeType == ExpressionType.MemberInit)
  45. {
  46. VisitMemberInit((MemberInitExpression)expression.Body);
  47. }
  48. else if(expression.Body.NodeType == ExpressionType.New)
  49. {
  50. VisitNew((NewExpression)expression.Body);
  51. }
  52. }
  53.  
  54. protected Expression VisitMember(MemberExpression node)
  55. {
  56. var rootType = node.GetRootType(out var stack);
  57. if (rootType == ExpressionType.Parameter)
  58. {
  59. if (stack.Count == )
  60. {
  61. var propertyName = stack.Pop();
  62. var memberName = node.Member.Name;
  63.  
  64. _dict.Add(new SelectResolveResult
  65. {
  66. PropertyName = propertyName,
  67. MemberName = memberName,
  68. FieldName = ""
  69. });
  70. }
  71. else if (stack.Count == )
  72. {
  73. var propertyName = stack.Pop();
  74. var fieldName = stack.Pop();
  75. var memberName = node.Member.Name;
  76. _dict.Add(new SelectResolveResult
  77. {
  78. MemberName = memberName,
  79. PropertyName = propertyName,
  80. FieldName = fieldName
  81. });
  82. }
  83. }
  84. return node;
  85. }
  86.  
  87. protected Expression VisitNew(NewExpression node)
  88. {
  89. _targetType = node.Type;
  90. Console.WriteLine(_targetType);
  91. if (node.Members != null)
  92. {
  93. for (var i = ; i < node.Members.Count; i++)
  94. {
  95. if (node.Arguments[i].NodeType == ExpressionType.MemberAccess)
  96. {
  97. var member = (MemberExpression) node.Arguments[i];
  98. var rootType = member.GetRootType(out var stack);
  99. if (rootType == ExpressionType.Parameter)
  100. {
  101. if (stack.Count == )
  102. {
  103. var propertyName = stack.Pop();
  104. var memberName = node.Members[i].Name;
  105.  
  106. _dict.Add(new SelectResolveResult
  107. {
  108. PropertyName = propertyName,
  109. MemberName = memberName,
  110. FieldName = ""
  111. });
  112. }
  113. else if (stack.Count == )
  114. {
  115. var propertyName = stack.Pop();
  116. var fieldName = stack.Pop();
  117. var memberName = node.Members[i].Name;
  118. _dict.Add(new SelectResolveResult
  119. {
  120. PropertyName = propertyName,
  121. MemberName = memberName,
  122. FieldName = fieldName
  123. });
  124. }
  125. }
  126. }
  127. }
  128. }
  129.  
  130. return node;
  131. }
  132.  
  133. protected void VisitMemberInit(MemberInitExpression node)
  134. {
  135. foreach (var binding in node.Bindings)
  136. {
  137. var result = new SelectResolveResult { MemberName = binding.Member.Name };
  138. if (binding.BindingType == MemberBindingType.Assignment)
  139. {
  140. var expression = ((MemberAssignment) binding).Expression;
  141. if (expression.NodeType == ExpressionType.MemberAccess)
  142. {
  143. var member = (MemberExpression)expression;
  144. var rootType = member.GetRootType(out var stack);
  145. if (rootType == ExpressionType.Parameter)
  146. {
  147. if (stack.Count == )
  148. {
  149. var propertyName = stack.Pop();
  150. var memberName = binding.Member.Name;
  151.  
  152. _dict.Add(new SelectResolveResult
  153. {
  154. PropertyName = propertyName,
  155. MemberName = memberName,
  156. FieldName = ""
  157. });
  158. }
  159. else if (stack.Count == )
  160. {
  161. var propertyName = stack.Pop();
  162. var fieldName = stack.Pop();
  163. var memberName = binding.Member.Name;
  164. _dict.Add(new SelectResolveResult
  165. {
  166. PropertyName = propertyName,
  167. MemberName = memberName,
  168. FieldName = fieldName
  169. });
  170. }
  171. }
  172. }
  173. }
  174. }
  175. }
  176.  
  177. private string ResolveStackToField(Stack<string> parameterStack)
  178. {
  179. switch (parameterStack.Count)
  180. {
  181. case :
  182. {
  183. // 调用了导航属性
  184. var propertyName = parameterStack.Pop();
  185. var propertyFieldName = parameterStack.Pop();
  186.  
  187. return $"{propertyName}.{propertyFieldName}";
  188. }
  189. case :
  190. {
  191. var propertyName = parameterStack.Pop();
  192. return propertyName;
  193. }
  194. default:
  195. throw new ArgumentException("尚未支持大于2层属性调用。如 student.Clazz.School.Id>10,请使用类似 student.Clazz.SchoolId > 0 替代");
  196. }
  197. }
  198. }
  199.  
  200. public class SelectResolveResult
  201. {
  202. public string MemberName { get; set; }
  203.  
  204. public string PropertyName { get; set; }
  205.  
  206. public string FieldName { get; set; }
  207. }
  208. }

【手撸一个ORM】第六步、对象表达式解析和Select表达式解析的更多相关文章

  1. 【手撸一个ORM】第一步、实体约定和描述

    一.约定 数据实体必须实现 IEntity 接口,该接口定义了一个int类型的Id属性,既每个实体必须有一个名称为Id的自增主键. 若数据表的主键列名称不是Id,可以通过 [MyKey("主 ...

  2. 【手撸一个ORM】第九步、orm默认配置类 MyDbConfiguration,一次配置,简化实例化流程

    这个实现比较简单,事实上可配置的项目很多,如有需要,请读者自行扩展 using System; namespace MyOrm { public class MyDbConfiguration { p ...

  3. 【手撸一个ORM】MyOrm的使用说明

    [手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...

  4. 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句

    说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...

  5. 【手撸一个ORM】第七步、SqlDataReader转实体

    说明 使用Expression(表达式目录树)转Entity的文章在园子里有很多,思路也大致也一样,我在前面有篇文章对解决思路有些说明,有兴趣的小伙伴可以看下 (传送门),刚接触表达式目录树时写的,不 ...

  6. 【手撸一个ORM】第十步、数据操作工具类 MyDb

    说明 其实就是数据库操作的一些封装,很久不用SqlCommand操作数据库了,看了点园子里的文章就直接上手写了,功能上没问题,但写法上是否完美高效无法保证,建议有需要的朋友自己重写,当然如果能把最佳实 ...

  7. 【手撸一个ORM】第三步、SQL语句构造器和SqlParameter封装

    既然是数据库工具,自然少不了增删改查的sql语句,在这里将这些常用SQL拼接操作集成到 [SqlServerBuilder.cs] 当中,方便后面调用. 近几年在项目中一直使用Dapper操作数据库, ...

  8. 【手撸一个ORM】第四步、Expression(表达式目录树)扩展

    到这里,Orm的基架已经搭起来了,接下来就是激动人心的部分,表达式目录树转Sql语句,SqlDataReader转数据实体等等,但是在这之前,我们需要扩展下表达式目录树的方法,以方便后面的相关操作. ...

  9. 【手撸一个ORM】第八步、查询工具类

    一.实体查询 using MyOrm.Commons; using MyOrm.DbParameters; using MyOrm.Expressions; using MyOrm.Mappers; ...

随机推荐

  1. C++中vector使用详细说明

    1. 在C++中的详细说明 vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库. vector之所以被认为是一个容器,是因为它能够像容器一样存放各 ...

  2. 写个sleep玩玩

    static void sig_when_weakup(int no){ printf("weakup weakup\n"); longjmp(buf, ); } void wea ...

  3. 「LOJ#10045」「一本通 2.2 练习 1」Radio Transmission (KMP

    题目描述 原题来自:BalticOI 2009 给你一个字符串,它是由某个字符串不断自我连接形成的.但是这个字符串是不确定的,现在只想知道它的最短长度是多少. 输入格式 第一行给出字符串的长度 L,第 ...

  4. 「USACO08DEC」「LuoguP2922」秘密消息Secret Message(AC自动机

    题目描述 Bessie is leading the cows in an attempt to escape! To do this, the cows are sending secret bin ...

  5. CodeForces - 434D Nanami's Power Plant

    Codeforces - 434D 题目大意: 给定一个长为n的序列,序列中的第i为上的值\(x_i\),序列第i位上的值\(x_i\in[l_i,r_i]\),价值为\(f_i(x_i)\),其中\ ...

  6. Django 发送email配置详解及各种错误类型

    跟随Django Book的内容发送邮件不成功,总结一下需要配置好settings.py文件,还要注意一些细节. 1.在settings文件最后添加以下内容,缺一不可! EMAIL_HOST= 'sm ...

  7. 红黑树的C语言实现

    rbtree.h #ifndef _RED_BLACK_TREE_H_ #define _RED_BLACK_TREE_H_ #define RED 0 // 红色节点 #define BLACK 1 ...

  8. Mysql常用命令行大全(一)

    登录到mysql中,然后在mysql的提示符下运行下列命令,每个命令以分号结束. 1. 显示数据库列表. show databases; 缺省有两个数据库:mysql和test. mysql库存放着m ...

  9. SimpliciTI简介

    SimpliciTI简介 SimpliciTI是TI开发的一份专门针对其CCxxxx系列无线通信芯片的网络协议.按照其官方说法SimpliciTI是一个基于连接的点对点通讯协议.它支持两种网络拓扑结构 ...

  10. appium+python 快速给真机安装app

    #coding=utf-8from appium import webdriverfrom time import sleepimport os,time,unittest '''给手机快速装app的 ...