删除并且优化部分代码
This commit is contained in:
parent
9e93ebef43
commit
9458349c7b
|
@ -1,9 +1,11 @@
|
|||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
|
||||
namespace ShardingCore.Core.ExtensionExpressionComparer.Internals
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
class ConstantValue
|
||||
{
|
||||
public object Value { get; }
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace ShardingCore.Core.ExtensionExpressionComparer.Internals
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
static class ExpressionExtensions
|
||||
{
|
||||
public static bool IsEqualTo<TExpression, TMember>(this TExpression value, TExpression other, Func<TExpression, TMember> reader)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace ShardingCore.Core.ExtensionExpressionComparer.Internals
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
class ExpressionFlattener : ExpressionVisitor
|
||||
{
|
||||
private List<Expression> _result;
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using ShardingCore.Extensions;
|
||||
|
||||
namespace ShardingCore.Core.ExtensionExpressionComparer.Internals
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
class ExpressionHashCodeResolver : ExpressionVisitor
|
||||
{
|
||||
private int _result;
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace ShardingCore.Core.ExtensionExpressionComparer.Internals
|
||||
{
|
||||
//inspired by
|
||||
//https://github.com/Poltuu/RouteParseExpressionEqualityComparer
|
||||
//https://github.com/yuriy-nelipovich/LambdaCompare/blob/master/Neleus.LambdaCompare/Comparer.cs
|
||||
//https://github.com/yesmarket/yesmarket.Linq.Expressions/blob/master/yesmarket.Linq.Expressions/Support/ExpressionValueComparer.cs
|
||||
[ExcludeFromCodeCoverage]
|
||||
sealed class ExpressionValueComparer : ExpressionVisitor
|
||||
{
|
||||
private Queue<Expression> _tracked;
|
||||
|
|
|
@ -9,7 +9,7 @@ using ShardingCore.Core.VirtualRoutes;
|
|||
|
||||
namespace ShardingCore.Core.ExtensionExpressionComparer
|
||||
{
|
||||
public class ExpressionEqualityComparer : IEqualityComparer<Expression>
|
||||
public class RouteParseExpressionEqualityComparer : IEqualityComparer<Expression>
|
||||
{
|
||||
|
||||
public bool Equals(Expression x, Expression y) =>
|
|
@ -1,32 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using ShardingCore.Core.VirtualRoutes;
|
||||
using ShardingCore.Core.VirtualRoutes.TableRoutes;
|
||||
|
||||
namespace ShardingCore.Core
|
||||
{
|
||||
internal class RouteFilterComparer : IEqualityComparer<Expression<Func<string, bool>>>
|
||||
{
|
||||
public int Compare(Expression<Func<string, bool>>? x, Expression<Func<string, bool>>? y)
|
||||
{
|
||||
if (LambdaCompare.Eq(x, y))
|
||||
return 0;
|
||||
return - 1;
|
||||
}
|
||||
|
||||
public bool Equals(Expression<Func<string, bool>>? x, Expression<Func<string, bool>>? y)
|
||||
{
|
||||
return LambdaCompare.Eq(x, y);
|
||||
}
|
||||
|
||||
public int GetHashCode(Expression<Func<string, bool>> obj)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
|
|||
/// <typeparam name="TKey"></typeparam>
|
||||
public abstract class AbstractShardingRouteParseCompileCacheVirtualDataSourceRoute<TEntity, TKey> : AbstractShardingFilterVirtualDataSourceRoute<TEntity, TKey> where TEntity : class
|
||||
{
|
||||
private static readonly ConcurrentDictionary<Expression<Func<string, bool>>, Func<string, bool>> _routeCompileCaches = new(new ExtensionExpressionComparer.ExpressionEqualityComparer());
|
||||
private static readonly ConcurrentDictionary<Expression<Func<string, bool>>, Func<string, bool>> _routeCompileCaches = new(new ExtensionExpressionComparer.RouteParseExpressionEqualityComparer());
|
||||
|
||||
static AbstractShardingRouteParseCompileCacheVirtualDataSourceRoute()
|
||||
{
|
||||
|
|
|
@ -1,374 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardingCore.Core.VirtualRoutes
|
||||
{
|
||||
public sealed class ExpressionComparer : IEqualityComparer<Expression>
|
||||
{
|
||||
public NameComparison CompareLambdaNames { get; set; }
|
||||
public NameComparison CompareParameterNames { get; set; }
|
||||
public ConstantComparison CompareConstants { get; set; }
|
||||
|
||||
public ExpressionComparer()
|
||||
{
|
||||
this.CompareConstants = ConstantComparison.ByCurrentValueValue;
|
||||
}
|
||||
|
||||
public bool Equals(Expression x, Expression y)
|
||||
{
|
||||
return EqualsImpl(x, y, null);
|
||||
}
|
||||
|
||||
private bool EqualsImpl(Expression x, Expression y, ParameterContext parameterContext)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (x == null || y == null || x.NodeType != y.NodeType || x.Type != y.Type) return false;
|
||||
|
||||
switch (x.NodeType)
|
||||
{
|
||||
case ExpressionType.Add:
|
||||
case ExpressionType.AddChecked:
|
||||
case ExpressionType.Subtract:
|
||||
case ExpressionType.SubtractChecked:
|
||||
case ExpressionType.Multiply:
|
||||
case ExpressionType.MultiplyChecked:
|
||||
case ExpressionType.Divide:
|
||||
case ExpressionType.Modulo:
|
||||
case ExpressionType.Power:
|
||||
case ExpressionType.And:
|
||||
case ExpressionType.AndAlso:
|
||||
case ExpressionType.Or:
|
||||
case ExpressionType.OrElse:
|
||||
case ExpressionType.LessThan:
|
||||
case ExpressionType.LessThanOrEqual:
|
||||
case ExpressionType.GreaterThan:
|
||||
case ExpressionType.GreaterThanOrEqual:
|
||||
case ExpressionType.Equal:
|
||||
case ExpressionType.NotEqual:
|
||||
case ExpressionType.ExclusiveOr:
|
||||
case ExpressionType.Coalesce:
|
||||
case ExpressionType.ArrayIndex:
|
||||
case ExpressionType.RightShift:
|
||||
case ExpressionType.LeftShift:
|
||||
case ExpressionType.Assign:
|
||||
case ExpressionType.AddAssign:
|
||||
case ExpressionType.AndAssign:
|
||||
case ExpressionType.DivideAssign:
|
||||
case ExpressionType.ExclusiveOrAssign:
|
||||
case ExpressionType.LeftShiftAssign:
|
||||
case ExpressionType.ModuloAssign:
|
||||
case ExpressionType.MultiplyAssign:
|
||||
case ExpressionType.OrAssign:
|
||||
case ExpressionType.PowerAssign:
|
||||
case ExpressionType.RightShiftAssign:
|
||||
case ExpressionType.SubtractAssign:
|
||||
case ExpressionType.AddAssignChecked:
|
||||
case ExpressionType.SubtractAssignChecked:
|
||||
case ExpressionType.MultiplyAssignChecked:
|
||||
{
|
||||
var xt = (BinaryExpression)x;
|
||||
var yt = (BinaryExpression)y;
|
||||
return xt.Method == yt.Method && EqualsImpl(xt.Left, yt.Left, parameterContext) && EqualsImpl(xt.Right, yt.Right, parameterContext) && EqualsImpl(xt.Conversion, yt.Conversion, parameterContext);
|
||||
}
|
||||
case ExpressionType.Block:
|
||||
{
|
||||
var xt = (BlockExpression)x;
|
||||
var yt = (BlockExpression)y;
|
||||
if (xt.Expressions.Count != yt.Expressions.Count || xt.Variables.Count != yt.Variables.Count) return false;
|
||||
for (var i = 0; i < xt.Variables.Count; i++)
|
||||
if (!EqualsImpl(xt.Variables[i], yt.Variables[i], parameterContext)) return false;
|
||||
for (var i = 0; i < xt.Expressions.Count; i++)
|
||||
if (!EqualsImpl(xt.Expressions[i], yt.Expressions[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Conditional:
|
||||
{
|
||||
var xt = (ConditionalExpression)x;
|
||||
var yt = (ConditionalExpression)y;
|
||||
return EqualsImpl(xt.Test, yt.Test, parameterContext) && EqualsImpl(xt.IfTrue, yt.IfTrue, parameterContext) && EqualsImpl(xt.IfFalse, yt.IfFalse, parameterContext);
|
||||
}
|
||||
case ExpressionType.Constant:
|
||||
{
|
||||
switch (this.CompareConstants)
|
||||
{
|
||||
case ConstantComparison.ByCurrentValueValue:
|
||||
return Equals(((ConstantExpression)x).Value, ((ConstantExpression)y).Value);
|
||||
case ConstantComparison.ByCurrentValueReference:
|
||||
return ((ConstantExpression)x).Value == ((ConstantExpression)y).Value;
|
||||
case ConstantComparison.ByExpressionReference:
|
||||
return x == y;
|
||||
default:
|
||||
throw new InvalidEnumArgumentException("CompareConstants", (int)this.CompareConstants, typeof(ConstantComparison));
|
||||
}
|
||||
}
|
||||
case ExpressionType.Default:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Dynamic:
|
||||
{
|
||||
var xt = (DynamicExpression)x;
|
||||
var yt = (DynamicExpression)y;
|
||||
if (xt.Binder != yt.Binder || xt.DelegateType != yt.DelegateType || xt.Arguments.Count != yt.Arguments.Count) return false;
|
||||
for (var i = 0; i < xt.Arguments.Count; i++)
|
||||
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Index:
|
||||
{
|
||||
var xt = (IndexExpression)x;
|
||||
var yt = (IndexExpression)y;
|
||||
if (xt.Arguments.Count != yt.Arguments.Count || xt.Indexer != yt.Indexer || !EqualsImpl(xt.Object, yt.Object, parameterContext)) return false;
|
||||
for (var i = 0; i < xt.Arguments.Count; i++)
|
||||
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Invoke:
|
||||
{
|
||||
var xt = (InvocationExpression)x;
|
||||
var yt = (InvocationExpression)y;
|
||||
if (xt.Arguments.Count != yt.Arguments.Count || !EqualsImpl(xt.Expression, yt.Expression, parameterContext)) return false;
|
||||
for (var i = 0; i < xt.Arguments.Count; i++)
|
||||
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Lambda:
|
||||
{
|
||||
var xt = (LambdaExpression)x;
|
||||
var yt = (LambdaExpression)y;
|
||||
if (!CompareNames(xt.Name, yt.Name, this.CompareLambdaNames) || xt.Parameters.Count != yt.Parameters.Count) return false;
|
||||
for (var i = 0; i < xt.Parameters.Count; i++)
|
||||
if (!EqualsImpl(xt.Parameters[i], yt.Parameters[i], null)) return false; // This and the catch parameter are the only cases where we compare parameters by value instead of positionally
|
||||
return EqualsImpl(xt.Body, yt.Body, new ParameterContext(parameterContext, xt.Parameters.ToArray(), yt.Parameters.ToArray()));
|
||||
}
|
||||
case ExpressionType.ListInit:
|
||||
{
|
||||
var xt = (ListInitExpression)x;
|
||||
var yt = (ListInitExpression)y;
|
||||
return EqualsImpl(xt.NewExpression, yt.NewExpression, parameterContext) && EqualsImpl(xt.Initializers, yt.Initializers, parameterContext);
|
||||
}
|
||||
case ExpressionType.MemberAccess:
|
||||
{
|
||||
var xt = (MemberExpression)x;
|
||||
var yt = (MemberExpression)y;
|
||||
return xt.Member == yt.Member && EqualsImpl(xt.Expression, yt.Expression, parameterContext);
|
||||
}
|
||||
case ExpressionType.MemberInit:
|
||||
{
|
||||
var xt = (MemberInitExpression)x;
|
||||
var yt = (MemberInitExpression)y;
|
||||
if (xt.Bindings.Count != yt.Bindings.Count || !EqualsImpl(xt.NewExpression, yt.NewExpression, parameterContext)) return false;
|
||||
for (var i = 0; i < xt.Bindings.Count; i++)
|
||||
if (!EqualsImpl(xt.Bindings[i], yt.Bindings[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Call:
|
||||
{
|
||||
var xt = (MethodCallExpression)x;
|
||||
var yt = (MethodCallExpression)y;
|
||||
if (xt.Arguments.Count != yt.Arguments.Count || xt.Method != yt.Method || !EqualsImpl(xt.Object, yt.Object, parameterContext)) return false;
|
||||
for (var i = 0; i < xt.Arguments.Count; i++)
|
||||
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.NewArrayBounds:
|
||||
case ExpressionType.NewArrayInit:
|
||||
{
|
||||
var xt = (NewArrayExpression)x;
|
||||
var yt = (NewArrayExpression)y;
|
||||
if (xt.Expressions.Count != yt.Expressions.Count) return false;
|
||||
for (var i = 0; i < xt.Expressions.Count; i++)
|
||||
if (!EqualsImpl(xt.Expressions[i], yt.Expressions[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.New:
|
||||
{
|
||||
var xt = (NewExpression)x;
|
||||
var yt = (NewExpression)y;
|
||||
// I believe NewExpression.Members is guaranteed to be the same if NewExpression.Constructor is the same.
|
||||
if (xt.Arguments.Count != yt.Arguments.Count || xt.Constructor == yt.Constructor) return false;
|
||||
for (var i = 0; i < xt.Arguments.Count; i++)
|
||||
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Parameter:
|
||||
{
|
||||
var xt = (ParameterExpression)x;
|
||||
var yt = (ParameterExpression)y;
|
||||
if (parameterContext != null)
|
||||
{
|
||||
int xIndex;
|
||||
var currentContext = parameterContext;
|
||||
while (true)
|
||||
{
|
||||
xIndex =Array.IndexOf(currentContext.XParameters,xt);
|
||||
if (xIndex != -1) break;
|
||||
currentContext = currentContext.ParentContext;
|
||||
if (currentContext == null) throw new InvalidOperationException("X parameter " + xt + " is not contained in a parent lambda context or catch block variable context. Since parameter equality is determined positionally, the equality is ambiguous.");
|
||||
}
|
||||
|
||||
var yIndex = Array.IndexOf(currentContext.YParameters,yt);
|
||||
if (yIndex == -1) throw new InvalidOperationException("Y parameter " + yt + " is not defined in the same parent lambda context or catch block variable context as the x parameter " + xt + ".");
|
||||
return xIndex == yIndex;
|
||||
}
|
||||
return CompareNames(xt.Name, yt.Name, this.CompareParameterNames) && xt.IsByRef == yt.IsByRef;
|
||||
}
|
||||
case ExpressionType.Switch:
|
||||
{
|
||||
var xt = (SwitchExpression)x;
|
||||
var yt = (SwitchExpression)y;
|
||||
if (xt.Comparison != yt.Comparison || xt.Cases.Count != yt.Cases.Count || !EqualsImpl(xt.SwitchValue, yt.SwitchValue, parameterContext) || !EqualsImpl(xt.DefaultBody, yt.DefaultBody, parameterContext)) return false;
|
||||
for (var i = 0; i < xt.Cases.Count; i++)
|
||||
{
|
||||
var xCase = xt.Cases[i];
|
||||
var yCase = yt.Cases[i];
|
||||
if (xCase.TestValues.Count != yCase.TestValues.Count || !EqualsImpl(xCase.Body, yCase.Body, parameterContext)) return false;
|
||||
for (var ti = 0; ti < xCase.TestValues.Count; ti++)
|
||||
if (!EqualsImpl(xCase.TestValues[ti], yCase.TestValues[ti], parameterContext)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.Try:
|
||||
{
|
||||
var xt = (TryExpression)x;
|
||||
var yt = (TryExpression)y;
|
||||
if (xt.Handlers.Count != yt.Handlers.Count || !EqualsImpl(xt.Body, yt.Body, parameterContext) || !EqualsImpl(xt.Fault, yt.Fault, parameterContext) || !EqualsImpl(xt.Finally, yt.Finally, parameterContext)) return false;
|
||||
for (var i = 0; i < xt.Handlers.Count; i++)
|
||||
{
|
||||
var xHandler = xt.Handlers[i];
|
||||
var yHandler = yt.Handlers[i];
|
||||
var newParameterContext = new ParameterContext(parameterContext, new[] { xHandler.Variable }, new[] { yHandler.Variable });
|
||||
if (xHandler.Test != yHandler.Test || !EqualsImpl(xHandler.Body, yHandler.Body, newParameterContext) || !EqualsImpl(xHandler.Filter, yHandler.Filter, newParameterContext) || !EqualsImpl(xHandler.Variable, yHandler.Variable, null)) return false; // This and the lambda definition are the only cases where we compare parameters by value instead of positionally
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case ExpressionType.TypeIs:
|
||||
{
|
||||
var xt = (TypeBinaryExpression)x;
|
||||
var yt = (TypeBinaryExpression)y;
|
||||
return xt.TypeOperand == yt.TypeOperand && EqualsImpl(xt.Expression, yt.Expression, parameterContext);
|
||||
}
|
||||
case ExpressionType.Negate:
|
||||
case ExpressionType.NegateChecked:
|
||||
case ExpressionType.Not:
|
||||
case ExpressionType.IsFalse:
|
||||
case ExpressionType.IsTrue:
|
||||
case ExpressionType.OnesComplement:
|
||||
case ExpressionType.ArrayLength:
|
||||
case ExpressionType.Convert:
|
||||
case ExpressionType.ConvertChecked:
|
||||
case ExpressionType.Throw:
|
||||
case ExpressionType.TypeAs:
|
||||
case ExpressionType.Quote:
|
||||
case ExpressionType.UnaryPlus:
|
||||
case ExpressionType.Unbox:
|
||||
case ExpressionType.Increment:
|
||||
case ExpressionType.Decrement:
|
||||
case ExpressionType.PreIncrementAssign:
|
||||
case ExpressionType.PostIncrementAssign:
|
||||
case ExpressionType.PreDecrementAssign:
|
||||
case ExpressionType.PostDecrementAssign:
|
||||
{
|
||||
var xt = (UnaryExpression)x;
|
||||
var yt = (UnaryExpression)y;
|
||||
return xt.Method == yt.Method && EqualsImpl(xt.Operand, yt.Operand, parameterContext);
|
||||
}
|
||||
default:
|
||||
throw new NotImplementedException(x.NodeType.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private bool EqualsImpl(MemberBinding x, MemberBinding y, ParameterContext parameterContext)
|
||||
{
|
||||
if (x.Member != y.Member || x.BindingType != y.BindingType) return false;
|
||||
|
||||
switch (x.BindingType)
|
||||
{
|
||||
case MemberBindingType.Assignment:
|
||||
return EqualsImpl(((MemberAssignment)x).Expression, ((MemberAssignment)y).Expression, parameterContext);
|
||||
case MemberBindingType.MemberBinding:
|
||||
{
|
||||
var xtBinding = (MemberMemberBinding)x;
|
||||
var ytBinding = (MemberMemberBinding)y;
|
||||
if (xtBinding.Bindings.Count != ytBinding.Bindings.Count) return false;
|
||||
for (var i = 0; i < xtBinding.Bindings.Count; i++)
|
||||
if (!EqualsImpl(xtBinding.Bindings[i], ytBinding.Bindings[i], parameterContext)) return false;
|
||||
return true;
|
||||
}
|
||||
case MemberBindingType.ListBinding:
|
||||
return EqualsImpl(((MemberListBinding)x).Initializers, ((MemberListBinding)y).Initializers, parameterContext);
|
||||
default:
|
||||
throw new NotImplementedException(x.BindingType.GetType() + " " + x.BindingType);
|
||||
}
|
||||
}
|
||||
|
||||
private bool EqualsImpl(IList<ElementInit> x, IList<ElementInit> y, ParameterContext parameterContext)
|
||||
{
|
||||
if (x.Count != y.Count) return false;
|
||||
for (var i = 0; i < x.Count; i++)
|
||||
{
|
||||
var xInitializer = x[i];
|
||||
var yInitializer = y[i];
|
||||
if (xInitializer.AddMethod != yInitializer.AddMethod || xInitializer.Arguments.Count != yInitializer.Arguments.Count) return false;
|
||||
for (var ai = 0; ai < xInitializer.Arguments.Count; ai++)
|
||||
if (!EqualsImpl(xInitializer.Arguments[ai], yInitializer.Arguments[ai], parameterContext)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private sealed class ParameterContext
|
||||
{
|
||||
public readonly ParameterContext ParentContext;
|
||||
public readonly ParameterExpression[] XParameters;
|
||||
public readonly ParameterExpression[] YParameters;
|
||||
|
||||
public ParameterContext(ParameterContext parentContext, ParameterExpression[] xParameters, ParameterExpression[] yParameters)
|
||||
{
|
||||
ParentContext = parentContext;
|
||||
XParameters = xParameters;
|
||||
YParameters = yParameters;
|
||||
}
|
||||
}
|
||||
|
||||
private bool CompareNames(string name1, string name2, NameComparison comparison)
|
||||
{
|
||||
switch (comparison)
|
||||
{
|
||||
case NameComparison.None:
|
||||
return true;
|
||||
case NameComparison.CaseSensitive:
|
||||
return StringComparer.Ordinal.Equals(name1, name2);
|
||||
case NameComparison.CaseInsensitive:
|
||||
return StringComparer.OrdinalIgnoreCase.Equals(name1, name2);
|
||||
default:
|
||||
throw new InvalidEnumArgumentException("comparison", (int)comparison, typeof(NameComparison));
|
||||
}
|
||||
}
|
||||
|
||||
public int GetHashCode(Expression obj)
|
||||
{
|
||||
// Better to put everything in one bin than to let the default reference-based GetHashCode cause a false negative.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public enum NameComparison
|
||||
{
|
||||
None,
|
||||
CaseSensitive,
|
||||
CaseInsensitive
|
||||
}
|
||||
|
||||
public enum ConstantComparison
|
||||
{
|
||||
ByExpressionReference,
|
||||
ByCurrentValueReference,
|
||||
ByCurrentValueValue
|
||||
}
|
||||
}
|
|
@ -1,236 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardingCore.Core.VirtualRoutes
|
||||
{
|
||||
public static class LambdaCompare
|
||||
{
|
||||
public static bool Eq<TSource, TValue>(
|
||||
Expression<Func<TSource, TValue>> x,
|
||||
Expression<Func<TSource, TValue>> y)
|
||||
{
|
||||
return ExpressionsEqual(x, y, null, null);
|
||||
}
|
||||
|
||||
public static bool Eq<TSource1, TSource2, TValue>(
|
||||
Expression<Func<TSource1, TSource2, TValue>> x,
|
||||
Expression<Func<TSource1, TSource2, TValue>> y)
|
||||
{
|
||||
return ExpressionsEqual(x, y, null, null);
|
||||
}
|
||||
|
||||
public static Expression<Func<Expression<Func<TSource, TValue>>, bool>> Eq<TSource, TValue>(Expression<Func<TSource, TValue>> y)
|
||||
{
|
||||
return x => ExpressionsEqual(x, y, null, null);
|
||||
}
|
||||
|
||||
private static bool ExpressionsEqual(Expression x, Expression y, LambdaExpression rootX, LambdaExpression rootY)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return true;
|
||||
if (x == null || y == null) return false;
|
||||
|
||||
var valueX = TryCalculateConstant(x);
|
||||
var valueY = TryCalculateConstant(y);
|
||||
|
||||
if (valueX.IsDefined && valueY.IsDefined)
|
||||
return ValuesEqual(valueX.Value, valueY.Value);
|
||||
|
||||
if (x.NodeType != y.NodeType
|
||||
|| x.Type != y.Type)
|
||||
{
|
||||
if (IsAnonymousType(x.Type) && IsAnonymousType(y.Type))
|
||||
throw new NotImplementedException("Comparison of Anonymous Types is not supported");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (x is LambdaExpression)
|
||||
{
|
||||
var lx = (LambdaExpression)x;
|
||||
var ly = (LambdaExpression)y;
|
||||
var paramsX = lx.Parameters;
|
||||
var paramsY = ly.Parameters;
|
||||
return CollectionsEqual(paramsX, paramsY, lx, ly) && ExpressionsEqual(lx.Body, ly.Body, lx, ly);
|
||||
}
|
||||
if (x is MemberExpression)
|
||||
{
|
||||
var mex = (MemberExpression)x;
|
||||
var mey = (MemberExpression)y;
|
||||
return Equals(mex.Member, mey.Member) && ExpressionsEqual(mex.Expression, mey.Expression, rootX, rootY);
|
||||
}
|
||||
if (x is BinaryExpression)
|
||||
{
|
||||
var bx = (BinaryExpression)x;
|
||||
var by = (BinaryExpression)y;
|
||||
return bx.Method == @by.Method && ExpressionsEqual(bx.Left, @by.Left, rootX, rootY) &&
|
||||
ExpressionsEqual(bx.Right, @by.Right, rootX, rootY);
|
||||
}
|
||||
if (x is UnaryExpression)
|
||||
{
|
||||
var ux = (UnaryExpression)x;
|
||||
var uy = (UnaryExpression)y;
|
||||
return ux.Method == uy.Method && ExpressionsEqual(ux.Operand, uy.Operand, rootX, rootY);
|
||||
}
|
||||
if (x is ParameterExpression)
|
||||
{
|
||||
var px = (ParameterExpression)x;
|
||||
var py = (ParameterExpression)y;
|
||||
return rootX.Parameters.IndexOf(px) == rootY.Parameters.IndexOf(py);
|
||||
}
|
||||
if (x is MethodCallExpression)
|
||||
{
|
||||
var cx = (MethodCallExpression)x;
|
||||
var cy = (MethodCallExpression)y;
|
||||
return cx.Method == cy.Method
|
||||
&& ExpressionsEqual(cx.Object, cy.Object, rootX, rootY)
|
||||
&& CollectionsEqual(cx.Arguments, cy.Arguments, rootX, rootY);
|
||||
}
|
||||
if (x is MemberInitExpression)
|
||||
{
|
||||
var mix = (MemberInitExpression)x;
|
||||
var miy = (MemberInitExpression)y;
|
||||
return ExpressionsEqual(mix.NewExpression, miy.NewExpression, rootX, rootY)
|
||||
&& MemberInitsEqual(mix.Bindings, miy.Bindings, rootX, rootY);
|
||||
}
|
||||
if (x is NewArrayExpression)
|
||||
{
|
||||
var nx = (NewArrayExpression)x;
|
||||
var ny = (NewArrayExpression)y;
|
||||
return CollectionsEqual(nx.Expressions, ny.Expressions, rootX, rootY);
|
||||
}
|
||||
if (x is NewExpression)
|
||||
{
|
||||
var nx = (NewExpression)x;
|
||||
var ny = (NewExpression)y;
|
||||
return
|
||||
Equals(nx.Constructor, ny.Constructor)
|
||||
&& CollectionsEqual(nx.Arguments, ny.Arguments, rootX, rootY)
|
||||
&& (nx.Members == null && ny.Members == null
|
||||
|| nx.Members != null && ny.Members != null && CollectionsEqual(nx.Members, ny.Members));
|
||||
}
|
||||
if (x is ConditionalExpression)
|
||||
{
|
||||
var cx = (ConditionalExpression)x;
|
||||
var cy = (ConditionalExpression)y;
|
||||
return
|
||||
ExpressionsEqual(cx.Test, cy.Test, rootX, rootY)
|
||||
&& ExpressionsEqual(cx.IfFalse, cy.IfFalse, rootX, rootY)
|
||||
&& ExpressionsEqual(cx.IfTrue, cy.IfTrue, rootX, rootY);
|
||||
}
|
||||
|
||||
throw new NotImplementedException(x.ToString());
|
||||
}
|
||||
|
||||
private static Boolean IsAnonymousType(Type type)
|
||||
{
|
||||
Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any();
|
||||
Boolean nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
|
||||
Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;
|
||||
|
||||
return isAnonymousType;
|
||||
}
|
||||
|
||||
private static bool MemberInitsEqual(ICollection<MemberBinding> bx, ICollection<MemberBinding> by, LambdaExpression rootX, LambdaExpression rootY)
|
||||
{
|
||||
if (bx.Count != by.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bx.Concat(by).Any(b => b.BindingType != MemberBindingType.Assignment))
|
||||
throw new NotImplementedException("Only MemberBindingType.Assignment is supported");
|
||||
|
||||
return
|
||||
bx.Cast<MemberAssignment>().OrderBy(b => b.Member.Name).Select((b, i) => new { Expr = b.Expression, b.Member, Index = i })
|
||||
.Join(
|
||||
by.Cast<MemberAssignment>().OrderBy(b => b.Member.Name).Select((b, i) => new { Expr = b.Expression, b.Member, Index = i }),
|
||||
o => o.Index, o => o.Index, (xe, ye) => new { XExpr = xe.Expr, XMember = xe.Member, YExpr = ye.Expr, YMember = ye.Member })
|
||||
.All(o => Equals(o.XMember, o.YMember) && ExpressionsEqual(o.XExpr, o.YExpr, rootX, rootY));
|
||||
}
|
||||
|
||||
private static bool ValuesEqual(object x, object y)
|
||||
{
|
||||
if (ReferenceEquals(x, y))
|
||||
return true;
|
||||
if (x is ICollection && y is ICollection)
|
||||
return CollectionsEqual((ICollection)x, (ICollection)y);
|
||||
|
||||
return Equals(x, y);
|
||||
}
|
||||
|
||||
private static ConstantValue TryCalculateConstant(Expression e)
|
||||
{
|
||||
if (e is ConstantExpression)
|
||||
return new ConstantValue(true, ((ConstantExpression)e).Value);
|
||||
if (e is MemberExpression)
|
||||
{
|
||||
var me = (MemberExpression)e;
|
||||
var parentValue = TryCalculateConstant(me.Expression);
|
||||
if (parentValue.IsDefined)
|
||||
{
|
||||
var result =
|
||||
me.Member is FieldInfo
|
||||
? ((FieldInfo)me.Member).GetValue(parentValue.Value)
|
||||
: ((PropertyInfo)me.Member).GetValue(parentValue.Value);
|
||||
return new ConstantValue(true, result);
|
||||
}
|
||||
}
|
||||
if (e is NewArrayExpression)
|
||||
{
|
||||
var ae = ((NewArrayExpression)e);
|
||||
var result = ae.Expressions.Select(TryCalculateConstant);
|
||||
if (result.All(i => i.IsDefined))
|
||||
return new ConstantValue(true, result.Select(i => i.Value).ToArray());
|
||||
}
|
||||
if (e is ConditionalExpression)
|
||||
{
|
||||
var ce = (ConditionalExpression)e;
|
||||
var evaluatedTest = TryCalculateConstant(ce.Test);
|
||||
if (evaluatedTest.IsDefined)
|
||||
{
|
||||
return TryCalculateConstant(Equals(evaluatedTest.Value, true) ? ce.IfTrue : ce.IfFalse);
|
||||
}
|
||||
}
|
||||
|
||||
return default(ConstantValue);
|
||||
}
|
||||
|
||||
private static bool CollectionsEqual(IEnumerable<Expression> x, IEnumerable<Expression> y, LambdaExpression rootX, LambdaExpression rootY)
|
||||
{
|
||||
return x.Count() == y.Count()
|
||||
&& x.Select((e, i) => new { Expr = e, Index = i })
|
||||
.Join(y.Select((e, i) => new { Expr = e, Index = i }),
|
||||
o => o.Index, o => o.Index, (xe, ye) => new { X = xe.Expr, Y = ye.Expr })
|
||||
.All(o => ExpressionsEqual(o.X, o.Y, rootX, rootY));
|
||||
}
|
||||
|
||||
private static bool CollectionsEqual(ICollection x, ICollection y)
|
||||
{
|
||||
return x.Count == y.Count
|
||||
&& x.Cast<object>().Select((e, i) => new { Expr = e, Index = i })
|
||||
.Join(y.Cast<object>().Select((e, i) => new { Expr = e, Index = i }),
|
||||
o => o.Index, o => o.Index, (xe, ye) => new { X = xe.Expr, Y = ye.Expr })
|
||||
.All(o => Equals(o.X, o.Y));
|
||||
}
|
||||
|
||||
private struct ConstantValue
|
||||
{
|
||||
public ConstantValue(bool isDefined, object value)
|
||||
: this()
|
||||
{
|
||||
IsDefined = isDefined;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public bool IsDefined { get; private set; }
|
||||
|
||||
public object Value { get; private set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
|
|||
/// <typeparam name="TKey"></typeparam>
|
||||
public abstract class AbstractShardingRouteParseCompileCacheVirtualTableRoute<TEntity, TKey> : AbstractShardingFilterVirtualTableRoute<TEntity, TKey> where TEntity : class
|
||||
{
|
||||
private readonly ConcurrentDictionary<Expression<Func<string, bool>>, Func<string, bool>> _routeCompileCaches = new(new ExtensionExpressionComparer.ExpressionEqualityComparer());
|
||||
private readonly ConcurrentDictionary<Expression<Func<string, bool>>, Func<string, bool>> _routeCompileCaches = new(new ExtensionExpressionComparer.RouteParseExpressionEqualityComparer());
|
||||
|
||||
protected AbstractShardingRouteParseCompileCacheVirtualTableRoute()
|
||||
{
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardingCore.Core.VirtualRoutes.TableRoutes
|
||||
{
|
||||
internal static class ExpressionHasher
|
||||
{
|
||||
private const int NullHashCode = 0x61E04917;
|
||||
|
||||
[ThreadStatic]
|
||||
private static HashVisitor _visitor;
|
||||
|
||||
private static HashVisitor Visitor
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_visitor == null)
|
||||
_visitor = new HashVisitor();
|
||||
return _visitor;
|
||||
}
|
||||
}
|
||||
|
||||
public static int GetHashCode(Expression e)
|
||||
{
|
||||
if (e == null)
|
||||
return NullHashCode;
|
||||
|
||||
var visitor = Visitor;
|
||||
|
||||
visitor.Reset();
|
||||
visitor.Visit(e);
|
||||
|
||||
return visitor.Hash;
|
||||
}
|
||||
|
||||
private sealed class HashVisitor : ExpressionVisitor
|
||||
{
|
||||
private int _hash;
|
||||
|
||||
internal int Hash
|
||||
{
|
||||
get { return _hash; }
|
||||
}
|
||||
|
||||
internal void Reset()
|
||||
{
|
||||
_hash = 0;
|
||||
}
|
||||
|
||||
private void UpdateHash(int value)
|
||||
{
|
||||
_hash = (_hash * 397) ^ value;
|
||||
}
|
||||
|
||||
private void UpdateHash(object component)
|
||||
{
|
||||
int componentHash;
|
||||
|
||||
if (component == null)
|
||||
{
|
||||
componentHash = NullHashCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
var member = component as MemberInfo;
|
||||
if (member != null)
|
||||
{
|
||||
componentHash = member.Name.GetHashCode();
|
||||
|
||||
var declaringType = member.DeclaringType;
|
||||
if (declaringType != null && declaringType.AssemblyQualifiedName != null)
|
||||
componentHash = (componentHash * 397) ^ declaringType.AssemblyQualifiedName.GetHashCode();
|
||||
}
|
||||
else
|
||||
{
|
||||
componentHash = component.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
_hash = (_hash * 397) ^ componentHash;
|
||||
}
|
||||
|
||||
public override Expression Visit(Expression node)
|
||||
{
|
||||
UpdateHash((int)node.NodeType);
|
||||
return base.Visit(node);
|
||||
}
|
||||
|
||||
protected override Expression VisitConstant(ConstantExpression node)
|
||||
{
|
||||
UpdateHash(node.Value);
|
||||
return base.VisitConstant(node);
|
||||
}
|
||||
|
||||
protected override Expression VisitMember(MemberExpression node)
|
||||
{
|
||||
UpdateHash(node.Member);
|
||||
return base.VisitMember(node);
|
||||
}
|
||||
|
||||
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
|
||||
{
|
||||
UpdateHash(node.Member);
|
||||
return base.VisitMemberAssignment(node);
|
||||
}
|
||||
|
||||
protected override MemberBinding VisitMemberBinding(MemberBinding node)
|
||||
{
|
||||
UpdateHash((int)node.BindingType);
|
||||
UpdateHash(node.Member);
|
||||
return base.VisitMemberBinding(node);
|
||||
}
|
||||
|
||||
protected override MemberListBinding VisitMemberListBinding(MemberListBinding node)
|
||||
{
|
||||
UpdateHash((int)node.BindingType);
|
||||
UpdateHash(node.Member);
|
||||
return base.VisitMemberListBinding(node);
|
||||
}
|
||||
|
||||
protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node)
|
||||
{
|
||||
UpdateHash((int)node.BindingType);
|
||||
UpdateHash(node.Member);
|
||||
return base.VisitMemberMemberBinding(node);
|
||||
}
|
||||
|
||||
protected override Expression VisitMethodCall(MethodCallExpression node)
|
||||
{
|
||||
UpdateHash(node.Method);
|
||||
return base.VisitMethodCall(node);
|
||||
}
|
||||
|
||||
protected override Expression VisitNew(NewExpression node)
|
||||
{
|
||||
UpdateHash(node.Constructor);
|
||||
return base.VisitNew(node);
|
||||
}
|
||||
|
||||
protected override Expression VisitNewArray(NewArrayExpression node)
|
||||
{
|
||||
UpdateHash(node.Type);
|
||||
return base.VisitNewArray(node);
|
||||
}
|
||||
|
||||
protected override Expression VisitParameter(ParameterExpression node)
|
||||
{
|
||||
UpdateHash(node.Type);
|
||||
return base.VisitParameter(node);
|
||||
}
|
||||
|
||||
protected override Expression VisitTypeBinary(TypeBinaryExpression node)
|
||||
{
|
||||
UpdateHash(node.Type);
|
||||
return base.VisitTypeBinary(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,308 +0,0 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ShardingCore.Core.VirtualRoutes.TableRoutes
|
||||
{
|
||||
public static class QueryResultCache
|
||||
{
|
||||
private static readonly ConcurrentDictionary<object, Func<string, bool>> _routeFilter =
|
||||
new ConcurrentDictionary<object, Func<string, bool>>();
|
||||
/// <summary>
|
||||
/// Returns the result of the query; if possible from the cache, otherwise
|
||||
/// the query is materialized and the result cached before being returned.
|
||||
/// </summary>
|
||||
public static Func<string, bool> FromCache(this Expression<Func<string,bool>> query)
|
||||
{
|
||||
string key = query.GetCacheKey();
|
||||
|
||||
return _routeFilter.GetOrAdd(key, k => query.Compile());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a cache key for a query.
|
||||
/// </summary>
|
||||
public static string GetCacheKey(this Expression expression)
|
||||
{
|
||||
|
||||
// locally evaluate as much of the query as possible
|
||||
expression = Evaluator.PartialEval(expression, QueryResultCache.CanBeEvaluatedLocally);
|
||||
|
||||
// support local collections
|
||||
expression = LocalCollectionExpander.Rewrite(expression);
|
||||
|
||||
// use the string representation of the expression for the cache key
|
||||
string key = expression.ToString();
|
||||
|
||||
// the key is potentially very long, so use an md5 fingerprint
|
||||
// (fine if the query result data isn't critically sensitive)
|
||||
key = key.ToMd5Fingerprint();
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
static Func<Expression, bool> CanBeEvaluatedLocally
|
||||
{
|
||||
get
|
||||
{
|
||||
return expression =>
|
||||
{
|
||||
// don't evaluate parameters
|
||||
if (expression.NodeType == ExpressionType.Parameter)
|
||||
return false;
|
||||
|
||||
// can't evaluate queries
|
||||
if (typeof(IQueryable).IsAssignableFrom(expression.Type))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables the partial evaluation of queries.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// From http://msdn.microsoft.com/en-us/library/bb546158.aspx
|
||||
/// Copyright notice http://msdn.microsoft.com/en-gb/cc300389.aspx#O
|
||||
/// </remarks>
|
||||
public static class Evaluator
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs evaluation & replacement of independent sub-trees
|
||||
/// </summary>
|
||||
/// <param name="expression">The root of the expression tree.</param>
|
||||
/// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
|
||||
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
|
||||
public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
|
||||
{
|
||||
return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs evaluation & replacement of independent sub-trees
|
||||
/// </summary>
|
||||
/// <param name="expression">The root of the expression tree.</param>
|
||||
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
|
||||
public static Expression PartialEval(Expression expression)
|
||||
{
|
||||
return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
|
||||
}
|
||||
|
||||
private static bool CanBeEvaluatedLocally(Expression expression)
|
||||
{
|
||||
return expression.NodeType != ExpressionType.Parameter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates & replaces sub-trees when first candidate is reached (top-down)
|
||||
/// </summary>
|
||||
class SubtreeEvaluator : ExpressionVisitor
|
||||
{
|
||||
HashSet<Expression> candidates;
|
||||
|
||||
internal SubtreeEvaluator(HashSet<Expression> candidates)
|
||||
{
|
||||
this.candidates = candidates;
|
||||
}
|
||||
|
||||
internal Expression Eval(Expression exp)
|
||||
{
|
||||
return this.Visit(exp);
|
||||
}
|
||||
|
||||
public override Expression Visit(Expression exp)
|
||||
{
|
||||
if (exp == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (this.candidates.Contains(exp))
|
||||
{
|
||||
return this.Evaluate(exp);
|
||||
}
|
||||
return base.Visit(exp);
|
||||
}
|
||||
|
||||
private Expression Evaluate(Expression e)
|
||||
{
|
||||
if (e.NodeType == ExpressionType.Constant)
|
||||
{
|
||||
return e;
|
||||
}
|
||||
LambdaExpression lambda = Expression.Lambda(e);
|
||||
Delegate fn = lambda.Compile();
|
||||
return Expression.Constant(fn.DynamicInvoke(null), e.Type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs bottom-up analysis to determine which nodes can possibly
|
||||
/// be part of an evaluated sub-tree.
|
||||
/// </summary>
|
||||
class Nominator : ExpressionVisitor
|
||||
{
|
||||
Func<Expression, bool> fnCanBeEvaluated;
|
||||
HashSet<Expression> candidates;
|
||||
bool cannotBeEvaluated;
|
||||
|
||||
internal Nominator(Func<Expression, bool> fnCanBeEvaluated)
|
||||
{
|
||||
this.fnCanBeEvaluated = fnCanBeEvaluated;
|
||||
}
|
||||
|
||||
internal HashSet<Expression> Nominate(Expression expression)
|
||||
{
|
||||
this.candidates = new HashSet<Expression>();
|
||||
this.Visit(expression);
|
||||
return this.candidates;
|
||||
}
|
||||
|
||||
public override Expression Visit(Expression expression)
|
||||
{
|
||||
if (expression != null)
|
||||
{
|
||||
bool saveCannotBeEvaluated = this.cannotBeEvaluated;
|
||||
this.cannotBeEvaluated = false;
|
||||
base.Visit(expression);
|
||||
if (!this.cannotBeEvaluated)
|
||||
{
|
||||
if (this.fnCanBeEvaluated(expression))
|
||||
{
|
||||
this.candidates.Add(expression);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.cannotBeEvaluated = true;
|
||||
}
|
||||
}
|
||||
this.cannotBeEvaluated |= saveCannotBeEvaluated;
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables cache key support for local collection values.
|
||||
/// </summary>
|
||||
public class LocalCollectionExpander : ExpressionVisitor
|
||||
{
|
||||
public static Expression Rewrite(Expression expression)
|
||||
{
|
||||
return new LocalCollectionExpander().Visit(expression);
|
||||
}
|
||||
|
||||
protected override Expression VisitMethodCall(MethodCallExpression node)
|
||||
{
|
||||
// pair the method's parameter types with its arguments
|
||||
var map = node.Method.GetParameters()
|
||||
.Zip(node.Arguments, (p, a) => new { Param = p.ParameterType, Arg = a })
|
||||
.ToLinkedList();
|
||||
|
||||
// deal with instance methods
|
||||
var instanceType = node.Object == null ? null : node.Object.Type;
|
||||
map.AddFirst(new { Param = instanceType, Arg = node.Object });
|
||||
|
||||
// for any local collection parameters in the method, make a
|
||||
// replacement argument which will print its elements
|
||||
var replacements = (from x in map
|
||||
where x.Param != null && x.Param.IsGenericType
|
||||
let g = x.Param.GetGenericTypeDefinition()
|
||||
where g == typeof(IEnumerable<>) || g == typeof(List<>)
|
||||
where x.Arg.NodeType == ExpressionType.Constant
|
||||
let elementType = x.Param.GetGenericArguments().Single()
|
||||
let printer = MakePrinter((ConstantExpression)x.Arg, elementType)
|
||||
select new { x.Arg, Replacement = printer }).ToList();
|
||||
|
||||
if (replacements.Any())
|
||||
{
|
||||
var args = map.Select(x => (from r in replacements
|
||||
where r.Arg == x.Arg
|
||||
select r.Replacement).SingleOrDefault() ?? x.Arg).ToList();
|
||||
|
||||
node = node.Update(args.First(), args.Skip(1));
|
||||
}
|
||||
|
||||
return base.VisitMethodCall(node);
|
||||
}
|
||||
|
||||
ConstantExpression MakePrinter(ConstantExpression enumerable, Type elementType)
|
||||
{
|
||||
var value = (IEnumerable)enumerable.Value;
|
||||
var printerType = typeof(Printer<>).MakeGenericType(elementType);
|
||||
var printer = Activator.CreateInstance(printerType, value);
|
||||
|
||||
return Expression.Constant(printer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides ToString to print each element of a collection.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Inherits List in order to support List.Contains instance method as well
|
||||
/// as standard Enumerable.Contains/Any extension methods.
|
||||
/// </remarks>
|
||||
class Printer<T> : List<T>
|
||||
{
|
||||
public Printer(IEnumerable collection)
|
||||
{
|
||||
this.AddRange(collection.Cast<T>());
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "{" + this.ToConcatenatedString(t => t.ToString(), "|") + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Utility
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an MD5 fingerprint of the string.
|
||||
/// </summary>
|
||||
public static string ToMd5Fingerprint(this string s)
|
||||
{
|
||||
var bytes = Encoding.Unicode.GetBytes(s.ToCharArray());
|
||||
var hash = new MD5CryptoServiceProvider().ComputeHash(bytes);
|
||||
|
||||
// concat the hash bytes into one long string
|
||||
return hash.Aggregate(new StringBuilder(32),
|
||||
(sb, b) => sb.Append(b.ToString("X2")))
|
||||
.ToString();
|
||||
}
|
||||
|
||||
public static string ToConcatenatedString<T>(this IEnumerable<T> source, Func<T, string> selector, string separator)
|
||||
{
|
||||
var b = new StringBuilder();
|
||||
bool needSeparator = false;
|
||||
|
||||
foreach (var item in source)
|
||||
{
|
||||
if (needSeparator)
|
||||
b.Append(separator);
|
||||
|
||||
b.Append(selector(item));
|
||||
needSeparator = true;
|
||||
}
|
||||
|
||||
return b.ToString();
|
||||
}
|
||||
|
||||
public static LinkedList<T> ToLinkedList<T>(this IEnumerable<T> source)
|
||||
{
|
||||
return new LinkedList<T>(source);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking.Internal;
|
||||
|
@ -18,6 +19,7 @@ using ShardingCore.Utils;
|
|||
|
||||
namespace ShardingCore.Extensions
|
||||
{
|
||||
[ExcludeFromCodeCoverage]
|
||||
public static class DbContextExtension
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
@ -68,14 +68,19 @@ namespace ShardingCore.Test
|
|||
_shardingTableCreator = shardingTableCreator;
|
||||
_shardingReadWriteManager = shardingReadWriteManager;
|
||||
_routeTailFactory = routeTailFactory;
|
||||
|
||||
//var dataSource = ShardingContainer.GetService<IVirtualDataSource<ShardingDefaultDbContext>>();
|
||||
//dataSource.AddPhysicDataSource(new DefaultPhysicDataSource("E", "XXXXX", false));
|
||||
//var virtualDataSourceRoute = dataSource.GetRoute<Order>();
|
||||
//virtualDataSourceRoute.AddDataSourceName("E");
|
||||
}
|
||||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
var xxxx = "202102";
|
||||
var queryable1 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202102);
|
||||
var routeParseExpression1 = ShardingUtil.GetRouteParseExpression<int>(queryable1, virtualTableRoute.EntityMetadata,
|
||||
(i, op) => virtualTableRoute.GetRouteToFilter(i, op), true);
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace ShardingCore.Test
|
|||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace ShardingCore.Test.Shardings
|
|||
{
|
||||
public class OrderAreaShardingVirtualDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<Order,string>
|
||||
{
|
||||
public override bool EnableRouteParseCompileCache => true;
|
||||
public override bool EnableRouteParseCompileCache => false;
|
||||
protected override bool EnableHintRoute =>true;
|
||||
|
||||
private readonly List<string> _dataSources = new List<string>()
|
||||
|
|
|
@ -9,7 +9,7 @@ namespace ShardingCore.Test.Shardings
|
|||
{
|
||||
public class OrderCreateTimeVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
|
||||
{
|
||||
public override bool EnableRouteParseCompileCache => true;
|
||||
public override bool EnableRouteParseCompileCache => false;
|
||||
public override DateTime GetBeginTime()
|
||||
{
|
||||
return new DateTime(2021, 1, 1);
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace ShardingCore.Test2x
|
|||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace ShardingCore.Test2x
|
|||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace ShardingCore.Test3x
|
|||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace ShardingCore.Test3x
|
|||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ namespace ShardingCore.Test5x
|
|||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ namespace ShardingCore.Test5x
|
|||
[Fact]
|
||||
public void RouteParseCompileCacheTest()
|
||||
{
|
||||
var expressionEqualityComparer = new ExpressionEqualityComparer();
|
||||
var expressionEqualityComparer = new RouteParseExpressionEqualityComparer();
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable<SysUserSalary>();
|
||||
var virtualTableRoute = (AbstractShardingOperatorVirtualTableRoute<SysUserSalary, int>)virtualTable.GetVirtualRoute();
|
||||
|
||||
|
|
Loading…
Reference in New Issue