diff --git a/Directory.Build.props b/Directory.Build.props index b20a032..29dcb6d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,6 +1,7 @@ + $(NoWarn);CS1591 latestMajor true diff --git a/examples/Demo.DateSharding/Program.cs b/examples/Demo.DateSharding/Program.cs index 46c459f..a969369 100644 --- a/examples/Demo.DateSharding/Program.cs +++ b/examples/Demo.DateSharding/Program.cs @@ -1,7 +1,9 @@ using EFCore.Sharding; using EFCore.Sharding.Tests; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; using System; using System.Threading.Tasks; @@ -21,6 +23,20 @@ //按分钟分表 config.SetDateSharding(nameof(Base_UnitTest.CreateTime), ExpandByDateMode.PerMinute, startTime); + + config.AddModelBuilderFilter(builder => + { + builder.Entity(b => + { + b.Property(s => s.Id).HasColumnName("id_2"); + b.Property(s => s.CreateTime).HasColumnName("create_time_2"); + b.Property(s => s.UserName).HasCharSet("utf8"); + + b.HasIndex(s => s.UserId).IsUnique(); + + b.HasIndex(s => s.Age); + }); + }); }); var serviceProvider = services.BuildServiceProvider(); diff --git a/src/EFCore.Sharding.MySql/EFCore.Sharding.MySql.csproj b/src/EFCore.Sharding.MySql/EFCore.Sharding.MySql.csproj index 181504e..d7c72f8 100644 --- a/src/EFCore.Sharding.MySql/EFCore.Sharding.MySql.csproj +++ b/src/EFCore.Sharding.MySql/EFCore.Sharding.MySql.csproj @@ -33,8 +33,8 @@ - - + + diff --git a/src/EFCore.Sharding.SqlServer/EFCore.Sharding.SqlServer.csproj b/src/EFCore.Sharding.SqlServer/EFCore.Sharding.SqlServer.csproj index b4d45c9..4fb1b4c 100644 --- a/src/EFCore.Sharding.SqlServer/EFCore.Sharding.SqlServer.csproj +++ b/src/EFCore.Sharding.SqlServer/EFCore.Sharding.SqlServer.csproj @@ -28,7 +28,7 @@ - + diff --git a/src/EFCore.Sharding/DbContext/GenericDbContext.cs b/src/EFCore.Sharding/DbContext/GenericDbContext.cs index 1271984..c4314ee 100644 --- a/src/EFCore.Sharding/DbContext/GenericDbContext.cs +++ b/src/EFCore.Sharding/DbContext/GenericDbContext.cs @@ -104,6 +104,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entityTypes = q.ToList(); } + ShardingOption.ModelBuilderFilter?.Invoke(modelBuilder); + entityTypes.ForEach(aEntity => { Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder entity = modelBuilder.Entity(aEntity); diff --git a/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs b/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs index a3cf7fb..46abef4 100644 --- a/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs +++ b/src/EFCore.Sharding/DependencyInjection/EFCoreShardingExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using EFCore.Sharding.DependencyInjection; +using Microsoft.Extensions.DependencyInjection; using System; namespace EFCore.Sharding @@ -23,7 +24,7 @@ public static IServiceCollection AddEFCoreSharding(this IServiceCollection servi shardingBuilder?.Invoke(container); _ = services.AddSingleton(container); _ = services.AddSingleton(container); - _ = services.AddSingleton(container); + _ = services.AddSingleton(); _ = services.AddScoped(); _ = services.AddScoped(); _ = services.AddScoped(); diff --git a/src/EFCore.Sharding/DependencyInjection/IShardingBuilder.cs b/src/EFCore.Sharding/DependencyInjection/IShardingBuilder.cs index 87c376c..e736aa7 100644 --- a/src/EFCore.Sharding/DependencyInjection/IShardingBuilder.cs +++ b/src/EFCore.Sharding/DependencyInjection/IShardingBuilder.cs @@ -1,5 +1,7 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using System; +using System.Collections.Generic; using System.Reflection; namespace EFCore.Sharding @@ -9,6 +11,10 @@ namespace EFCore.Sharding /// public interface IShardingBuilder { + SynchronizedCollection DataSources { get; } + SynchronizedCollection ShardingRules { get; } + SynchronizedCollection PhysicTables { get; } + /// /// 设置实体的程序集 /// @@ -30,6 +36,8 @@ public interface IShardingBuilder /// IShardingBuilder AddEntityTypeBuilderFilter(Action filter); + IShardingBuilder AddModelBuilderFilter(Action builder); + /// /// 使用Code First进行迁移时忽略外键 /// diff --git a/src/EFCore.Sharding/DependencyInjection/ShardingConfig.cs b/src/EFCore.Sharding/DependencyInjection/ShardingConfig.cs new file mode 100644 index 0000000..d851a97 --- /dev/null +++ b/src/EFCore.Sharding/DependencyInjection/ShardingConfig.cs @@ -0,0 +1,97 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace EFCore.Sharding.DependencyInjection +{ + internal class ShardingConfig : IShardingConfig + { + private readonly ILogger _logger; + + public ShardingConfig(ShardingContainer shardingContainer, ILogger logger) + { + ShardingContainer = shardingContainer; + _logger = logger; + } + + public ShardingContainer ShardingContainer { get; } + + public DatabaseType FindADbType() + { + return ShardingContainer.DataSources.FirstOrDefault().DbType; + } + public List<(string suffix, string conString, DatabaseType dbType)> GetReadTables(IQueryable source) + { + List<(string suffix, string conString, DatabaseType dbType)> allTables = GetTargetTables(ReadWriteType.Read); + + return FilterTable(allTables, source); + } + + public (string suffix, string conString, DatabaseType dbType) GetTheWriteTable(T obj) + { + return GetTargetTables(ReadWriteType.Write, obj).Single(); + } + + public List<(string suffix, string conString, DatabaseType dbType)> GetWriteTables(IQueryable source = null) + { + List<(string suffix, string conString, DatabaseType dbType)> tables = GetTargetTables(ReadWriteType.Write, null); + if (source != null) + { + tables = FilterTable(tables, source); + } + + return tables; + } + + private List<(string suffix, string conString, DatabaseType dbType)> FilterTable( + List<(string suffix, string conString, DatabaseType dbType)> allTables, IQueryable source) + { + Type entityType = typeof(T); + string absTable = AnnotationHelper.GetDbTableName(source.ElementType); + ShardingRule rule = ShardingContainer.ShardingRules.Where(x => x.EntityType == entityType).Single(); + List allTableSuffixs = allTables.Select(x => x.suffix).ToList(); + List findSuffixs = ShardingHelper.FilterTable(source, allTableSuffixs, rule); + allTables = allTables.Where(x => findSuffixs.Contains(x.suffix)).ToList(); +#if DEBUG + Console.WriteLine($"访问分表:{string.Join(",", findSuffixs.Select(x => $"{absTable}_{x}"))}"); +#endif + return allTables; + } + + private List<(string suffix, string conString, DatabaseType dbType)> + GetTargetTables(ReadWriteType opType, object obj = null) + { + Type entityType = typeof(TEntity); + ShardingRule rule = ShardingContainer.ShardingRules.Where(x => x.EntityType == entityType).FirstOrDefault(); + + //获取数据库组 + List tables = ShardingContainer.PhysicTables.Where(x => x.EntityType == entityType).ToList(); + + //若为写操作则只获取特定表 + if (obj != null) + { + string tableSuffix = rule.GetTableSuffixByEntity(obj); + tables = tables.Where(x => x.Suffix == tableSuffix).ToList(); + if (!tables.Any()) + { + _logger.LogWarning("找不到指定前缀的表 {TableSuffix}", tableSuffix); + } + } + + //数据库组中数据库负载均衡 + List<(string Suffix, string connectionString, DatabaseType DbType)> resList = tables.Select(x => + { + DataSource theSource = ShardingContainer.DataSources.Where(y => y.Name == x.DataSourceName).FirstOrDefault(); + + List<(string connectionString, ReadWriteType readWriteType)> dbs = theSource.Dbs.Where(y => y.readWriteType.HasFlag(opType)).ToList(); + (string connectionString, ReadWriteType readWriteType) = RandomHelper.Next(dbs); + + return (x.Suffix, connectionString, theSource.DbType); + }).ToList(); + + return resList; + } + } +} diff --git a/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs b/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs index e8a9378..f18f5a8 100644 --- a/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs +++ b/src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using System; @@ -8,7 +9,7 @@ namespace EFCore.Sharding { - internal class ShardingContainer : IShardingConfig, IShardingBuilder + internal class ShardingContainer : IShardingBuilder { #region 构造函数 @@ -22,44 +23,12 @@ public ShardingContainer(IServiceCollection services) #region 私有成员 - private readonly SynchronizedCollection _dataSources - = []; - private readonly SynchronizedCollection _shardingRules - = []; - private readonly SynchronizedCollection _physicTables - = []; - private List<(string suffix, string conString, DatabaseType dbType)> - GetTargetTables(ReadWriteType opType, object obj = null) - { - Type entityType = typeof(TEntity); - ShardingRule rule = _shardingRules.Where(x => x.EntityType == entityType).FirstOrDefault(); - - //获取数据库组 - List tables = _physicTables.Where(x => x.EntityType == entityType).ToList(); - - //若为写操作则只获取特定表 - if (obj != null) - { - string tableSuffix = rule.GetTableSuffixByEntity(obj); - tables = tables.Where(x => x.Suffix == tableSuffix).ToList(); - } - - //数据库组中数据库负载均衡 - List<(string Suffix, string connectionString, DatabaseType DbType)> resList = tables.Select(x => - { - DataSource theSource = _dataSources.Where(y => y.Name == x.DataSourceName).FirstOrDefault(); - - List<(string connectionString, ReadWriteType readWriteType)> dbs = theSource.Dbs.Where(y => y.readWriteType.HasFlag(opType)).ToList(); - (string connectionString, ReadWriteType readWriteType) = RandomHelper.Next(dbs); - - return (x.Suffix, connectionString, theSource.DbType); - }).ToList(); - - return resList; - } + public SynchronizedCollection DataSources { get; private set; } = []; + public SynchronizedCollection ShardingRules { get; private set; } = []; + public SynchronizedCollection PhysicTables { get; private set; } = []; private void CheckRule(ShardingType shardingType, string shardingField) { - if (_shardingRules.Any(x => x.EntityType == typeof(TEntity))) + if (ShardingRules.Any(x => x.EntityType == typeof(TEntity))) { throw new Exception($"{typeof(TEntity).Name}已存在分表规则!"); } @@ -87,9 +56,9 @@ private void AddPhysicTable(string suffix, string sourceName) { Type entityType = typeof(TEntity); - if (!_physicTables.Any(x => x.EntityType == entityType && x.Suffix == suffix && x.DataSourceName == sourceName)) + if (!PhysicTables.Any(x => x.EntityType == entityType && x.Suffix == suffix && x.DataSourceName == sourceName)) { - _physicTables.Add(new PhysicTable + PhysicTables.Add(new PhysicTable { DataSourceName = sourceName, EntityType = entityType, @@ -99,26 +68,12 @@ private void AddPhysicTable(string suffix, string sourceName) } private void CreateTable(IServiceProvider serviceProvider, string sourceName, string suffix) { - DataSource theSource = _dataSources.Where(x => x.Name == sourceName).FirstOrDefault(); + DataSource theSource = DataSources.Where(x => x.Name == sourceName).FirstOrDefault(); theSource.Dbs.ForEach(aDb => { serviceProvider.GetService().CreateTable(aDb.connectionString, theSource.DbType, typeof(TEntity), suffix); }); } - private List<(string suffix, string conString, DatabaseType dbType)> FilterTable( - List<(string suffix, string conString, DatabaseType dbType)> allTables, IQueryable source) - { - Type entityType = typeof(T); - string absTable = AnnotationHelper.GetDbTableName(source.ElementType); - ShardingRule rule = _shardingRules.Where(x => x.EntityType == entityType).Single(); - List allTableSuffixs = allTables.Select(x => x.suffix).ToList(); - List findSuffixs = ShardingHelper.FilterTable(source, allTableSuffixs, rule); - allTables = allTables.Where(x => findSuffixs.Contains(x.suffix)).ToList(); -#if DEBUG - Console.WriteLine($"访问分表:{string.Join(",", findSuffixs.Select(x => $"{absTable}_{x}"))}"); -#endif - return allTables; - } private void AddShardingTable(string absTableName, string fullTableName) { if (!ExistsShardingTables.ContainsKey(absTableName)) @@ -132,30 +87,6 @@ private void AddShardingTable(string absTableName, string fullTableName) #region 配置提供 - public List<(string suffix, string conString, DatabaseType dbType)> GetWriteTables(IQueryable source = null) - { - List<(string suffix, string conString, DatabaseType dbType)> tables = GetTargetTables(ReadWriteType.Write, null); - if (source != null) - { - tables = FilterTable(tables, source); - } - - return tables; - } - public (string suffix, string conString, DatabaseType dbType) GetTheWriteTable(T obj) - { - return GetTargetTables(ReadWriteType.Write, obj).Single(); - } - public List<(string suffix, string conString, DatabaseType dbType)> GetReadTables(IQueryable source) - { - List<(string suffix, string conString, DatabaseType dbType)> allTables = GetTargetTables(ReadWriteType.Read); - - return FilterTable(allTables, source); - } - public DatabaseType FindADbType() - { - return _dataSources.FirstOrDefault().DbType; - } public readonly Dictionary> ExistsShardingTables = []; @@ -187,6 +118,17 @@ public IShardingBuilder AddEntityTypeBuilderFilter(Action fil return this; } + + public IShardingBuilder AddModelBuilderFilter(Action builder) + { + _ = _services.Configure(x => + { + x.ModelBuilderFilter += builder; + }); + + return this; + } + public IShardingBuilder MigrationsWithoutForeignKey() { _ = _services.Configure(x => @@ -320,7 +262,7 @@ public IShardingBuilder AddDataSource(string connectionString, ReadWriteType rea } public IShardingBuilder AddDataSource((string connectionString, ReadWriteType readWriteType)[] dbs, DatabaseType dbType, string sourceName = "DefaultSource") { - _dataSources.Add(new DataSource + DataSources.Add(new DataSource { Dbs = dbs, DbType = dbType, @@ -344,7 +286,7 @@ public IShardingBuilder SetDateSharding(string shardingField, ExpandByD ShardingField = shardingField, ShardingType = ShardingType.Date }; - _shardingRules.Add(shardingRule); + ShardingRules.Add(shardingRule); EFCoreShardingOptions.Bootstrapper += serviceProvider => { @@ -425,7 +367,7 @@ public IShardingBuilder SetHashModSharding(string shardingField, int mo Mod = mod, ShardingType = ShardingType.HashMod }; - _shardingRules.Add(rule); + ShardingRules.Add(rule); EFCoreShardingOptions.Bootstrapper += serviceProvider => { diff --git a/src/EFCore.Sharding/Options/EFCoreShardingOptions.cs b/src/EFCore.Sharding/Options/EFCoreShardingOptions.cs index 52648ec..12dcf5e 100644 --- a/src/EFCore.Sharding/Options/EFCoreShardingOptions.cs +++ b/src/EFCore.Sharding/Options/EFCoreShardingOptions.cs @@ -37,6 +37,9 @@ public class EFCoreShardingOptions /// public Action EntityTypeBuilderFilter { get; set; } + public Action ModelBuilderFilter { get; set; } + + /// /// 保存过滤器,参数依次为:ServiceProvider,DbContext,真正执行SaveChanges /// diff --git a/src/EFCore.Sharding/Primitives/DataSource.cs b/src/EFCore.Sharding/Primitives/DataSource.cs index eef6e4d..c21bff1 100644 --- a/src/EFCore.Sharding/Primitives/DataSource.cs +++ b/src/EFCore.Sharding/Primitives/DataSource.cs @@ -1,6 +1,6 @@ namespace EFCore.Sharding { - internal class DataSource + public class DataSource { public string Name { get; set; } public DatabaseType DbType { get; set; } diff --git a/src/EFCore.Sharding/Primitives/PhysicTable.cs b/src/EFCore.Sharding/Primitives/PhysicTable.cs index 3a202e2..e26e627 100644 --- a/src/EFCore.Sharding/Primitives/PhysicTable.cs +++ b/src/EFCore.Sharding/Primitives/PhysicTable.cs @@ -2,7 +2,7 @@ namespace EFCore.Sharding { - internal class PhysicTable + public class PhysicTable { public Type EntityType { get; set; } public string DataSourceName { get; set; } diff --git a/src/EFCore.Sharding/Primitives/ShardingRule.cs b/src/EFCore.Sharding/Primitives/ShardingRule.cs index 4593d6a..ad3863b 100644 --- a/src/EFCore.Sharding/Primitives/ShardingRule.cs +++ b/src/EFCore.Sharding/Primitives/ShardingRule.cs @@ -2,7 +2,7 @@ namespace EFCore.Sharding { - internal class ShardingRule + public class ShardingRule { public Type EntityType { get; set; } public ShardingType ShardingType { get; set; }