• 自己动手写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

  • 相关阅读:
    redis数据同步之redis-shake
    spring拦截机制中Filter(过滤器)、interceptor(拦截器)和Aspect(切面)的使用及区别
    MySQL之MVCC与幻读
    Notepad
    mac环境,python+selenium环境搭建,解决chromedriver报错问题
    大规模抓取的抓取效率和抓取技巧问题
    安卓逆向8,解决app抓包抓不到的问题
    [loj6033]棋盘游戏
    [ccBB]Billboards
    [loj6051]PATH
  • 原文地址:https://www.cnblogs.com/sjqq/p/8428592.html
Copyright © 2020-2023  润新知