Skip to content

Commit

Permalink
添加如果不存在表就创建 [#147]
Browse files Browse the repository at this point in the history
  • Loading branch information
xuejmnet committed May 10, 2022
1 parent 22907db commit 98f570c
Show file tree
Hide file tree
Showing 14 changed files with 437 additions and 362 deletions.
7 changes: 7 additions & 0 deletions ShardingCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
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();
}
}
29 changes: 29 additions & 0 deletions samples/Sample.AutoCreateIfPresent/DefaultDbContext.cs
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; }
}
}
17 changes: 17 additions & 0 deletions samples/Sample.AutoCreateIfPresent/OrderByHour.cs
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; }
}
}
23 changes: 23 additions & 0 deletions samples/Sample.AutoCreateIfPresent/OrderByHourMap.cs
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));
}
}
}
173 changes: 173 additions & 0 deletions samples/Sample.AutoCreateIfPresent/OrderByHourRoute.cs
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];
}
}
}
58 changes: 58 additions & 0 deletions samples/Sample.AutoCreateIfPresent/Program.cs
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();
Loading

0 comments on commit 98f570c

Please sign in to comment.