自己动手写ORM(01):解析表达式树生成Sql碎片
在EF中,我们查询数据时可能会用拉姆达表达式 Where(Func<T,ture> func)这个方法来筛选数据,例如,我们定义一个User实体类
public class User
{
public Guid Id { get; set; }
public string LoginId { get; set; }
public string Name { get; set; }
public bool Enabled { get; set; }
public DateTime CreateTime { get; set; }
}
现在我想查询用户Enabled为true 并且Name 中包含'lilei'的用户,在EF中我们可能会这样去写:
XXX.Where(u => u.Name.Contains("lilei") && u.Enabled==true)
那么现在我想将Where方法中的那个泛型委托翻译成Sql语句中的一部分应该怎么做呢?下面是我已经写好的一个解析表达式的类,可能写得不是很规范,大家尽量吐槽:
using LC.Factory.Code;
using LC.Factory.Entity;
using LC.Factory.Resource;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
namespace LC.Factory.Common
{
public class ExpressionAnalyzer
{
/// <summary>
/// 表达式所有参数集合
/// </summary>
private Dictionary<string, object> _params;
/// <summary>
/// 命名参数别名
/// </summary>
private const string _argName = "TAB";
/// <summary>
/// 解析结果
/// </summary>
public AnalysisData ResultData { get; set; }
public ExpressionAnalyzer()
{
ResultData = new AnalysisData();
_params = new Dictionary<string, object>();
}
public ExpressionAnalyzer(LambdaExpression exp)
: this()
{
if (exp != null)
{
AppendParams(GetChildValue(exp.Body), _params);
foreach (var item in exp.Parameters)
{
AnalysisTables(item);
}
AnalysisExpression(exp.Body, true);
}
}
/// <summary>
/// 解析表达式
/// </summary>
/// <param name="exp"></param>
/// <param name="isLeftChild"></param>
private void AnalysisExpression(Expression exp, bool isLeftChild = true)
{
switch (exp.NodeType)
{
case ExpressionType.AndAlso:
ResultData.StackList.Add("(");
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add(")");
ResultData.StackList.Add("AND");
ResultData.StackList.Add("(");
AnalysisExpression(GetChildExpression(exp, false), false);
ResultData.StackList.Add(")");
break;
case ExpressionType.OrElse:
ResultData.StackList.Add("(");
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add(")");
ResultData.StackList.Add("OR");
ResultData.StackList.Add("(");
AnalysisExpression(GetChildExpression(exp, false), false);
ResultData.StackList.Add(")");
break;
case ExpressionType.Equal:
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add("=");
AnalysisExpression(GetChildExpression(exp, false), false);
break;
case ExpressionType.NotEqual:
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add("!=");
AnalysisExpression(GetChildExpression(exp, false), false);
break;
case ExpressionType.GreaterThanOrEqual:
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add(">=");
AnalysisExpression(GetChildExpression(exp, false), false);
break;
case ExpressionType.GreaterThan:
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add(">");
AnalysisExpression(GetChildExpression(exp, false), false);
break;
case ExpressionType.LessThan:
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add("<");
AnalysisExpression(GetChildExpression(exp, false), false);
break;
case ExpressionType.LessThanOrEqual:
AnalysisExpression(GetChildExpression(exp));
ResultData.StackList.Add("<=");
AnalysisExpression(GetChildExpression(exp, false), false);
break;
case ExpressionType.Call:
var imExp = exp as MethodCallExpression;
AnalysisExpression(imExp.Object, true);
ResultData.StackList.Add("LIKE");
if (imExp.Arguments.Count > 0)
{
var arg0 = imExp.Arguments[0] as MemberExpression;
ResultData.StackList.Add("'%'+");
AnalysisExpression(imExp.Arguments[0], false);
ResultData.StackList.Add("+'%'");
}
break;
case ExpressionType.MemberAccess:
if (isLeftChild)
{
AnalysisTables(exp);
var mberExp = exp as MemberExpression;
var parentName = GetExpressionName(mberExp.Expression);
if (!string.IsNullOrEmpty(parentName))
{
ResultData.StackList.Add(string.Format("[{0}].{1}", parentName, GetExpressionName(exp)));
break;
}
ResultData.StackList.Add(GetExpressionName(exp));
}
else
{
var paramName = GetParamName(exp);
ResultData.ParamList.Add(paramName, _params[paramName]);
ResultData.StackList.Add(paramName);
}
break;
case ExpressionType.Constant:
var constent = exp as ConstantExpression;
if (constent.Value == null)
{
var op = ResultData.StackList.ElementAt(ResultData.StackList.Count - 1);
ResultData.StackList.RemoveAt(ResultData.StackList.Count - 1);
if (string.Equals(op, "="))
{
ResultData.StackList.Add("IS NULL");
}
else
{
ResultData.StackList.Add("IS NOT NULL");
}
break;
}
if (constent.Value.GetType() == typeof(String))
{
ResultData.StackList.Add(string.Format("'{0}'", constent.Value));
break;
}
if (constent.Value.GetType() == typeof(bool))
{
if (ResultData.StackList.Count > 0)
{
var value = Convert.ToBoolean(constent.Value);
ResultData.StackList.Add(string.Format("{0}", value ? "1" : "0"));
}
break;
}
ResultData.StackList.Add(string.Format("{0}", constent.Value));
break;
case ExpressionType.Convert:
var uExp = exp as UnaryExpression;
AnalysisExpression(uExp.Operand, isLeftChild);
break;
case ExpressionType.New:
var newExp = exp as NewExpression;
//解析查询字段
for (int i = 0; i < newExp.Arguments.Count; i++)
{
AnalysisExpression(newExp.Arguments[i]);
ResultData.StackList.Add("AS");
ResultData.StackList.Add(string.Format("'{0}'", newExp.Members[i].Name));
}
break;
case ExpressionType.Parameter:
throw new BusinessException(BusinessRes.SelectObjectMastBeAnNewObject);
//AnalysisExpression(Expression.New(exp.Type));
//break;
default:
break;
}
}
/// <summary>
/// 获取孩子节点
/// </summary>
/// <param name="exp"></param>
/// <param name="getLeft"></param>
/// <returns></returns>
private Expression GetChildExpression(Expression exp, bool getLeft = true)
{
var className = exp.GetType().Name;
switch (className)
{
case "BinaryExpression":
case "LogicalBinaryExpression":
var bExp = exp as BinaryExpression;
return getLeft ? bExp.Left : bExp.Right;
case "PropertyExpression":
case "FieldExpression":
var mberExp = exp as MemberExpression;
return mberExp;
case "MethodBinaryExpression":
var mbExp = exp as BinaryExpression;
return getLeft ? mbExp.Left : mbExp.Right;
case "UnaryExpression":
var unaryExp = exp as UnaryExpression;
return unaryExp;
case "ConstantExpression":
var cExp = exp as ConstantExpression;
return cExp;
case "InstanceMethodCallExpressionN":
var imExp = exp as MethodCallExpression;
return imExp;
default:
return null;
}
}
/// <summary>
/// 获取变量名
/// </summary>
/// <param name="exp"></param>
/// <param name="isLeftChild"></param>
/// <returns></returns>
private string GetExpressionName(Expression exp)
{
var className = exp.GetType().Name;
switch (className)
{
case "PropertyExpression":
case "FieldExpression":
var mberExp = exp as MemberExpression;
return string.Format("{0}", mberExp.Member.Name);
case "TypedParameterExpression":
return _argName;
default:
return string.Empty;
}
}
/// <summary>
/// 获取参数名
/// </summary>
/// <param name="exp"></param>
/// <param name="isLeftChild"></param>
/// <returns></returns>
private string GetParamName(Expression exp)
{
var className = exp.GetType().Name;
switch (className)
{
case "PropertyExpression":
case "FieldExpression":
var mberExp = exp as MemberExpression;
return string.Format("@{0}", mberExp.Member.Name);
case "TypedParameterExpression":
var texp = exp as ParameterExpression;
return string.Format("@{0}", texp.Name);
default:
return string.Empty;
}
}
/// <summary>
/// 解析表信息
/// </summary>
/// <param name="exp"></param>
private void AnalysisTables(Expression exp)
{
var className = exp.GetType().Name;
switch (className)
{
case "PropertyExpression":
case "FieldExpression":
var mberExp = exp as MemberExpression;
if (!IsDefaultType(mberExp.Type))
{
if (!ResultData.TableList.ContainsKey(mberExp.Member.Name))
{
ResultData.TableList.Add(mberExp.Member.Name, new AnalysisTable()
{
Name = mberExp.Type.Name,
TableType = mberExp.Type,
IsMainTable = false
});
}
}
AnalysisTables(mberExp.Expression);
break;
case "TypedParameterExpression":
//命名参数表达式
var texp = exp as ParameterExpression;
if (!IsDefaultType(texp.Type))
{
if (!ResultData.TableList.ContainsKey(_argName))
{
ResultData.TableList.Add(_argName, new AnalysisTable()
{
Name = texp.Type.Name,
TableType = texp.Type,
IsMainTable = true
});
}
}
break;
default:
break;
}
}
/// <summary>
/// 解析获取表达式的值
/// </summary>
/// <param name="exp"></param>
/// <param name="leftChild"></param>
/// <returns></returns>
private object GetChildValue(Expression exp)
{
var className = exp.GetType().Name;
switch (className)
{
case "BinaryExpression":
case "LogicalBinaryExpression":
var lExp = exp as BinaryExpression;
var ret = GetChildValue(lExp.Left);
if (IsNullDefaultType(ret))
{
ret = GetChildValue(lExp.Right);
}
return ret;
case "MethodBinaryExpression":
var mbExp = exp as BinaryExpression;
var ret1 = GetChildValue(mbExp.Left);
if (IsNullDefaultType(ret1))
{
ret1 = GetChildValue(mbExp.Right);
}
return ret1;
case "PropertyExpression":
case "FieldExpression":
var mberExp = exp as MemberExpression;
return GetChildValue(mberExp.Expression);
case "ConstantExpression":
var cExp = exp as ConstantExpression;
return cExp.Value;
case "UnaryExpression":
var unaryExp = exp as UnaryExpression;
return GetChildValue(unaryExp.Operand);
case "InstanceMethodCallExpressionN":
var imExp = exp as MethodCallExpression;
if (imExp.Arguments.Count > 0)
{
return GetChildValue(imExp.Arguments[0]);
}
return null;
default:
return null;
}
}
/// <summary>
/// 初始化所有参数
/// </summary>
/// <param name="paramObj"></param>
private void AppendParams(object paramObj, Dictionary<string, object> _params)
{
if (IsNullDefaultType(paramObj))
{
return;
}
if (_params == null)
{
_params = new Dictionary<string, object>();
}
foreach (var item in paramObj.GetType().GetProperties())
{
if (IsDefaultType(item.PropertyType))
{
var value = item.GetValue(paramObj, null);
if (value != null)
{
_params.Add(string.Format("@{0}", item.Name), value);
}
continue;
}
AppendParams(item.GetValue(paramObj), _params);
}
foreach (var item in paramObj.GetType().GetFields())
{
if (IsDefaultType(item.FieldType))
{
var value = item.GetValue(paramObj);
if (value != null)
{
_params.Add(string.Format("@{0}", item.Name), value);
}
continue;
}
AppendParams(item.GetValue(paramObj), _params);
}
}
public Dictionary<string, object> GetParams(object paramObj)
{
Dictionary<string, object> dicParams = new Dictionary<string, object>();
AppendParams(paramObj, dicParams);
return dicParams;
}
/// <summary>
/// 判断是否是系统默认基本类型
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private bool IsNullDefaultType(object obj)
{
if (obj == null)
{
return true;
}
return IsDefaultType(obj.GetType());
}
private bool IsDefaultType(Type type)
{
string defaultType = @"String|Boolean|Double|Int32|Int64|Int16|Single|DateTime|Decimal|Char|Object|Guid";
Regex e = new Regex(defaultType, RegexOptions.IgnoreCase);
if (type.Name.ToLower().Contains("nullable"))
{
if (type.GenericTypeArguments.Count() > 0)
{
return e.IsMatch(type.GenericTypeArguments[0].Name);
}
}
return e.IsMatch(type.Name);
}
}
}
在这个类中主要的方法是AnalysisExpression(Expression exp, bool isLeftChild = true),次啊面我们来测试一下上面的那个表达式:
[TestMethod]
public void TestExpression()
{
Expression<Func<User, bool>> exp = u => u.Name.Contains("lilei") && u.Enabled == true;
var result = new ExpressionAnalyzer(exp).ResultData;
if (result.TableList.Count > 0)
{
foreach (var item in result.TableList)
{
Console.WriteLine("{0} AS {1}", item.Value.Name, item.Key);
}
}
Console.WriteLine("***************************************************");
if (result.StackList.Count > 0)
{
Console.WriteLine(string.Join(" ", result.StackList));
}
Console.WriteLine("***************************************************");
if (result.ParamList.Count > 0)
{
foreach (var item in result.ParamList)
{
Console.WriteLine("{0} {1}", item.Key, item.Value);
}
}
}
输出结果:

