using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace Kean.Infrastructure.Database
{
///
/// 条件表达式
///
internal sealed class ConditionExpression : ExpressionVisitor
{
private readonly StringBuilder _sql = new(); // Sql 语句
private Dictionary _schema; // 对象名
private string _prefix; // 参数前缀
private string _symbol; // 名称符号
private Parameters _param; // 参数
private string _operator; // 操作符(暂存)
private string _method; // 方法(暂存)
private int _sub; // 子查询数
///
/// 解析表达式
///
internal static string Build(Expression expression, Dictionary schema, string prefix, string symbol, ref Parameters param)
{
var visitor = new ConditionExpression
{
_schema = schema,
_prefix = prefix,
_symbol = symbol,
_param = (param ??= new Parameters())
};
visitor.Visit(expression);
return visitor._sql.ToString();
}
///
/// 设置参数
///
private void SetValue(Expression expression)
{
var key = $"p{_param.Count()}";
var value = GetValue(expression);
switch (value)
{
default:
_sql.AppendFormat("{0}{1}", _prefix, key);
_param.Add(key, value);
break;
case bool b:
_sql.AppendFormat("{0}{1}", _prefix, key);
_param.Add(key, b ? 1 : 0);
break;
case Enum e:
_sql.AppendFormat("{0}{1}", _prefix, key);
_param.Add(key, Convert.ToInt32(e));
break;
case string s:
_sql.AppendFormat("{0}{1}", _prefix, key);
if (_operator == "LIKE")
{
_param.Add(key, _method switch
{
"StartsWith" => $"{s}%",
"EndsWith" => $"%{s}",
_ => $"%{s}%"
});
}
else
{
_param.Add(key, value);
}
break;
case Query q:
if (q.Parameters?.Any() == true)
{
_sql.AppendFormat("({0})", q.Expression.Replace($"{_prefix}p", $"{_prefix}p_s{_sub}_"));
foreach (var item in q.Parameters)
{
_param.Add($"{_prefix}p_s{_sub}_{item.Key[1..]}", item.Value);
}
}
else
{
_sql.AppendFormat("({0})", q.Expression);
}
_sub++;
break;
}
}
///
/// 获取参数
///
private static object GetValue(Expression expression)
{
if ((expression is UnaryExpression ue) && ue.NodeType == ExpressionType.Convert)
{
expression = ue.Operand;
}
switch (expression)
{
case MemberExpression me:
object value = null;
var stack = new Stack();
var temp = me;
while (temp != null)
{
stack.Push(temp);
temp = temp.Expression as MemberExpression;
}
while (stack.Count > 0)
{
temp = stack.Pop();
if (temp.Expression is ConstantExpression c)
{
value = c.Value;
}
switch (temp.Member)
{
case PropertyInfo pi:
value = pi.GetValue(value);
break;
case FieldInfo fi:
value = fi.GetValue(value);
break;
}
}
return value;
case ConstantExpression ce:
return ce.Value;
default:
return Expression.Lambda(expression).Compile().DynamicInvoke();
}
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Expression is ParameterExpression pe && pe.NodeType == ExpressionType.Parameter)
{
if (_schema == null)
{
_sql.AppendFormat("{0}{2}{1}", _symbol[0], _symbol[1], node.Member.Name);
}
else
{
_sql.AppendFormat("{0}{2}{1}.{0}{3}{1}", _symbol[0], _symbol[1], _schema[pe.Name], node.Member.Name);
}
}
else
{
SetValue(node);
}
return node;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
var op = Operator.Get(node, out Expression left, out Expression right);
if (op == null)
{
SetValue(node);
}
else
{
_method = node.Method.Name;
_operator = op;
_sql.Append('(');
Visit(left);
_sql.AppendFormat(" {0} ", _operator);
Visit(right);
_sql.Append(')');
}
return node;
}
protected override Expression VisitBinary(BinaryExpression node)
{
_sql.Append('(');
Visit(node.Left);
if (node.Right is ConstantExpression ce && ce.Value == null)
{
switch (node.NodeType)
{
case ExpressionType.Equal:
_sql.Append(" IS NULL");
break;
case ExpressionType.NotEqual:
_sql.Append(" IS NOT NULL");
break;
default:
_sql.AppendFormat(" {0} ", Operator.Get(node.NodeType));
Visit(node.Right);
break;
}
}
else
{
_sql.AppendFormat(" {0} ", Operator.Get(node.NodeType));
Visit(node.Right);
}
_sql.Append(')');
return node;
}
protected override Expression VisitNew(NewExpression node)
{
SetValue(node);
return node;
}
protected override Expression VisitNewArray(NewArrayExpression node)
{
SetValue(node);
return node;
}
protected override Expression VisitUnary(UnaryExpression node)
{
if (node.NodeType == ExpressionType.Not)
{
_sql.AppendFormat("{0} ", Operator.Get(ExpressionType.Not));
Visit(node.Operand);
}
else
{
Visit(node.Operand);
}
return node;
}
protected override Expression VisitConstant(ConstantExpression node)
{
switch (node.Value)
{
default:
_sql.Append(node.Value);
break;
case null:
_sql.Replace(" = ", " IS ", _sql.Length - 3, 3).Replace(" <> ", " IS NOT ", _sql.Length - 4, 4);
_sql.Append("NULL");
break;
case bool b:
_sql.Append(b ? 1 : 0);
break;
case Enum e:
_sql.Append(Convert.ToInt32(e));
break;
case string s:
if (_operator == "LIKE")
{
_sql.AppendFormat(_method switch
{
"StartsWith" => "'{0}%'",
"EndsWith" => "'%{0}'",
_ => "'%{0}%'"
}, s);
}
else
{
_sql.AppendFormat("'{0}'", s);
}
break;
}
return node;
}
}
}