添加如果不存在表就创建 [#147]
This commit is contained in:
parent
22907db4ec
commit
98f570cc44
|
@ -61,6 +61,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.NoShardingMultiLevel
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.MultiConfig", "samples\Sample.MultiConfig\Sample.MultiConfig.csproj", "{D839D632-4AE4-4F75-8A2C-49EE029B0787}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.AutoCreateIfPresent", "samples\Sample.AutoCreateIfPresent\Sample.AutoCreateIfPresent.csproj", "{40C83D48-0614-4651-98C6-2ABBE857D83D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -155,6 +157,10 @@ Global
|
|||
{D839D632-4AE4-4F75-8A2C-49EE029B0787}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D839D632-4AE4-4F75-8A2C-49EE029B0787}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D839D632-4AE4-4F75-8A2C-49EE029B0787}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{40C83D48-0614-4651-98C6-2ABBE857D83D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{40C83D48-0614-4651-98C6-2ABBE857D83D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{40C83D48-0614-4651-98C6-2ABBE857D83D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{40C83D48-0614-4651-98C6-2ABBE857D83D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -182,6 +188,7 @@ Global
|
|||
{ED191305-AB19-4863-A48A-7BA4C21F467B} = {B458D737-33C5-4C10-9687-0BED2E7CD346}
|
||||
{DCEBAC86-E62B-4B6C-A352-B8C1C2C6F734} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
|
||||
{D839D632-4AE4-4F75-8A2C-49EE029B0787} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
|
||||
{40C83D48-0614-4651-98C6-2ABBE857D83D} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8C07A667-E8B4-43C7-8053-721584BAD291}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Sample.AutoCreateIfPresent.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]/[action]")]
|
||||
public class WeatherForecastController : ControllerBase
|
||||
{
|
||||
private static readonly string[] Summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
private readonly ILogger<WeatherForecastController> _logger;
|
||||
private readonly DefaultDbContext _defaultDbContext;
|
||||
|
||||
public WeatherForecastController(ILogger<WeatherForecastController> logger,DefaultDbContext defaultDbContext)
|
||||
{
|
||||
_logger = logger;
|
||||
_defaultDbContext = defaultDbContext;
|
||||
}
|
||||
|
||||
[HttpGet(Name = "GetWeatherForecast")]
|
||||
public IEnumerable<WeatherForecast> Get()
|
||||
{
|
||||
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
|
||||
{
|
||||
Date = DateTime.Now.AddDays(index),
|
||||
TemperatureC = Random.Shared.Next(-20, 55),
|
||||
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
|
||||
})
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public async Task<IActionResult> Query()
|
||||
{
|
||||
var list =await _defaultDbContext.Set<OrderByHour>().ToListAsync();
|
||||
return Ok(list);
|
||||
}
|
||||
public async Task<IActionResult> Insert()
|
||||
{
|
||||
var orderByHour = new OrderByHour();
|
||||
orderByHour.Id = Guid.NewGuid().ToString("n");
|
||||
orderByHour.Name=$"Name:"+ Guid.NewGuid().ToString("n");
|
||||
var dateTime = DateTime.Now;
|
||||
orderByHour.CreateTime = dateTime.AddHours(new Random().Next(1, 20));
|
||||
await _defaultDbContext.AddAsync(orderByHour);
|
||||
await _defaultDbContext.SaveChangesAsync();
|
||||
return Ok();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
|
||||
using ShardingCore.Sharding;
|
||||
using ShardingCore.Sharding.Abstractions;
|
||||
|
||||
/*
|
||||
* @Author: xjm
|
||||
* @Description:
|
||||
* @Date: DATE
|
||||
* @Email: 326308290@qq.com
|
||||
*/
|
||||
namespace Sample.AutoCreateIfPresent
|
||||
{
|
||||
public class DefaultDbContext:AbstractShardingDbContext,IShardingTableDbContext
|
||||
{
|
||||
public DefaultDbContext(DbContextOptions<DefaultDbContext> options) : base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
base.OnModelCreating(modelBuilder);
|
||||
modelBuilder.ApplyConfiguration(new OrderByHourMap());
|
||||
}
|
||||
|
||||
public IRouteTail RouteTail { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
/*
|
||||
* @Author: xjm
|
||||
* @Description:
|
||||
* @Date: DATE
|
||||
* @Email: 326308290@qq.com
|
||||
*/
|
||||
namespace Sample.AutoCreateIfPresent
|
||||
{
|
||||
public class OrderByHour
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||
|
||||
/*
|
||||
* @Author: xjm
|
||||
* @Description:
|
||||
* @Date: DATE
|
||||
* @Email: 326308290@qq.com
|
||||
*/
|
||||
namespace Sample.AutoCreateIfPresent
|
||||
{
|
||||
public class OrderByHourMap:IEntityTypeConfiguration<OrderByHour>
|
||||
{
|
||||
public void Configure(EntityTypeBuilder<OrderByHour> builder)
|
||||
{
|
||||
builder.HasKey(o => o.Id);
|
||||
builder.Property(o => o.Id).IsRequired().HasMaxLength(50);
|
||||
builder.Property(o => o.Name).IsRequired().HasMaxLength(128);
|
||||
builder.ToTable(nameof(OrderByHour));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Linq.Expressions;
|
||||
using MySqlConnector;
|
||||
using ShardingCore;
|
||||
using ShardingCore.Core.EntityMetadatas;
|
||||
using ShardingCore.Core.PhysicTables;
|
||||
using ShardingCore.Core.ShardingConfigurations;
|
||||
using ShardingCore.Core.ShardingConfigurations.Abstractions;
|
||||
using ShardingCore.Core.VirtualDatabase.VirtualDataSources.Abstractions;
|
||||
using ShardingCore.Core.VirtualDatabase.VirtualTables;
|
||||
using ShardingCore.Core.VirtualRoutes;
|
||||
using ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions;
|
||||
using ShardingCore.Exceptions;
|
||||
using ShardingCore.Extensions;
|
||||
using ShardingCore.TableCreator;
|
||||
|
||||
/*
|
||||
* @Author: xjm
|
||||
* @Description:
|
||||
* @Date: DATE
|
||||
* @Email: 326308290@qq.com
|
||||
*/
|
||||
namespace Sample.AutoCreateIfPresent
|
||||
{
|
||||
public class OrderByHourRoute : AbstractShardingOperatorVirtualTableRoute<OrderByHour, DateTime>
|
||||
{
|
||||
private const string Tables = "Tables";
|
||||
private const string TABLE_SCHEMA = "TABLE_SCHEMA";
|
||||
private const string TABLE_NAME = "TABLE_NAME";
|
||||
private readonly IVirtualDataSourceManager<DefaultDbContext> _virtualDataSourceManager;
|
||||
private readonly IVirtualTableManager<DefaultDbContext> _virtualTableManager;
|
||||
private readonly IShardingTableCreator<DefaultDbContext> _shardingTableCreator;
|
||||
private readonly ConcurrentDictionary<string, object?> _tails = new ConcurrentDictionary<string, object?>();
|
||||
private readonly object _lock = new object();
|
||||
|
||||
public OrderByHourRoute(IVirtualDataSourceManager<DefaultDbContext> virtualDataSourceManager,IVirtualTableManager<DefaultDbContext> virtualTableManager, IShardingTableCreator<DefaultDbContext> shardingTableCreator)
|
||||
{
|
||||
_virtualDataSourceManager = virtualDataSourceManager;
|
||||
_virtualTableManager = virtualTableManager;
|
||||
_shardingTableCreator = shardingTableCreator;
|
||||
}
|
||||
|
||||
private string ShardingKeyFormat(DateTime dateTime)
|
||||
{
|
||||
var tail = $"{dateTime:yyyyMMddHH}";
|
||||
|
||||
return tail;
|
||||
}
|
||||
|
||||
public override string ShardingKeyToTail(object shardingKey)
|
||||
{
|
||||
var dateTime = (DateTime)shardingKey;
|
||||
return ShardingKeyFormat(dateTime);
|
||||
}
|
||||
|
||||
public override List<string> GetAllTails()
|
||||
{
|
||||
//启动寻找有哪些表后缀
|
||||
using (var connection = new MySqlConnection(_virtualDataSourceManager.GetCurrentVirtualDataSource().DefaultConnectionString))
|
||||
{
|
||||
connection.Open();
|
||||
var database = connection.Database;
|
||||
|
||||
using (var dataTable = connection.GetSchema(Tables))
|
||||
{
|
||||
for (int i = 0; i < dataTable.Rows.Count; i++)
|
||||
{
|
||||
var schema = dataTable.Rows[i][TABLE_SCHEMA];
|
||||
if (database.Equals($"{schema}", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var tableName = dataTable.Rows[i][TABLE_NAME]?.ToString()??string.Empty;
|
||||
if (tableName.StartsWith(nameof(OrderByHour)))
|
||||
{
|
||||
_tails.TryAdd(tableName.Replace($"{nameof(OrderByHour)}_",""),null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return _tails.Keys.ToList();
|
||||
}
|
||||
|
||||
public override void Configure(EntityMetadataTableBuilder<OrderByHour> builder)
|
||||
{
|
||||
builder.ShardingProperty(o => o.CreateTime);
|
||||
}
|
||||
|
||||
public override Expression<Func<string, bool>> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
|
||||
{
|
||||
var t = ShardingKeyFormat(shardingKey);
|
||||
switch (shardingOperator)
|
||||
{
|
||||
case ShardingOperatorEnum.GreaterThan:
|
||||
case ShardingOperatorEnum.GreaterThanOrEqual:
|
||||
return tail => String.Compare(tail, t, StringComparison.Ordinal) >= 0;
|
||||
case ShardingOperatorEnum.LessThan:
|
||||
{
|
||||
var currentHourBeginTime = new DateTime(shardingKey.Year,shardingKey.Month,shardingKey.Day,shardingKey.Hour,0,0);
|
||||
//处于临界值 o=>o.time < [2021-01-01 00:00:00] 尾巴20210101不应该被返回
|
||||
if (currentHourBeginTime == shardingKey)
|
||||
return tail => String.Compare(tail, t, StringComparison.Ordinal) < 0;
|
||||
return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;
|
||||
}
|
||||
case ShardingOperatorEnum.LessThanOrEqual:
|
||||
return tail => String.Compare(tail, t, StringComparison.Ordinal) <= 0;
|
||||
case ShardingOperatorEnum.Equal: return tail => tail == t;
|
||||
default:
|
||||
{
|
||||
#if DEBUG
|
||||
Console.WriteLine($"shardingOperator is not equal scan all table tail");
|
||||
#endif
|
||||
return tail => true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IPhysicTable RouteWithValue(List<IPhysicTable> allPhysicTables, object shardingKey)
|
||||
{
|
||||
var shardingKeyToTail = ShardingKeyToTail(shardingKey);
|
||||
|
||||
if (!_tails.TryGetValue(shardingKeyToTail,out var _))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_tails.TryGetValue(shardingKeyToTail,out var _))
|
||||
{
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable(typeof(OrderByHour));
|
||||
_virtualTableManager.AddPhysicTable(virtualTable, new DefaultPhysicTable(virtualTable, shardingKeyToTail));
|
||||
try
|
||||
{
|
||||
_shardingTableCreator.CreateTable<OrderByHour>(_virtualDataSourceManager.GetCurrentVirtualDataSource().DefaultDataSourceName, shardingKeyToTail);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("尝试添加表失败" + ex);
|
||||
}
|
||||
|
||||
_tails.TryAdd(shardingKeyToTail,null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var needRefresh = allPhysicTables.Count != _tails.Count;
|
||||
if (needRefresh)
|
||||
{
|
||||
var virtualTable = _virtualTableManager.GetVirtualTable(typeof(OrderByHour));
|
||||
foreach (var tail in _tails.Keys)
|
||||
{
|
||||
var hashSet = allPhysicTables.Select(o=>o.Tail).ToHashSet();
|
||||
if (!hashSet.Contains(tail))
|
||||
{
|
||||
var tables = virtualTable.GetAllPhysicTables();
|
||||
var physicTable = tables.FirstOrDefault(o=>o.Tail==tail);
|
||||
if (physicTable!= null)
|
||||
{
|
||||
allPhysicTables.Add(physicTable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var physicTables = allPhysicTables.Where(o => o.Tail== shardingKeyToTail).ToList();
|
||||
if (physicTables.IsEmpty())
|
||||
{
|
||||
throw new ShardingCoreException($"sharding key route not match {EntityMetadata.EntityType} -> [{EntityMetadata.ShardingTableProperty.Name}] ->【{shardingKey}】 all tails ->[{string.Join(",", allPhysicTables.Select(o=>o.FullName))}]");
|
||||
}
|
||||
|
||||
if (physicTables.Count > 1)
|
||||
throw new ShardingCoreException($"more than one route match table:{string.Join(",", physicTables.Select(o => $"[{o.FullName}]"))}");
|
||||
return physicTables[0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Sample.AutoCreateIfPresent;
|
||||
using ShardingCore;
|
||||
using ShardingCore.Bootstrapers;
|
||||
using ShardingCore.Core.VirtualDatabase.VirtualTables;
|
||||
using ShardingCore.TableExists;
|
||||
|
||||
ILoggerFactory efLogger = LoggerFactory.Create(builder =>
|
||||
{
|
||||
builder.AddFilter((category, level) => category == DbLoggerCategory.Database.Command.Name && level == LogLevel.Information).AddConsole();
|
||||
});
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddShardingDbContext<DefaultDbContext>()
|
||||
.AddEntityConfig(o =>
|
||||
{
|
||||
o.CreateShardingTableOnStart = true;
|
||||
o.EnsureCreatedWithOutShardingTable = true;
|
||||
o.AddShardingTableRoute<OrderByHourRoute>();
|
||||
})
|
||||
.AddConfig(o =>
|
||||
{
|
||||
o.ConfigId = "c1";
|
||||
o.AddDefaultDataSource("ds0", "server=127.0.0.1;port=3306;database=shardingTest;userid=root;password=root;");
|
||||
o.UseShardingQuery((conn, b) =>
|
||||
{
|
||||
b.UseMySql(conn, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);
|
||||
});
|
||||
o.UseShardingTransaction((conn, b) =>
|
||||
{
|
||||
b.UseMySql(conn, new MySqlServerVersion(new Version())).UseLoggerFactory(efLogger);
|
||||
});
|
||||
o.ReplaceTableEnsureManager(sp=>new MySqlTableEnsureManager<DefaultDbContext>());
|
||||
}).EnsureConfig();
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
app.Services.GetRequiredService<IShardingBootstrapper>().Start();
|
||||
|
||||
|
||||
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.Run();
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:26918",
|
||||
"sslPort": 44347
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"Sample.AutoCreateIfPresent": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7066;http://localhost:5218",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\ShardingCore\ShardingCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,12 @@
|
|||
namespace Sample.AutoCreateIfPresent;
|
||||
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string? Summary { get; set; }
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using ShardingCore.Core;
|
||||
using ShardingCore.Core.VirtualRoutes.TableRoutes;
|
||||
using ShardingCore.Sharding.Abstractions;
|
||||
using ShardingCore.Sharding.ParallelTables;
|
||||
|
||||
namespace ShardingCore
|
||||
{
|
||||
/*
|
||||
* @Author: xjm
|
||||
* @Description:
|
||||
* @Date: 2021/8/20 6:56:49
|
||||
* @Ver: 1.0
|
||||
* @Email: 326308290@qq.com
|
||||
*/
|
||||
public interface IShardingConfigOption
|
||||
{
|
||||
bool UseReadWrite { get; }
|
||||
|
||||
bool HasVirtualTableRoute(Type entityType);
|
||||
Type GetVirtualTableRouteType(Type entityType);
|
||||
bool HasVirtualDataSourceRoute(Type entityType);
|
||||
Type GetVirtualDataSourceRouteType(Type entityType);
|
||||
|
||||
ISet<Type> GetShardingTableRouteTypes();
|
||||
ISet<Type> GetShardingDataSourceRouteTypes();
|
||||
|
||||
|
||||
IDictionary<string, string> GetDataSources();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 如果数据库不存在就创建并且创建表除了分表的
|
||||
/// </summary>
|
||||
public bool EnsureCreatedWithOutShardingTable { get; set; }
|
||||
/// <summary>
|
||||
/// 是否需要在启动时创建分表
|
||||
/// </summary>
|
||||
public bool? CreateShardingTableOnStart { get; set; }
|
||||
/// <summary>
|
||||
/// 添加尝试建表的对象
|
||||
/// </summary>
|
||||
/// <param name="entityType"></param>
|
||||
/// <returns></returns>
|
||||
public bool AddEntityTryCreateTable(Type entityType);
|
||||
public bool AnyEntityTryCreateTable();
|
||||
/// <summary>
|
||||
/// 是否需要启动创建表
|
||||
/// </summary>
|
||||
/// <param name="entityType"></param>
|
||||
/// <returns></returns>
|
||||
public bool NeedCreateTable(Type entityType);
|
||||
/// <summary>
|
||||
/// 忽略建表时的错误
|
||||
/// </summary>
|
||||
public bool? IgnoreCreateTableError { get; set; }
|
||||
|
||||
///// <summary>
|
||||
///// 自动追踪实体
|
||||
///// </summary>
|
||||
//public bool AutoTrackEntity { get; set; }
|
||||
/// <summary>
|
||||
/// 默认数据源名称
|
||||
/// </summary>
|
||||
public string DefaultDataSourceName { get; set; }
|
||||
/// <summary>
|
||||
/// 默认数据库链接字符串
|
||||
/// </summary>
|
||||
public string DefaultConnectionString { get; set; }
|
||||
/// <summary>
|
||||
/// 最大查询连接数限制
|
||||
/// </summary>
|
||||
public int MaxQueryConnectionsLimit { get; set; }
|
||||
/// <summary>
|
||||
/// 连接数限制
|
||||
/// </summary>
|
||||
public ConnectionModeEnum ConnectionMode { get; set; }
|
||||
/// <summary>
|
||||
/// 当查询遇到没有路由被命中时是否抛出错误
|
||||
/// </summary>
|
||||
public bool ThrowIfQueryRouteNotMatch { get; set; }
|
||||
|
||||
public bool AddParallelTableGroupNode(ParallelTableGroupNode parallelTableGroupNode);
|
||||
|
||||
public ISet<ParallelTableGroupNode> GetParallelTableGroupNodes();
|
||||
/// <summary>
|
||||
/// 是否启用表路由编译缓存
|
||||
/// </summary>
|
||||
public bool? EnableTableRouteCompileCache { get; set; }
|
||||
/// <summary>
|
||||
/// 是否启用分库路由编译缓存
|
||||
/// </summary>
|
||||
public bool? EnableDataSourceRouteCompileCache { get; set; }
|
||||
}
|
||||
|
||||
public interface IShardingConfigOption<TShardingDbContext>: IShardingConfigOption where TShardingDbContext : DbContext, IShardingDbContext
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -1,259 +0,0 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using ShardingCore.Core.VirtualRoutes.TableRoutes;
|
||||
using ShardingCore.Sharding.Abstractions;
|
||||
using ShardingCore.Sharding.ReadWriteConfigurations;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Common;
|
||||
using System.Linq;
|
||||
using ShardingCore.Core;
|
||||
using ShardingCore.Core.VirtualRoutes.DataSourceRoutes;
|
||||
using ShardingCore.Exceptions;
|
||||
using ShardingCore.Extensions;
|
||||
using ShardingCore.Sharding.ParallelTables;
|
||||
using ShardingCore.Sharding.ShardingComparision;
|
||||
using ShardingCore.Sharding.ShardingComparision.Abstractions;
|
||||
using ShardingCore.TableExists;
|
||||
using ShardingCore.TableExists.Abstractions;
|
||||
|
||||
namespace ShardingCore
|
||||
{
|
||||
/*
|
||||
* @Author: xjm
|
||||
* @Description:
|
||||
* @Date: 2021/8/16 15:18:37
|
||||
* @Ver: 1.0
|
||||
* @Email: 326308290@qq.com
|
||||
*/
|
||||
public class ShardingConfigOption<TShardingDbContext> : IShardingConfigOption<TShardingDbContext>
|
||||
where TShardingDbContext : DbContext, IShardingDbContext
|
||||
{
|
||||
private readonly Dictionary<Type, Type> _virtualDataSourceRoutes = new Dictionary<Type, Type>();
|
||||
private readonly Dictionary<Type, Type> _virtualTableRoutes = new Dictionary<Type, Type>();
|
||||
private readonly ISet<Type> _createTableEntities = new HashSet<Type>();
|
||||
public readonly ISet<ParallelTableGroupNode> _parallelTables = new HashSet<ParallelTableGroupNode>();
|
||||
|
||||
public Action<DbConnection, DbContextOptionsBuilder> SameConnectionConfigure { get; private set; }
|
||||
|
||||
public Func<IServiceProvider, IDictionary<string, string>> DataSourcesConfigure { get; private set; }
|
||||
public void UseShardingTransaction(Action<DbConnection, DbContextOptionsBuilder> transactionConfigure)
|
||||
{
|
||||
SameConnectionConfigure = transactionConfigure ?? throw new ArgumentNullException(nameof(transactionConfigure));
|
||||
}
|
||||
|
||||
public void AddShardingDataSource(Func<IServiceProvider, IDictionary<string, string>> dataSourcesConfigure)
|
||||
{
|
||||
DataSourcesConfigure = dataSourcesConfigure ?? throw new ArgumentNullException(nameof(dataSourcesConfigure));
|
||||
}
|
||||
|
||||
public Func<IServiceProvider, IShardingComparer> ReplaceShardingComparerFactory { get; private set; } = sp => new CSharpLanguageShardingComparer();
|
||||
/// <summary>
|
||||
/// 替换默认的比较器
|
||||
/// </summary>
|
||||
/// <param name="newShardingComparerFactory"></param>
|
||||
/// <exception cref="ArgumentNullException"></exception>
|
||||
public void ReplaceShardingComparer(Func<IServiceProvider, IShardingComparer> newShardingComparerFactory)
|
||||
{
|
||||
ReplaceShardingComparerFactory=newShardingComparerFactory ?? throw new ArgumentNullException(nameof(newShardingComparerFactory));
|
||||
}
|
||||
|
||||
public Func<IServiceProvider, ITableEnsureManager<TShardingDbContext>> TableEnsureManagerFactory { get; private set; } = sp => new EmptyTableEnsureManager< TShardingDbContext>();
|
||||
public void AddTableEnsureManager(Func<IServiceProvider, ITableEnsureManager<TShardingDbContext>> newTableEnsureManagerFactory)
|
||||
{
|
||||
TableEnsureManagerFactory= newTableEnsureManagerFactory?? throw new ArgumentNullException(nameof(newTableEnsureManagerFactory));
|
||||
}
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 配置数据库分表查询和保存时的DbContext创建方式
|
||||
///// </summary>
|
||||
///// <param name="sameConnectionConfigure">DbConnection下如何配置因为不同的DbContext支持事务需要使用同一个DbConnection</param>
|
||||
///// <param name="defaultQueryConfigure">默认查询DbContext创建的配置</param>
|
||||
|
||||
//public void UseShardingOptionsBuilder(Action<DbConnection, DbContextOptionsBuilder> sameConnectionConfigure, Action<string,DbContextOptionsBuilder> defaultQueryConfigure = null)
|
||||
//{
|
||||
// ConnectionConfigure = sameConnectionConfigure ?? throw new ArgumentNullException(nameof(sameConnectionConfigure));
|
||||
// ConnectionStringConfigure = defaultQueryConfigure ?? throw new ArgumentNullException(nameof(defaultQueryConfigure));
|
||||
//}
|
||||
|
||||
public bool UseReadWrite => ReadConnStringConfigure != null;
|
||||
public Func<IServiceProvider, IDictionary<string, IEnumerable<string>>> ReadConnStringConfigure { get; private set; }
|
||||
public ReadStrategyEnum ReadStrategyEnum { get; private set; }
|
||||
public bool ReadWriteDefaultEnable { get; private set; }
|
||||
public int ReadWriteDefaultPriority { get; private set; }
|
||||
public ReadConnStringGetStrategyEnum ReadConnStringGetStrategy { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 使用读写分离配置
|
||||
/// </summary>
|
||||
/// <param name="readConnStringConfigure"></param>
|
||||
/// <param name="readStrategyEnum"></param>
|
||||
/// <param name="defaultEnable">考虑到很多时候读写分离的延迟需要马上用到写入的数据所以默认关闭需要的话自己开启或者通过IShardingReadWriteManager,false表示默认不走读写分离除非你自己开启,true表示默认走读写分离除非你禁用,</param>
|
||||
/// <param name="defaultPriority">IShardingReadWriteManager.CreateScope()会判断dbcontext的priority然后判断是否启用readwrite</param>
|
||||
/// <param name="readConnStringGetStrategy">读写分离可能会造成每次查询不一样甚至分表后的分页会有错位问题,因为他不是一个原子操作,所以如果整个请求为一次读写切换大多数更加合适</param>
|
||||
public void UseReadWriteConfiguration(Func<IServiceProvider, IDictionary<string, IEnumerable<string>>> readConnStringConfigure, ReadStrategyEnum readStrategyEnum, bool defaultEnable = false, int defaultPriority = 10, ReadConnStringGetStrategyEnum readConnStringGetStrategy = ReadConnStringGetStrategyEnum.LatestFirstTime)
|
||||
{
|
||||
ReadConnStringConfigure = readConnStringConfigure ?? throw new ArgumentNullException(nameof(readConnStringConfigure));
|
||||
ReadStrategyEnum = readStrategyEnum;
|
||||
ReadWriteDefaultEnable = defaultEnable;
|
||||
ReadWriteDefaultPriority = defaultPriority;
|
||||
ReadConnStringGetStrategy = readConnStringGetStrategy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加分表路由
|
||||
/// </summary>
|
||||
/// <typeparam name="TRoute"></typeparam>
|
||||
public void AddShardingDataSourceRoute<TRoute>() where TRoute : IVirtualDataSourceRoute
|
||||
{
|
||||
var routeType = typeof(TRoute);
|
||||
AddShardingDataSourceRoute(routeType);
|
||||
}
|
||||
public void AddShardingDataSourceRoute(Type routeType)
|
||||
{
|
||||
if (!routeType.IsVirtualDataSourceRoute())
|
||||
throw new ShardingCoreInvalidOperationException(routeType.FullName);
|
||||
//获取类型
|
||||
var genericVirtualRoute = routeType.GetInterfaces().FirstOrDefault(it => it.IsInterface && it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IVirtualDataSourceRoute<>)
|
||||
&& it.GetGenericArguments().Any());
|
||||
if (genericVirtualRoute == null)
|
||||
throw new ArgumentException("add sharding route type error not assignable from IVirtualDataSourceRoute<>.");
|
||||
|
||||
var shardingEntityType = genericVirtualRoute.GetGenericArguments()[0];
|
||||
if (shardingEntityType == null)
|
||||
throw new ArgumentException("add sharding table route type error not assignable from IVirtualDataSourceRoute<>");
|
||||
if (!_virtualDataSourceRoutes.ContainsKey(shardingEntityType))
|
||||
{
|
||||
_virtualDataSourceRoutes.Add(shardingEntityType, routeType);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加分表路由
|
||||
/// </summary>
|
||||
/// <typeparam name="TRoute"></typeparam>
|
||||
public void AddShardingTableRoute<TRoute>() where TRoute : IVirtualTableRoute
|
||||
{
|
||||
var routeType = typeof(TRoute);
|
||||
AddShardingTableRoute(routeType);
|
||||
}
|
||||
public void AddShardingTableRoute(Type routeType)
|
||||
{
|
||||
if (!routeType.IsIVirtualTableRoute())
|
||||
throw new ShardingCoreInvalidOperationException(routeType.FullName);
|
||||
//获取类型
|
||||
var genericVirtualRoute = routeType.GetInterfaces().FirstOrDefault(it => it.IsInterface && it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IVirtualTableRoute<>)
|
||||
&& it.GetGenericArguments().Any());
|
||||
if (genericVirtualRoute == null)
|
||||
throw new ArgumentException("add sharding route type error not assignable from IVirtualTableRoute<>.");
|
||||
|
||||
var shardingEntityType = genericVirtualRoute.GetGenericArguments()[0];
|
||||
if (shardingEntityType == null)
|
||||
throw new ArgumentException("add sharding table route type error not assignable from IVirtualTableRoute<>");
|
||||
if (!_virtualTableRoutes.ContainsKey(shardingEntityType))
|
||||
{
|
||||
_virtualTableRoutes.Add(shardingEntityType, routeType);
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasVirtualTableRoute(Type entityType)
|
||||
{
|
||||
return _virtualTableRoutes.ContainsKey(entityType);
|
||||
}
|
||||
|
||||
public Type GetVirtualTableRouteType(Type entityType)
|
||||
{
|
||||
if (!_virtualTableRoutes.ContainsKey(entityType))
|
||||
throw new ArgumentException($"{entityType} not found IVirtualTableRoute");
|
||||
return _virtualTableRoutes[entityType];
|
||||
}
|
||||
|
||||
public bool HasVirtualDataSourceRoute(Type entityType)
|
||||
{
|
||||
return _virtualDataSourceRoutes.ContainsKey(entityType);
|
||||
}
|
||||
|
||||
public Type GetVirtualDataSourceRouteType(Type entityType)
|
||||
{
|
||||
if (!_virtualDataSourceRoutes.ContainsKey(entityType))
|
||||
throw new ArgumentException($"{entityType} not found IVirtualDataSourceRoute");
|
||||
return _virtualDataSourceRoutes[entityType];
|
||||
}
|
||||
|
||||
public ISet<Type> GetShardingTableRouteTypes()
|
||||
{
|
||||
return _virtualTableRoutes.Keys.ToHashSet();
|
||||
}
|
||||
|
||||
public ISet<Type> GetShardingDataSourceRouteTypes()
|
||||
{
|
||||
return _virtualDataSourceRoutes.Keys.ToHashSet();
|
||||
}
|
||||
|
||||
public IDictionary<string, string> GetDataSources()
|
||||
{
|
||||
var defaultDataSources = new Dictionary<string, string>(){{DefaultDataSourceName,DefaultConnectionString}};
|
||||
return defaultDataSources.Concat(DataSourcesConfigure?.Invoke(ShardingContainer.ServiceProvider)??new Dictionary<string, string>()).ToDictionary(o=>o.Key,o=>o.Value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 如果数据库不存在就创建并且创建表除了分表的
|
||||
/// </summary>
|
||||
public bool EnsureCreatedWithOutShardingTable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否需要在启动时创建分表
|
||||
/// </summary>
|
||||
public bool? CreateShardingTableOnStart { get; set; }
|
||||
|
||||
public bool AddEntityTryCreateTable(Type entityType)
|
||||
{
|
||||
return _createTableEntities.Add(entityType);
|
||||
}
|
||||
|
||||
public bool AnyEntityTryCreateTable()
|
||||
{
|
||||
return _createTableEntities.Any();
|
||||
}
|
||||
|
||||
public bool NeedCreateTable(Type entityType)
|
||||
{
|
||||
return _createTableEntities.Contains(entityType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 忽略建表时的错误
|
||||
/// </summary>
|
||||
public bool? IgnoreCreateTableError { get; set; } = true;
|
||||
///// <summary>
|
||||
///// 自动追踪实体
|
||||
///// </summary>
|
||||
//public bool AutoTrackEntity { get; set; }
|
||||
|
||||
public string DefaultDataSourceName { get; set; }
|
||||
public string DefaultConnectionString { get; set; }
|
||||
public int MaxQueryConnectionsLimit { get; set; } = Environment.ProcessorCount;
|
||||
public ConnectionModeEnum ConnectionMode { get; set; } = ConnectionModeEnum.SYSTEM_AUTO;
|
||||
/// <summary>
|
||||
/// 当查询遇到没有路由被命中时是否抛出错误
|
||||
/// </summary>
|
||||
public bool ThrowIfQueryRouteNotMatch { get; set; } = true;
|
||||
public bool? EnableTableRouteCompileCache { get; set; }
|
||||
public bool? EnableDataSourceRouteCompileCache { get; set; }
|
||||
|
||||
|
||||
public bool AddParallelTableGroupNode(ParallelTableGroupNode parallelTableGroupNode)
|
||||
{
|
||||
Check.NotNull(parallelTableGroupNode, $"{nameof(parallelTableGroupNode)}");
|
||||
return _parallelTables.Add(parallelTableGroupNode);
|
||||
}
|
||||
public ISet<ParallelTableGroupNode> GetParallelTableGroupNodes()
|
||||
{
|
||||
return _parallelTables;
|
||||
}
|
||||
//public bool? EnableTableRouteCompileCache { get; set; }
|
||||
//public bool? EnableDataSourceRouteCompileCache { get; set; }
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue