[#148] 支持like操作符的提取

This commit is contained in:
xuejiaming 2022-07-18 23:52:11 +08:00
parent 3f44744325
commit a7e987d163
8 changed files with 992 additions and 848 deletions

View File

@ -24,12 +24,12 @@ namespace ShardingCore.Core.VirtualRoutes
Equal, Equal,
[Description("!=")] [Description("!=")]
NotEqual, NotEqual,
// [Description("%w%")] [Description("%w%")]
// AllLike, AllLike,
// [Description("%w")] [Description("%w")]
// StartLike, StartLike,
// [Description("w%")] [Description("w%")]
// EndLike EndLike
// [Description("Contains")] // [Description("Contains")]
// BeContains // BeContains
} }

View File

@ -80,11 +80,26 @@ namespace ShardingCore.Extensions
/// 是否是集合contains方法 /// 是否是集合contains方法
/// </summary> /// </summary>
/// <param name="express"></param> /// <param name="express"></param>
/// <param name="methodName"></param>
/// <returns></returns> /// <returns></returns>
public static bool IsEnumerableContains(this MethodCallExpression express, string methodName) public static bool IsEnumerableContains(this MethodCallExpression express)
{ {
return express.Method.DeclaringType.Namespace.IsIn("System.Linq", "System.Collections.Generic") && methodName == nameof(IList.Contains); var methodName = express.Method.Name;
return methodName == nameof(IList.Contains)&& (express.Method.DeclaringType?.Namespace.IsInEnumerable()??false);
}
public static bool IsStringContains(this MethodCallExpression express)
{
var methodName = express.Method.Name;
return methodName == nameof(string.Contains)&& (express.Method.DeclaringType==typeof(string));
}
public static bool IsStringStartWith(this MethodCallExpression express)
{
var methodName = express.Method.Name;
return methodName == nameof(string.StartsWith)&& (express.Method.DeclaringType==typeof(string));
}
public static bool IsStringEndWith(this MethodCallExpression express)
{
var methodName = express.Method.Name;
return methodName == nameof(string.EndsWith)&& (express.Method.DeclaringType==typeof(string));
} }
/// <summary> /// <summary>
/// 是否是equal方法 /// 是否是equal方法

View File

@ -47,9 +47,14 @@ namespace ShardingCore.Extensions
{ {
return !source.IsEmpty(); return !source.IsEmpty();
} }
public static bool IsIn<T>(this T thisValue, params T[] values)
private static readonly HashSet<string> _enumerableContainsNamespace = new HashSet<string>()
{ {
return values.Contains(thisValue); "System.Linq", "System.Collections.Generic"
};
public static bool IsInEnumerable(this string thisValue)
{
return _enumerableContainsNamespace.Contains(thisValue);
} }
/// <summary> /// <summary>
/// 按size分区,每个区size个数目 /// 按size分区,每个区size个数目

View File

