删除并且优化部分代码

This commit is contained in:
xuejiaming 2021-12-03 13:35:08 +08:00
parent 9e93ebef43
commit 9458349c7b
24 changed files with 33 additions and 1129 deletions

View File

@ -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; }

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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) =>

View File

@ -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;
}
}
}

View File

@ -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()
{

View File

@ -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
}
}

View File

@ -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; }
}
}
}

View File

@ -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()
{

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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();

View File

@ -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>()

View File

@ -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);

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();