-
Notifications
You must be signed in to change notification settings - Fork 172
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
437 additions
and
362 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
samples/Sample.AutoCreateIfPresent/Controllers/WeatherForecastController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: [email protected] | ||
*/ | ||
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; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System; | ||
|
||
/* | ||
* @Author: xjm | ||
* @Description: | ||
* @Date: DATE | ||
* @Email: [email protected] | ||
*/ | ||
namespace Sample.AutoCreateIfPresent | ||
{ | ||
public class OrderByHour | ||
{ | ||
public string Id { get; set; } | ||
public DateTime CreateTime { get; set; } | ||
public string Name { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System; | ||
using Microsoft.EntityFrameworkCore; | ||
using Microsoft.EntityFrameworkCore.Metadata.Builders; | ||
|
||
/* | ||
* @Author: xjm | ||
* @Description: | ||
* @Date: DATE | ||
* @Email: [email protected] | ||
*/ | ||
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)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: [email protected] | ||
*/ | ||
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]; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); |
Oops, something went wrong.