diff --git a/samples/Sample.SqlServer/Controllers/ValuesController.cs b/samples/Sample.SqlServer/Controllers/ValuesController.cs index 766d7b85..136bf2e3 100644 --- a/samples/Sample.SqlServer/Controllers/ValuesController.cs +++ b/samples/Sample.SqlServer/Controllers/ValuesController.cs @@ -224,7 +224,7 @@ namespace Sample.SqlServer.Controllers var xxxaaa = await _defaultTableDbContext.Set().FirstOrDefaultAsync(); Console.WriteLine("----0----"); - var xxx = await _defaultTableDbContext.Set().OrderByDescending(o=>o.DateOfMonth).FirstOrDefaultAsync(); + var xxx = await _defaultTableDbContext.Set().IgnoreQueryFilters().OrderByDescending(o=>o.DateOfMonth).FirstOrDefaultAsync(); Console.WriteLine("----1----"); var xxx1 = await _defaultTableDbContext.Set().OrderByDescending(o=>o.DateOfMonth).LastOrDefaultAsync(); Console.WriteLine("----2----"); diff --git a/samples/Sample.SqlServer/Startup.cs b/samples/Sample.SqlServer/Startup.cs index 31e845c9..036730ed 100644 --- a/samples/Sample.SqlServer/Startup.cs +++ b/samples/Sample.SqlServer/Startup.cs @@ -139,7 +139,7 @@ namespace Sample.SqlServer public bool IsNotSupportSharding(IQueryCompilerContext queryCompilerContext) { - return queryCompilerContext.isUnion(); + return queryCompilerContext.IsUnion(); } } } \ No newline at end of file diff --git a/samples/Sample.SqlServerShardingTable/Controllers/TestController.cs b/samples/Sample.SqlServerShardingTable/Controllers/TestController.cs index 0cee238c..a3d1c514 100644 --- a/samples/Sample.SqlServerShardingTable/Controllers/TestController.cs +++ b/samples/Sample.SqlServerShardingTable/Controllers/TestController.cs @@ -42,6 +42,7 @@ namespace Sample.SqlServerShardingTable.Controllers public async Task Query2() { var multiOrder =await _myDbContext.Set().Where(o=>o.Id== 232398109278351360).FirstOrDefaultAsync(); + var multiOrder1 =await _myDbContext.Set().IgnoreQueryFilters().Where(o=>o.Id== 232398109278351360).FirstOrDefaultAsync(); var longs = new []{ 232398109278351360 , 255197859283087360 }; var multiOrders = await _myDbContext.Set().Where(o => longs.Contains(o.Id)).ToListAsync(); var dateTime = new DateTime(2021, 11, 1); diff --git a/src/ShardingCore/Core/NotSupportShardingProviders/DefaultNotSupportShardingProvider.cs b/src/ShardingCore/Core/NotSupportShardingProviders/DefaultNotSupportShardingProvider.cs index fcb1d90c..4e06748d 100644 --- a/src/ShardingCore/Core/NotSupportShardingProviders/DefaultNotSupportShardingProvider.cs +++ b/src/ShardingCore/Core/NotSupportShardingProviders/DefaultNotSupportShardingProvider.cs @@ -20,7 +20,7 @@ namespace ShardingCore.Core.NotSupportShardingProviders public bool IsNotSupportSharding(IQueryCompilerContext queryCompilerContext) { - return queryCompilerContext.isUnion(); + return queryCompilerContext.IsUnion(); } } } diff --git a/src/ShardingCore/Sharding/ShardingExecutors/Abstractions/IQueryCompilerContext.cs b/src/ShardingCore/Sharding/ShardingExecutors/Abstractions/IQueryCompilerContext.cs index 6d4fdaa3..96a815e8 100644 --- a/src/ShardingCore/Sharding/ShardingExecutors/Abstractions/IQueryCompilerContext.cs +++ b/src/ShardingCore/Sharding/ShardingExecutors/Abstractions/IQueryCompilerContext.cs @@ -32,6 +32,6 @@ namespace ShardingCore.Sharding.ShardingExecutors.Abstractions /// bool IsQueryTrack(); - bool isUnion(); + bool IsUnion(); } } diff --git a/src/ShardingCore/Sharding/ShardingExecutors/MergeQueryCompilerContext.cs b/src/ShardingCore/Sharding/ShardingExecutors/MergeQueryCompilerContext.cs index fdeb826f..80f4c320 100644 --- a/src/ShardingCore/Sharding/ShardingExecutors/MergeQueryCompilerContext.cs +++ b/src/ShardingCore/Sharding/ShardingExecutors/MergeQueryCompilerContext.cs @@ -104,9 +104,9 @@ namespace ShardingCore.Sharding.ShardingExecutors return _queryCompilerContext.IsQueryTrack(); } - public bool isUnion() + public bool IsUnion() { - return _queryCompilerContext.isUnion(); + return _queryCompilerContext.IsUnion(); } public QueryCompilerExecutor GetQueryCompilerExecutor() diff --git a/src/ShardingCore/Sharding/ShardingExecutors/QueryCompilerContext.cs b/src/ShardingCore/Sharding/ShardingExecutors/QueryCompilerContext.cs index 8287b644..52fb8ea2 100644 --- a/src/ShardingCore/Sharding/ShardingExecutors/QueryCompilerContext.cs +++ b/src/ShardingCore/Sharding/ShardingExecutors/QueryCompilerContext.cs @@ -20,24 +20,23 @@ namespace ShardingCore.Sharding.ShardingExecutors private readonly Expression _queryExpression; private readonly IEntityMetadataManager _entityMetadataManager; private readonly Type _shardingDbContextType; - private readonly IShardingEntityConfigOptions _entityConfigOptions; private QueryCompilerExecutor _queryCompilerExecutor; private bool? hasQueryCompilerExecutor; - private bool? _isNoTracking; - private bool _isUnion; + private readonly bool? _isNoTracking; + private readonly bool _isUnion; private readonly bool _isParallelQuery; - private QueryCompilerContext( IShardingDbContext shardingDbContext, Expression queryExpression) + private QueryCompilerContext(IShardingDbContext shardingDbContext, Expression queryExpression) { _shardingDbContextType = shardingDbContext.GetType(); - _queryEntities = ShardingUtil.GetQueryEntitiesByExpression(queryExpression, _shardingDbContextType); - _isNoTracking = queryExpression.GetIsNoTracking(); - _isUnion = queryExpression.GetIsUnion(); + var compileParseResult = ShardingUtil.GetQueryCompileParseResultByExpression(queryExpression, _shardingDbContextType); + _queryEntities = compileParseResult.QueryEntities; + _isNoTracking = compileParseResult.IsNoTracking; + _isUnion = compileParseResult.IsUnion; _shardingDbContext = shardingDbContext; _queryExpression = queryExpression; - _entityMetadataManager = (IEntityMetadataManager)ShardingContainer.GetService(typeof(IEntityMetadataManager<>).GetGenericType0(_shardingDbContextType)); + _entityMetadataManager = ShardingContainer.GetRequiredEntityMetadataManager(_shardingDbContextType); - _entityConfigOptions = ShardingContainer.GetRequiredShardingEntityConfigOption(_shardingDbContextType); //原生对象的原生查询如果是读写分离就需要启用并行查询 _isParallelQuery = shardingDbContext.IsUseReadWriteSeparation() && _shardingDbContext.CurrentIsReadWriteSeparation(); } @@ -93,11 +92,12 @@ namespace ShardingCore.Sharding.ShardingExecutors } } - public bool isUnion() + public bool IsUnion() { return _isUnion; } + public QueryCompilerExecutor GetQueryCompilerExecutor() { if (!hasQueryCompilerExecutor.HasValue) diff --git a/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs b/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs index 42add254..08651836 100644 --- a/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs +++ b/src/ShardingCore/Sharding/Visitors/QueryableRouteDiscoverVisitor.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Query; using ShardingCore.Core.EntityMetadatas; using ShardingCore.Core.VirtualDatabase; @@ -36,6 +37,7 @@ namespace ShardingCore.Core.Internal.Visitors private Expression> _where = x => true; private LambdaExpression _entityLambdaExpression; private readonly ShardingPredicateResult _noShardingPredicateResult = new ShardingPredicateResult(false, null); + private bool isIgnoreQueryFilter; public QueryableRouteShardingTableDiscoverVisitor(EntityMetadata entityMetadata, Func>> keyToTailWithFilter, bool shardingTableRoute) { @@ -47,7 +49,7 @@ namespace ShardingCore.Core.Internal.Visitors public Expression> GetRouteParseExpression() { - if (_entityMetadata.QueryFilterExpression != null) + if (_entityMetadata.QueryFilterExpression != null&&!isIgnoreQueryFilter) { if (_entityLambdaExpression == null) { @@ -243,34 +245,40 @@ namespace ShardingCore.Core.Internal.Visitors protected override Expression VisitMethodCall(MethodCallExpression node) { - if (node.Method.Name == nameof(Queryable.Where)) + switch (node.Method.Name) { - if (node.Arguments[1] is UnaryExpression unaryExpression) - { - if (unaryExpression.Operand is LambdaExpression lambdaExpression) - { - if (lambdaExpression.Parameters[0].Type == _entityMetadata.EntityType) - { - if (_entityLambdaExpression == null) - { - _entityLambdaExpression = lambdaExpression; - } - else - { - var body = Expression.AndAlso(_entityLambdaExpression.Body, lambdaExpression.Body); - var lambda = Expression.Lambda(body, _entityLambdaExpression.Parameters[0]); - _entityLambdaExpression = lambda; - } - } - //var newWhere = DoResolve(lambdaExpression); - //_where = _where.And(newWhere); - } - } + case nameof(EntityFrameworkQueryableExtensions.IgnoreQueryFilters): isIgnoreQueryFilter = true;break; + case nameof(Queryable.Where): CombineEntityLambdaExpression(node);break; } return base.VisitMethodCall(node); } + private void CombineEntityLambdaExpression(MethodCallExpression node) + { + if (node.Arguments[1] is UnaryExpression unaryExpression) + { + if (unaryExpression.Operand is LambdaExpression lambdaExpression) + { + if (lambdaExpression.Parameters[0].Type == _entityMetadata.EntityType) + { + if (_entityLambdaExpression == null) + { + _entityLambdaExpression = lambdaExpression; + } + else + { + var body = Expression.AndAlso(_entityLambdaExpression.Body, lambdaExpression.Body); + var lambda = Expression.Lambda(body, _entityLambdaExpression.Parameters[0]); + _entityLambdaExpression = lambda; + } + } + //var newWhere = DoResolve(lambdaExpression); + //_where = _where.And(newWhere); + } + } + } + //private Expression> DoResolve(LambdaExpression lambdaExpression) //{ diff --git a/src/ShardingCore/Sharding/Visitors/Querys/CompileParseResult.cs b/src/ShardingCore/Sharding/Visitors/Querys/CompileParseResult.cs new file mode 100644 index 00000000..a0a9322f --- /dev/null +++ b/src/ShardingCore/Sharding/Visitors/Querys/CompileParseResult.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace ShardingCore.Sharding.Visitors.Querys +{ + public class CompileParseResult + { + public CompileParseResult(bool isUnion, bool? isNoTracking, bool isIgnoreFilter, ISet queryEntities) + { + IsUnion = isUnion; + IsNoTracking = isNoTracking; + IsIgnoreFilter = isIgnoreFilter; + QueryEntities = queryEntities; + } + /// + /// 是否使用了union查询 + /// + public bool IsUnion { get; } + /// + /// 是否使用了追踪 + /// + public bool? IsNoTracking { get; } + /// + /// 是否使用了忽略filter + /// + public bool IsIgnoreFilter { get; } + /// + /// 当前涉及到的查询对象 + /// + public ISet QueryEntities { get; } + } +} diff --git a/src/ShardingCore/Sharding/Visitors/Querys/QueryCompileParseVisitors.cs b/src/ShardingCore/Sharding/Visitors/Querys/QueryCompileParseVisitors.cs new file mode 100644 index 00000000..f1adc7ab --- /dev/null +++ b/src/ShardingCore/Sharding/Visitors/Querys/QueryCompileParseVisitors.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Query; +using ShardingCore.Core.TrackerManagers; +using ShardingCore.Extensions; + +namespace ShardingCore.Sharding.Visitors.Querys +{ + internal class QueryCompileParseVisitors : ExpressionVisitor + { + private readonly ITrackerManager _trackerManager; + private bool isUnion; + private bool? isNoTracking; + private bool isIgnoreFilter; + private readonly ISet shardingEntities = new HashSet(); + + public QueryCompileParseVisitors(ITrackerManager trackerManager) + { + _trackerManager = trackerManager; + } + + public CompileParseResult GetCompileParseResult() + { + return new CompileParseResult(isUnion, isNoTracking, isIgnoreFilter, shardingEntities); + } +#if EFCORE2 || EFCORE3 + protected override Expression VisitConstant(ConstantExpression node) + { + if (node.Value is IQueryable queryable) + { + shardingEntities.Add(queryable.ElementType); + } + + return base.VisitConstant(node); + } +#endif +#if EFCORE5 || EFCORE6 + protected override Expression VisitExtension(Expression node) + { + if (node is QueryRootExpression queryRootExpression) + { + shardingEntities.Add(queryRootExpression.EntityType.ClrType); + } + return base.VisitExtension(node); + } +#endif + protected override Expression VisitMethodCall(MethodCallExpression node) + { + switch (node.Method.Name) + { + case nameof(Queryable.Union): isUnion = true; break; + case nameof(EntityFrameworkQueryableExtensions.AsNoTracking): isNoTracking = true; break; + case nameof(EntityFrameworkQueryableExtensions.AsTracking): isNoTracking = false; break; + case nameof(EntityFrameworkQueryableExtensions.IgnoreQueryFilters): isIgnoreFilter = true; break; + case nameof(EntityFrameworkQueryableExtensions.Include): + case nameof(EntityFrameworkQueryableExtensions.ThenInclude): DiscoverQueryEntities(node); break; + } + + return base.VisitMethodCall(node); + } + + private void DiscoverQueryEntities(MethodCallExpression node) + { + var genericArguments = node.Type.GetGenericArguments(); + for (var i = 0; i < genericArguments.Length; i++) + { + var genericArgument = genericArguments[i]; + if (typeof(IEnumerable).IsAssignableFrom(genericArgument)) + { + var arguments = genericArgument.GetGenericArguments(); + foreach (var argument in arguments) + { + //if is db context model + if (_trackerManager.IsDbContextModel(argument)) + { + shardingEntities.Add(argument); + } + } + } + + if (!genericArgument.IsSimpleType()) + { + //if is db context model + if (_trackerManager.IsDbContextModel(genericArgument)) + { + shardingEntities.Add(genericArgument); + } + } + } + } + } +} diff --git a/src/ShardingCore/ShardingContainer.cs b/src/ShardingCore/ShardingContainer.cs index 2a3e123a..0d73fec8 100644 --- a/src/ShardingCore/ShardingContainer.cs +++ b/src/ShardingCore/ShardingContainer.cs @@ -10,6 +10,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using ShardingCore.Core.EntityMetadatas; namespace ShardingCore { @@ -148,5 +149,16 @@ namespace ShardingCore { return GetRequiredVirtualDataSourceManager(shardingDbContextType).GetCurrentVirtualDataSource()??throw new InvalidOperationException("cant resolve CurrentVirtualDataSource"); } + + public static IEntityMetadataManager GetRequiredEntityMetadataManager(Type shardingDbContextType) + { + return (IEntityMetadataManager)ServiceProvider.GetService(typeof(IEntityMetadataManager<>).GetGenericType0(shardingDbContextType)); + } + + public static IEntityMetadataManager GetRequiredEntityMetadataManager() + where TShardingDbContext : DbContext, IShardingDbContext + { + return (IEntityMetadataManager)GetRequiredEntityMetadataManager(typeof(TShardingDbContext)); + } } } \ No newline at end of file diff --git a/src/ShardingCore/Utils/ShardingUtil.cs b/src/ShardingCore/Utils/ShardingUtil.cs index a06ca9b0..67d8b35b 100644 --- a/src/ShardingCore/Utils/ShardingUtil.cs +++ b/src/ShardingCore/Utils/ShardingUtil.cs @@ -15,6 +15,7 @@ using ShardingCore.Core.TrackerManagers; using ShardingCore.Core.VirtualDatabase; using ShardingCore.Core.VirtualRoutes; using ShardingCore.Extensions; +using ShardingCore.Sharding.Visitors.Querys; namespace ShardingCore.Utils { @@ -82,6 +83,22 @@ namespace ShardingCore.Utils return visitor.GetQueryEntities(); } + /// + /// ȡҪıʽϢ + /// + /// + /// + /// + public static CompileParseResult GetQueryCompileParseResultByExpression(Expression expression, Type dbContextType) + { + var trackerManager = (ITrackerManager)ShardingContainer.GetService(typeof(ITrackerManager<>).GetGenericType0(dbContextType)); + + QueryCompileParseVisitors visitor = new QueryCompileParseVisitors(trackerManager); + + visitor.Visit(expression); + + return visitor.GetCompileParseResult(); + } } } \ No newline at end of file