【手撸一个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; ...
随机推荐
- mysql七:视图、触发器、事务、存储过程、函数
阅读目录 一 视图 二 触发器 三 事务 四 存储过程 五 函数 六 流程控制 一 视图 视图是一个虚拟表(非真实存在),其本质是[根据SQL语句获取动态的数据集,并为其命名],用户使用时只需使用[名 ...
- IDEAL葵花宝典:java代码开发规范插件 checkstyle、visualVM、PMD 插件
前言: visualVM: 运行java程序的时候启动visualvm,方便查看jvm的情况 比如堆内存大小的分配:某个对象占用了多大的内存,jvm调优必备工具. checkstyle: CheckS ...
- ffmpeg给视频加文字水印
ffmpeg -i dd2800.mp4 -vf "drawtext=fontfile=Arial.ttf: text='Hu':x=100:y=10:fontsize=24:fontcol ...
- loading bar
上面的loading条,想到的办法是用两个半圆覆盖实现,结果也就这么做了,可是明明一个圆就可以的,哎智商堪忧... <!DOCTYPE html> <html lang=" ...
- Chapter2 二分与三分
T1 给一个N个数的序列,分成M段,每段最大值最小 sol:二分最大值,贪心Check T2 平面上n个点,每个点每s会向周围扩散一个单位长度,两个点联通当且仅当扩散有交点,问什么时候这n个点联通 s ...
- bjwc Day2 玄学
早晨起来很开心,因为昨天跟妹子聊天聊到很晚 然后看到了题,感觉:这tm才是冬令营呀! T1构造,并没有找到性质,暴力都懒得打 T2数位dp,状态比较麻烦,看来跟dmy想到一起了,然后搞一下搞完 T3放 ...
- codevs 1576最长严格上升子序列
传送门 1576 最长严格上升子序列 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 黄金 Gold 题目描述 Description 给一个数组a1, a2 ... an ...
- bootstrap 全局样式
reset.css html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100% ...
- Stored Procedures CASE 用法错误
)) ) select @type=[type] from sys.objects with(nolock) where name=@ObjectName case @typ ...
- WebService基础入门(转)
一.概念: 1.WebService,顾名思义就是基于Web的服务.它使用Web(HTTP)方式,接收和响应外部系统的某种请求.从而实现远程调用. 2.我们可以调用互联网上查询天气信息Web服务,然后 ...