diff --git a/nuget-publish.bat b/nuget-publish.bat index c4d3a3fc..f8c03dc6 100644 --- a/nuget-publish.bat +++ b/nuget-publish.bat @@ -1,9 +1,9 @@ :start ::定义版本 -set EFCORE2=2.6.0.31 -set EFCORE3=3.6.0.31 -set EFCORE5=5.6.0.31 -set EFCORE6=6.6.0.31 +set EFCORE2=2.6.0.32 +set EFCORE3=3.6.0.32 +set EFCORE5=5.6.0.32 +set EFCORE6=6.6.0.32 ::删除所有bin与obj下的文件 @echo off diff --git a/samples/Sample.AutoCreateIfPresent/Save7DayRoute.cs b/samples/Sample.AutoCreateIfPresent/Save7DayRoute.cs new file mode 100644 index 00000000..1bee2f00 --- /dev/null +++ b/samples/Sample.AutoCreateIfPresent/Save7DayRoute.cs @@ -0,0 +1,79 @@ +using System.Data; +using Microsoft.EntityFrameworkCore; +using ShardingCore.Core.EntityMetadatas; +using ShardingCore.Core.VirtualRoutes; +using ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine; +using ShardingCore.VirtualRoutes.Days; + +namespace Sample.AutoCreateIfPresent; + +public class Save7DayRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute +{ + public override void Configure(EntityMetadataTableBuilder builder) + { + builder.ShardingProperty(o => o.CreateTime); + } + + /// + /// 自动定时任务 + /// + /// + public override bool AutoCreateTableByTime() + { + return true; + } + + /// + /// 下次启动的时候只保留7天的 + /// + /// + public override DateTime GetBeginTime() + { + return DateTime.Now.AddDays(-7); + } + + /// + /// 路由查询后置处理器 + /// must手动指定的情况下无效不会经过这个方法 + /// + /// + /// + /// + protected override List AfterShardingRouteUnitFilter(DataSourceRouteResult dataSourceRouteResult, List shardingRouteUnits) + { + var minTail = ShardingKeyToTail(DateTime.Now.AddDays(-7)); + return shardingRouteUnits.Where(o => String.Compare(o.Tail, minTail, StringComparison.Ordinal)>=0).ToList(); + // if (shardingRouteUnits.Count > 7) + // { + // return shardingRouteUnits.OrderByDescending(o => o.Tail).Take(7).ToList(); + // } + // + // return shardingRouteUnits; + } + + public override async Task ExecuteAsync() + { + await base.ExecuteAsync(); + using (var scope = RouteShardingProvider.ApplicationServiceProvider.CreateScope()) + { + using (var defaultDbContext = scope.ServiceProvider.GetService()) + { + var dbConnection = defaultDbContext.Database.GetDbConnection(); + if (dbConnection.State != ConnectionState.Open) + { + await dbConnection.OpenAsync(); + } + var entityMetadataManager = RouteShardingProvider.GetRequiredService(); + + var entityMetadata = entityMetadataManager.TryGet(typeof(OrderByHour)); + var deleteTail = ShardingKeyToTail(DateTime.Now.AddHours(1).AddDays(-7)); + using (var dbCommand = dbConnection.CreateCommand()) + { + dbCommand.CommandText = + $"delete from {entityMetadata.LogicTableName}{entityMetadata.TableSeparator}{deleteTail};"; + await dbCommand.ExecuteNonQueryAsync(); + } + } + } + } +} \ No newline at end of file diff --git a/samples/Sample.MySql/Controllers/WeatherForecastController.cs b/samples/Sample.MySql/Controllers/WeatherForecastController.cs index 644aa515..aec95bf2 100644 --- a/samples/Sample.MySql/Controllers/WeatherForecastController.cs +++ b/samples/Sample.MySql/Controllers/WeatherForecastController.cs @@ -16,6 +16,11 @@ using ShardingCore.TableCreator; namespace Sample.MySql.Controllers { + public class ssss + { + public string Id { get; set; } + public int C { get; set; } + } [ApiController] [Route("[controller]/[action]")] public class WeatherForecastController : ControllerBase @@ -30,6 +35,11 @@ namespace Sample.MySql.Controllers _otherDbContext = otherDbContext; } + public IQueryable GetAll() + { + + return _defaultTableDbContext.Set(); + } [HttpGet] public async Task Get() { @@ -48,6 +58,9 @@ namespace Sample.MySql.Controllers // using (var tran = _defaultTableDbContext.Database.BeginTransaction()) // { var sysUserMods = _defaultTableDbContext.Set().OrderBy(o=>o.Id).ThenBy(o=>o.Name); + + var sysUserMods1 = _defaultTableDbContext.Set() + .Select(o => new ssss(){ Id = o.Id, C = GetAll().Count(x => x.Id == o.Id) }).ToList(); var resultX = await _defaultTableDbContext.Set() .Where(o => o.Id == "2" || o.Id == "3").FirstOrDefaultAsync(); var resultY = await _defaultTableDbContext.Set().FirstOrDefaultAsync(o => o.Id == "2" || o.Id == "3"); diff --git a/samples/Sample.SqlServer/Startup.cs b/samples/Sample.SqlServer/Startup.cs index 97bf7f84..8b472465 100644 --- a/samples/Sample.SqlServer/Startup.cs +++ b/samples/Sample.SqlServer/Startup.cs @@ -71,6 +71,12 @@ namespace Sample.SqlServer new ReadNode("X","Data Source=localhost;Initial Catalog=ShardingCoreDBXA123;Integrated Security=True;"), }} },ReadStrategyEnum.Loop); + }).AddServiceConfigure(s => + { + s.AddSingleton(sp => LoggerFactory.Create(builder => + { + builder.AddConsole(); + })); }).AddShardingCore(); //services.AddShardingDbContext( // (conn, o) => diff --git a/src/ShardingCore/Extensions/CommonExtension.cs b/src/ShardingCore/Extensions/CommonExtension.cs index 54c9d567..eb920ecc 100644 --- a/src/ShardingCore/Extensions/CommonExtension.cs +++ b/src/ShardingCore/Extensions/CommonExtension.cs @@ -255,5 +255,12 @@ namespace ShardingCore.Extensions type = type.BaseType; } } + + public static bool IsMethodReturnTypeQueryableType(this Type type) + { + if (type == null) + throw new ArgumentNullException(nameof(type)); + return typeof(IQueryable).IsAssignableFrom(type); + } } } \ No newline at end of file diff --git a/src/ShardingCore/Extensions/IShardingQueryableExtension.cs b/src/ShardingCore/Extensions/IShardingQueryableExtension.cs index b97fd530..4b5f7241 100644 --- a/src/ShardingCore/Extensions/IShardingQueryableExtension.cs +++ b/src/ShardingCore/Extensions/IShardingQueryableExtension.cs @@ -125,6 +125,12 @@ namespace ShardingCore.Extensions var newExpression = replaceQueryableVisitor.Visit(source.Expression); return replaceQueryableVisitor.Source.Provider.CreateQuery(newExpression); } + internal static IQueryable ReplaceDbContextQueryableWithType(this IQueryable source, DbContext dbContext) + { + DbContextReplaceQueryableVisitor replaceQueryableVisitor = new DbContextReplaceQueryableVisitor(dbContext); + var newExpression = replaceQueryableVisitor.Visit(source.Expression); + return (IQueryable)replaceQueryableVisitor.Source.Provider.CreateQuery(newExpression); + } internal static Expression ReplaceDbContextExpression(this Expression queryExpression, DbContext dbContext) { DbContextReplaceQueryableVisitor replaceQueryableVisitor = new DbContextReplaceQueryableVisitor(dbContext); diff --git a/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor.cs b/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor.cs index 1855b53e..77ab47f2 100644 --- a/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor.cs +++ b/src/ShardingCore/Sharding/Visitors/DbContextReplaceQueryableVisitor.cs @@ -33,42 +33,43 @@ namespace ShardingCore.Core.Internal.Visitors // Recurse down to see if we can simplify... //if (memberExpression.IsMemberQueryable()) //2x,3x 路由 单元测试 分表和不分表 //{ - var expression = Visit(memberExpression.Expression); + var expression = Visit(memberExpression.Expression); - // If we've ended up with a constant, and it's a property or a field, - // we can simplify ourselves to a constant - if (expression is ConstantExpression constantExpression) + // If we've ended up with a constant, and it's a property or a field, + // we can simplify ourselves to a constant + if (expression is ConstantExpression constantExpression) + { + object container = constantExpression.Value; + var member = memberExpression.Member; + if (member is FieldInfo fieldInfo) { - object container = constantExpression.Value; - var member = memberExpression.Member; - if (member is FieldInfo fieldInfo) + object value = fieldInfo.GetValue(container); + if (value is IQueryable queryable) { - object value = fieldInfo.GetValue(container); - if (value is IQueryable queryable) - { - return ReplaceMemberExpression(queryable); - } - - if (value is DbContext dbContext) - { - return ReplaceMemberExpression(dbContext); - } - //return Expression.Constant(value); + return ReplaceMemberExpression(queryable); } - if (member is PropertyInfo propertyInfo) + if (value is DbContext dbContext) { - object value = propertyInfo.GetValue(container, null); - if (value is IQueryable queryable) - { - return ReplaceMemberExpression(queryable); - } - if (value is DbContext dbContext) - { - return ReplaceMemberExpression(dbContext); - } + return ReplaceMemberExpression(dbContext); + } + //return Expression.Constant(value); + } + + if (member is PropertyInfo propertyInfo) + { + object value = propertyInfo.GetValue(container, null); + if (value is IQueryable queryable) + { + return ReplaceMemberExpression(queryable); + } + + if (value is DbContext dbContext) + { + return ReplaceMemberExpression(dbContext); } } + } //} return base.VisitMember(memberExpression); @@ -85,14 +86,50 @@ namespace ShardingCore.Core.Internal.Visitors Expression.Property(ConstantExpression.Constant(tempVariable), nameof(TempVariable.Queryable)); return queryableMemberReplaceExpression; } + private MemberExpression ReplaceMemberExpression(DbContext dbContext) { var tempVariableGenericType = typeof(TempDbVariable<>).GetGenericType0(dbContext.GetType()); var tempVariable = Activator.CreateInstance(tempVariableGenericType, _dbContext); MemberExpression dbContextMemberReplaceExpression = - Expression.Property(ConstantExpression.Constant(tempVariable), nameof(TempDbVariable.DbContext)); + Expression.Property(ConstantExpression.Constant(tempVariable), + nameof(TempDbVariable.DbContext)); return dbContextMemberReplaceExpression; } + protected override Expression VisitMethodCall(MethodCallExpression node) + { + if (node.Method.ReturnType.IsMethodReturnTypeQueryableType()&&node.Method.ReturnType.IsGenericType) + { +#if EFCORE2 || EFCORE3 + var notRoot = node.Arguments.All(o => !(o is ConstantExpression constantExpression&&constantExpression.Value is IQueryable)); +#endif +#if !EFCORE2 && !EFCORE3 + var notRoot = node.Arguments.All(o => !(o is QueryRootExpression)); +#endif + if (notRoot) + { + var entityType = node.Method.ReturnType.GenericTypeArguments[0]; + + var whereCallExpression = ReplaceMethodCallExpression(node, entityType); + return whereCallExpression; + } + } + + return base.VisitMethodCall(node); + } + + private MethodCallExpression ReplaceMethodCallExpression(MethodCallExpression methodCallExpression, + Type entityType) + { + MethodCallExpression whereCallExpression = Expression.Call( + typeof(IShardingQueryableExtension), + nameof(IShardingQueryableExtension.ReplaceDbContextQueryableWithType), + new Type[] { entityType }, + methodCallExpression, Expression.Constant(_dbContext) + ); + return whereCallExpression; + } + internal sealed class TempVariable { @@ -103,6 +140,7 @@ namespace ShardingCore.Core.Internal.Visitors Queryable = queryable; } } + internal sealed class TempDbVariable { public T1 DbContext { get; } @@ -112,7 +150,6 @@ namespace ShardingCore.Core.Internal.Visitors DbContext = dbContext; } } - } #if EFCORE2 || EFCORE3 @@ -170,9 +207,11 @@ namespace ShardingCore.Core.Internal.Visitors if (node is QueryRootExpression queryRootExpression) { var dbContextDependencies = - typeof(DbContext).GetTypePropertyValue(_dbContext, "DbContextDependencies") as IDbContextDependencies; + typeof(DbContext).GetTypePropertyValue(_dbContext, "DbContextDependencies") as + IDbContextDependencies; var targetIQ = - (IQueryable)((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, queryRootExpression.EntityType.ClrType); + (IQueryable)((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, + queryRootExpression.EntityType.ClrType); var newQueryable = targetIQ.Provider.CreateQuery(targetIQ.Expression); if (Source == null) @@ -204,5 +243,4 @@ namespace ShardingCore.Core.Internal.Visitors } } #endif - } \ No newline at end of file