Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>

<PropertyGroup>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<LangVersion>latestMajor</LangVersion>
<!--默认统一输出XML注释文档,不需要输出的项目可以单独关闭-->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down
16 changes: 16 additions & 0 deletions examples/Demo.DateSharding/Program.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -21,6 +23,20 @@

//按分钟分表
config.SetDateSharding<Base_UnitTest>(nameof(Base_UnitTest.CreateTime), ExpandByDateMode.PerMinute, startTime);

config.AddModelBuilderFilter(builder =>
{
builder.Entity<Base_UnitTest>(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();
Expand Down
4 changes: 2 additions & 2 deletions src/EFCore.Sharding.MySql/EFCore.Sharding.MySql.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql.NetTopologySuite" Version="8.*" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.0.0-preview.3.efcore.9.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql.NetTopologySuite" Version="9.0.0-preview.3.efcore.9.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="9.*" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql.NetTopologySuite" Version="9.*" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.6" />
<PackageReference Include="Microsoft.Data.SqlClient" Version="5.1.7" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/EFCore.Sharding/DbContext/GenericDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using EFCore.Sharding.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace EFCore.Sharding
Expand All @@ -23,7 +24,7 @@ public static IServiceCollection AddEFCoreSharding(this IServiceCollection servi
shardingBuilder?.Invoke(container);
_ = services.AddSingleton(container);
_ = services.AddSingleton<IShardingBuilder>(container);
_ = services.AddSingleton<IShardingConfig>(container);
_ = services.AddSingleton<IShardingConfig, ShardingConfig>();
_ = services.AddScoped<DbFactory>();
_ = services.AddScoped<IDbFactory, DbFactory>();
_ = services.AddScoped<IShardingDbAccessor, ShardingDbAccessor>();
Expand Down
10 changes: 9 additions & 1 deletion src/EFCore.Sharding/DependencyInjection/IShardingBuilder.cs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -9,6 +11,10 @@ namespace EFCore.Sharding
/// </summary>
public interface IShardingBuilder
{
SynchronizedCollection<DataSource> DataSources { get; }
SynchronizedCollection<ShardingRule> ShardingRules { get; }
SynchronizedCollection<PhysicTable> PhysicTables { get; }

/// <summary>
/// 设置实体的程序集
/// </summary>
Expand All @@ -30,6 +36,8 @@ public interface IShardingBuilder
/// <returns></returns>
IShardingBuilder AddEntityTypeBuilderFilter(Action<EntityTypeBuilder> filter);

IShardingBuilder AddModelBuilderFilter(Action<ModelBuilder> builder);

/// <summary>
/// 使用Code First进行迁移时忽略外键
/// </summary>
Expand Down
97 changes: 97 additions & 0 deletions src/EFCore.Sharding/DependencyInjection/ShardingConfig.cs
Original file line number Diff line number Diff line change
@@ -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<ShardingConfig> _logger;

public ShardingConfig(ShardingContainer shardingContainer, ILogger<ShardingConfig> 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<T>(IQueryable<T> source)
{
List<(string suffix, string conString, DatabaseType dbType)> allTables = GetTargetTables<T>(ReadWriteType.Read);

return FilterTable(allTables, source);
}

public (string suffix, string conString, DatabaseType dbType) GetTheWriteTable<T>(T obj)
{
return GetTargetTables<T>(ReadWriteType.Write, obj).Single();
}

public List<(string suffix, string conString, DatabaseType dbType)> GetWriteTables<T>(IQueryable<T> source = null)
{
List<(string suffix, string conString, DatabaseType dbType)> tables = GetTargetTables<T>(ReadWriteType.Write, null);
if (source != null)
{
tables = FilterTable(tables, source);
}

return tables;
}

private List<(string suffix, string conString, DatabaseType dbType)> FilterTable<T>(
List<(string suffix, string conString, DatabaseType dbType)> allTables, IQueryable<T> source)
{
Type entityType = typeof(T);
string absTable = AnnotationHelper.GetDbTableName(source.ElementType);
ShardingRule rule = ShardingContainer.ShardingRules.Where(x => x.EntityType == entityType).Single();
List<string> allTableSuffixs = allTables.Select(x => x.suffix).ToList();
List<string> 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<TEntity>(ReadWriteType opType, object obj = null)
{
Type entityType = typeof(TEntity);
ShardingRule rule = ShardingContainer.ShardingRules.Where(x => x.EntityType == entityType).FirstOrDefault();

//获取数据库组
List<PhysicTable> 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;
}
}
}
106 changes: 24 additions & 82 deletions src/EFCore.Sharding/DependencyInjection/ShardingContainer.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -8,7 +9,7 @@

namespace EFCore.Sharding
{
internal class ShardingContainer : IShardingConfig, IShardingBuilder
internal class ShardingContainer : IShardingBuilder
{
#region 构造函数

Expand All @@ -22,44 +23,12 @@ public ShardingContainer(IServiceCollection services)

#region 私有成员

private readonly SynchronizedCollection<DataSource> _dataSources
= [];
private readonly SynchronizedCollection<ShardingRule> _shardingRules
= [];
private readonly SynchronizedCollection<PhysicTable> _physicTables
= [];
private List<(string suffix, string conString, DatabaseType dbType)>
GetTargetTables<TEntity>(ReadWriteType opType, object obj = null)
{
Type entityType = typeof(TEntity);
ShardingRule rule = _shardingRules.Where(x => x.EntityType == entityType).FirstOrDefault();

//获取数据库组
List<PhysicTable> 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<DataSource> DataSources { get; private set; } = [];
public SynchronizedCollection<ShardingRule> ShardingRules { get; private set; } = [];
public SynchronizedCollection<PhysicTable> PhysicTables { get; private set; } = [];
private void CheckRule<TEntity>(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}已存在分表规则!");
}
Expand Down Expand Up @@ -87,9 +56,9 @@ private void AddPhysicTable<TEntity>(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,
Expand All @@ -99,26 +68,12 @@ private void AddPhysicTable<TEntity>(string suffix, string sourceName)
}
private void CreateTable<TEntity>(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<DbFactory>().CreateTable(aDb.connectionString, theSource.DbType, typeof(TEntity), suffix);
});
}
private List<(string suffix, string conString, DatabaseType dbType)> FilterTable<T>(
List<(string suffix, string conString, DatabaseType dbType)> allTables, IQueryable<T> source)
{
Type entityType = typeof(T);
string absTable = AnnotationHelper.GetDbTableName(source.ElementType);
ShardingRule rule = _shardingRules.Where(x => x.EntityType == entityType).Single();
List<string> allTableSuffixs = allTables.Select(x => x.suffix).ToList();
List<string> 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))
Expand All @@ -132,30 +87,6 @@ private void AddShardingTable(string absTableName, string fullTableName)

#region 配置提供

public List<(string suffix, string conString, DatabaseType dbType)> GetWriteTables<T>(IQueryable<T> source = null)
{
List<(string suffix, string conString, DatabaseType dbType)> tables = GetTargetTables<T>(ReadWriteType.Write, null);
if (source != null)
{
tables = FilterTable(tables, source);
}

return tables;
}
public (string suffix, string conString, DatabaseType dbType) GetTheWriteTable<T>(T obj)
{
return GetTargetTables<T>(ReadWriteType.Write, obj).Single();
}
public List<(string suffix, string conString, DatabaseType dbType)> GetReadTables<T>(IQueryable<T> source)
{
List<(string suffix, string conString, DatabaseType dbType)> allTables = GetTargetTables<T>(ReadWriteType.Read);

return FilterTable(allTables, source);
}
public DatabaseType FindADbType()
{
return _dataSources.FirstOrDefault().DbType;
}
public readonly Dictionary<string, List<string>> ExistsShardingTables
= [];

Expand Down Expand Up @@ -187,6 +118,17 @@ public IShardingBuilder AddEntityTypeBuilderFilter(Action<EntityTypeBuilder> fil

return this;
}

public IShardingBuilder AddModelBuilderFilter(Action<ModelBuilder> builder)
{
_ = _services.Configure<EFCoreShardingOptions>(x =>
{
x.ModelBuilderFilter += builder;
});

return this;
}

public IShardingBuilder MigrationsWithoutForeignKey()
{
_ = _services.Configure<EFCoreShardingOptions>(x =>
Expand Down Expand Up @@ -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,
Expand All @@ -344,7 +286,7 @@ public IShardingBuilder SetDateSharding<TEntity>(string shardingField, ExpandByD
ShardingField = shardingField,
ShardingType = ShardingType.Date
};
_shardingRules.Add(shardingRule);
ShardingRules.Add(shardingRule);

EFCoreShardingOptions.Bootstrapper += serviceProvider =>
{
Expand Down Expand Up @@ -425,7 +367,7 @@ public IShardingBuilder SetHashModSharding<TEntity>(string shardingField, int mo
Mod = mod,
ShardingType = ShardingType.HashMod
};
_shardingRules.Add(rule);
ShardingRules.Add(rule);

EFCoreShardingOptions.Bootstrapper += serviceProvider =>
{
Expand Down
Loading