- 优化 ExpressionCall + DynamicInvoke 的解析;

This commit is contained in:
2881099
2024-11-04 17:51:13 +08:00
parent 8b16e30d3b
commit 01a0bfda63
3 changed files with 54 additions and 23 deletions

View File

@@ -1,12 +1,25 @@
using FreeSql.DataAnnotations; using FreeSql.DataAnnotations;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Xml.Linq;
using static FreeSql.SqlExtExtensions; using static FreeSql.SqlExtExtensions;
internal static class FreeSqlInternalExpressionCallExtensions
{
static ConcurrentDictionary<Type, bool> _dicTypeExistsExpressionCallAttribute = new ConcurrentDictionary<Type, bool>();
public static bool IsExpressionCall(this MethodCallExpression node)
{
return node.Object == null && (
_dicTypeExistsExpressionCallAttribute.GetOrAdd(node.Method.DeclaringType, dttp => dttp.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()) ||
node.Method.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any());
}
}
[ExpressionCall] [ExpressionCall]
public static class FreeSqlGlobalExpressionCallExtensions public static class FreeSqlGlobalExpressionCallExtensions
{ {
@@ -24,10 +37,10 @@ public static class FreeSqlGlobalExpressionCallExtensions
{ {
if (expContext.IsValueCreated == false || expContext.Value == null || expContext.Value.ParsedContent == null) if (expContext.IsValueCreated == false || expContext.Value == null || expContext.Value.ParsedContent == null)
return that >= between && that <= and; return that >= between && that <= and;
var time1 = expContext.Value.RawExpression["between"].IsParameter() == false ? var time1 = expContext.Value.RawExpression["between"].CanDynamicInvoke() ?
expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["between"]).Compile().DynamicInvoke()) : expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["between"]).Compile().DynamicInvoke()) :
expContext.Value.ParsedContent["between"]; expContext.Value.ParsedContent["between"];
var time2 = expContext.Value.RawExpression["and"].IsParameter() == false ? var time2 = expContext.Value.RawExpression["and"].CanDynamicInvoke() ?
expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["and"]).Compile().DynamicInvoke()) : expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["and"]).Compile().DynamicInvoke()) :
expContext.Value.ParsedContent["and"]; expContext.Value.ParsedContent["and"];
expContext.Value.Result = $"{expContext.Value.ParsedContent["that"]} between {time1} and {time2}"; expContext.Value.Result = $"{expContext.Value.ParsedContent["that"]} between {time1} and {time2}";
@@ -47,10 +60,10 @@ public static class FreeSqlGlobalExpressionCallExtensions
{ {
if (expContext.IsValueCreated == false || expContext.Value == null || expContext.Value.ParsedContent == null) if (expContext.IsValueCreated == false || expContext.Value == null || expContext.Value.ParsedContent == null)
return that >= start && that < end; return that >= start && that < end;
var time1 = expContext.Value.RawExpression["start"].IsParameter() == false ? var time1 = expContext.Value.RawExpression["start"].CanDynamicInvoke() ?
expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["start"]).Compile().DynamicInvoke()) : expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["start"]).Compile().DynamicInvoke()) :
expContext.Value.ParsedContent["start"]; expContext.Value.ParsedContent["start"];
var time2 = expContext.Value.RawExpression["end"].IsParameter() == false ? var time2 = expContext.Value.RawExpression["end"].CanDynamicInvoke() ?
expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["end"]).Compile().DynamicInvoke()) : expContext.Value.FormatSql(Expression.Lambda(expContext.Value.RawExpression["end"]).Compile().DynamicInvoke()) :
expContext.Value.ParsedContent["end"]; expContext.Value.ParsedContent["end"];
expContext.Value.Result = $"{expContext.Value.ParsedContent["that"]} >= {time1} and {expContext.Value.ParsedContent["that"]} < {time2}"; expContext.Value.Result = $"{expContext.Value.ParsedContent["that"]} >= {time1} and {expContext.Value.ParsedContent["that"]} < {time2}";

View File