@ -25,19 +25,45 @@ namespace ShardingCore.Core.Internal.Visitors
*/ */
public class QueryableRouteShardingTableDiscoverVisitor : ShardingExpressionVisitor public class QueryableRouteShardingTableDiscoverVisitor : ShardingExpressionVisitor
{ {
private static readonly Func<bool, ExpressionType, ShardingOperatorEnum> _shardingOperatorFunc =
(conditionOnRight, nodeType) =>
{
var op = nodeType switch
{
ExpressionType.GreaterThan => conditionOnRight
? ShardingOperatorEnum.GreaterThan
: ShardingOperatorEnum.LessThan,
ExpressionType.GreaterThanOrEqual => conditionOnRight
? ShardingOperatorEnum.GreaterThanOrEqual
: ShardingOperatorEnum.LessThanOrEqual,
ExpressionType.LessThan => conditionOnRight
? ShardingOperatorEnum.LessThan
: ShardingOperatorEnum.GreaterThan,
ExpressionType.LessThanOrEqual => conditionOnRight
? ShardingOperatorEnum.LessThanOrEqual
: ShardingOperatorEnum.GreaterThanOrEqual,
ExpressionType.Equal => ShardingOperatorEnum.Equal,
ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual,
_ => ShardingOperatorEnum.UnKnown
};
return op;
};
private readonly EntityMetadata _entityMetadata; private readonly EntityMetadata _entityMetadata;
private readonly Func<object, ShardingOperatorEnum, string, Func<string, bool>> _keyToTailWithFilter; private readonly Func<object, ShardingOperatorEnum, string, Func<string, bool>> _keyToTailWithFilter;
/// <summary> /// <summary>
/// 是否是分表路由 /// 是否是分表路由
/// </summary> /// </summary>
private readonly bool _shardingTableRoute; private readonly bool _shardingTableRoute;
private LambdaExpression _entityLambdaExpression; private LambdaExpression _entityLambdaExpression;
private readonly ShardingPredicateResult _noShardingPredicateResult = new ShardingPredicateResult(false, null); private readonly ShardingPredicateResult _noShardingPredicateResult = new ShardingPredicateResult(false, null);
private bool isIgnoreQueryFilter; private bool isIgnoreQueryFilter;
private RoutePredicateExpression _where = RoutePredicateExpression.Default; private RoutePredicateExpression _where = RoutePredicateExpression.Default;
public QueryableRouteShardingTableDiscoverVisitor(EntityMetadata entityMetadata, Func<object, ShardingOperatorEnum, string, Func<string, bool>> keyToTailWithFilter, bool shardingTableRoute) public QueryableRouteShardingTableDiscoverVisitor(EntityMetadata entityMetadata,
Func<object, ShardingOperatorEnum, string, Func<string, bool>> keyToTailWithFilter, bool shardingTableRoute)
{ {
_entityMetadata = entityMetadata; _entityMetadata = entityMetadata;
_keyToTailWithFilter = keyToTailWithFilter; _keyToTailWithFilter = keyToTailWithFilter;
@ -50,7 +76,6 @@ namespace ShardingCore.Core.Internal.Visitors
/// <returns></returns> /// <returns></returns>
public RoutePredicateExpression GetRouteParseExpression() public RoutePredicateExpression GetRouteParseExpression()
{ {
if (_entityMetadata.QueryFilterExpression != null && !isIgnoreQueryFilter) if (_entityMetadata.QueryFilterExpression != null && !isIgnoreQueryFilter)
{ {
if (_entityLambdaExpression == null) if (_entityLambdaExpression == null)
@ -59,7 +84,8 @@ namespace ShardingCore.Core.Internal.Visitors
} }
else else
{ {
var body = Expression.AndAlso(_entityLambdaExpression.Body, _entityMetadata.QueryFilterExpression.Body); var body = Expression.AndAlso(_entityLambdaExpression.Body,
_entityMetadata.QueryFilterExpression.Body);
_entityLambdaExpression = Expression.Lambda(body, _entityLambdaExpression.Parameters[0]); _entityLambdaExpression = Expression.Lambda(body, _entityLambdaExpression.Parameters[0]);
} }
} }
@ -69,6 +95,7 @@ namespace ShardingCore.Core.Internal.Visitors
var newWhere = Resolve(_entityLambdaExpression); var newWhere = Resolve(_entityLambdaExpression);
_where = _where.And(newWhere); _where = _where.And(newWhere);
} }
return _where; return _where;
} }
@ -77,7 +104,8 @@ namespace ShardingCore.Core.Internal.Visitors
{ {
if (expression is MemberExpression member) if (expression is MemberExpression member)
{ {
if (member.Expression?.Type == _entityMetadata.EntityType|| MemberExpressionIsConvertAndOriginalIsEntityType(member)) if (member.Expression?.Type == _entityMetadata.EntityType ||
MemberExpressionIsConvertAndOriginalIsEntityType(member))
{ {
var isShardingKey = false; var isShardingKey = false;
if (_shardingTableRoute) if (_shardingTableRoute)
@ -100,6 +128,7 @@ namespace ShardingCore.Core.Internal.Visitors
shardingPredicateResult = _noShardingPredicateResult; shardingPredicateResult = _noShardingPredicateResult;
return false; return false;
} }
/// <summary> /// <summary>
/// 成员表达式是强转并且强转前的类型是当前对象 /// 成员表达式是强转并且强转前的类型是当前对象
/// </summary> /// </summary>
@ -111,6 +140,7 @@ namespace ShardingCore.Core.Internal.Visitors
member.Expression is UnaryExpression unaryExpression && member.Expression is UnaryExpression unaryExpression &&
unaryExpression.Operand.Type == _entityMetadata.EntityType; unaryExpression.Operand.Type == _entityMetadata.EntityType;
} }
/// <summary> /// <summary>
/// 方法是否包含shardingKey xxx.invoke(shardingkey) eg. <code>o=>new[]{}.Contains(o.Id)</code> /// 方法是否包含shardingKey xxx.invoke(shardingkey) eg. <code>o=>new[]{}.Contains(o.Id)</code>
/// </summary> /// </summary>
@ -126,6 +156,7 @@ namespace ShardingCore.Core.Internal.Visitors
return result; return result;
} }
} }
return _noShardingPredicateResult; return _noShardingPredicateResult;
} }
@ -138,14 +169,25 @@ namespace ShardingCore.Core.Internal.Visitors
return result; return result;
} }
} }
return _noShardingPredicateResult; return _noShardingPredicateResult;
} }
private bool IsConstantOrMember(Expression expression)
/// <summary>
/// 表达式是否可以获取值
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
private bool ExpressionCanGetValue(Expression expression)
{ {
return expression is ConstantExpression return expression is ConstantExpression
|| (expression is MemberExpression member && (member.Expression is ConstantExpression || member.Expression is MemberExpression || member.Expression is MemberExpression)) || (expression is MemberExpression member && (member.Expression is ConstantExpression ||
member.Expression is MemberExpression ||
member.Expression is MemberExpression))
|| expression is MethodCallExpression || expression is MethodCallExpression
|| (expression is UnaryExpression unaryExpression && unaryExpression.NodeType is ExpressionType.Convert ) ; || (expression is UnaryExpression unaryExpression &&
unaryExpression.NodeType is ExpressionType.Convert)
|| expression.NodeType == ExpressionType.ArrayIndex;
} }
private bool IsMethodCall(Expression expression) private bool IsMethodCall(Expression expression)
@ -157,8 +199,12 @@ namespace ShardingCore.Core.Internal.Visitors
{ {
switch (node.Method.Name) switch (node.Method.Name)
{ {
case nameof(EntityFrameworkQueryableExtensions.IgnoreQueryFilters): isIgnoreQueryFilter = true; break; case nameof(EntityFrameworkQueryableExtensions.IgnoreQueryFilters):
case nameof(Queryable.Where): CombineEntityLambdaExpression(node); break; isIgnoreQueryFilter = true;
break;
case nameof(Queryable.Where):
CombineEntityLambdaExpression(node);
break;
} }
return base.VisitMethodCall(node); return base.VisitMethodCall(node);
@ -196,9 +242,10 @@ namespace ShardingCore.Core.Internal.Visitors
return Resolve(expression); return Resolve(expression);
} }
//解析左右结构属性判断
if (expression is BinaryExpression binaryExpression) //解析二元运算符 if (expression is BinaryExpression binaryExpression) //解析二元运算符
{ {
return ParseGetWhere(binaryExpression); return ParsePropertyCondition(binaryExpression);
} }
if (expression is UnaryExpression unary) //解析一元运算符 if (expression is UnaryExpression unary) //解析一元运算符
@ -214,12 +261,13 @@ namespace ShardingCore.Core.Internal.Visitors
{ {
return ResolveInFunc(methodCallExpression, true); return ResolveInFunc(methodCallExpression, true);
} }
return RoutePredicateExpression.Default; return RoutePredicateExpression.Default;
} }
private RoutePredicateExpression ResolveInFunc(MethodCallExpression methodCallExpression, bool @in) private RoutePredicateExpression ResolveInFunc(MethodCallExpression methodCallExpression, bool @in)
{ {
if (methodCallExpression.IsEnumerableContains(methodCallExpression.Method.Name)) if (methodCallExpression.IsEnumerableContains())
{ {
var shardingPredicateResult = IsMethodWrapShardingKey(methodCallExpression); var shardingPredicateResult = IsMethodWrapShardingKey(methodCallExpression);
if (shardingPredicateResult.IsShardingKey) if (shardingPredicateResult.IsShardingKey)
@ -247,26 +295,67 @@ namespace ShardingCore.Core.Internal.Visitors
if (arrayObject != null) if (arrayObject != null)
{ {
var contains=@in ? RoutePredicateExpression.DefaultFalse : RoutePredicateExpression.Default; var contains = @in ? RoutePredicateExpression.DefaultFalse : RoutePredicateExpression.Default;
if (arrayObject is IEnumerable enumerableObj) if (arrayObject is IEnumerable enumerableObj)
{ {
foreach (var shardingValue in enumerableObj) foreach (var shardingValue in enumerableObj)
{ {
var eq = _keyToTailWithFilter(shardingValue, @in ? ShardingOperatorEnum.Equal : ShardingOperatorEnum.NotEqual, shardingPredicateResult.ShardingPropertyName); var eq = _keyToTailWithFilter(shardingValue,
@in ? ShardingOperatorEnum.Equal : ShardingOperatorEnum.NotEqual,
shardingPredicateResult.ShardingPropertyName);
if (@in) if (@in)
contains = contains.Or(new RoutePredicateExpression(eq)); contains = contains.Or(new RoutePredicateExpression(eq));
else else
contains = contains.And(new RoutePredicateExpression(eq)); contains = contains.And(new RoutePredicateExpression(eq));
} }
} }
return contains; return contains;
} }
} }
} }
else if (methodCallExpression.IsStringContains())
if (methodCallExpression.IsNamedEquals()) {
if (IsShardingKey(methodCallExpression.Object, out var shardingPredicateResult))
{
if (methodCallExpression.Arguments.Count == 1)
{
var shardingValue = GetExpressionValue(methodCallExpression.Arguments[0]);
var keyToTailWithFilter = _keyToTailWithFilter(shardingValue, ShardingOperatorEnum.AllLike,
shardingPredicateResult.ShardingPropertyName);
return new RoutePredicateExpression(keyToTailWithFilter);
}
}
}
else if (methodCallExpression.IsStringStartWith())
{
if (IsShardingKey(methodCallExpression.Object, out var shardingPredicateResult))
{
if (methodCallExpression.Arguments.Count == 1)
{
var shardingValue = GetExpressionValue(methodCallExpression.Arguments[0]);
var keyToTailWithFilter = _keyToTailWithFilter(shardingValue, ShardingOperatorEnum.StartLike,
shardingPredicateResult.ShardingPropertyName);
return new RoutePredicateExpression(keyToTailWithFilter);
}
}
}
else if (methodCallExpression.IsStringEndWith())
{
if (IsShardingKey(methodCallExpression.Object, out var shardingPredicateResult))
{
if (methodCallExpression.Arguments.Count == 1)
{
var shardingValue = GetExpressionValue(methodCallExpression.Arguments[0]);
var keyToTailWithFilter = _keyToTailWithFilter(shardingValue, ShardingOperatorEnum.EndLike,
shardingPredicateResult.ShardingPropertyName);
return new RoutePredicateExpression(keyToTailWithFilter);
}
}
}
else if (methodCallExpression.IsNamedEquals())
{ {
//"".equals(o.id) //"".equals(o.id)
var shardingPredicateResult = IsMethodWrapShardingKey(methodCallExpression); var shardingPredicateResult = IsMethodWrapShardingKey(methodCallExpression);
@ -275,7 +364,8 @@ namespace ShardingCore.Core.Internal.Visitors
var shardingValue = GetExpressionValue(methodCallExpression.Object); var shardingValue = GetExpressionValue(methodCallExpression.Object);
if (shardingValue != null) if (shardingValue != null)
{ {
var keyToTailWithFilter = _keyToTailWithFilter(shardingValue, ShardingOperatorEnum.Equal, shardingPredicateResult.ShardingPropertyName); var keyToTailWithFilter = _keyToTailWithFilter(shardingValue, ShardingOperatorEnum.Equal,
shardingPredicateResult.ShardingPropertyName);
return new RoutePredicateExpression(keyToTailWithFilter); return new RoutePredicateExpression(keyToTailWithFilter);
} }
} }
@ -297,7 +387,8 @@ namespace ShardingCore.Core.Internal.Visitors
if (shardingValue != default) if (shardingValue != default)
{ {
var keyToTailWithFilter = _keyToTailWithFilter(shardingValue, ShardingOperatorEnum.Equal, shardingPredicateResult.ShardingPropertyName); var keyToTailWithFilter = _keyToTailWithFilter(shardingValue, ShardingOperatorEnum.Equal,
shardingPredicateResult.ShardingPropertyName);
return new RoutePredicateExpression(keyToTailWithFilter); return new RoutePredicateExpression(keyToTailWithFilter);
} }
} }
@ -313,30 +404,45 @@ namespace ShardingCore.Core.Internal.Visitors
return RoutePredicateExpression.Default; return RoutePredicateExpression.Default;
} }
private ShardingOperatorEnum GetParseCompareShardingOperatorEnum(bool conditionOnRight, ExpressionType expressionType, int compare) private ShardingOperatorEnum GetParseCompareShardingOperatorEnum(bool conditionOnRight,
ExpressionType expressionType, int compare)
{ {
if (compare == 1) if (compare == 1)
{ {
return expressionType switch return expressionType switch
{ {
ExpressionType.GreaterThanOrEqual => conditionOnRight ? ShardingOperatorEnum.GreaterThan : ShardingOperatorEnum.LessThan,//1 ExpressionType.GreaterThanOrEqual => conditionOnRight
ExpressionType.GreaterThan => ShardingOperatorEnum.UnKnown,//无 ? ShardingOperatorEnum.GreaterThan
ExpressionType.LessThanOrEqual => ShardingOperatorEnum.UnKnown,//1,0,-1 = 无 : ShardingOperatorEnum.LessThan, //1
ExpressionType.LessThan => conditionOnRight ? ShardingOperatorEnum.LessThanOrEqual : ShardingOperatorEnum.GreaterThanOrEqual,//0,-1 ExpressionType.GreaterThan => ShardingOperatorEnum.UnKnown, //无
ExpressionType.Equal => conditionOnRight ? ShardingOperatorEnum.GreaterThan : ShardingOperatorEnum.LessThan,//1 ExpressionType.LessThanOrEqual => ShardingOperatorEnum.UnKnown, //1,0,-1 = 无
ExpressionType.LessThan => conditionOnRight
? ShardingOperatorEnum.LessThanOrEqual
: ShardingOperatorEnum.GreaterThanOrEqual, //0,-1
ExpressionType.Equal => conditionOnRight
? ShardingOperatorEnum.GreaterThan
: ShardingOperatorEnum.LessThan, //1
ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual, ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual,
_ => ShardingOperatorEnum.UnKnown _ => ShardingOperatorEnum.UnKnown
}; };
} }
if (compare == 0) if (compare == 0)
{ {
return expressionType switch return expressionType switch
{ {
ExpressionType.GreaterThanOrEqual => conditionOnRight ? ShardingOperatorEnum.GreaterThanOrEqual : ShardingOperatorEnum.LessThanOrEqual,//0,1 ExpressionType.GreaterThanOrEqual => conditionOnRight
ExpressionType.GreaterThan => conditionOnRight ? ShardingOperatorEnum.GreaterThan: ShardingOperatorEnum.LessThan,//1 ? ShardingOperatorEnum.GreaterThanOrEqual
ExpressionType.LessThanOrEqual => conditionOnRight ? ShardingOperatorEnum.LessThanOrEqual : ShardingOperatorEnum.GreaterThanOrEqual,//0,-1 : ShardingOperatorEnum.LessThanOrEqual, //0,1
ExpressionType.LessThan => conditionOnRight ? ShardingOperatorEnum.LessThan : ShardingOperatorEnum.GreaterThan,//-1 ExpressionType.GreaterThan => conditionOnRight
? ShardingOperatorEnum.GreaterThan
: ShardingOperatorEnum.LessThan, //1
ExpressionType.LessThanOrEqual => conditionOnRight
? ShardingOperatorEnum.LessThanOrEqual
: ShardingOperatorEnum.GreaterThanOrEqual, //0,-1
ExpressionType.LessThan => conditionOnRight
? ShardingOperatorEnum.LessThan
: ShardingOperatorEnum.GreaterThan, //-1
ExpressionType.Equal => ShardingOperatorEnum.Equal, ExpressionType.Equal => ShardingOperatorEnum.Equal,
ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual, ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual,
_ => ShardingOperatorEnum.UnKnown _ => ShardingOperatorEnum.UnKnown
@ -347,146 +453,155 @@ namespace ShardingCore.Core.Internal.Visitors
{ {
return expressionType switch return expressionType switch
{ {
ExpressionType.GreaterThanOrEqual => ShardingOperatorEnum.UnKnown,//-1,0,1 ExpressionType.GreaterThanOrEqual => ShardingOperatorEnum.UnKnown, //-1,0,1
ExpressionType.GreaterThan => conditionOnRight? ShardingOperatorEnum.GreaterThanOrEqual: ShardingOperatorEnum.LessThanOrEqual,//0,1 ExpressionType.GreaterThan => conditionOnRight
ExpressionType.LessThanOrEqual => conditionOnRight? ShardingOperatorEnum.LessThan:ShardingOperatorEnum.GreaterThan,//-1 ? ShardingOperatorEnum.GreaterThanOrEqual
ExpressionType.LessThan =>ShardingOperatorEnum.UnKnown,//无 : ShardingOperatorEnum.LessThanOrEqual, //0,1
ExpressionType.Equal => conditionOnRight ? ShardingOperatorEnum.LessThan : ShardingOperatorEnum.GreaterThan,//1 ExpressionType.LessThanOrEqual => conditionOnRight
? ShardingOperatorEnum.LessThan
: ShardingOperatorEnum.GreaterThan, //-1
ExpressionType.LessThan => ShardingOperatorEnum.UnKnown, //无
ExpressionType.Equal => conditionOnRight
? ShardingOperatorEnum.LessThan
: ShardingOperatorEnum.GreaterThan, //1
ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual, ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual,
_ => ShardingOperatorEnum.UnKnown _ => ShardingOperatorEnum.UnKnown
}; };
} }
return ShardingOperatorEnum.UnKnown; return ShardingOperatorEnum.UnKnown;
} }
private RoutePredicateExpression ParseCompare(MethodCallExpression methodCallExpression, Expression left,Expression right, ExpressionType expressionType, int compare)
private RoutePredicateExpression ParseCompare(MethodCallExpression methodCallExpression, Expression left,
Expression right, ExpressionType expressionType, int compare)
{ {
if (left.Type == right.Type) if (left.Type == right.Type)
{ {
if (methodCallExpression.Method.ReturnType == typeof(int)) if (methodCallExpression.Method.ReturnType == typeof(int))
{ {
return ParseCondition0(left, right, conditionOnRight => GetParseCompareShardingOperatorEnum(conditionOnRight, expressionType, compare)); return ParseCondition0(left, right, expressionType,
(conditionOnRight, nodeType) =>
GetParseCompareShardingOperatorEnum(conditionOnRight, nodeType, compare));
} }
} }
return RoutePredicateExpression.Default; return RoutePredicateExpression.Default;
} }
private RoutePredicateExpression ParseCondition0(Expression left, Expression right,Func<bool,ShardingOperatorEnum> shardingOperatorFunc) private RoutePredicateExpression ParseConditionOnRight0(bool conditionOnRight,
ShardingPredicateResult predicateLeftResult,
Expression conditionExpression, ExpressionType expressionType)
{ {
if (ExpressionCanGetValue(conditionExpression))
bool conditionOnRight = false;
string shardingPropertyName = null;
object value = default;
if (IsShardingKey(left, out var predicateLeftResult) && IsConstantOrMember(right))
{ {
if (predicateLeftResult.IsShardingKey) var shardingPropertyName = predicateLeftResult.ShardingPropertyName;
{ var value = GetExpressionValue(conditionExpression);
conditionOnRight = true;
shardingPropertyName = predicateLeftResult.ShardingPropertyName; if (shardingPropertyName == null || value == default)
value = GetExpressionValue(right);
}
else
return RoutePredicateExpression.Default;
}
else if (IsShardingKey(right, out var predicateRightResult) && IsConstantOrMember(left))
{
if (predicateRightResult.IsShardingKey)
{
conditionOnRight = false;
shardingPropertyName = predicateRightResult.ShardingPropertyName;
value = GetExpressionValue(left);
}
else
return RoutePredicateExpression.Default; return RoutePredicateExpression.Default;
var op = _shardingOperatorFunc(conditionOnRight, expressionType);
return new RoutePredicateExpression(_keyToTailWithFilter(value, op, shardingPropertyName));
} }
else else
return RoutePredicateExpression.Default; return RoutePredicateExpression.Default;
var op = shardingOperatorFunc(conditionOnRight);
if (shardingPropertyName == null || value == default)
return RoutePredicateExpression.Default;
return new RoutePredicateExpression( _keyToTailWithFilter(value, op, shardingPropertyName));
} }
private RoutePredicateExpression ParseCondition(BinaryExpression binaryExpression) private RoutePredicateExpression ParseCondition0(Expression left, Expression right,
ExpressionType expressionType, Func<bool, ExpressionType, ShardingOperatorEnum> shardingOperatorFunc)
{ {
if (binaryExpression.IsNamedComparison(out var methodCallExpression)) if (IsShardingKey(left, out var predicateLeftResult))
{ {
if (methodCallExpression.GetComparisonLeftAndRight(out var result)) return ParseConditionOnRight0(true, predicateLeftResult, right, expressionType);
{ }
return ParseCompare(methodCallExpression, result.Left, result.Right, else if (IsShardingKey(right, out var predicateRightResult))
binaryExpression.NodeType, (int)GetExpressionValue(binaryExpression.Right)); {
} return ParseConditionOnRight0(false, predicateRightResult, left, expressionType);
}
else
return RoutePredicateExpression.Default;
}
private RoutePredicateExpression ParseNamedComparison(BinaryExpression binaryExpression,
MethodCallExpression methodCallExpression)
{
if (methodCallExpression.GetComparisonLeftAndRight(out var result))
{
return ParseCompare(methodCallExpression, result.Left, result.Right,
binaryExpression.NodeType, (int)GetExpressionValue(binaryExpression.Right));
}
return RoutePredicateExpression.Default;
}
private RoutePredicateExpression ParsePropertyCondition(BinaryExpression binaryExpression)
{
// RoutePredicateExpression left = RoutePredicateExpression.Default;
// RoutePredicateExpression right = RoutePredicateExpression.Default;
//左边是属性判断是否是分片的
if (IsShardingKey(binaryExpression.Left, out var predicateLeftResult))
{
return ParseConditionOnRight0(true, predicateLeftResult, binaryExpression.Right,
binaryExpression.NodeType);
}
else if (IsShardingKey(binaryExpression.Right, out var predicateRightResult))
{
return ParseConditionOnRight0(false, predicateRightResult, binaryExpression.Left,
binaryExpression.NodeType);
}
else if (binaryExpression.IsNamedComparison(out var methodCallExpression))
{
return ParseNamedComparison(binaryExpression, methodCallExpression);
} }
else else
{ {
RoutePredicateExpression left = RoutePredicateExpression.Default;
RoutePredicateExpression right = RoutePredicateExpression.Default;
return ParseCondition0(binaryExpression.Left, binaryExpression.Right, conditionOnRight => //递归获取
if (binaryExpression.Left is BinaryExpression binaryExpression1)
left = ParsePropertyCondition(binaryExpression1);
if (binaryExpression.Right is BinaryExpression binaryExpression2)
right = ParsePropertyCondition(binaryExpression2);
if (binaryExpression.Left is MethodCallExpression methodCallLeftExpression)
{ {
var op = binaryExpression.NodeType switch if (!methodCallLeftExpression.IsNamedComparison())
{ {
ExpressionType.GreaterThan => conditionOnRight ? ShardingOperatorEnum.GreaterThan : ShardingOperatorEnum.LessThan, left = Resolve(methodCallLeftExpression);
ExpressionType.GreaterThanOrEqual => conditionOnRight ? ShardingOperatorEnum.GreaterThanOrEqual : ShardingOperatorEnum.LessThanOrEqual, }
ExpressionType.LessThan => conditionOnRight ? ShardingOperatorEnum.LessThan : ShardingOperatorEnum.GreaterThan,
ExpressionType.LessThanOrEqual => conditionOnRight ? ShardingOperatorEnum.LessThanOrEqual : ShardingOperatorEnum.GreaterThanOrEqual,
ExpressionType.Equal => ShardingOperatorEnum.Equal,
ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual,
_ => ShardingOperatorEnum.UnKnown
};
return op;
});
}
return RoutePredicateExpression.Default;
}
private RoutePredicateExpression ParseGetWhere(BinaryExpression binaryExpression)
{
RoutePredicateExpression left = RoutePredicateExpression.Default;
RoutePredicateExpression right = RoutePredicateExpression.Default;
//递归获取
if (binaryExpression.Left is BinaryExpression binaryExpression1)
left = ParseGetWhere(binaryExpression1);
if (binaryExpression.Right is BinaryExpression binaryExpression2)
right = ParseGetWhere(binaryExpression2);
if (binaryExpression.Left is MethodCallExpression methodCallLeftExpression)
{
if (!methodCallLeftExpression.IsNamedComparison())
{
left = Resolve(methodCallLeftExpression);
} }
}
if (binaryExpression.Right is MethodCallExpression methodCallRightExpression) if (binaryExpression.Right is MethodCallExpression methodCallRightExpression)
{
if (!methodCallRightExpression.IsNamedComparison())
{ {
right = Resolve(methodCallRightExpression); if (!methodCallRightExpression.IsNamedComparison())
{
right = Resolve(methodCallRightExpression);
}
} }
}
if (binaryExpression.Left is UnaryExpression unaryExpression1 && (binaryExpression.Right is MemberExpression&& !IsShardingKey(binaryExpression.Right, out var _))) if (binaryExpression.Left is UnaryExpression unaryExpression1 &&
left = Resolve(unaryExpression1); (binaryExpression.Right is MemberExpression))
if (binaryExpression.Right is UnaryExpression unaryExpression2 && (binaryExpression.Left is MemberExpression && !IsShardingKey(binaryExpression.Left, out var _))) left = Resolve(unaryExpression1);
right = Resolve(unaryExpression2); if (binaryExpression.Right is UnaryExpression unaryExpression2 &&
(binaryExpression.Left is MemberExpression))
right = Resolve(unaryExpression2);
//组合 //组合
if (binaryExpression.NodeType == ExpressionType.AndAlso) if (binaryExpression.NodeType == ExpressionType.AndAlso)
{ {
return left.And(right); return left.And(right);
} }
else if (binaryExpression.NodeType == ExpressionType.OrElse) else if (binaryExpression.NodeType == ExpressionType.OrElse)
{ {
return left.Or(right); return left.Or(right);
} }
//单个 else
else {
{ return RoutePredicateExpression.Default;
return ParseCondition(binaryExpression); }
} }
} }
} }
@ -502,10 +617,12 @@ namespace ShardingCore.Core.Internal.Visitors
IsShardingKey = isShardingKey; IsShardingKey = isShardingKey;
ShardingPropertyName = shardingPropertyName; ShardingPropertyName = shardingPropertyName;
} }
/// <summary> /// <summary>
/// 是否是分片字段 /// 是否是分片字段
/// </summary> /// </summary>
public bool IsShardingKey { get; } public bool IsShardingKey { get; }
/// <summary> /// <summary>
/// 分片字段名称 /// 分片字段名称
/// </summary> /// </summary>

