【手撸一个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; ...
随机推荐
- php设置文件编码
<?php @header('Content-type: text/html;charset=UTF-8'); ?>
- hdu 6103(Kirinriki)
题目链接:Kirinriki 题目描述: 找两个不重叠的字符串A,B. 使得dis(A,B)<=m;\(dis(A,B)= \sum _{i=0}^{n-1} \left | A_i-B_{n- ...
- CodeForces - 884F :Anti-Palindromize(贪心&费用流)
A string a of length m is called antipalindromic iff m is even, and for each i (1 ≤ i ≤ m) ai ≠ am - ...
- 【LeetCode】259 3Sum Smaller
题目: Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 ...
- 【Lintcode】099.Reorder List
题目: Given a singly linked list L: L0 → L1 → … → Ln-1 → Ln reorder it to: L0 → Ln → L1 → Ln-1 → L2 → ...
- 湖南程序设计竞赛赛题总结 XTU 1237 Magic Triangle(计算几何)
这个月月初我们一行三人去湖南参加了ccpc湖南程序设计比赛,虽然路途遥远,六月的湘潭天气燥热,不过在一起的努力之下,拿到了一块铜牌,也算没空手而归啦.不过通过比赛,还是发现我们的差距,希望这几个月自己 ...
- sqlServer对内存的管理
简介 理解SQL Server对于内存的管理是对于SQL Server问题处理和性能调优的基本,本篇文章讲述SQL Server对于内存管理的内存原理. 二级存储(secondary storage) ...
- Silverlight 5 系列学习之二
昨天学习了一下Silverlight基础感觉也没有什么特别之处,不过圈里朋友劝我不要深入学习了,因为ms已不再爱他的这个孩子了,好吧那就把上些简单的东西稍微过一下吧,要不然公司有什么需求要改的小弟不会 ...
- Newtonsoft.Json null值不序列化
如果对当前序列化的实体全部生效的话使用如下: var jSetting = new JsonSerializerSettings {NullValueHandling = NullValueHandl ...
- 《SpringBoot揭秘 快速构建微服务体系》读后感(五)
应用日志和spring-boot-starter-logging 快速web应用开发与spring-boot-starter-web 1.项目结构层面的约定