说明

一个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表达式解析的更多相关文章

  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. HDU 1850 Being a Good Boy in Spring Festival(博弈·Nim游戏)

    Being a Good Boy in Spring Festival Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32 ...

  2. LinkedList_1.打印两个有序链表的公共部分

    思路: 实例化出两个链表($link_a, $link_b),比较连个链表当前元素的大小,谁小谁执行next()方法继续比较,当出现相当的时候把相等的值塞入数组$common里,当两个链表有一个元素比 ...

  3. 华为机试题 N阶楼梯的走法,每次走一步或者两步

    在Stairs函数中实现该功能: 一个楼梯有N阶,从下往上走,一步可以走一阶,也可以走两阶,有多少种走法? (0<n<=30)<> 例如3阶楼梯有3种走法: 1.1.1 1.2 ...

  4. Python: scikit-image 彩色图像滤波

    一般的滤波器都是针对灰度图像的,scikit-image 库提供了针对彩色图像滤波的decorator:adapt_rgb,adapt_rgb 提供两种形式的滤波,一种是对rgb三个通道分别进行处理, ...

  5. Linux下的Tomcat JVM 调优

    1. 适用场景 Tomcat 运行过程遇到Caused by: java.lang.OutOfMemoryError: PermGen space或者java.lang.OutOfMemoryErro ...

  6. QT(1)介绍

    Qt官网 Qt官网:https://www.qt.io Qt下载:http://www.qt.io/download Qt所有下载:http://download.qt.io/archive/qt Q ...

  7. AtCoder Regular Contest 073 E:Ball Coloring

    题目传送门:https://arc073.contest.atcoder.jp/tasks/arc073_c 题目翻译 给你\(N\)个袋子,每个袋子里有俩白球,白球上写了数字.对于每一个袋子,你需要 ...

  8. bzoj 4032 [ HEOI 2015 ] 最短不公共子串 —— 后缀自动机+序列自动机

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4032 序列自动机其实就是每个位置记录一下某字母后面第一个出现位置,为了子序列能尽量长. 对字 ...

  9. WPF ValidationRule 触发ErrorTemplate 的注意事项

    ValidationRule 验证时, 当验证失败后,再次验证成功, errorTemplate 还是触发, 不会被清掉. 因此需要主动调用 Validation.ClearInvalid(dtpTe ...

  10. RT-Thread OS的启动流程

    1.RT进入main之前, SystemInit函数初始化时钟. 2.main函数位于startup.c文件中.进行两个工作 系统开始前,rt_hw_interrupt_disable关闭所有中断. ...