说明

一个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. redis压力测试详解

    redis做压测可以用自带的redis-benchmark工具,使用简单,效果也比较不错. linux下一般无需下载,windows下redis-benchmark压力测试工具下载地址:http:// ...

  2. listen 62

    The Hangover I'm never drinking again. And this time I mean it! Anyone who's suffered through a bad ...

  3. Collaborative Index Embedding for Image Retrieval

    最近看了一篇比较好的文章,效果很好,简单记录一下. 这篇文章的核心思想是,融合两种不同类型的特征.文章中用的是SIFT和CNN提取的特征.还是神经大法好啊. 第一步就是建立两种不同特征的索引,文章用的 ...

  4. BaseAdapter/AsyncTask/..等等细节

    BaseAdapter中的getCount之类的函数,是在constructor之后才启动的.这印证了构造函数的优先级是max的. 图片 这一点的意义在于,当你想给getCount返回一个具体参数的时 ...

  5. bzoj 4592(洛谷 4344) [Shoi2015]脑洞治疗仪——线段树上二分

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4592 1操作就是用线段树来二分找到第一个有 k 个0的位置. 在洛谷上A了,与暴力和网上题解 ...

  6. RT-Thread RTOS

    RT-ThreadRTOS是一款来自中国的开源实时操作系统,由RT-Thread工作室的专业开发人员开发.维护. 起初RT-Thread是一个实时的内核(全抢占优先级调度,调度器时间复杂度O(1)), ...

  7. Day05:装饰器,三元表达式,函数的递归,匿名/内置函数,迭代器,模块,开发目录

    上节课复习:1.函数的对象    函数可以被当作数据取处理2.函数嵌套    嵌套调用:在调用一个函数时,函数体代码又调用了其他函数    嵌套定义:在一个函数内部又定义了另一个函数 def foo( ...

  8. SpringMVC 全注解实现 (1) servlet3.0以上的容器支持

    一. Spring MVC入门 1.1 request的处理过程 用户每次点击浏览器界面的一个按钮,都发出一个web请求(request).一个web请求的工作就像一个快递员,负责将信息从一个地方运送 ...

  9. 使用XMLConfiguration解析xml,PropertiesConfiguration解析properties等相应信息

    org.apache.commons.configuration.XMLConfiguration; Apache Common-Configuration工具可以从Properties文件,XML文 ...

  10. 希尔排序(java)

    希尔排序是对直接插入排序的一种优化,基本思想是把待排序的数据元素分成若干个小组,对同一小组内的数据元素用直接插入法排序:小组的个数逐次缩小:当完成了所有数据元素都在一个组内的排序后排序过程结束.希尔排 ...