View File

@ -44,15 +44,6 @@ namespace ShardingCore.Utils
return visitor.GetRouteParseExpression(); return visitor.GetRouteParseExpression();
} }
public static RoutePredicateExpression GetRouteParseExpressionV2(IQueryable queryable, EntityMetadata entityMetadata, Func<object, ShardingOperatorEnum,string, Func<string, bool>> keyToTailExpression,bool shardingTableRoute)
{
QueryableRouteShardingTableDiscoverVisitorV2 visitor = new QueryableRouteShardingTableDiscoverVisitorV2(entityMetadata, keyToTailExpression, shardingTableRoute);
visitor.Visit(queryable.Expression);
return visitor.GetRouteParseExpression();
}
} }
} }

View File

@ -12,14 +12,16 @@ namespace ShardingCore.CommonTest
{ {
private readonly EntityMetadata _testEntityMetadata; private readonly EntityMetadata _testEntityMetadata;
private readonly List<string> _allDataSources; private readonly List<string> _allDataSources;
public ShardingDataSourceMod() public ShardingDataSourceMod()
{ {
var entityMetadata = new EntityMetadata(typeof(TestEntity)); var entityMetadata = new EntityMetadata(typeof(TestEntity));
var entityMetadataDataSourceBuilder = EntityMetadataDataSourceBuilder<TestEntity>.CreateEntityMetadataDataSourceBuilder(entityMetadata); var entityMetadataDataSourceBuilder =
EntityMetadataDataSourceBuilder<TestEntity>.CreateEntityMetadataDataSourceBuilder(entityMetadata);
entityMetadataDataSourceBuilder.ShardingProperty(o => o.Id); entityMetadataDataSourceBuilder.ShardingProperty(o => o.Id);
entityMetadata.CheckShardingDataSourceMetadata(); entityMetadata.CheckShardingDataSourceMetadata();
_testEntityMetadata = entityMetadata; _testEntityMetadata = entityMetadata;
_allDataSources = Enumerable.Range(0,10).Select(o=>o.ToString()).ToList(); _allDataSources = Enumerable.Range(0, 10).Select(o => o.ToString()).ToList();
} }
public static Func<string, bool> GetRouteFilter(object shardingValue, ShardingOperatorEnum shardingOperator, public static Func<string, bool> GetRouteFilter(object shardingValue, ShardingOperatorEnum shardingOperator,
@ -31,7 +33,7 @@ namespace ShardingCore.CommonTest
} }
var stringHashCode = ShardingCoreHelper.GetStringHashCode(shardingValue.ToString()); var stringHashCode = ShardingCoreHelper.GetStringHashCode(shardingValue.ToString());
var dataSourceName = (stringHashCode%10).ToString(); var dataSourceName = (stringHashCode % 10).ToString();
switch (shardingOperator) switch (shardingOperator)
{ {
case ShardingOperatorEnum.Equal: return t => t == dataSourceName; case ShardingOperatorEnum.Equal: return t => t == dataSourceName;
@ -44,16 +46,17 @@ namespace ShardingCore.CommonTest
private void TestId(IQueryable<TestEntity> queryable, string[] dataSourceNames) private void TestId(IQueryable<TestEntity> queryable, string[] dataSourceNames)
{ {
var routePredicateExpression = ShardingUtil.GetRouteParseExpressionV2(queryable,_testEntityMetadata,GetRouteFilter,false); var routePredicateExpression =
ShardingUtil.GetRouteParseExpression(queryable, _testEntityMetadata, GetRouteFilter, false);
Assert.NotNull(routePredicateExpression); Assert.NotNull(routePredicateExpression);
var routePredicate = routePredicateExpression.GetRoutePredicate(); var routePredicate = routePredicateExpression.GetRoutePredicate();
var list = _allDataSources.Where(routePredicate).ToList(); var list = _allDataSources.Where(routePredicate).ToList();
Assert.NotNull(list); Assert.NotNull(list);
Assert.Equal(dataSourceNames.Length,list.Count); Assert.Equal(dataSourceNames.Length, list.Count);
foreach (var dataSourceName in dataSourceNames) foreach (var dataSourceName in dataSourceNames)
{ {
Assert.True(list.Any(o=>o==dataSourceName)); Assert.True(list.Any(o => o == dataSourceName));
} }
} }
@ -64,68 +67,81 @@ namespace ShardingCore.CommonTest
TestId(queryable, dataSourceNames); TestId(queryable, dataSourceNames);
} }
} }
[Fact] [Fact]
public void TestSingleDataSource() public void TestSingleDataSource()
{ {
var stringHashCode = ShardingCoreHelper.GetStringHashCode("1"); var stringHashCode = ShardingCoreHelper.GetStringHashCode("1");
var dataSourceName = (stringHashCode%10).ToString(); var dataSourceName = (stringHashCode % 10).ToString();
var dataSourceNames = new []{dataSourceName}; var dataSourceNames = new[] { dataSourceName };
var dataSourceNames1 = Enumerable.Range(0, 10).Select(o => o.ToString()).ToArray();;
var id = "1"; var id = "1";
var ids = new []{"1"}; var ids = new[] { "1" };
var ids1 = new List<string>(){"1"}; var ids1 = new List<string>() { "1" };
var obj1 = new {Id="1"}; var obj1 = new { Id = "1" };
var queryables=new List<IQueryable<TestEntity>>() var queryables = new List<IQueryable<TestEntity>>()
{ {
new List<TestEntity>().AsQueryable().Where(o=>o.Id=="1"), new List<TestEntity>().AsQueryable().Where(o => o.Id == "1"),
new List<TestEntity>().AsQueryable().Where(o=>o.Id=="1"&&o.Id!="2"), new List<TestEntity>().AsQueryable().Where(o => o.Id == "1" && o.Id != "2"),
new List<TestEntity>().AsQueryable().Where(o=>"1"==o.Id), new List<TestEntity>().AsQueryable().Where(o => "1" == o.Id),
new List<TestEntity>().AsQueryable().Where(o=>o.Id==id), new List<TestEntity>().AsQueryable().Where(o => o.Id == id),
new List<TestEntity>().AsQueryable().Where(o=>id==o.Id), new List<TestEntity>().AsQueryable().Where(o => id == o.Id),
new List<TestEntity>().AsQueryable().Where(o=>o.Id.Equals("1")), new List<TestEntity>().AsQueryable().Where(o => o.Id.Equals("1")),
new List<TestEntity>().AsQueryable().Where(o=>"1".Equals(o.Id)), new List<TestEntity>().AsQueryable().Where(o => "1".Equals(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>o.Id.Equals(id)), new List<TestEntity>().AsQueryable().Where(o => o.Id.Equals(id)),
new List<TestEntity>().AsQueryable().Where(o=>o.Id.Equals(id)), new List<TestEntity>().AsQueryable().Where(o => o.Id.Equals(id)),
new List<TestEntity>().AsQueryable().Where(o=>ids.Contains(o.Id)), new List<TestEntity>().AsQueryable().Where(o => ids.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>new []{"1"}.Contains(o.Id)), new List<TestEntity>().AsQueryable().Where(o => new[] { "1" }.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>ids1.Contains(o.Id)), new List<TestEntity>().AsQueryable().Where(o => ids1.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>new List<string>(){"1"}.Contains(o.Id)), new List<TestEntity>().AsQueryable().Where(o => new List<string>() { "1" }.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>o.Id==obj1.Id), new List<TestEntity>().AsQueryable().Where(o => o.Id == obj1.Id),
new List<TestEntity>().AsQueryable().Where(o=>obj1.Id==o.Id), new List<TestEntity>().AsQueryable().Where(o => obj1.Id == o.Id),
new List<TestEntity>().AsQueryable().Where(o=>o.Id.Equals(obj1.Id)), new List<TestEntity>().AsQueryable().Where(o => o.Id.Equals(obj1.Id)),
new List<TestEntity>().AsQueryable().Where(o=>obj1.Id.Equals(o.Id)), new List<TestEntity>().AsQueryable().Where(o => obj1.Id.Equals(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>new []{obj1.Id}.Contains(o.Id)) new List<TestEntity>().AsQueryable().Where(o => new[] { obj1.Id }.Contains(o.Id))
}; };
TestFor(queryables,dataSourceNames); TestFor(queryables, dataSourceNames);
var queryables1 = new List<IQueryable<TestEntity>>()
{
new List<TestEntity>().AsQueryable().Where(o => o.Id.Contains("1")),
new List<TestEntity>().AsQueryable().Where(o => o.Id.StartsWith("1")),
new List<TestEntity>().AsQueryable().Where(o => o.Id.EndsWith("1"))
};
TestFor(queryables1, dataSourceNames1);
} }
[Fact] [Fact]
public void TestTwoDataSource() public void TestTwoDataSource()
{ {
var dataSourceNames = new []{( ShardingCoreHelper.GetStringHashCode("1")%10).ToString(),( ShardingCoreHelper.GetStringHashCode("2")%10).ToString()}; var dataSourceNames = new[]
var ids = new []{"1","2"};
var ids1 = new List<string>(){"1","2"};
var obj1 = new {Id="1"};
var obj2 = new {Id="2"};
var queryables=new List<IQueryable<TestEntity>>()
{ {
new List<TestEntity>().AsQueryable().Where(o=>o.Id=="1"||o.Id=="2"), (ShardingCoreHelper.GetStringHashCode("1") % 10).ToString(),
new List<TestEntity>().AsQueryable().Where(o=>(o.Id=="1"||o.Id=="2")&&o.Id!="3"), (ShardingCoreHelper.GetStringHashCode("2") % 10).ToString()
new List<TestEntity>().AsQueryable().Where(o=>(o.Id=="1"||o.Id=="2")&&!o.Id.Equals("3")),
new List<TestEntity>().AsQueryable().Where(o=>"1"==o.Id||o.Id=="2"),
new List<TestEntity>().AsQueryable().Where(o=>"1"==o.Id||"2"==o.Id),
new List<TestEntity>().AsQueryable().Where(o=>o.Id.Equals("1")||o.Id=="2"),
new List<TestEntity>().AsQueryable().Where(o=>o.Id.Equals("1")||o.Id.Equals("2")),
new List<TestEntity>().AsQueryable().Where(o=>"1".Equals(o.Id)||o.Id=="2"),
new List<TestEntity>().AsQueryable().Where(o=>"1".Equals(o.Id)||o.Id.Equals("2")),
new List<TestEntity>().AsQueryable().Where(o=>"1".Equals(o.Id)||"2".Equals(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>ids.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>new []{"1","2"}.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>ids1.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>new List<string>(){"1","2"}.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>new List<string>(){obj1.Id,obj2.Id}.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o=>new []{obj1.Id,obj2.Id}.Contains(o.Id)),
}; };
TestFor(queryables,dataSourceNames); var ids = new[] { "1", "2" };
var ids1 = new List<string>() { "1", "2" };
var obj1 = new { Id = "1" };
var obj2 = new { Id = "2" };
var queryables = new List<IQueryable<TestEntity>>()
{
new List<TestEntity>().AsQueryable().Where(o => o.Id == "1" || o.Id == "2"),
new List<TestEntity>().AsQueryable().Where(o => (o.Id == "1" || o.Id == "2") && o.Id != "3"),
new List<TestEntity>().AsQueryable().Where(o => (o.Id == "1" || o.Id == "2") && !o.Id.Equals("3")),
new List<TestEntity>().AsQueryable().Where(o => "1" == o.Id || o.Id == "2"),
new List<TestEntity>().AsQueryable().Where(o => "1" == o.Id || "2" == o.Id),
new List<TestEntity>().AsQueryable().Where(o => o.Id.Equals("1") || o.Id == "2"),
new List<TestEntity>().AsQueryable().Where(o => o.Id.Equals("1") || o.Id.Equals("2")),
new List<TestEntity>().AsQueryable().Where(o => "1".Equals(o.Id) || o.Id == "2"),
new List<TestEntity>().AsQueryable().Where(o => "1".Equals(o.Id) || o.Id.Equals("2")),
new List<TestEntity>().AsQueryable().Where(o => "1".Equals(o.Id) || "2".Equals(o.Id)),
new List<TestEntity>().AsQueryable().Where(o => ids.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o => new[] { "1", "2" }.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o => ids1.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o => new List<string>() { "1", "2" }.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o => new List<string>() { obj1.Id, obj2.Id }.Contains(o.Id)),
new List<TestEntity>().AsQueryable().Where(o => new[] { obj1.Id, obj2.Id }.Contains(o.Id)),
};
TestFor(queryables, dataSourceNames);
} }
} }
@ -133,4 +149,4 @@ namespace ShardingCore.CommonTest
{ {
public string Id { get; set; } public string Id { get; set; }
} }
} }

View File

@ -59,7 +59,7 @@ namespace ShardingCore.CommonTest
private void TestId(IQueryable<TestTimeEntity> queryable, string[] tables) private void TestId(IQueryable<TestTimeEntity> queryable, string[] tables)
{ {
var routePredicateExpression = ShardingUtil.GetRouteParseExpressionV2(queryable,_testEntityMetadata,GetRouteFilter,true); var routePredicateExpression = ShardingUtil.GetRouteParseExpression(queryable,_testEntityMetadata,GetRouteFilter,true);
Assert.NotNull(routePredicateExpression); Assert.NotNull(routePredicateExpression);
var routePredicate = routePredicateExpression.GetRoutePredicate(); var routePredicate = routePredicateExpression.GetRoutePredicate();