You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

616 lines
27 KiB

using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace SqlSugar
{
public class MethodCallExpressionResolve : BaseResolve
{
public MethodCallExpressionResolve(ExpressionParameter parameter) : base(parameter)
{
var express = base.Expression as MethodCallExpression;
var isLeft = parameter.IsLeft;
var methodName = express.Method.Name;
var isValidNativeMethod = IsValidNativeMethod(express, methodName);
List<MethodCallExpressionArgs> appendArgs = null;
if (MethodTimeMapping.ContainsKey(methodName))
{
appendArgs = new List<MethodCallExpressionArgs>();
var dateType = MethodTimeMapping[methodName];
var paramterName = this.Context.SqlParameterKeyWord + ExpressionConst.Const + this.Context.ParameterIndex;
appendArgs.Add(new MethodCallExpressionArgs { IsMember = false, MemberName = paramterName, MemberValue = dateType });
this.Context.Parameters.Add(new SugarParameter(paramterName, dateType.ToString()));
this.Context.ParameterIndex++;
methodName = "DateAdd";
isValidNativeMethod = true;
}
else if (methodName == "get_Item")
{
var paramterName = this.Context.SqlParameterKeyWord + ExpressionConst.Const + this.Context.ParameterIndex;
this.Context.Parameters.Add(new SugarParameter(paramterName, ExpressionTool.DynamicInvoke(express)));
this.Context.Result.Append(string.Format(" {0} ", paramterName));
this.Context.ParameterIndex++;
return;
}
else if (methodName == "NewGuid")
{
this.Context.Result.Append(this.Context.DbMehtods.GuidNew());
return;
}
else if (IsSubMethod(express, methodName))
{
//Check.Exception(!(parameter.BaseExpression is BinaryExpression), "Current expressions are not supported");
var subResolve = new SubResolve(express, this.Context, parameter.OppsiteExpression);
var appendSql = subResolve.GetSql();
if (this.Context.ResolveType.IsIn(ResolveExpressType.SelectMultiple, ResolveExpressType.SelectSingle) || (parameter.BaseParameter != null && parameter.BaseParameter.CommonTempData != null && parameter.BaseParameter.CommonTempData.Equals(CommonTempDataType.Result)))
{
parameter.BaseParameter.CommonTempData = appendSql;
}
else
{
base.AppendValue(parameter, isLeft, appendSql);
}
return;
}
else if (IsIfElse(express, methodName))
{
var caseResole = new CaseWhenResolve(express, this.Context, parameter.OppsiteExpression);
var appendSql = caseResole.GetSql();
if (this.Context.ResolveType.IsIn(ResolveExpressType.SelectMultiple, ResolveExpressType.SelectSingle))
{
parameter.BaseParameter.CommonTempData = appendSql;
}
else
{
base.AppendValue(parameter, isLeft, appendSql);
}
return;
}
if (IsContainsArray(express, methodName, isValidNativeMethod))
{
methodName = "ContainsArray";
isValidNativeMethod = true;
}
if (isValidNativeMethod)
{
NativeExtensionMethod(parameter, express, isLeft, MethodMapping[methodName], appendArgs);
}
else
{
SqlFuncMethod(parameter, express, isLeft);
}
}
private bool IsValidNativeMethod(MethodCallExpression express, string methodName)
{
return MethodMapping.ContainsKey(methodName) && express.Method.DeclaringType.Namespace == ("System");
}
private bool IsExtMethod(string methodName)
{
if (this.Context.SqlFuncServices == null) return false;
return this.Context.SqlFuncServices.Select(it => it.UniqueMethodName).Contains(methodName);
}
private bool IsIfElse(MethodCallExpression express, string methodName)
{
if (methodName == "End" && express.Object.Type == typeof(CaseWhen))
return true;
else
return false;
}
private void SqlFuncMethod(ExpressionParameter parameter, MethodCallExpression express, bool? isLeft)
{
if (!CheckMethod(express))
{
CusMethod(parameter, express, isLeft);
}
else
{
var method = express.Method;
var name = method.Name;
var args = express.Arguments.Cast<Expression>().ToList();
var model = new MethodCallExpressionModel
{
Args = new List<MethodCallExpressionArgs>()
};
switch (this.Context.ResolveType)
{
case ResolveExpressType.WhereSingle:
case ResolveExpressType.WhereMultiple:
Check.Exception(name == "GetSelfAndAutoFill", "SqlFunc.GetSelfAndAutoFill can only be used in Select.");
Where(parameter, isLeft, name, args, model);
break;
case ResolveExpressType.SelectSingle:
case ResolveExpressType.SelectMultiple:
case ResolveExpressType.Update:
Select(parameter, isLeft, name, args, model);
break;
case ResolveExpressType.FieldSingle:
case ResolveExpressType.FieldMultiple:
Field(parameter, isLeft, name, args, model);
break;
default:
break;
}
}
}
private void CusMethod(ExpressionParameter parameter, MethodCallExpression express, bool? isLeft)
{
try
{
var constValue = ExpressionTool.DynamicInvoke(express);
parameter.BaseParameter.CommonTempData = constValue;
var parameterName = base.AppendParameter(constValue);
if (parameter.BaseParameter.CommonTempData != null && parameter.BaseParameter.CommonTempData.Equals(CommonTempDataType.Result))
{
this.Context.Result.Append(parameterName);
}
else
{
base.AppendValue(parameter, isLeft, parameterName);
}
}
catch
{
Check.Exception(true, string.Format(ErrorMessage.MethodError, express.Method.Name));
}
}
private void NativeExtensionMethod(ExpressionParameter parameter, MethodCallExpression express, bool? isLeft, string name, List<MethodCallExpressionArgs> appendArgs = null)
{
var method = express.Method;
var args = express.Arguments.Cast<Expression>().ToList();
var model = new MethodCallExpressionModel
{
Args = new List<MethodCallExpressionArgs>()
};
switch (this.Context.ResolveType)
{
case ResolveExpressType.WhereSingle:
case ResolveExpressType.WhereMultiple:
if (express.Object != null)
args.Insert(0, express.Object);
Where(parameter, isLeft, name, args, model, appendArgs);
break;
case ResolveExpressType.SelectSingle:
case ResolveExpressType.SelectMultiple:
case ResolveExpressType.Update:
if (express.Object != null)
args.Insert(0, express.Object);
Select(parameter, isLeft, name, args, model, appendArgs);
break;
case ResolveExpressType.FieldSingle:
case ResolveExpressType.FieldMultiple:
default:
break;
}
}
private void Field(ExpressionParameter parameter, bool? isLeft, string name, IEnumerable<Expression> args, MethodCallExpressionModel model, List<MethodCallExpressionArgs> appendArgs = null)
{
if (this.Context.ResolveType == ResolveExpressType.FieldSingle)
{
this.Context.ResolveType = ResolveExpressType.WhereSingle;
}
else
{
this.Context.ResolveType = ResolveExpressType.WhereMultiple;
}
Where(parameter, isLeft, name, args, model);
}
private void Select(ExpressionParameter parameter, bool? isLeft, string name, IEnumerable<Expression> args, MethodCallExpressionModel model, List<MethodCallExpressionArgs> appendArgs = null)
{
if (name == "GetSelfAndAutoFill")
{
var memberValue = (args.First() as MemberExpression).Expression.ToString();
model.Args.Add(new MethodCallExpressionArgs { MemberValue = memberValue, IsMember = true, MemberName = memberValue });
}
else
{
foreach (var item in args)
{
AppendItem(parameter, name, args, model, item);
}
if (appendArgs != null)
{
model.Args.AddRange(appendArgs);
}
}
if (parameter.BaseParameter.BaseParameter.BaseParameter == null)
{
this.Context.Result.Append(GetMdthodValue(name, model));
}
else
{
parameter.BaseParameter.CommonTempData = GetMdthodValue(name, model);
}
}
private void Where(ExpressionParameter parameter, bool? isLeft, string name, IEnumerable<Expression> args, MethodCallExpressionModel model, List<MethodCallExpressionArgs> appendArgs = null)
{
foreach (var item in args)
{
var expItem = item;
if (item is UnaryExpression)
{
expItem = (item as UnaryExpression).Operand;
}
AppendItem(parameter, name, args, model, expItem);
}
if (appendArgs != null)
{
model.Args.AddRange(appendArgs);
}
var methodValue = GetMdthodValue(name, model);
if (parameter.BaseExpression is BinaryExpression && parameter.OppsiteExpression.Type == UtilConstants.BoolType && name == "HasValue" && !(parameter.OppsiteExpression is BinaryExpression))
{
methodValue = this.Context.DbMehtods.CaseWhen(new List<KeyValuePair<string, string>> {
new KeyValuePair<string, string>("IF",methodValue.ObjToString()),
new KeyValuePair<string, string>("Return","1"),
new KeyValuePair<string, string>("End","0")
});
}
base.AppendValue(parameter, isLeft, methodValue);
}
private void AppendItem(ExpressionParameter parameter, string name, IEnumerable<Expression> args, MethodCallExpressionModel model, Expression item)
{
var isBinaryExpression = item is BinaryExpression || item is MethodCallExpression;
var isConst = item is ConstantExpression;
var isIIF = name == "IIF";
var isIFFBoolMember = isIIF && (item is MemberExpression) && (item as MemberExpression).Type == UtilConstants.BoolType;
var isIFFUnary = isIIF && (item is UnaryExpression) && (item as UnaryExpression).Operand.Type == UtilConstants.BoolType;
var isIFFBoolBinary = isIIF && (item is BinaryExpression) && (item as BinaryExpression).Type == UtilConstants.BoolType;
var isIFFBoolMethod = isIIF && (item is MethodCallExpression) && (item as MethodCallExpression).Type == UtilConstants.BoolType;
var isFirst = item == args.First();
if (isFirst && isIIF && isConst)
{
var value = (item as ConstantExpression).Value.ObjToBool() ? this.Context.DbMehtods.True() : this.Context.DbMehtods.False();
var methodCallExpressionArgs = new MethodCallExpressionArgs
{
IsMember = true,
MemberName = value,
MemberValue = value
};
model.Args.Add(methodCallExpressionArgs);
}
else if (isIFFUnary && !isFirst)
{
AppendModelByIIFMember(parameter, model, (item as UnaryExpression).Operand);
}
else if (isIFFBoolMember && !isFirst)
{
AppendModelByIIFMember(parameter, model, item);
}
else if (isIFFBoolBinary && !isFirst)
{
AppendModelByIIFBinary(parameter, model, item);
}
else if (isIFFBoolMethod && !isFirst)
{
AppendModelByIIFMethod(parameter, model, item);
}
else if (isBinaryExpression)
{
model.Args.Add(GetMethodCallArgs(parameter, item));
}
else
{
AppendModel(parameter, model, item);
}
}
private void AppendModelByIIFMember(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item)
{
parameter.CommonTempData = CommonTempDataType.Result;
base.Expression = item;
base.Start();
var methodCallExpressionArgs = new MethodCallExpressionArgs
{
IsMember = parameter.ChildExpression is MemberExpression,
MemberName = parameter.CommonTempData
};
if (methodCallExpressionArgs.IsMember && parameter.ChildExpression != null && parameter.ChildExpression.ToString() == "DateTime.Now")
{
methodCallExpressionArgs.IsMember = false;
}
var value = methodCallExpressionArgs.MemberName;
if (methodCallExpressionArgs.IsMember)
{
var childExpression = parameter.ChildExpression as MemberExpression;
if (childExpression.Expression != null && childExpression.Expression is ConstantExpression)
{
methodCallExpressionArgs.IsMember = false;
}
}
if (!methodCallExpressionArgs.IsMember)
{
var parameterName = this.Context.SqlParameterKeyWord + ExpressionConst.MethodConst + this.Context.ParameterIndex;
this.Context.ParameterIndex++;
methodCallExpressionArgs.MemberName = parameterName;
methodCallExpressionArgs.MemberValue = value;
this.Context.Parameters.Add(new SugarParameter(parameterName, value));
}
model.Args.Add(methodCallExpressionArgs);
parameter.ChildExpression = null;
}
private void AppendModelByIIFBinary(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item)
{
Check.Exception(true, "The SqlFunc.IIF(arg1,arg2,arg3) , {0} argument do not support ", item.ToString());
}
private void AppendModelByIIFMethod(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item)
{
var methodExpression = item as MethodCallExpression;
if (methodExpression.Method.Name.IsIn("ToBool", "ToBoolean", "IIF"))
{
model.Args.Add(base.GetMethodCallArgs(parameter, item));
}
else
{
Check.Exception(true, "The SqlFunc.IIF(arg1,arg2,arg3) , {0} argument do not support ", item.ToString());
}
}
private void AppendModel(ExpressionParameter parameter, MethodCallExpressionModel model, Expression item)
{
parameter.CommonTempData = CommonTempDataType.Result;
base.Expression = item;
base.Start();
var methodCallExpressionArgs = new MethodCallExpressionArgs
{
IsMember = parameter.ChildExpression is MemberExpression && !ExpressionTool.IsConstExpression(parameter.ChildExpression as MemberExpression),
MemberName = parameter.CommonTempData
};
if (methodCallExpressionArgs.IsMember && parameter.ChildExpression != null && parameter.ChildExpression.ToString() == "DateTime.Now")
{
methodCallExpressionArgs.IsMember = false;
}
var value = methodCallExpressionArgs.MemberName;
if (methodCallExpressionArgs.IsMember)
{
var childExpression = parameter.ChildExpression as MemberExpression;
if (childExpression.Expression != null && childExpression.Expression is ConstantExpression)
{
methodCallExpressionArgs.IsMember = false;
}
}
if (!methodCallExpressionArgs.IsMember)
{
var parameterName = this.Context.SqlParameterKeyWord + ExpressionConst.MethodConst + this.Context.ParameterIndex;
this.Context.ParameterIndex++;
methodCallExpressionArgs.MemberName = parameterName;
methodCallExpressionArgs.MemberValue = value;
this.Context.Parameters.Add(new SugarParameter(parameterName, value));
}
model.Args.Add(methodCallExpressionArgs);
parameter.ChildExpression = null;
}
private object GetMdthodValue(string name, MethodCallExpressionModel model)
{
if (IsExtMethod(name))
{
var type = DbType.SqlServer;
if (this.Context is SqlServerExpressionContext)
type = DbType.SqlServer;
else if (this.Context is MySqlExpressionContext)
type = DbType.MySql;
else if (this.Context is SqliteExpressionContext)
type = DbType.Sqlite;
else if (this.Context is OracleExpressionContext)
type = DbType.Oracle;
return this.Context.SqlFuncServices.First(it => it.UniqueMethodName == name).MethodValue(model, type, this.Context);
}
else
{
switch (name)
{
case "IIF":
return this.Context.DbMehtods.IIF(model);
case "HasNumber":
return this.Context.DbMehtods.HasNumber(model);
case "HasValue":
return this.Context.DbMehtods.HasValue(model);
case "IsNullOrEmpty":
return this.Context.DbMehtods.IsNullOrEmpty(model);
case "ToLower":
return this.Context.DbMehtods.ToLower(model);
case "ToUpper":
return this.Context.DbMehtods.ToUpper(model);
case "Trim":
return this.Context.DbMehtods.Trim(model);
case "Contains":
return this.Context.DbMehtods.Contains(model);
case "ContainsArray":
var caResult = this.Context.DbMehtods.ContainsArray(model);
this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[0].MemberName.ObjToString());
return caResult;
case "Equals":
return this.Context.DbMehtods.Equals(model);
case "DateIsSame":
if (model.Args.Count == 2)
return this.Context.DbMehtods.DateIsSameDay(model);
else
{
var dsResult = this.Context.DbMehtods.DateIsSameByType(model);
this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[2].MemberName.ObjToString());
return dsResult;
}
case "DateAdd":
if (model.Args.Count == 2)
return this.Context.DbMehtods.DateAddDay(model);
else
{
var daResult = this.Context.DbMehtods.DateAddByType(model);
this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[2].MemberName.ObjToString());
return daResult;
}
case "DateValue":
var dvResult = this.Context.DbMehtods.DateValue(model);
this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[1].MemberName.ObjToString());
return dvResult;
case "Between":
return this.Context.DbMehtods.Between(model);
case "StartsWith":
return this.Context.DbMehtods.StartsWith(model);
case "EndsWith":
return this.Context.DbMehtods.EndsWith(model);
case "ToInt32":
return this.Context.DbMehtods.ToInt32(model);
case "ToInt64":
return this.Context.DbMehtods.ToInt64(model);
case "ToDate":
return this.Context.DbMehtods.ToDate(model);
case "ToTime":
return this.Context.DbMehtods.ToTime(model);
case "ToString":
Check.Exception(model.Args.Count > 1, "ToString (Format) is not supported, Use ToString().If time formatting can be used it.Date.Year+\"-\"+it.Data.Month+\"-\"+it.Date.Day ");
return this.Context.DbMehtods.ToString(model);
case "ToDecimal":
return this.Context.DbMehtods.ToDecimal(model);
case "ToGuid":
return this.Context.DbMehtods.ToGuid(model);
case "ToDouble":
return this.Context.DbMehtods.ToDouble(model);
case "ToBool":
return this.Context.DbMehtods.ToBool(model);
case "Substring":
return this.Context.DbMehtods.Substring(model);
case "Replace":
return this.Context.DbMehtods.Replace(model);
case "Length":
return this.Context.DbMehtods.Length(model);
case "AggregateSum":
return this.Context.DbMehtods.AggregateSum(model);
case "AggregateAvg":
return this.Context.DbMehtods.AggregateAvg(model);
case "AggregateMin":
return this.Context.DbMehtods.AggregateMin(model);
case "AggregateMax":
return this.Context.DbMehtods.AggregateMax(model);
case "AggregateCount":
return this.Context.DbMehtods.AggregateCount(model);
case nameof(MappingColumn):
var mappingColumnResult = this.Context.DbMehtods.MappingColumn(model);
var isValid = model.Args[0].IsMember && !model.Args[1].IsMember;
Check.Exception(!isValid, "SqlFunc.MappingColumn parameters error, The property name on the left, string value on the right");
this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[1].MemberName.ObjToString());
return mappingColumnResult;
case "IsNull":
return this.Context.DbMehtods.IsNull(model);
case "MergeString":
return this.Context.DbMehtods.MergeString(model.Args.Select(it => it.MemberName.ObjToString()).ToArray());
case "GetSelfAndAutoFill":
this.Context.Parameters.RemoveAll(it => it.ParameterName == model.Args[0].MemberName.ObjToString());
return this.Context.DbMehtods.GetSelfAndAutoFill(model.Args[0].MemberValue.ObjToString(), this.Context.IsSingle);
case "GetDate":
return this.Context.DbMehtods.GetDate();
default:
break;
}
}
return null;
}
private static Dictionary<string, string> MethodMapping = new Dictionary<string, string> {
{ "ToString","ToString"},
{ "ToInt32","ToInt32"},
{ "ToInt16","ToInt32"},
{ "ToInt64","ToInt64"},
{ "ToDecimal","ToDecimal"},
{ "ToDateTime","ToDate"},
{ "ToBoolean","ToBool"},
{ "ToDouble","ToDouble"},
{ "Length","Length"},
{ "Replace","Replace"},
{ "Contains","Contains"},
{ "ContainsArray","ContainsArray"},
{ "EndsWith","EndsWith"},
{ "StartsWith","StartsWith"},
{ "HasValue","HasValue"},
{ "Trim","Trim"},
{ "Equals","Equals"},
{ "ToLower","ToLower"},
{ "ToUpper","ToUpper"},
{ "Substring","Substring"},
{ "DateAdd","DateAdd"}
};
private static Dictionary<string, DateType> MethodTimeMapping = new Dictionary<string, DateType> {
{ "AddYears",DateType.Year},
{ "AddMonths",DateType.Month},
{ "AddDays",DateType.Day},
{ "AddHours",DateType.Hour},
{ "AddMinutes",DateType.Minute},
{ "AddSeconds",DateType.Second},
{ "AddMilliseconds",DateType.Millisecond}
};
private bool IsContainsArray(MethodCallExpression express, string methodName, bool isValidNativeMethod)
{
return !isValidNativeMethod && express.Method.DeclaringType.Namespace.IsIn("System.Linq", "System.Collections.Generic") && methodName == "Contains";
}
private bool IsSubMethod(MethodCallExpression express, string methodName)
{
return SubTools.SubItemsConst.Any(it => it.Name == methodName) && express.Object != null && express.Object.Type.Name == "Subqueryable`1";
}
private bool CheckMethod(MethodCallExpression expression)
{
if (IsExtMethod(expression.Method.Name))
return true;
if (expression.Method.ReflectedType().FullName != ExpressionConst.SqlFuncFullName)
return false;
else
return true;
}
}
}