sharding/samples/Sample.OracleIssue/OracleMigrationHelper.cs

249 lines
11 KiB
C#

using System.Collections;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Storage;
using Oracle.EntityFrameworkCore.Migrations;
using ShardingCore.Core.RuntimeContexts;
using ShardingCore.Core.ShardingMigrations.Abstractions;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Extensions;
using ShardingCore.Helpers;
namespace Sample.OracleIssue;
public class OracleMigrationHelper
{
private OracleMigrationHelper()
{
}
public static void Generate(
IShardingRuntimeContext shardingRuntimeContext,
MigrationOperation operation,
MigrationCommandListBuilder builder,
ISqlGenerationHelper sqlGenerationHelper,
List<MigrationCommand> addCmds
)
{
var migrationCommands = (List<OracleMigrationCommand>)builder.GetFieldValue("_commands");
var shardingMigrationManager = shardingRuntimeContext.GetRequiredService<IShardingMigrationManager>();
var virtualDataSource = shardingRuntimeContext.GetRequiredService<IVirtualDataSource>();
var currentCurrentDataSourceName = shardingMigrationManager.Current?.CurrentDataSourceName ??
virtualDataSource.DefaultDataSourceName;
addCmds.ForEach(aAddCmd =>
{
var (migrationResult, shardingCmds) = BuildDataSourceShardingCmds(shardingRuntimeContext,
virtualDataSource.DefaultDataSourceName, currentCurrentDataSourceName, operation,
aAddCmd.CommandText, sqlGenerationHelper);
if (!migrationResult.InDataSource)
{
if (migrationResult.CommandType == MigrationCommandTypeEnum.TableCommand)
{
migrationCommands.Remove((OracleMigrationCommand)aAddCmd);
}
}
else
{
if (migrationResult.CommandType == MigrationCommandTypeEnum.TableCommand)
{
//如果是分表
if (shardingCmds.IsNotEmpty())
{
migrationCommands.Remove((OracleMigrationCommand)aAddCmd);
//针对builder的原始表进行移除
shardingCmds.ForEach(aShardingCmd =>
{
builder.Append(aShardingCmd)
.EndCommand();
});
}
}
}
});
}
private static (MigrationResult migrationResult, List<string>) BuildDataSourceShardingCmds(
IShardingRuntimeContext shardingRuntimeContext, string defaultDataSourceName, string dataSourceName,
MigrationOperation operation, string sourceCmd, ISqlGenerationHelper sqlGenerationHelper)
{
//所有MigrationOperation定义
//https://github.com/dotnet/efcore/tree/b970bf29a46521f40862a01db9e276e6448d3cb0/src/EFCore.Relational/Migrations/Operations
//ColumnOperation仅替换Table
//其余其余都是将Name和Table使用分表名替换
var dataSourceRouteManager = shardingRuntimeContext.GetDataSourceRouteManager();
var entityMetadataManager = shardingRuntimeContext.GetEntityMetadataManager();
var tableRouteManager = shardingRuntimeContext.GetTableRouteManager();
var tableRoutes = tableRouteManager.GetRoutes();
var existsShardingTables = tableRoutes.ToDictionary(o => o.EntityMetadata.LogicTableName,
o => o.GetTails().Select(p => $"{o.EntityMetadata.LogicTableName}{o.EntityMetadata.TableSeparator}{p}")
.ToList());
//Dictionary<string, List<string>> _existsShardingTables
// = Cache.ServiceProvider.GetService<ShardingContainer>().ExistsShardingTables;
List<string> resList = new List<string>();
string absTableName = string.Empty;
string name = operation.GetPropertyValue("Name") as string;
string tableName = operation.GetPropertyValue("Table") as string;
string pattern = string.Format("^({0})$|^({0}_.*?)$|^(.*?_{0}_.*?)$|^(.*?_{0})$", absTableName);
Func<KeyValuePair<string, List<string>>, bool> where = x =>
existsShardingTables.Any(y => x.Key == y.Key && Regex.IsMatch(name, BuildPattern(y.Key)));
if (!string.IsNullOrWhiteSpace(tableName))
{
absTableName = tableName;
}
else if (!string.IsNullOrWhiteSpace(name))
{
if (existsShardingTables.Any(x => where(x)))
{
absTableName = existsShardingTables.FirstOrDefault(x => where(x)).Key;
}
else
{
absTableName = name;
}
}
MigrationResult migrationResult = new MigrationResult();
var entityMetadatas = entityMetadataManager.TryGetByLogicTableName(absTableName);
if (entityMetadatas.IsNotEmpty())
{
migrationResult.CommandType = MigrationCommandTypeEnum.TableCommand;
bool isShardingDataSource = entityMetadatas.Count == 1 && entityMetadatas[0].IsShardingDataSource();
if (isShardingDataSource)
{
var virtualDataSourceRoute = dataSourceRouteManager.GetRoute(entityMetadatas[0].EntityType);
isShardingDataSource = virtualDataSourceRoute.GetAllDataSourceNames().Contains(dataSourceName);
if (isShardingDataSource)
{
migrationResult.InDataSource = true;
}
else
{
migrationResult.InDataSource = false;
}
}
else
{
migrationResult.InDataSource = defaultDataSourceName == dataSourceName;
}
//分表
if (migrationResult.InDataSource && !string.IsNullOrWhiteSpace(absTableName) &&
existsShardingTables.ContainsKey(absTableName))
{
var shardings = existsShardingTables[absTableName];
shardings.ForEach(aShardingTable =>
{
string newCmd = sourceCmd;
var replaceGroups = GetReplaceGroups(operation, absTableName, aShardingTable);
foreach (var migrationReplaceItem in replaceGroups)
{
var delimitSourceNameIdentifier =
sqlGenerationHelper.DelimitIdentifier(migrationReplaceItem.SourceName);
var delimitTargetNameIdentifier =
sqlGenerationHelper.DelimitIdentifier(migrationReplaceItem.TargetName);
newCmd = newCmd.Replace(
delimitSourceNameIdentifier,
delimitTargetNameIdentifier);
}
if (newCmd.Contains(
"EXEC sp_addextendedproperty 'MS_Description', @description, 'SCHEMA', @defaultSchema, 'TABLE'"))
{
newCmd = newCmd.Replace(
$"EXEC sp_addextendedproperty 'MS_Description', @description, 'SCHEMA', @defaultSchema, 'TABLE', N'{absTableName}'",
$"EXEC sp_addextendedproperty 'MS_Description', @description, 'SCHEMA', @defaultSchema, 'TABLE', N'{aShardingTable}'");
}
resList.Add(newCmd);
});
}
}
return (migrationResult, resList);
string BuildPattern(string absTableName)
{
return string.Format("^({0})$|^({0}_.*?)$|^(.*?_{0}_.*?)$|^(.*?_{0})$", absTableName);
}
}
private static ISet<MigrationReplaceItem> GetReplaceGroups(
MigrationOperation operation, string sourceTableName, string targetTableName)
{
ISet<MigrationReplaceItem> resList =
new HashSet<MigrationReplaceItem>()
{
new MigrationReplaceItem(sourceTableName, targetTableName)
};
string name = operation.GetPropertyValue("Name") as string;
if (!string.IsNullOrWhiteSpace(name))
{
if (!(operation is ColumnOperation columnOperation))
{
string[] patterns = new string[]
{
$"^()({sourceTableName})()$", $"^()({sourceTableName})(_.*?)$",
$"^(.*?_)({sourceTableName})(_.*?)$", $"^(.*?_)({sourceTableName})()$"
};
foreach (var aPattern in patterns)
{
if (Regex.IsMatch(name, aPattern))
{
var newName = new Regex(aPattern).Replace(name, "${1}" + targetTableName + "$3");
resList.Add(new MigrationReplaceItem(name, newName));
break;
}
}
}
}
Func<PropertyInfo, bool> listPropertyWhere = x =>
x.PropertyType.IsGenericType
&& x.PropertyType.GetGenericTypeDefinition() == typeof(List<>)
&& typeof(MigrationOperation).IsAssignableFrom(x.PropertyType.GetGenericArguments()[0]);
//其它
var propertyInfos = operation.GetType().GetProperties()
.Where(x => x.Name != "Name"
&& x.Name != "Table"
&& x.PropertyType != typeof(object)
&& (typeof(MigrationOperation).IsAssignableFrom(x.PropertyType) || listPropertyWhere(x))
)
.ToList();
propertyInfos
.ForEach(aProperty =>
{
var propertyValue = aProperty.GetValue(operation);
if (propertyValue is MigrationOperation propertyOperation)
{
var migrationReplaceItems = GetReplaceGroups(propertyOperation, sourceTableName, targetTableName);
foreach (var migrationReplaceItem in migrationReplaceItems)
{
resList.Add(migrationReplaceItem);
}
}
else if (listPropertyWhere(aProperty))
{
foreach (var aValue in (IEnumerable)propertyValue)
{
var migrationReplaceItems = GetReplaceGroups((MigrationOperation)aValue, sourceTableName,
targetTableName);
foreach (var migrationReplaceItem in migrationReplaceItems)
{
resList.Add(migrationReplaceItem);
}
}
}
});
return resList;
}
}