再测试带参数的查询:
[TestMethod]
public void TestExpression()
{
var keyWord = "lilei";
var enable = true;
Expression<Func<User, bool>> exp = u => u.Name.Contains(keyWord) && u.Enabled == enable;
var result = new ExpressionAnalyzer(exp).ResultData;
if (result.TableList.Count > 0)
{
foreach (var item in result.TableList)
{
Console.WriteLine("{0} AS {1}", item.Value.Name, item.Key);
}
}
Console.WriteLine("***************************************************");
if (result.StackList.Count > 0)
{
Console.WriteLine(string.Join(" ", result.StackList));
}
Console.WriteLine("***************************************************");
if (result.ParamList.Count > 0)
{
foreach (var item in result.ParamList)
{
Console.WriteLine("{0} {1}", item.Key, item.Value);
}
}
}
输出结果:

以上是根据表达式树解析出来的Sql碎片,这为后续生成Sql语句奠定了基础。
http://www.cnblogs.com/lispring/p/5324348.html
自己动手写ORM(01):解析表达式树生成Sql碎片的更多相关文章
- 自己动手写ORM(02):Sql生成器实现
上一节中鄙人通过解析表达式树生成Sql碎片,其中我也把解析表达式类代码贴了出来,文章发布之后我对ExpressionAnalyzer类做了些改动,下面我还会将代码贴出来,废话不多说,直接进入今天的主题 ...
- 自己动手写ORM的感受
之前看到奋斗前辈和时不我待前辈的自己动手写ORM系列博客,感觉讲解的通俗易懂,清晰透彻.作为一个菜鸟,闲来也想着自己写一个ORM,一来加深自己对 ORM的理解,以求对EF,NHibernate等ROM ...
- (新)自己动手写ORM框架(1)-增删查改的使用
之前写过一个系列文章自己动手写ORM框架,经过在多个项目的中的使用,对这套代码进行了许多改进,下面是使用方法: 新增学员信息代码预览: DBHelper db = DBHelper.getInstan ...
- 自己动手写ORM框架
提起ORM框架,大家都很熟悉,网上流行的ORM框架有很多,其中出名的有一些,不出名的更是数不胜数. 下面是自己实现的一个简单的ORM框架,实现了常用的增删查改功能,供大家研究ORM实现原理. 功能描述 ...
- 自己动手写ORM
http://blog.csdn.net/sundacheng1989/article/category/1350100
- 太实用了!自己动手写软件——SSH、FTP和SQL server的密码破解
我们的密码破解工具一共分为如下六个部分,前面四个部分我们都有在之前的文章中介绍过了 用户图形界面——GUI编程 密码字典获取——Excel文件读取 数据库类——MySQL.Oracle和SQL ser ...
- Python - 动手写个ORM
Python - 动手写个ORM 任务: 模拟简单的ORM - Object Relational Mapping 为model添加create方法 代码很简单,直接上 字段类型类 class Fie ...
- 为初学者写ORM,ORM的原理及测试案例
提纲 一.什么是ORM.二.反射以及Attribute在ORM中的应用.三.创建一个数据库表和表对应的实体model.四.实体model如何映射出数据库表.五.组合ORM映射生成insert语句.六. ...
- 【原创】自己动手写工具----XSmartNote [Beta 2.0]
一.前面的话 在上一篇自己动手写工具----XSmartNote中,我简单介绍了这个小玩意儿的大致界面和要实现的功能,看了一下园子里的评论,评价褒贬不一,有人说“现在那么多云笔记的工具”,“极简版ev ...
随机推荐
- 打补丁以及WebLogic Server的版本
12.1.2开始采用了Oracle传统的opatch打补丁的方式,但在此之前,包括 10.3.x版本以及12.1.1版本还是通过bea的smart update方式来进行. smart update基 ...
- python 输出所有大小写字母, range()以及列表切片
所以在写的时候,只要把它们的ASCII列出,并转化成字符型chr 即可. print [chr(i) for i in range(65,91)]#所有大写字母 print [chr(i) for i ...
- c++之list学习
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <list> using namespace std; ...
- shareToQQ,qq 4.1.1 for android,闪退
用shareToQQ函数分享图文消息,在qq 4.1.1 for android版本下打开联系人列表数秒后会闪退!在更高版本的V4.5.2.1,V4.2.1下则没有这个问题(证明各种设置没问题),各位 ...
- Centos 7 进入单用户模式图文详解
由于昨晚做了一个很傻X的事情,所以有幸进入了CentOS 7 的单用户模式. CentOS 7 在进入单用户的时候和6.x做了很多的改变, 下面让我们来看看如何进入单用户模式. 如何进入CentOS ...
- ETL技术入门之ETL初认识
ETL是什么 ETL是Extract Transform Load三个英文单词的缩写 中文意思就是抽取.转换.载入.说到ETL就必须提到数据仓库. 先说下背景知识: 信息是现代企业的重要资源,是企业运 ...
- 播放器设置 Player Settings
原地址:http://game.ceeger.com/Manual/class-PlayerSettings.html#Android Player Settings is where you def ...
- 【Python】八大排序算法的比较
排序是数据处理比较核心的操作,八大排序算法分别是:直接插入排序.希尔排序.简单选择排序.堆排序.冒泡排序.快速排序.归并排序.基数排序 以下是排序图解: 直接插入排序 思想 直接插入排序是一种最简单的 ...
- windows lua 多线程 线程同步
今天在改一个程序,改成部分逻辑用lua写,这个程序是多线程的.将程序中部分逻辑改成lua之后,各种非法访问内存错误,各种奇奇怪怪的问题,不分时间,不分地点的出现崩溃.从调用堆栈来看,基本都是使用lua ...
- 你被美国监控了,美国监控丑闻——"棱镜项目"事件
http://www.ittime.com.cn/index.php?m=content&c=index&a=show&catid=29&id=3795 “棱镜”项目所 ...