@@ -1,4 +1,6 @@
using FreeSql; using FreeSql;
using FreeSql.DataAnnotations;
using FreeSql.Internal;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@@ -227,6 +229,12 @@ namespace System.Linq.Expressions
public static Expression<Func<T1, T2, T3, T4, T5, bool>> Not<T1, T2, T3, T4, T5>(this Expression<Func<T1, T2, T3, T4, T5, bool>> exp, bool condition = true) => (Expression<Func<T1, T2, T3, T4, T5, bool>>)InternalNotExpression(condition, exp); public static Expression<Func<T1, T2, T3, T4, T5, bool>> Not<T1, T2, T3, T4, T5>(this Expression<Func<T1, T2, T3, T4, T5, bool>> exp, bool condition = true) => (Expression<Func<T1, T2, T3, T4, T5, bool>>)InternalNotExpression(condition, exp);
#endregion #endregion
public static bool CanDynamicInvoke(this Expression exp)
{
var test = new TestCanDynamicInvokeExpressionVisitor();
test.Visit(exp);
return test.Result;
}
public static bool IsParameter(this Expression exp) public static bool IsParameter(this Expression exp)
{ {
var test = new TestParameterExpressionVisitor(); var test = new TestParameterExpressionVisitor();
@@ -364,6 +372,23 @@ namespace System.Linq.Expressions
} }
} }
internal class TestCanDynamicInvokeExpressionVisitor : ExpressionVisitor
{
public bool Result { get; private set; } = true;
protected override Expression VisitParameter(ParameterExpression node)
{
if (Result) Result = false;
return node;
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (Result && node.IsExpressionCall()) Result = false;
return base.VisitMethodCall(node);
}
}
internal class GetParameterExpressionVisitor : ExpressionVisitor internal class GetParameterExpressionVisitor : ExpressionVisitor
{ {
public ParameterExpression Result { get; private set; } public ParameterExpression Result { get; private set; }

View File

@@ -136,7 +136,7 @@ namespace FreeSql.Internal
return false; return false;
case ExpressionType.Conditional: case ExpressionType.Conditional:
var condExp = exp as ConditionalExpression; var condExp = exp as ConditionalExpression;
if (condExp.Test.IsParameter() == false) return ReadAnonymousField(_tables, _tableRule, field, parent, ref index, if (condExp.Test.CanDynamicInvoke()) return ReadAnonymousField(_tables, _tableRule, field, parent, ref index,
(bool)Expression.Lambda(condExp.Test).Compile().DynamicInvoke() ? condExp.IfTrue : condExp.IfFalse, select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap); (bool)Expression.Lambda(condExp.Test).Compile().DynamicInvoke() ? condExp.IfTrue : condExp.IfFalse, select, diymemexp, whereGlobalFilter, findIncludeMany, findSubSelectMany, isAllDtoMap);
break; break;
case ExpressionType.Call: case ExpressionType.Call:
@@ -871,7 +871,7 @@ namespace FreeSql.Internal
if (isLeftMapType) oldMapType = tsc.SetMapTypeReturnOld(leftMapColumn.Attribute.MapType); if (isLeftMapType) oldMapType = tsc.SetMapTypeReturnOld(leftMapColumn.Attribute.MapType);
var right = (leftMapColumn != null && var right = (leftMapColumn != null &&
(leftMapColumn.Table.AsTableColumn == leftMapColumn && rightExp.IsParameter() == false)) ? //自动分表 (leftMapColumn.Table.AsTableColumn == leftMapColumn && rightExp.CanDynamicInvoke())) ? //自动分表
formatSql(Expression.Lambda(rightExp).Compile().DynamicInvoke(), leftMapColumn.Attribute.MapType, leftMapColumn, tsc.dbParams) : formatSql(Expression.Lambda(rightExp).Compile().DynamicInvoke(), leftMapColumn.Attribute.MapType, leftMapColumn, tsc.dbParams) :
ExpressionLambdaToSql(rightExp, tsc); ExpressionLambdaToSql(rightExp, tsc);
if (right != "NULL" && isLeftMapType && if (right != "NULL" && isLeftMapType &&
@@ -897,7 +897,7 @@ namespace FreeSql.Internal
{ {
oldMapType = tsc.SetMapTypeReturnOld(rightMapColumn.Attribute.MapType); oldMapType = tsc.SetMapTypeReturnOld(rightMapColumn.Attribute.MapType);
left = (rightMapColumn != null && left = (rightMapColumn != null &&
(rightMapColumn.Table.AsTableColumn == rightMapColumn && leftExp.IsParameter() == false)) ? //自动分表 (rightMapColumn.Table.AsTableColumn == rightMapColumn && leftExp.CanDynamicInvoke())) ? //自动分表
formatSql(Expression.Lambda(leftExp).Compile().DynamicInvoke(), rightMapColumn.Attribute.MapType, rightMapColumn, tsc.dbParams) : formatSql(Expression.Lambda(leftExp).Compile().DynamicInvoke(), rightMapColumn.Attribute.MapType, rightMapColumn, tsc.dbParams) :
ExpressionLambdaToSql(leftExp, tsc); ExpressionLambdaToSql(leftExp, tsc);
if (left != "NULL" && isRightMapType && if (left != "NULL" && isRightMapType &&
@@ -963,7 +963,6 @@ namespace FreeSql.Internal
tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType); tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType);
return $"{left} {oper} {right}"; return $"{left} {oper} {right}";
} }
static ConcurrentDictionary<Type, bool> _dicTypeExistsExpressionCallAttribute = new ConcurrentDictionary<Type, bool>();
static ConcurrentDictionary<Type, ConcurrentDictionary<string, bool>> _dicMethodExistsExpressionCallAttribute = new ConcurrentDictionary<Type, ConcurrentDictionary<string, bool>>(); static ConcurrentDictionary<Type, ConcurrentDictionary<string, bool>> _dicMethodExistsExpressionCallAttribute = new ConcurrentDictionary<Type, ConcurrentDictionary<string, bool>>();
static ConcurrentDictionary<Type, FieldInfo[]> _dicTypeExpressionCallClassContextFields = new ConcurrentDictionary<Type, FieldInfo[]>(); static ConcurrentDictionary<Type, FieldInfo[]> _dicTypeExpressionCallClassContextFields = new ConcurrentDictionary<Type, FieldInfo[]>();
static ThreadLocal<List<BaseDiyMemberExpression>> _subSelectParentDiyMemExps = new ThreadLocal<List<BaseDiyMemberExpression>>(); //子查询的所有父自定义查询,比如分组之后的子查询 static ThreadLocal<List<BaseDiyMemberExpression>> _subSelectParentDiyMemExps = new ThreadLocal<List<BaseDiyMemberExpression>>(); //子查询的所有父自定义查询,比如分组之后的子查询
@@ -1019,7 +1018,7 @@ namespace FreeSql.Internal
//var othercExp = ExpressionLambdaToSqlOther(exp, tsc); //var othercExp = ExpressionLambdaToSqlOther(exp, tsc);
//if (string.IsNullOrEmpty(othercExp) == false) return othercExp; //if (string.IsNullOrEmpty(othercExp) == false) return othercExp;
var expOperand = (exp as UnaryExpression)?.Operand; var expOperand = (exp as UnaryExpression)?.Operand;
if (expOperand.Type.NullableTypeOrThis().IsEnum && exp.IsParameter() == false) if (expOperand.Type.NullableTypeOrThis().IsEnum && exp.CanDynamicInvoke())
return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); //bug: Where(a => a.Id = (int)enum) return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); //bug: Where(a => a.Id = (int)enum)
return ExpressionLambdaToSql(expOperand, tsc); return ExpressionLambdaToSql(expOperand, tsc);
case ExpressionType.Negate: case ExpressionType.Negate:
@@ -1028,7 +1027,7 @@ namespace FreeSql.Internal
case ExpressionType.Conditional: case ExpressionType.Conditional:
var condExp = exp as ConditionalExpression; var condExp = exp as ConditionalExpression;
var conditionalTestOldMapType = tsc.SetMapTypeReturnOld(null); var conditionalTestOldMapType = tsc.SetMapTypeReturnOld(null);
if (condExp.Test.IsParameter()) if (condExp.Test.CanDynamicInvoke() == false)
{ {
var condExp2 = condExp.Test; var condExp2 = condExp.Test;
if (condExp2.NodeType == ExpressionType.MemberAccess) condExp2 = Expression.Equal(condExp2, Expression.Constant(true)); if (condExp2.NodeType == ExpressionType.MemberAccess) condExp2 = Expression.Equal(condExp2, Expression.Constant(true));
@@ -1055,10 +1054,7 @@ namespace FreeSql.Internal
case ExpressionType.Call: case ExpressionType.Call:
tsc.mapType = null; tsc.mapType = null;
var exp3 = exp as MethodCallExpression; var exp3 = exp as MethodCallExpression;
if (exp3.Object == null && ( if (exp3.IsExpressionCall())
_dicTypeExistsExpressionCallAttribute.GetOrAdd(exp3.Method.DeclaringType, dttp => dttp.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()) ||
exp3.Method.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()
))
{ {
var ecc = new ExpressionCallContext var ecc = new ExpressionCallContext
{ {
@@ -1097,10 +1093,7 @@ namespace FreeSql.Internal
if (exp3.Arguments[a].NodeType == ExpressionType.Call) //判断如果参数也是标记 ExpressionCall if (exp3.Arguments[a].NodeType == ExpressionType.Call) //判断如果参数也是标记 ExpressionCall
{ {
var exp3ArgsACallExp = exp3.Arguments[a] as MethodCallExpression; var exp3ArgsACallExp = exp3.Arguments[a] as MethodCallExpression;
if (exp3ArgsACallExp.Object == null && ( if (exp3ArgsACallExp.IsExpressionCall())
_dicTypeExistsExpressionCallAttribute.GetOrAdd(exp3ArgsACallExp.Method.DeclaringType, dttp => dttp.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()) ||
exp3ArgsACallExp.Method.GetCustomAttributes(typeof(ExpressionCallAttribute), true).Any()
))
isdyInvoke = false; isdyInvoke = false;
} }
if (isdyInvoke) if (isdyInvoke)
@@ -1824,7 +1817,7 @@ namespace FreeSql.Internal
} }
other3Exp = ExpressionLambdaToSqlOther(exp3, tsc); other3Exp = ExpressionLambdaToSqlOther(exp3, tsc);
if (string.IsNullOrEmpty(other3Exp) == false) return other3Exp; if (string.IsNullOrEmpty(other3Exp) == false) return other3Exp;
if (exp3.IsParameter() == false) return formatSql(Expression.Lambda(exp3).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); if (exp3.CanDynamicInvoke()) return formatSql(Expression.Lambda(exp3).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams);
if (exp3.Method.DeclaringType == typeof(Enumerable)) throw new Exception(CoreStrings.Not_Implemented_Expression_UseAsSelect(exp3, exp3.Method.Name, (exp3.Arguments.Count > 1 ? "..." : ""))); if (exp3.Method.DeclaringType == typeof(Enumerable)) throw new Exception(CoreStrings.Not_Implemented_Expression_UseAsSelect(exp3, exp3.Method.Name, (exp3.Arguments.Count > 1 ? "..." : "")));
throw new Exception(CoreStrings.Not_Implemented_Expression(exp3)); throw new Exception(CoreStrings.Not_Implemented_Expression(exp3));
case ExpressionType.Parameter: case ExpressionType.Parameter:
@@ -2230,7 +2223,7 @@ namespace FreeSql.Internal
} }
if (dicExpressionOperator.TryGetValue(expBinary.NodeType, out var tryoper) == false) if (dicExpressionOperator.TryGetValue(expBinary.NodeType, out var tryoper) == false)
{ {
if (exp.IsParameter() == false) return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams); if (exp.CanDynamicInvoke()) return formatSql(Expression.Lambda(exp).Compile().DynamicInvoke(), tsc.mapType, tsc.mapColumnTmp, tsc.dbParams);
return ""; return "";
} }
switch (expBinary.NodeType) switch (expBinary.NodeType)
@@ -2392,11 +2385,11 @@ namespace FreeSql.Internal
var expStackFirstMem = expStack.First() as MemberExpression; var expStackFirstMem = expStack.First() as MemberExpression;
if (expStackFirstMem.Expression?.NodeType == ExpressionType.Constant) if (expStackFirstMem.Expression?.NodeType == ExpressionType.Constant)
firstValue = (expStackFirstMem.Expression as ConstantExpression)?.Value; firstValue = (expStackFirstMem.Expression as ConstantExpression)?.Value;
else if (exp.IsParameter() == false) else if (exp.CanDynamicInvoke())
return Expression.Lambda(exp).Compile().DynamicInvoke(); return Expression.Lambda(exp).Compile().DynamicInvoke();
break; break;
case ExpressionType.Call: case ExpressionType.Call:
if (exp.IsParameter() == false) if (exp.CanDynamicInvoke())
return Expression.Lambda(exp).Compile().DynamicInvoke(); return Expression.Lambda(exp).Compile().DynamicInvoke();
break; break;
} }
@@ -2417,7 +2410,7 @@ namespace FreeSql.Internal
} }
return Expression.Lambda(exp).Compile().DynamicInvoke(); return Expression.Lambda(exp).Compile().DynamicInvoke();
} }
if (exp.IsParameter() == false) if (exp.CanDynamicInvoke())
return Expression.Lambda(exp).Compile().DynamicInvoke(); return Expression.Lambda(exp).Compile().DynamicInvoke();
success = false; success = false;
return null; return null;