说明

一个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. 配置 git 以ssh公钥访问github

    #生成ssh config touch .ssh/config chmod 600 config 填写: Host github.com User betachen Hostname ssh.gith ...

  2. ThreadPoolExecutor线程池进阶使用

    一.简介 线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为: ThreadPoolExecutor(int corePoolSize, int ...

  3. (转)epoll非阻塞读写规则

    EPOLL技术 在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是epoll.相比于select,epoll最大的好处在于它不会随 ...

  4. intent实现Activity之间跳转的各种传值

    一.在Activity之间传递String类型的数据 传递 @Override public void onClick(View v) { String num1 = firstNum.getText ...

  5. 把myeclipse中的web项目导入eclipse中不能编程web项目的解决办法

    title: 把myeclipse中的web项目导入eclipse中不能编程web项目的解决办法 tags: grammar_cjkRuby: true --- 右键单击项目,properties-- ...

  6. APNS消息推送实现

    转自:http://blog.csdn.net/biaobiaoqi/article/details/8058503 一.消息推送原理: 在实现消息推送之前先提及几个于推送相关概念,如下图1-1: 1 ...

  7. 【转】python的复制,深拷贝和浅拷贝的区别

    在python中,对象赋值实际上是对象的引用.当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用 一般有三种方法, alist=[1,2,3,[& ...

  8. MD04

    MD04执行MRP分析后, 将计划订单转换为采购申请单后,,如图所示 采购申请转为采购订单后,如图所示 采购订单生成后,MMBE查看库存 MIGO进行收货后,如下图 此物料在SO中已经收货,已有库存

  9. Javascript 获取客户端的运营商 IP 地址 等

    客户端获取运营商 会弹出安全隐患问题,需要修改IE activx 选项, 非常麻烦,用我的代码可以轻松获取. <script src="JS/jquery-1.4.1.js" ...

  10. Animation Blueprint, Set Custom Variables Via C++

    https://wiki.unrealengine.com/Animation_Blueprint,_Set_Custom_Variables_Via_C%2B%2B Animation Bluepr ...