【手撸一个ORM】第六步、对象表达式解析和Select表达式解析
说明
一个Orm自然不仅仅包含条件表达式,还会有如下的场景:
- OrderBy(s => s.StudentName)
- Select<StudentDto>(s => new StudentDto { s.Id, s.Name, SchoolName = s.School.Name})
而应用场景的不同,导致解析的方式也有所不同,在这里我们又定义了两个解析类:[ObjectMemberVisitor] 和 [SelectExpressionResolver]。
[ObjectMemberVisitor] 主要用于从表达式中解析出参数的属性名,会自动忽略导航属性
[SelectExpressionResolver] 主要用于查询的Select方法,也是用于从表达式中解析出属性名,与ObjectMemberVisitor不同的是它不会忽略导航属性。
从下面的代码可以看出,两个类虽然功能类似,但是代码差异很大,主要是因为ObjectMemberVisitor的使用场景比较简单,只需要拿到表达式的Member(成员)就可以了,不必考虑太多。但SelectExpressionResolver不同,其解析结果需要反馈给查询工具更多信息,包括Member与Parameter的映射关系等。
对象表达式解析
- using System.Collections.Generic;
- using System.Linq.Expressions;
- namespace MyOrm.Expressions
- {
- public class ObjectMemberVisitor : ExpressionVisitor
- {
- private readonly List<string> _propertyList;
- public ObjectMemberVisitor()
- {
- _propertyList = new List<string>();
- }
- public List<string> GetPropertyList()
- {
- return _propertyList;
- }
- public void Clear()
- {
- _propertyList.Clear();
- }
- protected override Expression VisitMember(MemberExpression node)
- {
- if (node.Expression != null && node.Expression.NodeType == ExpressionType.Parameter)
- {
- _propertyList.Add(node.Member.Name);
- }
- return node;
- }
- protected override Expression VisitNew(NewExpression node)
- {
- foreach (var arg in node.Arguments)
- {
- if (arg.NodeType == ExpressionType.MemberAccess)
- {
- var member = (MemberExpression) arg;
- if (member.Expression != null && member.Expression.NodeType == ExpressionType.Parameter)
- {
- _propertyList.Add(member.Member.Name);
- }
- }
- }
- return node;
- }
- }
- }
Select表达式解析
- using System;
- using System.Collections.Generic;
- using System.Linq.Expressions;
- using System.Text;
- using MyOrm.Reflections;
- namespace MyOrm.Expressions
- {
- public class SelectExpressionResolver
- {
- private readonly List<string> _propertyList;
- private readonly List<SelectResolveResult> _dict;
- private Type _targetType;
- public SelectExpressionResolver()
- {
- _propertyList = new List<string>();
- _dict = new List<SelectResolveResult>();
- }
- public List<SelectResolveResult> GetPropertyList()
- {
- return _dict;
- }
- public Type GetTargetType()
- {
- return _targetType;
- }
- public void Clear()
- {
- _propertyList.Clear();
- }
- public void Visit(LambdaExpression expression)
- {
- if (expression.Body.NodeType == ExpressionType.MemberAccess)
- {
- VisitMember((MemberExpression)expression.Body);
- }
- else if (expression.Body.NodeType == ExpressionType.MemberInit)
- {
- VisitMemberInit((MemberInitExpression)expression.Body);
- }
- else if(expression.Body.NodeType == ExpressionType.New)
- {
- VisitNew((NewExpression)expression.Body);
- }
- }
- protected Expression VisitMember(MemberExpression node)
- {
- var rootType = node.GetRootType(out var stack);
- if (rootType == ExpressionType.Parameter)
- {
- if (stack.Count == )
- {
- var propertyName = stack.Pop();
- var memberName = node.Member.Name;
- _dict.Add(new SelectResolveResult
- {
- PropertyName = propertyName,
- MemberName = memberName,
- FieldName = ""
- });
- }
- else if (stack.Count == )
- {
- var propertyName = stack.Pop();
- var fieldName = stack.Pop();
- var memberName = node.Member.Name;
- _dict.Add(new SelectResolveResult
- {
- MemberName = memberName,
- PropertyName = propertyName,
- FieldName = fieldName
- });
- }
- }
- return node;
- }
- protected Expression VisitNew(NewExpression node)
- {
- _targetType = node.Type;
- Console.WriteLine(_targetType);
- if (node.Members != null)
- {
- for (var i = ; i < node.Members.Count; i++)
- {
- if (node.Arguments[i].NodeType == ExpressionType.MemberAccess)
- {
- var member = (MemberExpression) node.Arguments[i];
- var rootType = member.GetRootType(out var stack);
- if (rootType == ExpressionType.Parameter)
- {
- if (stack.Count == )
- {
- var propertyName = stack.Pop();
- var memberName = node.Members[i].Name;
- _dict.Add(new SelectResolveResult
- {
- PropertyName = propertyName,
- MemberName = memberName,
- FieldName = ""
- });
- }
- else if (stack.Count == )
- {
- var propertyName = stack.Pop();
- var fieldName = stack.Pop();
- var memberName = node.Members[i].Name;
- _dict.Add(new SelectResolveResult
- {
- PropertyName = propertyName,
- MemberName = memberName,
- FieldName = fieldName
- });
- }
- }
- }
- }
- }
- return node;
- }
- protected void VisitMemberInit(MemberInitExpression node)
- {
- foreach (var binding in node.Bindings)
- {
- var result = new SelectResolveResult { MemberName = binding.Member.Name };
- if (binding.BindingType == MemberBindingType.Assignment)
- {
- var expression = ((MemberAssignment) binding).Expression;
- if (expression.NodeType == ExpressionType.MemberAccess)
- {
- var member = (MemberExpression)expression;
- var rootType = member.GetRootType(out var stack);
- if (rootType == ExpressionType.Parameter)
- {
- if (stack.Count == )
- {
- var propertyName = stack.Pop();
- var memberName = binding.Member.Name;
- _dict.Add(new SelectResolveResult
- {
- PropertyName = propertyName,
- MemberName = memberName,
- FieldName = ""
- });
- }
- else if (stack.Count == )
- {
- var propertyName = stack.Pop();
- var fieldName = stack.Pop();
- var memberName = binding.Member.Name;
- _dict.Add(new SelectResolveResult
- {
- PropertyName = propertyName,
- MemberName = memberName,
- FieldName = fieldName
- });
- }
- }
- }
- }
- }
- }
- private string ResolveStackToField(Stack<string> parameterStack)
- {
- switch (parameterStack.Count)
- {
- case :
- {
- // 调用了导航属性
- var propertyName = parameterStack.Pop();
- var propertyFieldName = parameterStack.Pop();
- return $"{propertyName}.{propertyFieldName}";
- }
- case :
- {
- var propertyName = parameterStack.Pop();
- return propertyName;
- }
- default:
- throw new ArgumentException("尚未支持大于2层属性调用。如 student.Clazz.School.Id>10,请使用类似 student.Clazz.SchoolId > 0 替代");
- }
- }
- }
- public class SelectResolveResult
- {
- public string MemberName { get; set; }
- public string PropertyName { get; set; }
- public string FieldName { get; set; }
- }
- }
【手撸一个ORM】第六步、对象表达式解析和Select表达式解析的更多相关文章
- 【手撸一个ORM】第一步、实体约定和描述
一.约定 数据实体必须实现 IEntity 接口,该接口定义了一个int类型的Id属性,既每个实体必须有一个名称为Id的自增主键. 若数据表的主键列名称不是Id,可以通过 [MyKey("主 ...
- 【手撸一个ORM】第九步、orm默认配置类 MyDbConfiguration,一次配置,简化实例化流程
这个实现比较简单,事实上可配置的项目很多,如有需要,请读者自行扩展 using System; namespace MyOrm { public class MyDbConfiguration { p ...
- 【手撸一个ORM】MyOrm的使用说明
[手撸一个ORM]第一步.约定和实体描述 [手撸一个ORM]第二步.封装实体描述和实体属性描述 [手撸一个ORM]第三步.SQL语句构造器和SqlParameter封装 [手撸一个ORM]第四步.Ex ...
- 【手撸一个ORM】第五步、Expression(表达式目录树)转换为Where子句
说明 在SQL中,查询.修改比较常用到WHERE子句,在这里根据使用场景不同,定义了两个类,一个用于查询,一个用于修改(插入)操作.原因是: 查询操作支持一级导航属性查询,如student.Schoo ...
- 【手撸一个ORM】第七步、SqlDataReader转实体
说明 使用Expression(表达式目录树)转Entity的文章在园子里有很多,思路也大致也一样,我在前面有篇文章对解决思路有些说明,有兴趣的小伙伴可以看下 (传送门),刚接触表达式目录树时写的,不 ...
- 【手撸一个ORM】第十步、数据操作工具类 MyDb
说明 其实就是数据库操作的一些封装,很久不用SqlCommand操作数据库了,看了点园子里的文章就直接上手写了,功能上没问题,但写法上是否完美高效无法保证,建议有需要的朋友自己重写,当然如果能把最佳实 ...
- 【手撸一个ORM】第三步、SQL语句构造器和SqlParameter封装
既然是数据库工具,自然少不了增删改查的sql语句,在这里将这些常用SQL拼接操作集成到 [SqlServerBuilder.cs] 当中,方便后面调用. 近几年在项目中一直使用Dapper操作数据库, ...
- 【手撸一个ORM】第四步、Expression(表达式目录树)扩展
到这里,Orm的基架已经搭起来了,接下来就是激动人心的部分,表达式目录树转Sql语句,SqlDataReader转数据实体等等,但是在这之前,我们需要扩展下表达式目录树的方法,以方便后面的相关操作. ...
- 【手撸一个ORM】第八步、查询工具类
一.实体查询 using MyOrm.Commons; using MyOrm.DbParameters; using MyOrm.Expressions; using MyOrm.Mappers; ...
随机推荐
- C++中vector使用详细说明
1. 在C++中的详细说明 vector是C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库. vector之所以被认为是一个容器,是因为它能够像容器一样存放各 ...
- 写个sleep玩玩
static void sig_when_weakup(int no){ printf("weakup weakup\n"); longjmp(buf, ); } void wea ...
- 「LOJ#10045」「一本通 2.2 练习 1」Radio Transmission (KMP
题目描述 原题来自:BalticOI 2009 给你一个字符串,它是由某个字符串不断自我连接形成的.但是这个字符串是不确定的,现在只想知道它的最短长度是多少. 输入格式 第一行给出字符串的长度 L,第 ...
- 「USACO08DEC」「LuoguP2922」秘密消息Secret Message(AC自动机
题目描述 Bessie is leading the cows in an attempt to escape! To do this, the cows are sending secret bin ...
- CodeForces - 434D Nanami's Power Plant
Codeforces - 434D 题目大意: 给定一个长为n的序列,序列中的第i为上的值\(x_i\),序列第i位上的值\(x_i\in[l_i,r_i]\),价值为\(f_i(x_i)\),其中\ ...
- Django 发送email配置详解及各种错误类型
跟随Django Book的内容发送邮件不成功,总结一下需要配置好settings.py文件,还要注意一些细节. 1.在settings文件最后添加以下内容,缺一不可! EMAIL_HOST= 'sm ...
- 红黑树的C语言实现
rbtree.h #ifndef _RED_BLACK_TREE_H_ #define _RED_BLACK_TREE_H_ #define RED 0 // 红色节点 #define BLACK 1 ...
- Mysql常用命令行大全(一)
登录到mysql中,然后在mysql的提示符下运行下列命令,每个命令以分号结束. 1. 显示数据库列表. show databases; 缺省有两个数据库:mysql和test. mysql库存放着m ...
- SimpliciTI简介
SimpliciTI简介 SimpliciTI是TI开发的一份专门针对其CCxxxx系列无线通信芯片的网络协议.按照其官方说法SimpliciTI是一个基于连接的点对点通讯协议.它支持两种网络拓扑结构 ...
- appium+python 快速给真机安装app
#coding=utf-8from appium import webdriverfrom time import sleepimport os,time,unittest '''给手机快速装app的 ...