添加oracle的code-first下的bug [#188]
This commit is contained in:
parent
7326656ff4
commit
19aee26e85
|
@ -71,6 +71,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src6x", "src6x", "{BB80F31B
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.6x", "src6x\ShardingCore.6x\ShardingCore.6x.csproj", "{1FF83CC3-A5F2-45E0-AA39-23C4DDF87D71}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.OracleIssue", "samples\Sample.OracleIssue\Sample.OracleIssue.csproj", "{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
@ -181,6 +183,10 @@ Global
|
|||
{1FF83CC3-A5F2-45E0-AA39-23C4DDF87D71}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1FF83CC3-A5F2-45E0-AA39-23C4DDF87D71}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1FF83CC3-A5F2-45E0-AA39-23C4DDF87D71}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
@ -212,6 +218,7 @@ Global
|
|||
{3E895438-E609-4860-8391-6897ED55DA06} = {CC2C88C0-65F2-445D-BE78-973B840FE281}
|
||||
{BAB101D4-2BFF-44CE-90E3-8B72DDB266B8} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
|
||||
{1FF83CC3-A5F2-45E0-AA39-23C4DDF87D71} = {BB80F31B-37F1-44C2-BCFA-F45D1AC765FE}
|
||||
{BF4FEA2A-3F09-47D8-9BF7-4261D8D1671D} = {EDF8869A-C1E1-491B-BC9F-4A33F4DE1C73}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8C07A667-E8B4-43C7-8053-721584BAD291}
|
||||
|
|
|
@ -3,6 +3,9 @@ using Microsoft.EntityFrameworkCore;
|
|||
using Sample.MySql.DbContexts;
|
||||
using Sample.MySql.Domain.Entities;
|
||||
using Sample.MySql.multi;
|
||||
using Sample.MySql.Shardings;
|
||||
using ShardingCore.Core.RuntimeContexts;
|
||||
using ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions;
|
||||
|
||||
namespace Sample.MySql.Controllers
|
||||
{
|
||||
|
@ -17,15 +20,24 @@ namespace Sample.MySql.Controllers
|
|||
{
|
||||
|
||||
private readonly DefaultShardingDbContext _defaultTableDbContext;
|
||||
private readonly IShardingRuntimeContext _shardingRuntimeContext;
|
||||
|
||||
public WeatherForecastController(DefaultShardingDbContext defaultTableDbContext)
|
||||
public WeatherForecastController(DefaultShardingDbContext defaultTableDbContext,IShardingRuntimeContext shardingRuntimeContext)
|
||||
{
|
||||
_defaultTableDbContext = defaultTableDbContext;
|
||||
_shardingRuntimeContext = shardingRuntimeContext;
|
||||
}
|
||||
|
||||
public IQueryable<SysTest> GetAll()
|
||||
{
|
||||
|
||||
var shardingTableCreator = _shardingRuntimeContext.GetShardingTableCreator();
|
||||
var tableRouteManager = _shardingRuntimeContext.GetTableRouteManager();
|
||||
//系统的时间分片都会实现 ITailAppendable 如果不是系统的自定义的转成你自己的对象即可
|
||||
var virtualTableRoute = (ITailAppendable)tableRouteManager.GetRoute(typeof(SysUserMod));
|
||||
//一定要先在路由里面添加尾巴
|
||||
virtualTableRoute.Append("20220921");
|
||||
shardingTableCreator.CreateTable<SysUserMod>("ds0","20220921");
|
||||
|
||||
return _defaultTableDbContext.Set<SysTest>();
|
||||
}
|
||||
[HttpGet]
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
namespace Sample.OracleIssue;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Oracle.EntityFrameworkCore" Version="6.21.61" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\ShardingCore\ShardingCore.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -0,0 +1,30 @@
|
|||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Migrations.Operations;
|
||||
using Oracle.EntityFrameworkCore.Infrastructure.Internal;
|
||||
using Oracle.EntityFrameworkCore.Migrations;
|
||||
using ShardingCore.Core.RuntimeContexts;
|
||||
using ShardingCore.Helpers;
|
||||
|
||||
namespace Sample.OracleIssue;
|
||||
|
||||
public class ShardingOracleMigrationsSqlGenerator:OracleMigrationsSqlGenerator
|
||||
{
|
||||
private readonly IShardingRuntimeContext _shardingRuntimeContext;
|
||||
|
||||
public ShardingOracleMigrationsSqlGenerator(IShardingRuntimeContext shardingRuntimeContext,MigrationsSqlGeneratorDependencies dependencies, IOracleOptions options) : base(dependencies, options)
|
||||
{
|
||||
_shardingRuntimeContext = shardingRuntimeContext;
|
||||
}
|
||||
|
||||
protected override void Generate(MigrationOperation operation, IModel? model, MigrationCommandListBuilder builder)
|
||||
{
|
||||
var oldCmds = builder.GetCommandList().ToList();
|
||||
base.Generate(operation, model, builder);
|
||||
var newCmds = builder.GetCommandList().ToList();
|
||||
var addCmds = newCmds.Where(x => !oldCmds.Contains(x)).ToList();
|
||||
|
||||
//oracle需要自行重写因为和其他的数据库不一样并不是使用了基类来处理
|
||||
OracleMigrationHelper.Generate(_shardingRuntimeContext,operation, builder, Dependencies.SqlGenerationHelper, addCmds);
|
||||
}
|
||||
}
|
|
@ -4,10 +4,10 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
namespace ShardingCore.Core.ServiceProviders
|
||||
{
|
||||
|
||||
public class ShardingProvider:IShardingProvider
|
||||
public sealed class ShardingProvider:IShardingProvider
|
||||
{
|
||||
private readonly IServiceProvider _internalServiceProvider;
|
||||
private IServiceProvider _applicationServiceProvider;
|
||||
private readonly IServiceProvider _applicationServiceProvider;
|
||||
|
||||
public ShardingProvider(IServiceProvider internalServiceProvider,IServiceProvider applicationServiceProvider)
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
namespace ShardingCore.Core.ServiceProviders
|
||||
{
|
||||
|
||||
public class ShardingScope:IShardingScope
|
||||
public sealed class ShardingScope:IShardingScope
|
||||
{
|
||||
private readonly IServiceScope _internalServiceScope;
|
||||
private readonly IServiceScope _applicationServiceScope;
|
||||
|
|
Loading…
Reference in New Issue