提交初始化项目

This commit is contained in:
xuejmnet 2021-01-26 10:18:49 +08:00
parent 8d5bc4cb28
commit 878c658cda
107 changed files with 7293 additions and 0 deletions

345
.gitignore vendored Normal file
View File

@ -0,0 +1,345 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
*/.DS_Store
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
/src/Bd.ShopRent.Api/Properties/launchSettings.json
/src/Bd.ShopRent.DTO/__autoDTOConfig.json
/src/Bd.ShopRent.DTO/Bd.ShopRent.Dto.csproj

60
ShardingCore.sln Normal file
View File

@ -0,0 +1,60 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{490FAE47-4476-4508-B216-505FC850447F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore", "src\ShardingCore\ShardingCore.csproj", "{3CAF09A6-6ABD-41D9-BA57-9A822B8095F7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.SqlServer", "src\ShardingCore.SqlServer\ShardingCore.SqlServer.csproj", "{2B65466D-D4F8-48FE-A516-014404414897}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src3x", "src3x", "{62AAE0FE-4099-4A48-AA3C-F76F14C62655}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.3x", "src3x\ShardingCore.3x\ShardingCore.3x.csproj", "{E0FDBFA9-AE5F-42FB-8A60-4E5D9E7D5414}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.SqlServer.3x", "src3x\ShardingCore.SqlServer.3x\ShardingCore.SqlServer.3x.csproj", "{37E11FFD-14D7-41B4-B04C-2950206C64DB}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src2x", "src2x", "{679E6084-0C45-4807-BFEE-D2FDA44B2188}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.2x", "src2x\ShardingCore.2x\ShardingCore.2x.csproj", "{FB92A4BE-A43E-4755-8132-EC38E9650B80}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.SqlServer.2x", "src2x\ShardingCore.SqlServer.2x\ShardingCore.SqlServer.2x.csproj", "{73B802A7-3DDE-4B02-9E3D-D6B9EE42DE5D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{3CAF09A6-6ABD-41D9-BA57-9A822B8095F7} = {490FAE47-4476-4508-B216-505FC850447F}
{2B65466D-D4F8-48FE-A516-014404414897} = {490FAE47-4476-4508-B216-505FC850447F}
{E0FDBFA9-AE5F-42FB-8A60-4E5D9E7D5414} = {62AAE0FE-4099-4A48-AA3C-F76F14C62655}
{37E11FFD-14D7-41B4-B04C-2950206C64DB} = {62AAE0FE-4099-4A48-AA3C-F76F14C62655}
{FB92A4BE-A43E-4755-8132-EC38E9650B80} = {679E6084-0C45-4807-BFEE-D2FDA44B2188}
{73B802A7-3DDE-4B02-9E3D-D6B9EE42DE5D} = {679E6084-0C45-4807-BFEE-D2FDA44B2188}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{3CAF09A6-6ABD-41D9-BA57-9A822B8095F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CAF09A6-6ABD-41D9-BA57-9A822B8095F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CAF09A6-6ABD-41D9-BA57-9A822B8095F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CAF09A6-6ABD-41D9-BA57-9A822B8095F7}.Release|Any CPU.Build.0 = Release|Any CPU
{2B65466D-D4F8-48FE-A516-014404414897}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B65466D-D4F8-48FE-A516-014404414897}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B65466D-D4F8-48FE-A516-014404414897}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B65466D-D4F8-48FE-A516-014404414897}.Release|Any CPU.Build.0 = Release|Any CPU
{E0FDBFA9-AE5F-42FB-8A60-4E5D9E7D5414}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0FDBFA9-AE5F-42FB-8A60-4E5D9E7D5414}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0FDBFA9-AE5F-42FB-8A60-4E5D9E7D5414}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E0FDBFA9-AE5F-42FB-8A60-4E5D9E7D5414}.Release|Any CPU.Build.0 = Release|Any CPU
{37E11FFD-14D7-41B4-B04C-2950206C64DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37E11FFD-14D7-41B4-B04C-2950206C64DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37E11FFD-14D7-41B4-B04C-2950206C64DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37E11FFD-14D7-41B4-B04C-2950206C64DB}.Release|Any CPU.Build.0 = Release|Any CPU
{FB92A4BE-A43E-4755-8132-EC38E9650B80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FB92A4BE-A43E-4755-8132-EC38E9650B80}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FB92A4BE-A43E-4755-8132-EC38E9650B80}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FB92A4BE-A43E-4755-8132-EC38E9650B80}.Release|Any CPU.Build.0 = Release|Any CPU
{73B802A7-3DDE-4B02-9E3D-D6B9EE42DE5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{73B802A7-3DDE-4B02-9E3D-D6B9EE42DE5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{73B802A7-3DDE-4B02-9E3D-D6B9EE42DE5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73B802A7-3DDE-4B02-9E3D-D6B9EE42DE5D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,76 @@
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore.Core.ShardingAccessors;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts;
using ShardingCore.DbContexts.VirtualDbContexts;
using ShardingCore.Extensions;
using ShardingCore.SqlServer.EFCores;
using ShardingCore.TableCreator;
#if EFCORE2
using Microsoft.EntityFrameworkCore.Query.Sql;
#endif
namespace ShardingCore.SqlServer
{
/*
* @Author: xjm
* @Description:
* @Date: 202047 9:30:18
* @Email: 326308290@qq.com
*/
public static class DIExtension
{
public static IServiceCollection AddShardingSqlServer(this IServiceCollection services, Action<SqlServerOptions> configure)
{
if (configure == null)
throw new ArgumentNullException($"AddScfSqlServerProvider 参数不能为空:{nameof(configure)}");
var options = new SqlServerOptions();
configure(options);
services.AddSingleton(options);
services.AddScoped<IVirtualDbContext, VirtualDbContext>();
services.AddScoped<IDbContextOptionsProvider, SqlServerDbContextOptionsProvider>();
services.AddSingleton<IShardingDbContextFactory, ShardingDbContextFactory>();
services.AddSingleton<IShardingTableCreator, ShardingTableCreator>();
services.AddSingleton<IVirtualTableManager, OneDbVirtualTableManager>();
services.AddSingleton(typeof(IVirtualTable<>), typeof(OneDbVirtualTable<>));
services.AddSingleton<IShardingAccessor, ShardingAccessor>();
services.AddSingleton<IShardingScopeFactory, ShardingScopeFactory>();
services.AddSingleton<IShardingParallelDbContextFactory, ShardingSqlServerParallelDbContextFactory>();
if (options.HasSharding)
{
foreach (var shardingRoute in options.ShardingRoutes)
{
var genericVirtualRoute = shardingRoute.GetInterfaces().FirstOrDefault(it => it.IsInterface && it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IVirtualRoute<>)
&& it.GetGenericArguments().Any());
if (genericVirtualRoute == null)
throw new ArgumentException("add sharding route type error not assignable from IVirtualRoute<>.");
var shardingEntity=genericVirtualRoute.GetGenericArguments()[0];
if(!shardingEntity.IsShardingEntity())
throw new ArgumentException("add sharding route type error generic arguments first not assignable from IShardingEntity.");
Type genericType = typeof(IVirtualRoute<>);
Type interfaceType = genericType.MakeGenericType(shardingEntity);
services.AddSingleton(interfaceType, shardingRoute);
}
}
var shardingCoreConfig = new ShardingCoreConfig();
shardingCoreConfig.EnsureCreated = options.EnsureCreated;
services.AddSingleton(sp => shardingCoreConfig);
services.AddSingleton<ShardingBootstrapper>();
return services;
}
public static DbContextOptionsBuilder UseShardingSqlServerQuerySqlGenerator(this DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ReplaceService<IQuerySqlGeneratorFactory, ShardingSqlServerQuerySqlGeneratorFactory>();
return optionsBuilder;
}
}
}

View File

@ -0,0 +1,214 @@
#if !EFCORE2
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore.Core.ShardingAccessors;
using ShardingCore.Core.VirtualTables;
using ShardingCore.Extensions;
namespace ShardingCore.SqlServer.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 22 December 2020 09:47:59
* @Email: 326308290@qq.com
*/
public class ShardingSqlServerQuerySqlGeneratorFactory : IQuerySqlGeneratorFactory
{
public ShardingSqlServerQuerySqlGeneratorFactory(QuerySqlGeneratorDependencies dependencies)
{
Dependencies = dependencies;
}
public QuerySqlGeneratorDependencies Dependencies { get; }
public QuerySqlGenerator Create() => new ShardingSqlServerQuerySqlGenerator(Dependencies);
}
public class ShardingSqlServerQuerySqlGenerator : SqlServerQuerySqlGenerator
{
public ShardingSqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies)
: base(dependencies)
{
}
protected override Expression VisitTable(TableExpression tableExpression)
{
return OverrideVisitTable(tableExpression);
// this._relationalCommandBuilder.Append((object) this._sqlGenerationHelper.DelimitIdentifier(tableExpression.Name, tableExpression.Schema)).Append((object) this.AliasSeparator).Append((object) this._sqlGenerationHelper.DelimitIdentifier(tableExpression.Alias));
// return (Expression) tableExpression;
// typeof(TableExpression)
// .GetFields( BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(o=>o.Name.Contains(nameof(tableExpression.Name)))
// .SetValue(tableExpression,"(select * from Log_1Message union all select * from Log_1Message)");
// base will append schema, table and alias
}
private Expression OverrideVisitTable(TableExpression tableExpression)
{
var shardingAccessor = ShardingContainer.Services.GetService<IShardingAccessor>();
if (shardingAccessor?.ShardingContext != null)
{
var virtualTableManager = ShardingContainer.Services.GetService<IVirtualTableManager>();
var virtualTable = virtualTableManager.GetAllVirtualTables().FirstOrDefault(o => o.GetOriginalTableName() == tableExpression.Name);
if(virtualTable!=null)
{
var tails = shardingAccessor.ShardingContext.GetContextQueryTails(virtualTable);
var tailPrefix = virtualTable.ShardingConfig.TailPrefix;
string newTableName = null;
var sqlGenerationHelper = typeof(QuerySqlGenerator).GetTypeFieldValue(this, "_sqlGenerationHelper") as ISqlGenerationHelper;
if (tails.IsEmpty())
{
var firstTail = virtualTableManager.GetVirtualTable(tableExpression.Name).GetAllPhysicTables()[0].Tail;
newTableName = $"( select * from {sqlGenerationHelper.DelimitIdentifier($"{tableExpression.Name}{tailPrefix}{firstTail}", tableExpression.Schema)} where 1=2 )";
}
else if (tails.Count == 1)
{
//对tableExpresion进行重写
//typeof(TableExpression)
// .GetFields( BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(o=>o.Name==$"<{nameof(tableExpression.Name)}>k__BackingField")
// .SetValue(tableExpression,$"{tableExpression.Name}{tailPrefix}{entry.Tails[0]}");
newTableName = sqlGenerationHelper.DelimitIdentifier($"{tableExpression.Name}{tailPrefix}{tails[0]}", tableExpression.Schema);
}
else
{
newTableName = "(" + string.Join(" union all ", tails.Select(tail => $"select * from {sqlGenerationHelper.DelimitIdentifier($"{tableExpression.Name}{tailPrefix}{tail}", tableExpression.Schema)}")) + ")";
}
var relationalCommandBuilder = typeof(QuerySqlGenerator).GetTypeFieldValue(this, "_relationalCommandBuilder") as IRelationalCommandBuilder;
relationalCommandBuilder.Append(newTableName).Append(this.AliasSeparator).Append(sqlGenerationHelper.DelimitIdentifier(tableExpression.Alias));
return tableExpression;
}
}
var result = base.VisitTable(tableExpression);
return result;
}
}
}
#endif
#if EFCORE2
using System.Linq;
using System.Linq.Expressions;
using ShardingCore;
using ShardingCore.Extensions;
using ShardingCore.Core.ShardingAccessors;
using ShardingCore.Core.VirtualTables;
using Microsoft.EntityFrameworkCore.Query.Expressions;
using Microsoft.EntityFrameworkCore.Query.Sql;
using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
namespace ShardingCore.SqlServer.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 22 December 2020 09:47:59
* @Email: 326308290@qq.com
*/
public class ShardingSqlServerQuerySqlGeneratorFactory :QuerySqlGeneratorFactoryBase
{
private readonly ISqlServerOptions _sqlServerOptions;
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public ShardingSqlServerQuerySqlGeneratorFactory(
QuerySqlGeneratorDependencies dependencies,
ISqlServerOptions sqlServerOptions)
: base(dependencies)
{
_sqlServerOptions = sqlServerOptions;
}
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public override IQuerySqlGenerator CreateDefault(SelectExpression selectExpression)
=> new ShardingSqlServerQuerySqlGenerator(
Dependencies,
selectExpression,
_sqlServerOptions.RowNumberPagingEnabled);
}
public class ShardingSqlServerQuerySqlGenerator : SqlServerQuerySqlGenerator
{
public ShardingSqlServerQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies, SelectExpression selectExpression, bool rowNumberPagingEnabled) : base(dependencies, selectExpression, rowNumberPagingEnabled)
{
}
public override Expression VisitTable(TableExpression tableExpression)
{
return OverrideVisitTable(tableExpression);
}
// protected override Expression VisitTable(TableExpression tableExpression)
// {
// return OverrideVisitTable(tableExpression);
// // this._relationalCommandBuilder.Append((object) this._sqlGenerationHelper.DelimitIdentifier(tableExpression.Name, tableExpression.Schema)).Append((object) this.AliasSeparator).Append((object) this._sqlGenerationHelper.DelimitIdentifier(tableExpression.Alias));
// // return (Expression) tableExpression;
//
// // typeof(TableExpression)
// // .GetFields( BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(o=>o.Name.Contains(nameof(tableExpression.Name)))
// // .SetValue(tableExpression,"(select * from Log_1Message union all select * from Log_1Message)");
//
// // base will append schema, table and alias
// }
private Expression OverrideVisitTable(TableExpression tableExpression)
{
var shardingAccessor = ShardingContainer.Services.GetService<IShardingAccessor>();
if (shardingAccessor?.ShardingContext != null)
{
var virtualTableManager = ShardingContainer.Services.GetService<IVirtualTableManager>();
var virtualTable = virtualTableManager.GetAllVirtualTables().FirstOrDefault(o => o.GetOriginalTableName() == tableExpression.Table);
if(virtualTable!=null)
{
var tails = shardingAccessor.ShardingContext.GetContextQueryTails(virtualTable);
var tailPrefix = virtualTable.ShardingConfig.TailPrefix;
string newTableName = null;
var sqlGenerationHelper = typeof(DefaultQuerySqlGenerator).GetTypeFieldValue(this, "_sqlGenerationHelper") as ISqlGenerationHelper;
if (tails.IsEmpty())
{
var firstTail = virtualTableManager.GetVirtualTable(tableExpression.Table).GetAllPhysicTables()[0].Tail;
newTableName = $"( select * from {sqlGenerationHelper.DelimitIdentifier($"{tableExpression.Table}{tailPrefix}{firstTail}", tableExpression.Schema)} where 1=2 )";
}
else if (tails.Count == 1)
{
//对tableExpresion进行重写
//typeof(TableExpression)
// .GetFields( BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(o=>o.Name==$"<{nameof(tableExpression.Name)}>k__BackingField")
// .SetValue(tableExpression,$"{tableExpression.Name}{tailPrefix}{entry.Tails[0]}");
newTableName = sqlGenerationHelper.DelimitIdentifier($"{tableExpression.Table}{tailPrefix}{tails[0]}", tableExpression.Schema);
}
else
{
newTableName = "(" + string.Join(" union all ", tails.Select(tail => $"select * from {sqlGenerationHelper.DelimitIdentifier($"{tableExpression.Table}{tailPrefix}{tail}", tableExpression.Schema)}")) + ")";
}
var relationalCommandBuilder = typeof(DefaultQuerySqlGenerator).GetTypeFieldValue(this, "_relationalCommandBuilder") as IRelationalCommandBuilder;
relationalCommandBuilder.Append(newTableName).Append(this.AliasSeparator).Append(sqlGenerationHelper.DelimitIdentifier(tableExpression.Alias));
return tableExpression;
}
}
var result = base.VisitTable(tableExpression);
return result;
}
}
}
#endif

View File

@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Version>$(EFCORE5)</Version>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<DefineConstants>TRACE;DEBUG;EFCORE5;</DefineConstants>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ShardingCore\ShardingCore.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,47 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query.Internal;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts;
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.EFCores;
using ShardingCore.Extensions;
namespace ShardingCore.SqlServer
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 29 December 2020 15:22:50
* @Email: 326308290@qq.com
*/
public class ShardingSqlServerParallelDbContextFactory : IShardingParallelDbContextFactory
{
private readonly IVirtualTableManager _virtualTableManager;
private readonly SqlServerOptions _sqlServerOptions;
public ShardingSqlServerParallelDbContextFactory(IVirtualTableManager virtualTableManager, SqlServerOptions sqlServerOptions)
{
_virtualTableManager = virtualTableManager;
_sqlServerOptions = sqlServerOptions;
}
public ShardingDbContext Create(string tail)
{
var virtualTableConfigs = _virtualTableManager.GetAllVirtualTables().GetVirtualTableDbContextConfigs();
var shardingDbContextOptions = new ShardingDbContextOptions(CreateOptions(), tail, virtualTableConfigs);
return new ShardingDbContext(shardingDbContextOptions);
}
private DbContextOptions CreateOptions()
{
return new DbContextOptionsBuilder()
.UseSqlServer(_sqlServerOptions.ConnectionString)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.ReplaceService<IQueryCompiler, ShardingQueryCompiler>()
.ReplaceService<IModelCacheKeyFactory, ShardingModelCacheKeyFactory>()
.UseShardingSqlServerQuerySqlGenerator()
.Options;
}
}
}

View File

@ -0,0 +1,49 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.Extensions.Logging;
using ShardingCore.DbContexts.VirtualDbContexts;
using ShardingCore.EFCores;
#if EFCORE2
using System.Data.SqlClient;
#endif
#if !EFCORE2
using Microsoft.Data.SqlClient;
#endif
namespace ShardingCore.SqlServer
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 24 December 2020 10:33:51
* @Email: 326308290@qq.com
*/
public class SqlServerDbContextOptionsProvider:IDbContextOptionsProvider
{
private DbContextOptions _dbContextOptions;
private SqlConnection _connection;
public SqlServerDbContextOptionsProvider(SqlServerOptions sqlServerOptions,ILoggerFactory loggerFactory)
{
_connection=new SqlConnection(sqlServerOptions.ConnectionString);
_dbContextOptions = new DbContextOptionsBuilder()
.UseSqlServer(_connection)
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.UseLoggerFactory(loggerFactory)
.ReplaceService<IQueryCompiler, ShardingQueryCompiler>()
.ReplaceService<IModelCacheKeyFactory, ShardingModelCacheKeyFactory>()
.UseShardingSqlServerQuerySqlGenerator()
.Options;
}
public DbContextOptions GetDbContextOptions()
{
return _dbContextOptions;
}
public void Dispose()
{
_connection?.Dispose();
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Extensions;
namespace ShardingCore.SqlServer
{
/*
* @Author: xjm
* @Description:
* @Date: 202047 8:34:04
* @Email: 326308290@qq.com
*/
public class SqlServerOptions
{
public LinkedList<Type> ShardingRoutes=new LinkedList<Type>();
public string ConnectionString { get; set; }
public void AddSharding<TRoute>()where TRoute:IVirtualRoute
{
ShardingRoutes.AddLast(typeof(TRoute));
}
public bool HasSharding => ShardingRoutes.IsNotEmpty();
/// <summary>
/// 创建表如果数据库不存在的话创建的表是非sharding表
/// </summary>
public bool EnsureCreated { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace ShardingCore.Core
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 16 December 2020 10:41:27
* @Email: 326308290@qq.com
*/
public interface IShardingEntity
{
}
}

View File

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading.Tasks;
using ShardingCore.Core.VirtualTables;
namespace ShardingCore.Core
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 22 December 2020 16:29:18
* @Email: 326308290@qq.com
*/
public interface IShardingQueryable<T>
{
/// <summary>
/// 启用自动路由解析
/// </summary>
/// <returns></returns>
IShardingQueryable<T> EnableAutoRouteParse();
/// <summary>
/// 禁用自动路由解析
/// </summary>
/// <returns></returns>
IShardingQueryable<T> DisableAutoRouteParse();
/// <summary>
/// 设置其他表条件
/// </summary>
/// <param name="predicate"></param>
/// <typeparam name="TShardingEntity"></typeparam>
/// <returns></returns>
IShardingQueryable<T> AddManualRoute<TShardingEntity>(Expression<Func<TShardingEntity, bool>> predicate) where TShardingEntity : class, IShardingEntity;
/// <summary>
/// 手动指定路由
/// </summary>
/// <param name="virtualTable"></param>
/// <param name="tail"></param>
/// <returns></returns>
IShardingQueryable<T> AddManualRoute(IVirtualTable virtualTable,string tail);
/// <summary>
/// 异步获取数量
/// </summary>
/// <returns></returns>
Task<int> CountAsync();
/// <summary>
/// 异步获取数量
/// </summary>
/// <returns></returns>
Task<long> LongCountAsync();
/// <summary>
/// 异步获取列表
/// </summary>
/// <returns></returns>
Task<List<T>> ToListAsync();
/// <summary>
/// 获取第一个,若不存在则返回默认值
/// </summary>
/// <returns></returns>
Task<T> FirstOrDefaultAsync();
/// <summary>
/// 异步判断是否存在
/// </summary>
/// <param name="predicate">表达式</param>
/// <returns></returns>
Task<bool> AnyAsync();
/// <summary>
/// 异步计算最大值
/// </summary>
/// <returns></returns>
Task<T> MaxAsync();
/// <summary>
/// 异步计算最小值
/// </summary>
/// <returns></returns>
Task<T> MinAsync();
/// <summary>
/// 异步求和
/// </summary>
/// <returns></returns>
Task<int> SumAsync();
/// <summary>
/// 异步求和
/// </summary>
/// <returns></returns>
Task<long> LongSumAsync();
/// <summary>
/// 异步求和
/// </summary>
/// <returns></returns>
Task<decimal> DecimalSumAsync();
/// <summary>
/// 异步求和
/// </summary>
/// <returns></returns>
Task<double> DoubleSumAsync();
/// <summary>
/// 异步求和
/// </summary>
/// <returns></returns>
Task<float> FloatSumAsync();
}
}

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
namespace ShardingCore.Core.Internal.PriorityQueues {
/// <summary>
/// 大顶堆
/// </summary>
/// <typeparam name="T"></typeparam>
internal class Heap<T> {
public static void HeapSort(T[] objects,IComparer<T> comparer) {
HeapSort(objects, false,comparer);
}
public static void HeapSort(T[] objects, bool descending,IComparer<T> comparer) {
for (int i = objects.Length / 2 - 1; i >= 0; --i)
heapAdjustFromTop(objects, i, objects.Length, descending,comparer);
for (int i = objects.Length - 1; i > 0; --i) {
swap(objects, i, 0);
heapAdjustFromTop(objects, 0, i, descending,comparer);
}
}
public static void heapAdjustFromBottom(T[] objects, int n,IComparer<T> comparer) {
heapAdjustFromBottom(objects, n, false,comparer);
}
public static void heapAdjustFromBottom(T[] objects, int n, bool descending,IComparer<T> comparer) {
while (n > 0 && descending ^ comparer.Compare(objects[(n - 1) >> 1],objects[n]) < 0) {
swap(objects, n, (n - 1) >> 1);
n = (n - 1) >> 1;
}
}
public static void heapAdjustFromTop(T[] objects, int n, int len,IComparer<T> comparer) {
heapAdjustFromTop(objects, n, len, false,comparer);
}
public static void heapAdjustFromTop(T[] objects, int n, int len, bool descending,IComparer<T> comparer) {
while ((n << 1) + 1 < len) {
int m = (n << 1) + 1;
if (m + 1 < len && descending ^ comparer.Compare(objects[m],objects[m + 1]) < 0)
++m;
if (descending ^ comparer.Compare(objects[n],objects[m])> 0) return;
swap(objects, n, m);
n = m;
}
}
private static void swap(T[] objects, int a, int b) {
T tmp = objects[a];
objects[a] = objects[b];
objects[b] = tmp;
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
namespace ShardingCore.Core.Internal.PriorityQueues {
/// <summary>
/// 泛型优先队列 https://www.cnblogs.com/skyivben/archive/2009/04/18/1438731.html
/// </summary>
/// <typeparam name="T">实现IComparable&lt;T&gt;的类型</typeparam>
internal class PriorityQueue<T> {
private const int defaultCapacity = 0x10;//默认容量为16
private IComparer<T> comparer;
public PriorityQueue()
: this(defaultCapacity) {
}
public PriorityQueue(int initCapacity,bool ascending = false,IComparer<T> comparer=null) {
buffer = new T[initCapacity];
heapLength = 0;
descending = ascending;
this.comparer = comparer ?? Comparer<T>.Default;
}
public bool IsEmpty() {
return heapLength == 0;
}
public T Top() {
if (heapLength == 0) throw new OverflowException("queu is empty no element can return");
return buffer[0];
}
public void Push(T obj) {
if (IsFull()) expand();
buffer[heapLength] = obj;
Heap<T>.heapAdjustFromBottom(buffer, heapLength, descending,comparer);
heapLength++;
}
public void Pop() {
if (heapLength == 0) throw new OverflowException("优先队列为空时无法执行出队操作");
--heapLength;
swap(0, heapLength);
Heap<T>.heapAdjustFromTop(buffer, 0, heapLength, descending,this.comparer);
}
/// <summary>
/// 集合是否满了
/// </summary>
/// <returns></returns>
public bool IsFull()
{
return heapLength == buffer.Length;
}
private void expand() {
Array.Resize<T>(ref buffer, buffer.Length * 2);
}
private void swap(int a, int b) {
T tmp = buffer[a];
buffer[a] = buffer[b];
buffer[b] = tmp;
}
private bool descending;
private int heapLength;
private T[] buffer;
}
}

View File

@ -0,0 +1,55 @@
using System;
namespace ShardingCore.Core.Internal.PriorityQueues
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 25 January 2021 10:10:04
* @Email: 326308290@qq.com
*/
internal static class PriorityQueueExtension
{
/// <summary>
/// 返回默认第一个元素集合为空返回null
/// </summary>
/// <param name="queue"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T Peek<T>(this PriorityQueue<T> queue) where T: IComparable<T>
{
if (queue.IsEmpty())
return default(T);
return queue.Top();
}
/// <summary>
/// 返回并删除第一个元素集合为空返回null
/// </summary>
/// <param name="queue"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T Poll<T>(this PriorityQueue<T> queue) where T: IComparable<T>
{
if (queue.IsEmpty())
return default(T);
var first= queue.Top();
queue.Pop();
return first;
}
/// <summary>
/// 向队列添加元素如果集合满了返回false
/// </summary>
/// <param name="queue"></param>
/// <param name="element"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static bool Offer<T>(this PriorityQueue<T> queue, T element) where T : IComparable<T>
{
if (queue.IsFull())
return false;
queue.Push(element);
return true;
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace ShardingCore.Core.Internal.StreamMerge
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 25 January 2021 12:48:41
* @Email: 326308290@qq.com
*/
internal class CompareHelper
{
private CompareHelper(){}
public static int CompareTo(IComparable value, IComparable other)
{
if (null == value && null == other) {
return 0;
}
if (null == value)
{
return -1;
}
if (null == other) {
return 1;
}
return value.CompareTo(other);
}
public static int CompareToWith(IComparable value, IComparable other,bool asc)
{
if (asc)
return CompareTo(value, other);
return CompareTo(other, value);
}
}
}

View File

@ -0,0 +1,148 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using ShardingCore.Core.Internal.PriorityQueues;
namespace ShardingCore.Core.Internal.StreamMerge.ListMerge
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 25 January 2021 08:06:12
* @Email: 326308290@qq.com
*/
#if !EFCORE2
internal class OrderAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly StreamMergeContext _mergeContext;
private readonly List<IAsyncEnumerator<T>> _sources;
private readonly PriorityQueue<OrderMergeItem<T>> _queue;
private bool isFirst;
private IAsyncEnumerator<T> _currentEnumerator;
public OrderAsyncEnumerator(StreamMergeContext mergeContext,List<IAsyncEnumerator<T>> sources)
{
_mergeContext = mergeContext;
_sources = sources;
_queue = new PriorityQueue<OrderMergeItem<T>>(sources.Count);
isFirst = true;
SetOrderEnumerator();
}
private void SetOrderEnumerator()
{
foreach (var source in _sources)
{
var orderMergeItem = new OrderMergeItem<T>(_mergeContext, source);
if (null!=orderMergeItem.GetCurrentEnumerator().Current)
_queue.Offer(orderMergeItem);
}
_currentEnumerator = _queue.IsEmpty() ? _sources.FirstOrDefault() : _queue.Peek().GetCurrentEnumerator();
}
public async ValueTask<bool> MoveNextAsync()
{
if (_queue.IsEmpty())
return false;
if (isFirst)
{
isFirst = false;
return true;
}
var first = _queue.Poll();
if (await first.MoveNextAsync())
{
_queue.Offer(first);
}
if (_queue.IsEmpty())
{
return false;
}
_currentEnumerator = _queue.Peek().GetCurrentEnumerator();
return true;
}
public async ValueTask DisposeAsync()
{
foreach (var source in _sources)
{
await source.DisposeAsync();
}
}
public T Current => _currentEnumerator.Current;
}
#endif
#if EFCORE2
internal class OrderAsyncEnumerator<T> : IAsyncEnumerator<T>
{
private readonly StreamMergeContext _mergeContext;
private readonly List<IAsyncEnumerator<T>> _sources;
private readonly PriorityQueue<OrderMergeItem<T>> _queue;
private bool isFirst;
private IAsyncEnumerator<T> _currentEnumerator;
public OrderAsyncEnumerator(StreamMergeContext mergeContext, List<IAsyncEnumerator<T>> sources)
{
_mergeContext = mergeContext;
_sources = sources;
_queue = new PriorityQueue<OrderMergeItem<T>>(sources.Count);
isFirst = true;
SetOrderEnumerator();
}
private void SetOrderEnumerator()
{
foreach (var source in _sources)
{
var orderMergeItem = new OrderMergeItem<T>(_mergeContext, source);
if (null!=orderMergeItem.GetCurrentEnumerator().Current)
_queue.Offer(orderMergeItem);
}
_currentEnumerator = _queue.IsEmpty() ? _sources.FirstOrDefault() : _queue.Peek().GetCurrentEnumerator();
}
public async Task<bool> MoveNext(CancellationToken cancellationToken)
{
if (_queue.IsEmpty())
return false;
if (isFirst)
{
isFirst = false;
return true;
}
var first = _queue.Poll();
if (await first.MoveNextAsync())
{
_queue.Offer(first);
}
if (_queue.IsEmpty())
{
return false;
}
_currentEnumerator = _queue.Peek().GetCurrentEnumerator();
return true;
}
public void Dispose()
{
foreach (var source in _sources)
{
source.Dispose();
}
}
public T Current => _currentEnumerator.Current;
}
#endif
}

View File

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ShardingCore.Extensions;
namespace ShardingCore.Core.Internal.StreamMerge.ListMerge
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 25 January 2021 11:33:57
* @Email: 326308290@qq.com
*/
#if !EFCORE2
internal class OrderMergeItem<T> : IComparable<OrderMergeItem<T>>, IAsyncDisposable
{
/// <summary>
/// 合并数据上下文
/// </summary>
private readonly StreamMergeContext _mergeContext;
private readonly IAsyncEnumerator<T> _source;
private List<IComparable> _orderValues;
public OrderMergeItem(StreamMergeContext mergeContext, IAsyncEnumerator<T> source)
{
_mergeContext = mergeContext;
_source = source;
SetOrderValues(null!=GetCurrentEnumerator().Current);
}
public IAsyncEnumerator<T> GetCurrentEnumerator() => _source;
private void SetOrderValues(bool hasElement)
{
_orderValues = hasElement ? GetCurrentOrderValues() : new List<IComparable>(0);
}
public async Task<bool> MoveNextAsync()
{
var has = await _source.MoveNextAsync();
SetOrderValues(has);
return has;
}
private List<IComparable> GetCurrentOrderValues()
{
if (!_mergeContext.Orders.Any())
return new List<IComparable>(0);
var list = new List<IComparable>(_mergeContext.Orders.Count());
foreach (var order in _mergeContext.Orders)
{
var value = GetCurrentEnumerator().Current.GetValueByExpression(order.PropertyExpression);
if (value is IComparable comparable)
list.Add(comparable);
else
throw new NotSupportedException($"order by value [{order}] must implements IComparable");
}
return list;
}
public int CompareTo(OrderMergeItem<T> other)
{
int i = 0;
foreach (var order in _mergeContext.Orders) {
int result = CompareHelper.CompareToWith(_orderValues[i], other._orderValues[i], order.IsAsc);
if (0 != result) {
return result;
}
i++;
}
return 0;
}
public ValueTask DisposeAsync()
{
return _source.DisposeAsync();
}
}
#endif
#if EFCORE2
internal class OrderMergeItem<T> : IComparable<OrderMergeItem<T>>, IDisposable
{
/// <summary>
/// 合并数据上下文
/// </summary>
private readonly StreamMergeContext _mergeContext;
private readonly IAsyncEnumerator<T> _source;
private List<IComparable> _orderValues;
public OrderMergeItem(StreamMergeContext mergeContext, IAsyncEnumerator<T> source)
{
_mergeContext = mergeContext;
_source = source;
SetOrderValues(null!=GetCurrentEnumerator().Current);
}
public IAsyncEnumerator<T> GetCurrentEnumerator() => _source;
private void SetOrderValues(bool hasElement)
{
_orderValues = hasElement ? GetCurrentOrderValues() : new List<IComparable>(0);
}
public async Task<bool> MoveNextAsync()
{
var has = await _source.MoveNext();
SetOrderValues(has);
return has;
}
private List<IComparable> GetCurrentOrderValues()
{
if (!_mergeContext.Orders.Any())
return new List<IComparable>(0);
var list = new List<IComparable>(_mergeContext.Orders.Count());
foreach (var order in _mergeContext.Orders)
{
var value = GetCurrentEnumerator().Current.GetValueByExpression(order.PropertyExpression);
if (value is IComparable comparable)
list.Add(comparable);
else
throw new NotSupportedException($"order by value [{order}] must implements IComparable");
}
return list;
}
public int CompareTo(OrderMergeItem<T> other)
{
int i = 0;
foreach (var order in _mergeContext.Orders) {
int result = CompareHelper.CompareToWith(_orderValues[i], other._orderValues[i], order.IsAsc);
if (0 != result) {
return result;
}
i++;
}
return 0;
}
public void Dispose()
{
_source.Dispose();
}
}
#endif
}

View File

@ -0,0 +1,62 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace ShardingCore.Core.Internal.StreamMerge.ListMerge
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 25 January 2021 07:57:59
* @Email: 326308290@qq.com
*/
internal class StreamMergeListEngine<T>
{
private const int defaultCapacity = 0x10;//默认容量为16
private readonly StreamMergeContext _mergeContext;
private readonly List<IAsyncEnumerator<T>> _sources;
public StreamMergeListEngine(StreamMergeContext mergeContext,List<IAsyncEnumerator<T>> sources)
{
_mergeContext = mergeContext;
_sources = sources;
}
public async Task<List<T>> Execute()
{
//如果合并数据的时候不需要跳过也没有take多少那么就是直接next
var skip = _mergeContext.Skip;
var take = _mergeContext.Take;
var list = new List<T>(skip.GetValueOrDefault() + take ?? defaultCapacity);
var enumerator=new OrderAsyncEnumerator<T>(_mergeContext,_sources);
var realSkip = 0;
var realTake = 0;
#if !EFCORE2
while (await enumerator.MoveNextAsync())
#endif
#if EFCORE2
while (await enumerator.MoveNext())
#endif
{
//获取真实的需要跳过的条数
if (skip.HasValue)
{
if (realSkip < skip)
{
realSkip++;
continue;
}
}
list.Add(enumerator.Current);
if (take.HasValue)
{
realTake++;
if(realTake<=take.Value)
break;
}
}
return list;
}
}
}

View File

@ -0,0 +1,25 @@
using System.Collections.Generic;
using ShardingCore.Core.Internal.Visitors;
namespace ShardingCore.Core.Internal.StreamMerge
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 25 January 2021 11:38:27
* @Email: 326308290@qq.com
*/
internal class StreamMergeContext
{
public StreamMergeContext(int? skip, int? take, IEnumerable<PropertyOrder> orders = null)
{
Skip = skip;
Take = take;
Orders = orders??new List<PropertyOrder>();
}
public int? Skip { get; set; }
public int? Take { get; set; }
public IEnumerable<PropertyOrder> Orders { get; set; }
}
}

View File

@ -0,0 +1,126 @@
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 16:32:27
* @Email: 326308290@qq.com
*/
#if !EFCORE5
internal class DbContextReplaceQueryableVisitor : ExpressionVisitor
{
private readonly DbContext _dbContext;
public IQueryable Source;
public DbContextReplaceQueryableVisitor(DbContext dbContext)
{
_dbContext = dbContext;
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Value is IQueryable queryable)
{
var dbContextDependencies = typeof(DbContext).GetTypePropertyValue(_dbContext, "DbContextDependencies") as IDbContextDependencies;
var targetIQ = (IQueryable)((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, queryable.ElementType);
var newQueryable = targetIQ.Provider.CreateQuery((Expression) Expression.Call((Expression) null, typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod("AsNoTracking").MakeGenericMethod(queryable.ElementType), targetIQ.Expression));
Source = newQueryable;
return base.Visit(Expression.Constant(newQueryable));
// return Expression.Constant(newQueryable);
}
return base.VisitConstant(node);
}
}
#endif
#if EFCORE5
internal class DbContextReplaceQueryableVisitor : ExpressionVisitor
{
private readonly DbContext _dbContext;
public IQueryable Source;
public DbContextReplaceQueryableVisitor(DbContext dbContext)
{
_dbContext = dbContext;
}
protected override Expression VisitExtension(Expression node)
{
if (node is QueryRootExpression queryRootExpression)
{
var dbContextDependencies = typeof(DbContext).GetTypePropertyValue(_dbContext, "DbContextDependencies") as IDbContextDependencies;
var targetIQ = (IQueryable) ((IDbSetCache) _dbContext).GetOrAddSet(dbContextDependencies.SetSource, queryRootExpression.EntityType.ClrType);
var newQueryable = targetIQ.Provider.CreateQuery((Expression) Expression.Call((Expression) null, typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod("AsNoTracking").MakeGenericMethod(queryRootExpression.EntityType.ClrType), targetIQ.Expression));
Source = newQueryable;
//如何替换ef5的set
var replaceQueryRoot = new ReplaceSingleQueryRootExpressionVisitor();
replaceQueryRoot.Visit(newQueryable.Expression);
return base.VisitExtension(replaceQueryRoot.QueryRootExpression);
}
return base.VisitExtension(node);
}
class ReplaceSingleQueryRootExpressionVisitor : ExpressionVisitor
{
public QueryRootExpression QueryRootExpression { get; set; }
protected override Expression VisitExtension(Expression node)
{
if (node is QueryRootExpression queryRootExpression)
{
if (QueryRootExpression != null)
throw new InvalidReplaceQueryRootException("more than one query root");
QueryRootExpression = queryRootExpression;
}
return base.VisitExtension(node);
}
}
}
#endif
// class ReplaceQueryableVisitor : ExpressionVisitor
// {
// private readonly QueryRootExpression _queryRootExpression;
// public ReplaceQueryableVisitor(IQueryable newQuery)
// {
// var visitor = new GetQueryRootVisitor();
// visitor.Visit(newQuery.Expression);
// _queryRootExpression = visitor.QueryRootExpression;
// }
//
// protected override Expression VisitExtension(Expression node)
// {
// if (node is QueryRootExpression)
// {
// return _queryRootExpression;
// }
//
// return base.VisitExtension(node);
// }
// }
// class GetQueryRootVisitor : ExpressionVisitor
// {
// public QueryRootExpression QueryRootExpression { get; set; }
// protected override Expression VisitExtension(Expression node)
// {
// if (node is QueryRootExpression expression)
// {
// QueryRootExpression = expression;
// }
//
// return base.VisitExtension(node);
// }
// }
}

View File

@ -0,0 +1,24 @@
using System.Collections.Generic;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 13:13:45
* @Email: 326308290@qq.com
*/
internal class ExtraEntry
{
public ExtraEntry(int? skip, int? take, IEnumerable<PropertyOrder> orders)
{
Skip = skip;
Take = take;
Orders = orders;
}
public int? Skip { get; set; }
public int? Take { get; set; }
public IEnumerable<PropertyOrder> Orders { get; set; }
}
}

View File

@ -0,0 +1,24 @@
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 15:35:15
* @Email: 326308290@qq.com
*/
internal class PropertyOrder
{
public PropertyOrder(string propertyExpression, bool isAsc)
{
PropertyExpression = propertyExpression;
IsAsc = isAsc;
}
public string PropertyExpression { get; set; }
public bool IsAsc { get; set; }
public override string ToString()
{
return $"{PropertyExpression} {(IsAsc ? "asc" : "desc")}";
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 11:04:50
* @Email: 326308290@qq.com
*/
internal class QueryableExtraDiscoverVisitor: ShardingExpressionVisitor
{
private int? _skip;
private int? _take;
private LinkedList<PropertyOrder> _orders = new LinkedList<PropertyOrder>();
public int? GetSkip()
{
return _skip;
}
public bool HasSkip()
{
return _skip.HasValue;
}
public int? GetTake()
{
return _take;
}
public bool HasTake()
{
return _take.HasValue;
}
public IEnumerable<PropertyOrder> GetOrders()
{
return _orders;
}
public string GetOrderExpression()
{
return string.Join(",", _orders);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
var method = node.Method;
if (node.Method.Name == nameof(Queryable.Skip))
{
if (HasSkip())
throw new InvalidOperationException("more than one skip found");
_skip = (int)GetFieldValue(node.Arguments[1]);
}
else if (node.Method.Name == nameof(Queryable.Take))
{
if (HasTake())
throw new InvalidOperationException("more than one take found");
_take = (int)GetFieldValue(node.Arguments[1]);
}
else if (method.Name == nameof(Queryable.OrderBy) || method.Name == nameof(Queryable.OrderByDescending) || method.Name == nameof(Queryable.ThenBy) || method.Name == nameof(Queryable.ThenByDescending))
{
var expression=(((node.Arguments[1] as UnaryExpression).Operand as LambdaExpression).Body as MemberExpression);
if (expression == null)
throw new NotSupportedException("sharding order not support ");
List<string> properties = new List<string>();
GetProperty(properties, expression);
if (!properties.Any())
throw new NotSupportedException("sharding order only support property expression");
properties.Reverse();
var propertyExpression=string.Join(".", properties);
_orders.AddFirst(new PropertyOrder(propertyExpression,method.Name == nameof(Queryable.OrderBy)||method.Name == nameof(Queryable.ThenBy)));
}
return base.VisitMethodCall(node);
}
private void GetProperty(List<string> properties,MemberExpression memberExpression)
{
properties.Add(memberExpression.Member.Name);
if (memberExpression.Expression is MemberExpression member)
{
GetProperty(properties, member);
}
}
}
}

View File

@ -0,0 +1,251 @@
using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualTables;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 28 December 2020 22:09:39
* @Email: 326308290@qq.com
*/
public class QueryableRouteDiscoverVisitor<TKey> : ExpressionVisitor
{
private readonly ShardingEntityConfig _shardingConfig;
private readonly Func<object, TKey> _shardingKeyConvert;
private readonly Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> _keyToTailWithFilter;
private Expression<Func<string, bool>> _where = x => true;
public QueryableRouteDiscoverVisitor(ShardingEntityConfig shardingConfig, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailWithFilter)
{
_shardingConfig = shardingConfig;
_shardingKeyConvert = shardingKeyConvert;
_keyToTailWithFilter = keyToTailWithFilter;
}
public Func<string, bool> GetStringFilterTail()
{
return _where.Compile();
}
private bool IsShardingKey(Expression expression)
{
return expression is MemberExpression member
&& member.Expression.Type == _shardingConfig.ShardingEntityType
&& member.Member.Name == _shardingConfig.ShardingField;
}
/// <summary>
/// 方法是否包含shardingKey
/// </summary>
/// <param name="methodCallExpression"></param>
/// <returns></returns>
private bool IsMethodWrapShardingKey(MethodCallExpression methodCallExpression)
{
if (methodCallExpression.Arguments.IsNotEmpty())
{
for (int i = 0; i < methodCallExpression.Arguments.Count; i++)
{
var isShardingKey = methodCallExpression.Arguments[i] is MemberExpression member
&& member.Expression.Type == _shardingConfig.ShardingEntityType
&& member.Member.Name == _shardingConfig.ShardingField;
if (isShardingKey) return true;
}
}
return false;
}
private bool IsConstantOrMember(Expression expression)
{
return expression is ConstantExpression
|| (expression is MemberExpression member && (member.Expression is ConstantExpression || member.Expression is MemberExpression || member.Expression is MemberExpression));
}
private object GetFieldValue(Expression expression)
{
if (expression is ConstantExpression)
return (expression as ConstantExpression).Value;
if (expression is UnaryExpression)
{
UnaryExpression unary = expression as UnaryExpression;
LambdaExpression lambda = Expression.Lambda(unary.Operand);
Delegate fn = lambda.Compile();
return fn.DynamicInvoke(null);
}
if (expression is MemberExpression member1Expression)
{
return Expression.Lambda(member1Expression).Compile().DynamicInvoke();
}
throw new ShardingKeyGetValueException("cant get value " + expression);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == nameof(Queryable.Where))
{
if (node.Arguments[1] is UnaryExpression unaryExpression)
{
if (unaryExpression.Operand is LambdaExpression lambdaExpression)
{
var newWhere = Resolve(lambdaExpression);
_where = _where.And(newWhere);
}
}
}
return base.VisitMethodCall(node);
}
private Expression<Func<string, bool>> Resolve(Expression expression)
{
if (expression is LambdaExpression)
{
LambdaExpression lambda = expression as LambdaExpression;
expression = lambda.Body;
return Resolve(expression);
}
if (expression is BinaryExpression binaryExpression) //解析二元运算符
{
return ParseGetWhere(binaryExpression);
}
if (expression is UnaryExpression) //解析一元运算符
{
UnaryExpression unary = expression as UnaryExpression;
if (unary.Operand is MethodCallExpression methodCall1Expression)
{
// return ResolveLinqToObject(unary.Operand, false);
return ResolveInFunc(methodCall1Expression, unary.NodeType != ExpressionType.Not);
}
}
if (expression is MethodCallExpression methodCallExpression) //解析扩展方法
{
return ResolveInFunc(methodCallExpression, true);
}
return o => true;
}
private Expression<Func<string, bool>> ResolveInFunc(MethodCallExpression methodCallExpression, bool @in)
{
if (methodCallExpression.IsEnumerableContains(methodCallExpression.Method.Name) && IsMethodWrapShardingKey(methodCallExpression))
{
object arrayObject = null;
if (methodCallExpression.Object != null)
{
if (methodCallExpression.Object is MemberExpression member1Expression)
{
arrayObject = Expression.Lambda(member1Expression).Compile().DynamicInvoke();
}
else if (methodCallExpression.Object is ListInitExpression member2Expression)
{
arrayObject = Expression.Lambda(member2Expression).Compile().DynamicInvoke();
}
}
else if (methodCallExpression.Arguments[0] is MemberExpression member2Expression)
{
arrayObject = Expression.Lambda(member2Expression).Compile().DynamicInvoke();
}
else if (methodCallExpression.Arguments[0] is NewArrayExpression member3Expression)
{
arrayObject = Expression.Lambda(member3Expression).Compile().DynamicInvoke();
}
if (arrayObject != null)
{
var enumerable = (IEnumerable) arrayObject;
Expression<Func<string, bool>> contains = x => false;
if (!@in)
contains = x => true;
foreach (var item in enumerable)
{
var keyValue = _shardingKeyConvert(item);
var eq = _keyToTailWithFilter(keyValue, @in ? ShardingOperatorEnum.Equal : ShardingOperatorEnum.NotEqual);
if (@in)
contains = contains.Or(eq);
else
contains = contains.And(eq);
}
return contains;
}
}
return x => true;
}
private Expression<Func<string, bool>> ParseGetWhere(BinaryExpression binaryExpression)
{
Expression<Func<string, bool>> left = x => true;
Expression<Func<string, bool>> right = x => true;
//递归获取
if (binaryExpression.Left is BinaryExpression)
left = ParseGetWhere(binaryExpression.Left as BinaryExpression);
if (binaryExpression.Left is MethodCallExpression methodCallExpression)
left = Resolve(methodCallExpression);
if (binaryExpression.Left is UnaryExpression unaryExpression)
left = Resolve(unaryExpression);
if (binaryExpression.Right is BinaryExpression)
right = ParseGetWhere(binaryExpression.Right as BinaryExpression);
//组合
if (binaryExpression.NodeType == ExpressionType.AndAlso)
{
return left.And(right);
}
else if (binaryExpression.NodeType == ExpressionType.OrElse)
{
return left.Or(right);
}
//单个
else
{
bool paramterAtLeft;
object value = null;
if (IsShardingKey(binaryExpression.Left) && IsConstantOrMember(binaryExpression.Right))
{
paramterAtLeft = true;
value = GetFieldValue(binaryExpression.Right);
}
else if (IsConstantOrMember(binaryExpression.Left) && IsShardingKey(binaryExpression.Right))
{
paramterAtLeft = false;
value = GetFieldValue(binaryExpression.Left);
}
else
return x => true;
var op = binaryExpression.NodeType switch
{
ExpressionType.GreaterThan => paramterAtLeft ? ShardingOperatorEnum.GreaterThan : ShardingOperatorEnum.LessThan,
ExpressionType.GreaterThanOrEqual => paramterAtLeft ? ShardingOperatorEnum.GreaterThanOrEqual : ShardingOperatorEnum.LessThanOrEqual,
ExpressionType.LessThan => paramterAtLeft ? ShardingOperatorEnum.LessThan : ShardingOperatorEnum.GreaterThan,
ExpressionType.LessThanOrEqual => paramterAtLeft ? ShardingOperatorEnum.LessThanOrEqual : ShardingOperatorEnum.GreaterThanOrEqual,
ExpressionType.Equal => ShardingOperatorEnum.Equal,
ExpressionType.NotEqual => ShardingOperatorEnum.NotEqual,
_ => ShardingOperatorEnum.UnKnown
};
if (value == null)
return x => true;
var keyValue = _shardingKeyConvert(value);
return _keyToTailWithFilter(keyValue, op);
}
}
}
}

View File

@ -0,0 +1,25 @@
using System.Linq;
using System.Linq.Expressions;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 16:32:57
* @Email: 326308290@qq.com
*/
/// <summary>
/// 删除Skip表达式
/// </summary>
internal class RemoveSkipVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == nameof(Queryable.Skip))
return base.Visit(node.Arguments[0]);
return base.VisitMethodCall(node);
}
}
}

View File

@ -0,0 +1,25 @@
using System.Linq;
using System.Linq.Expressions;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 16:33:55
* @Email: 326308290@qq.com
*/
/// <summary>
/// 删除Take表达式
/// </summary>
internal class RemoveTakeVisitor : ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == nameof(Queryable.Take))
return base.Visit(node.Arguments[0]);
return base.VisitMethodCall(node);
}
}
}

View File

@ -0,0 +1,201 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Query;
using ShardingCore.Extensions;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 16:26:41
* @Email: 326308290@qq.com
*/
#if !EFCORE5
/// <summary>
/// 获取分表类型
/// </summary>
internal class ShardingEntitiesVisitor : ExpressionVisitor
{
private readonly ISet<Type> _shardingEntities = new HashSet<Type>();
public ISet<Type> GetShardingEntities()
{
return _shardingEntities;
}
protected override Expression VisitConstant(ConstantExpression node)
{
if (node.Value is IQueryable queryable&&queryable.ElementType.IsShardingEntity())
{
_shardingEntities.Add(queryable.ElementType);
}
return base.VisitConstant(node);
}
}
#endif
#if EFCORE5
/// <summary>
/// 获取分表类型
/// </summary>
internal class ShardingEntitiesVisitor : ExpressionVisitor
{
private readonly ISet<Type> _shardingEntities = new HashSet<Type>();
public ISet<Type> GetShardingEntities()
{
return _shardingEntities;
}
protected override Expression VisitExtension(Expression node)
{
if (node is QueryRootExpression queryRootExpression&&queryRootExpression.EntityType.ClrType.IsShardingEntity())
{
_shardingEntities.Add(queryRootExpression.EntityType.ClrType);
}
return base.VisitExtension(node);
}
}
#endif
// internal class ShardingEntitiesVisitor : ExpressionVisitor
// {
// private readonly IVirtualTableManager _virtualTableManager;
// private readonly ISet<Type> _shardingEntities = new HashSet<Type>();
//
// public ShardingEntitiesVisitor(IVirtualTableManager virtualTableManager)
// {
// _virtualTableManager = virtualTableManager;
// }
//
// public ISet<Type> GetShardingEntities()
// {
// return _shardingEntities;
// }
//
// private bool IsShardingKey(Expression expression, out Type shardingEntity)
// {
// if (expression is MemberExpression member
// && _virtualTableManager.IsShardingKey(member.Expression.Type, member.Member.Name))
// {
// shardingEntity = member.Expression.Type;
// return true;
// }
//
// shardingEntity = null;
// return false;
// }
//
// private bool IsMethodShardingKey(MethodCallExpression methodCallExpression, out Type shardingEntity)
// {
// if (methodCallExpression.Arguments.IsNotEmpty())
// {
// for (int i = 0; i < methodCallExpression.Arguments.Count; i++)
// {
// if (methodCallExpression.Arguments[i] is MemberExpression member
// && _virtualTableManager.IsShardingKey(member.Expression.Type, member.Member.Name))
// {
// shardingEntity = member.Expression.Type;
// return true;
// }
// }
// }
//
// shardingEntity = null;
// return false;
// }
//
// protected override Expression VisitMethodCall(MethodCallExpression node)
// {
// var methodName = node.Method.Name;
// switch (methodName)
// {
// case nameof(Queryable.Where):
// ResolveWhere(node);
// break;
// }
//
//
// return base.VisitMethodCall(node);
// }
//
// private void ResolveWhere(MethodCallExpression node)
// {
// if (node.Arguments[1] is UnaryExpression unaryExpression)
// {
// if (unaryExpression.Operand is LambdaExpression lambdaExpression)
// {
// Resolve(lambdaExpression);
// }
// }
// }
//
//
// private void Resolve(Expression expression)
// {
// if (expression is LambdaExpression)
// {
// LambdaExpression lambda = expression as LambdaExpression;
// expression = lambda.Body;
// Resolve(expression);
// }
//
// if (expression is BinaryExpression binaryExpression) //解析二元运算符
// {
// ParseGetWhere(binaryExpression);
// }
//
// if (expression is UnaryExpression) //解析一元运算符
// {
// UnaryExpression unary = expression as UnaryExpression;
// if (unary.Operand is MethodCallExpression methodCall1Expression)
// {
// ResolveInFunc(methodCall1Expression, unary.NodeType != ExpressionType.Not);
// }
// }
//
// if (expression is MethodCallExpression methodCallExpression) //解析扩展方法
// {
// ResolveInFunc(methodCallExpression, true);
// }
// }
//
// private void ResolveInFunc(MethodCallExpression methodCallExpression, bool @in)
// {
// if (methodCallExpression.IsEnumerableContains(methodCallExpression.Method.Name) && IsMethodShardingKey(methodCallExpression, out var shardingEntity))
// {
// _shardingEntities.Add(shardingEntity);
// }
// }
//
// private void ParseGetWhere(BinaryExpression binaryExpression)
// {
// //递归获取
// if (binaryExpression.Left is BinaryExpression)
// ParseGetWhere(binaryExpression.Left as BinaryExpression);
// if (binaryExpression.Left is MethodCallExpression methodCallExpression)
// {
// Resolve(methodCallExpression);
// }
//
// if (binaryExpression.Left is UnaryExpression unaryExpression)
// Resolve(unaryExpression);
//
// if (binaryExpression.Right is BinaryExpression)
// ParseGetWhere(binaryExpression.Right as BinaryExpression);
//
// if (IsShardingKey(binaryExpression.Left, out var shardingEntity1))
// {
// _shardingEntities.Add(shardingEntity1);
// }
// else if (IsShardingKey(binaryExpression.Right, out var shardingEntity2))
// {
// _shardingEntities.Add(shardingEntity2);
// }
// }
// }
}

View File

@ -0,0 +1,36 @@
using System;
using System.Linq.Expressions;
using ShardingCore.Exceptions;
namespace ShardingCore.Core.Internal.Visitors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 11:31:01
* @Email: 326308290@qq.com
*/
internal abstract class ShardingExpressionVisitor:ExpressionVisitor
{
public object GetFieldValue(Expression expression)
{
if (expression is ConstantExpression)
return (expression as ConstantExpression).Value;
if (expression is UnaryExpression)
{
UnaryExpression unary = expression as UnaryExpression;
LambdaExpression lambda = Expression.Lambda(unary.Operand);
Delegate fn = lambda.Compile();
return fn.DynamicInvoke(null);
}
if (expression is MemberExpression member1Expression)
{
return Expression.Lambda(member1Expression).Compile().DynamicInvoke();
}
throw new ShardingKeyGetValueException("cant get value " + expression);
}
}
}

View File

@ -0,0 +1,29 @@
using System;
namespace ShardingCore.Core.PhysicTables
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 13:57:50
* @Email: 326308290@qq.com
*/
/// <summary>
/// 默认的物理表
/// </summary>
public class DefaultPhysicTable:IPhysicTable
{
public DefaultPhysicTable(string originalName, string tailPrefix, string tail, Type virtualType)
{
OriginalName = originalName;
TailPrefix = tailPrefix;
Tail = tail;
VirtualType = virtualType;
}
public string FullName => $"{OriginalName}{TailPrefix}{Tail}";
public string OriginalName { get; }
public string TailPrefix { get; }
public string Tail { get; }
public Type VirtualType { get; }
}
}

View File

@ -0,0 +1,35 @@
using System;
namespace ShardingCore.Core.PhysicTables
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 13:54:46
* @Email: 326308290@qq.com
*/
public interface IPhysicTable
{
/// <summary>
/// 表全称
/// </summary>
string FullName { get; }
/// <summary>
/// 原表名称
/// </summary>
string OriginalName { get;}
/// <summary>
/// 尾巴前缀 tail prefix
/// </summary>
string TailPrefix { get; }
/// <summary>
/// 尾巴
/// </summary>
string Tail { get;}
/// <summary>
/// 映射类类型
/// </summary>
Type VirtualType { get; }
}
}

View File

@ -0,0 +1,13 @@
namespace ShardingCore.Core.ShardingAccessors
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 22 December 2020 15:13:44
* @Email: 326308290@qq.com
*/
public interface IShardingAccessor
{
ShardingContext ShardingContext { get; set; }
}
}

View File

@ -0,0 +1,20 @@
namespace ShardingCore.Core.ShardingAccessors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 23 December 2020 07:51:00
* @Email: 326308290@qq.com
*/
/// <summary>
/// 查询scope创建
/// </summary>
public interface IShardingScopeFactory
{
/// <summary>
/// 创建查询scope
/// </summary>
/// <returns></returns>
ShardingScope CreateScope();
}
}

View File

@ -0,0 +1,39 @@
using System.Threading;
using ShardingCore.Core.VirtualTables;
namespace ShardingCore.Core.ShardingAccessors
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 22 December 2020 15:14:15
* @Email: 326308290@qq.com
*/
/// <summary>
/// 分表访问器
/// </summary>
public class ShardingAccessor : IShardingAccessor
{
private static AsyncLocal<ShardingContext> _shardingContext = new AsyncLocal<ShardingContext>();
/// <summary>
/// 分表访问器
/// </summary>
public ShardingAccessor(IVirtualTableManager virtualTableManager)
{
VirtualTableManager = virtualTableManager;
}
/// <inheritdoc />
public ShardingContext ShardingContext
{
get => _shardingContext.Value;
set => _shardingContext.Value = value;
}
/// <summary>
/// 虚拟表管理者
/// </summary>
public IVirtualTableManager VirtualTableManager { get; }
}
}

View File

@ -0,0 +1,66 @@
using System.Collections.Generic;
using ShardingCore.Core.VirtualTables;
using ShardingCore.Extensions;
namespace ShardingCore.Core.ShardingAccessors
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 22 December 2020 15:04:47
* @Email: 326308290@qq.com
*/
public class ShardingContext
{
private ShardingContext()
{
}
/// <summary>
/// 分表操作上下文 key:物理表名 value:虚拟表和本次分表tails
/// </summary>
private readonly Dictionary<IVirtualTable, List<string>> _shardingTables = new Dictionary<IVirtualTable, List<string>>();
/// <summary>
/// 尝试添加本次操作表
/// </summary>
/// <param name="virtualTable"></param>
/// <param name="tails"></param>
/// <returns></returns>
public void TryAddShardingTable(IVirtualTable virtualTable, List<string> tails)
{
_shardingTables.Add(virtualTable, tails);
}
/// <summary>
/// 创建一个分表上下文
/// </summary>
/// <returns></returns>
public static ShardingContext Create()
{
return new ShardingContext();
}
/// <summary>
/// 获取分表信息
/// </summary>
/// <param name="virtualTable"></param>
/// <returns></returns>
public List<string> GetContextQueryTails(IVirtualTable virtualTable)
{
if (_shardingTables.ContainsKey(virtualTable))
return _shardingTables[virtualTable] ?? new List<string>(0);
return new List<string>(0);
}
/// <summary>
/// 是否是空的
/// </summary>
/// <returns></returns>
public bool IsEmpty()
{
return _shardingTables.IsEmpty();
}
}
}

View File

@ -0,0 +1,36 @@
using System;
namespace ShardingCore.Core.ShardingAccessors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 23 December 2020 07:51:30
* @Email: 326308290@qq.com
*/
public class ShardingScope : IDisposable
{
/// <summary>
/// 分表配置访问器
/// </summary>
public IShardingAccessor ShardingAccessor { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="shardingAccessor"></param>
public ShardingScope(IShardingAccessor shardingAccessor)
{
ShardingAccessor = shardingAccessor;
}
/// <summary>
/// 回收
/// </summary>
public void Dispose()
{
ShardingAccessor.ShardingContext = null;
}
}
}

View File

@ -0,0 +1,33 @@
namespace ShardingCore.Core.ShardingAccessors
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 23 December 2020 08:11:06
* @Email: 326308290@qq.com
*/
/// <summary>
/// 分表查询环境创建
/// </summary>
public class ShardingScopeFactory : IShardingScopeFactory
{
private readonly IShardingAccessor _shardingAccessor;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="shardingAccessor"></param>
public ShardingScopeFactory(IShardingAccessor shardingAccessor)
{
_shardingAccessor = shardingAccessor;
}
/// <summary>
/// 创建scope
/// </summary>
/// <returns></returns>
public ShardingScope CreateScope()
{
return new ShardingScope(_shardingAccessor);
}
}
}

View File

@ -0,0 +1,34 @@
using System;
namespace ShardingCore.Core
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 16 December 2020 11:04:51
* @Email: 326308290@qq.com
*/
/// <summary>
/// AbstractVirtualRoute 最基础分表规则 需要自己解析如何分表
/// 仅ShardingMode为Custom:以下接口提供自定义分表
/// AbstractShardingKeyObjectEqualVirtualRoute 自定义分表
/// SimpleShardingKeyStringModVirtualRoute 默认对AbstractShardingKeyObjectEqualVirtualRoute的实现,字符串按取模分表
/// 仅ShardingMode非Custom以下接口提供自动按时间分表
/// SimpleShardingDateByDayVirtualRoute 分表
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class ShardingKeyAttribute : Attribute
{
public const string DEFAULT_TAIL_PREFIX = "_";
/// <summary>
/// 是否需要在启动的时候创建表
/// </summary>
public bool AutoCreateTableOnStart { get; set; } = false;
/// <summary>
/// 分表尾巴前缀
/// </summary>
public string TailPrefix { get; set; } = DEFAULT_TAIL_PREFIX;
}
}

View File

@ -0,0 +1,448 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore.Core.Internal.StreamMerge;
using ShardingCore.Core.Internal.StreamMerge.ListMerge;
using ShardingCore.Core.ShardingAccessors;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts;
using ShardingCore.Extensions;
#if EFCORE2
using Microsoft.EntityFrameworkCore.Extensions.Internal;
#endif
namespace ShardingCore.Core
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 22 December 2020 16:26:16
* @Email: 326308290@qq.com
*/
/// <summary>
/// 分表查询构造器
/// </summary>
/// <typeparam name="T"></typeparam>
public class ShardingQueryable<T> : IShardingQueryable<T>
{
private IQueryable<T> _source;
private bool _autoParseRoute = true;
private readonly IShardingScopeFactory _shardingScopeFactory;
private readonly IVirtualTableManager _virtualTableManager;
private readonly IShardingParallelDbContextFactory _shardingParallelDbContextFactory;
public Dictionary<Type, Expression> _routes = new Dictionary<Type, Expression>();
private readonly Dictionary<IVirtualTable, List<string>> _endRoutes = new Dictionary<IVirtualTable, List<string>>();
public ShardingQueryable(IQueryable<T> source)
{
_source = source;
_shardingScopeFactory = ShardingContainer.Services.GetService<IShardingScopeFactory>();
_virtualTableManager = ShardingContainer.Services.GetService<IVirtualTableManager>();
_shardingParallelDbContextFactory = ShardingContainer.Services.GetService<IShardingParallelDbContextFactory>();
}
public IShardingQueryable<T> EnableAutoRouteParse()
{
_autoParseRoute = true;
return this;
}
public IShardingQueryable<T> DisableAutoRouteParse()
{
_autoParseRoute = false;
return this;
}
public IShardingQueryable<T> AddManualRoute<TShardingEntity>(Expression<Func<TShardingEntity, bool>> predicate) where TShardingEntity : class, IShardingEntity
{
var shardingEntityType = typeof(TShardingEntity);
if (!_routes.ContainsKey(shardingEntityType))
{
((Expression<Func<TShardingEntity, bool>>) _routes[shardingEntityType]).And(predicate);
}
else
{
_routes.Add(typeof(TShardingEntity), predicate);
}
return this;
}
public IShardingQueryable<T> AddManualRoute(IVirtualTable virtualTable, string tail)
{
if (_endRoutes.ContainsKey(virtualTable))
{
var tails = _endRoutes[virtualTable];
if (!tails.Contains(tail))
{
tails.Add(tail);
}
}
else
{
_endRoutes.Add(virtualTable, new List<string>()
{
tail
});
}
return this;
}
public async Task<int> CountAsync()
{
var shardingCounts = await GetShardingQueryAsync(async queryable =>await EntityFrameworkQueryableExtensions.CountAsync((IQueryable<T>) queryable));
return shardingCounts.Sum();
}
public async Task<long> LongCountAsync()
{
var shardingCounts = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.LongCountAsync((IQueryable<T>) queryable));
return shardingCounts.Sum();
}
public async Task<List<T>> ToListAsync()
{
return await GetShardingListQueryAsync();
}
public async Task<T> FirstOrDefaultAsync()
{
var result = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.FirstOrDefaultAsync((IQueryable<T>) queryable));
var q = result.Where(o => o != null).AsQueryable();
var extraEntry = _source.GetExtraEntry();
if (extraEntry.Orders.Any())
q = q.OrderWithExpression(extraEntry.Orders);
return q.FirstOrDefault();
}
public async Task<bool> AnyAsync()
{
return (await GetShardingQueryAsync(x => EntityFrameworkQueryableExtensions.AnyAsync((IQueryable<T>) x)))
.Any(o => o);
}
public async Task<T> MaxAsync()
{
var results = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.MaxAsync((IQueryable<T>) queryable));
return results.Max();
}
public async Task<T> MinAsync()
{
var results = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.MinAsync((IQueryable<T>) queryable));
return results.Min();
}
public async Task<int> SumAsync()
{
if (typeof(T) != typeof(int))
throw new InvalidOperationException($"{typeof(T)} cast to int failed");
var results = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.SumAsync((IQueryable<int>) queryable));
return results.Sum();
}
public async Task<long> LongSumAsync()
{
if (typeof(T) != typeof(long))
throw new InvalidOperationException($"{typeof(T)} cast to long failed");
var results = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.SumAsync((IQueryable<long>) queryable));
return results.Sum();
}
public async Task<decimal> DecimalSumAsync()
{
if (typeof(T) != typeof(decimal))
throw new InvalidOperationException($"{typeof(T)} cast to decimal failed");
var results = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.SumAsync((IQueryable<decimal>) queryable));
return results.Sum();
}
public async Task<double> DoubleSumAsync()
{
if (typeof(T) != typeof(double))
throw new InvalidOperationException($"{typeof(T)} cast to double failed");
var results = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.SumAsync((IQueryable<double>) queryable));
return results.Sum();
}
public async Task<float> FloatSumAsync()
{
if (typeof(T) != typeof(float))
throw new InvalidOperationException($"{typeof(T)} cast to double failed");
var results = await GetShardingQueryAsync(async queryable => await EntityFrameworkQueryableExtensions.SumAsync((IQueryable<float>) queryable));
return results.Sum();
}
private void BeginShardingQuery(ShardingScope scope)
{
var context = ShardingContext.Create();
foreach (var route in _endRoutes)
{
context.TryAddShardingTable(route.Key, route.Value);
}
scope.ShardingAccessor.ShardingContext = context;
}
private void GetQueryableRoutes()
{
//先添加手动路由到当前上下文,之后将不再手动路由里面的自动路由添加到当前上下文
foreach (var kv in _routes)
{
var virtualTable = _virtualTableManager.GetVirtualTable(kv.Key);
if (!_endRoutes.ContainsKey(virtualTable))
{
var physicTables = virtualTable.RouteTo(new RouteConfig(null, null, null, kv.Value));
_endRoutes.Add(virtualTable, physicTables.Select(o => o.Tail).ToList());
}
}
if (_autoParseRoute)
{
var shardingEntities = _source.ParseQueryableRoute();
var autoRoutes = shardingEntities.Where(o => !_routes.ContainsKey(o)).ToList();
foreach (var shardingEntity in autoRoutes)
{
var virtualTable = _virtualTableManager.GetVirtualTable(shardingEntity);
if (!_endRoutes.ContainsKey(virtualTable))
{
//路由获取本次操作物理表
var physicTables = virtualTable.RouteTo(new RouteConfig(_source));
_endRoutes.Add(virtualTable, physicTables.Select(o => o.Tail).ToList());
}
}
}
}
private async Task<List<TResult>> GetShardingQueryAsync<TResult>(Func<IQueryable, Task<TResult>> efQuery)
{
GetQueryableRoutes();
//本次查询仅一张表是对应多个数据源的情况
if (_endRoutes.Values.Count(o => o.Count > 1) == 1)
{
return await GetShardingMultiQueryAsync(efQuery);
}
else
{
#if DEBUG
if (_endRoutes.Values.Count(o => o.Count > 1) > 1)
{
Console.WriteLine("存在性能可能有问题");
}
#endif
var result= await GetShardingSingleQueryAsync(efQuery);
return result;
}
}
private async Task<List<TResult>> GetShardingSingleQueryAsync<TResult>(Func<IQueryable, Task<TResult>> efQuery)
{
using (var scope = _shardingScopeFactory.CreateScope())
{
BeginShardingQuery(scope);
return new List<TResult>() {await efQuery(_source)};
}
}
private async Task<List<TResult>> GetShardingMultiQueryAsync<TResult>(Func<IQueryable, Task<TResult>> efQuery)
{
var extraEntry = _source.GetExtraEntry();
//去除分页,获取前Take+Skip数量
int? take = extraEntry.Take;
int skip = extraEntry.Skip.GetValueOrDefault();
var noPageSource = _source.RemoveTake().RemoveSkip();
if (take.HasValue)
noPageSource = noPageSource.Take(take.Value + skip);
//从各个分表获取数据
var multiRouteEntry = _endRoutes.FirstOrDefault(o => o.Value.Count() > 1);
var tasks = multiRouteEntry.Value.Select(tail =>
{
return Task.Run(async () =>
{
using (var shardingDbContext = _shardingParallelDbContextFactory.Create(string.Empty))
{
var newQ = (IQueryable<T>) noPageSource.ReplaceDbContextQueryable(shardingDbContext);
var shardingQueryable = new ShardingQueryable<T>(newQ);
shardingQueryable.DisableAutoRouteParse();
shardingQueryable.AddManualRoute(multiRouteEntry.Key, tail);
foreach (var singleRouteEntry in _endRoutes.Where(o => o.Key != multiRouteEntry.Key))
{
shardingQueryable.AddManualRoute(singleRouteEntry.Key, singleRouteEntry.Value[0]);
}
return await shardingQueryable.GetShardingQueryAsync(efQuery);
}
});
}).ToArray();
var all = (await Task.WhenAll(tasks)).SelectMany(o => o).ToList();
//合并数据
var resList = all;
if (extraEntry.Orders.Any())
resList = resList.AsQueryable().OrderWithExpression(extraEntry.Orders).ToList();
if (skip > 0)
resList = resList.Skip(skip).ToList();
if (take.HasValue)
resList = resList.Take(take.Value).ToList();
return resList;
}
#region list
private async Task<List<T>> GetShardingListQueryAsync()
{
GetQueryableRoutes();
//本次查询仅一张表是对应多个数据源的情况
if (_endRoutes.Values.Count(o => o.Count > 1) == 1)
{
return await GetShardingMultiListQueryAsync();
}
else
{
#if DEBUG
if (_endRoutes.Values.Count(o => o.Count > 1) > 1)
{
Console.WriteLine("存在性能可能有问题");
}
#endif
return await GetShardingSingleListQueryAsync();
}
}
private async Task<IAsyncEnumerator<T>> GetAsyncEnumerator()
{
using (var scope = _shardingScopeFactory.CreateScope())
{
BeginShardingQuery(scope);
#if !EFCORE2
var enumator = _source.AsAsyncEnumerable().GetAsyncEnumerator();
await enumator.MoveNextAsync();
#endif
#if EFCORE2
var enumator = _source.AsAsyncEnumerable().GetEnumerator();
await enumator.MoveNext();
#endif
return enumator;
}
}
private async Task<List<T>> GetShardingSingleListQueryAsync()
{
using (var scope = _shardingScopeFactory.CreateScope())
{
BeginShardingQuery(scope);
return await _source.ToListAsync();
}
}
private async Task<List<T>> GetShardingMultiListQueryAsync()
{
var extraEntry = _source.GetExtraEntry();
//去除分页,获取前Take+Skip数量
int? take = extraEntry.Take;
int skip = extraEntry.Skip.GetValueOrDefault();
var noPageSource = _source.RemoveTake().RemoveSkip();
if (take.HasValue)
noPageSource = noPageSource.Take(take.Value + skip);
//从各个分表获取数据
var multiRouteEntry = _endRoutes.FirstOrDefault(o => o.Value.Count() > 1);
List<DbContext> parallelDbContexts = new List<DbContext>(multiRouteEntry.Value.Count);
var enumatorTasks= multiRouteEntry.Value.Select(tail =>
{
return Task.Run(async () =>
{
var shardingDbContext = _shardingParallelDbContextFactory.Create(string.Empty);
parallelDbContexts.Add(shardingDbContext);
var newQ = (IQueryable<T>) noPageSource.ReplaceDbContextQueryable(shardingDbContext);
var shardingQueryable = new ShardingQueryable<T>(newQ);
shardingQueryable.DisableAutoRouteParse();
shardingQueryable.AddManualRoute(multiRouteEntry.Key, tail);
foreach (var singleRouteEntry in _endRoutes.Where(o => o.Key != multiRouteEntry.Key))
{
shardingQueryable.AddManualRoute(singleRouteEntry.Key, singleRouteEntry.Value[0]);
}
#if !EFCORE2
return await shardingQueryable.GetAsyncEnumerator();
#endif
#if EFCORE2
return await shardingQueryable.GetAsyncEnumerator();
#endif
});
}).ToArray();
var enumators = (await Task.WhenAll(enumatorTasks)).ToList();
var engine=new StreamMergeListEngine<T>(new StreamMergeContext(extraEntry.Skip,extraEntry.Take,extraEntry.Orders), enumators);
var result= await engine.Execute();
parallelDbContexts.ForEach(o=>o.Dispose());
return result;
}
#endregion
// private async Task<TResult> GetShardingMultiQueryAsync<TResult>(Func<IQueryable, Task<TResult>> efQuery)
// {
// //去除分页,获取前Take+Skip数量
// int? take = _source.GetTakeCount();
// int skip = _source.GetSkipCount();
// var (sortColumn, sortType) = _source.GetOrderBy();
//
// var noPageSource = _source.RemoveTake().RemoveSkip();
// if (take.HasValue)
// noPageSource = noPageSource.Take(take.Value + skip);
//
// //从各个分表获取数据
// var multiRouteEntry = _endRoutes.FirstOrDefault(o => o.Value.Count() > 1);
// var tasks = multiRouteEntry.Value.Select(tail =>
// {
// return Task.Run(async () =>
// {
// using (var shardingDbContext = _shardingParallelDbContextFactory.Create(string.Empty))
// {
// var newQ = (IQueryable<T>) noPageSource.ReplaceDbContextQueryable(shardingDbContext);
// var shardingQueryable = new ShardingQueryable<T>(newQ);
// shardingQueryable.AddManualRoute(multiRouteEntry.Key, tail);
// foreach (var singleRouteEntry in _endRoutes.Where(o => o.Key != multiRouteEntry.Key))
// {
// shardingQueryable.AddManualRoute(singleRouteEntry.Key, singleRouteEntry.Value[0]);
// }
//
// return await shardingQueryable.GetShardingQueryAsync(efQuery);
// }
// });
// }).ToArray();
// var all=(await Task.WhenAll(tasks)).ToList().SelectMany(o=>o).ToList();
//
// //合并数据
// var resList = all;
// if (!sortColumn.IsNullOrEmpty() && !sortType.IsNullOrEmpty())
// resList = resList.AsQueryable().OrderBy($"{sortColumn} {sortType}").ToList();
// if (skip > 0)
// resList = resList.Skip(skip).ToList();
// if (take.HasValue)
// resList = resList.Take(take.Value).ToList();
//
// return resList;
// }
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
namespace ShardingCore.Core.VirtualRoutes.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 19 December 2020 16:00:15
* @Email: 326308290@qq.com
*/
public abstract class AbstractShardingKeyObjectEqualVirtualRoute<T,TKey>: AbstractShardingOperatorVirtualRoute<T,TKey> where T : class, IShardingEntity
{
private readonly ILogger<AbstractShardingKeyObjectEqualVirtualRoute<T, TKey>> _logger;
protected AbstractShardingKeyObjectEqualVirtualRoute(ILogger<AbstractShardingKeyObjectEqualVirtualRoute<T,TKey>> _logger)
{
this._logger = _logger;
}
/// <summary>
/// 如何路由到具体表 shardingKeyValue:分表的值,operate where的操作值 返回结果:如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表
/// </summary>
/// <param name="shardingKey">分表的值</param>
/// <param name="shardingOperator">分表的类型</param>
/// <returns>如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表</returns>
protected override Expression<Func<string, bool>> GetRouteToFilter(TKey shardingKey, ShardingOperatorEnum shardingOperator)
{
if (shardingOperator == ShardingOperatorEnum.Equal)
return GetRouteEqualToFilter(shardingKey);
_logger.LogWarning($"没有找到对应的匹配需要进行多表扫描:ShardingOperator:[{shardingOperator}] ");
return s => true;
}
/// <summary>
/// 如何路由到具体表 shardingKeyValue:分表的值, 返回结果:如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表
/// </summary>
/// <param name="shardingKey">分表的值</param>
/// <returns>如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表</returns>
protected abstract Expression<Func<string, bool>> GetRouteEqualToFilter(TKey shardingKey);
}
}

View File

@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.Core.VirtualRoutes.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 19 December 2020 19:55:24
* @Email: 326308290@qq.com
*/
public abstract class AbstractShardingOperatorVirtualRoute<T, TKey> : AbstractVirtualRoute<T, TKey> where T : class, IShardingEntity
{
protected override List<IPhysicTable> DoRouteWithWhere(List<IPhysicTable> allPhysicTables, IQueryable queryable)
{
//获取所有需要路由的表后缀
var filter = ShardingKeyUtil.GetRouteObjectOperatorFilter(queryable, ShardingKeyUtil.Parse(typeof(T)), ConvertToShardingKey, GetRouteToFilter);
var physicTables = allPhysicTables.Where(o => filter(o.Tail)).ToList();
return physicTables;
}
/// <summary>
/// 如何路由到具体表 shardingKeyValue:分表的值, 返回结果:如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表
/// </summary>
/// <param name="shardingKey">分表的值</param>
/// <param name="shardingOperator">操作</param>
/// <returns>如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表</returns>
protected abstract Expression<Func<string, bool>> GetRouteToFilter(TKey shardingKey, ShardingOperatorEnum shardingOperator);
public override IPhysicTable RouteWithValue(List<IPhysicTable> allPhysicTables, object shardingKey)
{
var filter = GetRouteToFilter(ConvertToShardingKey(shardingKey), ShardingOperatorEnum.Equal).Compile();
var physicTables = allPhysicTables.Where(o => filter(o.Tail)).ToList();
if (physicTables.IsEmpty())
{
var routeConfig = ShardingKeyUtil.Parse(typeof(T));
throw new ShardingKeyRouteNotMatchException($"{routeConfig.ShardingEntityType} -> [{routeConfig.ShardingField}] ->【{shardingKey}】");
}
if (physicTables.Count > 1)
throw new ShardingKeyRouteMoreException($"table:{string.Join(",", physicTables.Select(o => $"[{o.FullName}]"))}");
return physicTables[0];
}
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ShardingCore.Core.PhysicTables;
namespace ShardingCore.Core.VirtualRoutes.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 14:33:01
* @Email: 326308290@qq.com
*/
public abstract class AbstractVirtualRoute<T, TKey> : IVirtualRoute<T> where T : class, IShardingEntity
{
public Type ShardingEntityType => typeof(T);
protected abstract TKey ConvertToShardingKey(object shardingKey);
public abstract string ShardingKeyToTail(object shardingKey);
/// <summary>
/// 对外路由方法
/// </summary>
/// <param name="allPhysicTables"></param>
/// <param name="queryable"></param>
/// <returns></returns>
public List<IPhysicTable> RouteWithWhere(List<IPhysicTable> allPhysicTables, IQueryable queryable)
{
return AfterPhysicTableFilter(allPhysicTables,DoRouteWithWhere(allPhysicTables,queryable));
}
/// <summary>
/// 实际路由
/// </summary>
/// <param name="allPhysicTables"></param>
/// <param name="queryable"></param>
/// <returns></returns>
protected abstract List<IPhysicTable> DoRouteWithWhere(List<IPhysicTable> allPhysicTables, IQueryable queryable);
/// <summary>
/// 物理表过滤后
/// </summary>
/// <param name="allPhysicTables">所有的物理表</param>
/// <param name="filterPhysicTables">过滤后的物理表</param>
/// <returns></returns>
public virtual List<IPhysicTable> AfterPhysicTableFilter(List<IPhysicTable> allPhysicTables,List<IPhysicTable> filterPhysicTables)
{
return filterPhysicTables;
}
public abstract IPhysicTable RouteWithValue(List<IPhysicTable> allPhysicTables, object shardingKeyValue);
/// <summary>
/// 返回数据库现有的尾巴
/// </summary>
/// <returns></returns>
public abstract List<string> GetAllTails();
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using ShardingCore.Extensions;
namespace ShardingCore.Core.VirtualRoutes.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 19 December 2020 13:47:54
* @Email: 326308290@qq.com
*/
public abstract class SimpleShardingDateByDayVirtualRoute<T>:AbstractShardingOperatorVirtualRoute<T,long> where T:class,IShardingEntity
{
private readonly ILogger<SimpleShardingDateByDayVirtualRoute<T>> _logger;
protected SimpleShardingDateByDayVirtualRoute(ILogger<SimpleShardingDateByDayVirtualRoute<T>> logger)
{
_logger = logger;
}
protected override long ConvertToShardingKey(object shardingKey)
{
return (long) shardingKey;
}
public override string ShardingKeyToTail(object shardingKey)
{
return ConvertToShardingKey(shardingKey).ConvertLongToTime().ToString("yyyyMMdd");
}
protected override Expression<Func<string, bool>> GetRouteToFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
{
switch (shardingOperator)
{
case ShardingOperatorEnum.UnKnown:
_logger.LogWarning($"没有找到对应的匹配需要进行多表扫描:ShardingOperator:[{shardingOperator}]");
return tail => true;
//throw new NotSupportedException(xxxx);
break;
case ShardingOperatorEnum.GreaterThan:
return tail =>int.Parse(tail) > int.Parse(shardingKey.ConvertLongToTime().ToString("yyyyMMdd"));
case ShardingOperatorEnum.GreaterThanOrEqual:
//yyyyMMdd
return tail =>int.Parse(tail) >= int.Parse(shardingKey.ConvertLongToTime().ToString("yyyyMMdd"));
break;
case ShardingOperatorEnum.LessThan:
return tail =>int.Parse(tail) < int.Parse(shardingKey.ConvertLongToTime().ToString("yyyyMMdd"));
case ShardingOperatorEnum.LessThanOrEqual:
//yyyyMMdd
return tail =>int.Parse(tail) <= int.Parse(shardingKey.ConvertLongToTime().ToString("yyyyMMdd"));
break;
case ShardingOperatorEnum.Equal:
//yyyyMMdd
return tail =>int.Parse(tail) == int.Parse(shardingKey.ConvertLongToTime().ToString("yyyyMMdd"));
break;
case ShardingOperatorEnum.NotEqual:
//yyyyMMdd
return tail =>int.Parse(tail) != int.Parse(shardingKey.ConvertLongToTime().ToString("yyyyMMdd"));
break;
default:
throw new ArgumentOutOfRangeException(nameof(shardingOperator), shardingOperator, null);
}
}
}
}

View File

@ -0,0 +1,71 @@
using System;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using ShardingCore.Extensions;
namespace ShardingCore.Core.VirtualRoutes.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Sunday, 20 December 2020 20:46:36
* @Email: 326308290@qq.com
*/
public abstract class SimpleShardingDateByWeekVirtualRoute<T>:AbstractShardingOperatorVirtualRoute<T,long> where T:class,IShardingEntity
{
private readonly ILogger<SimpleShardingDateByWeekVirtualRoute<T>> _logger;
public SimpleShardingDateByWeekVirtualRoute(ILogger<SimpleShardingDateByWeekVirtualRoute<T>> logger)
{
_logger = logger;
}
protected override long ConvertToShardingKey(object shardingKey)
{
return (long) shardingKey;
}
public override string ShardingKeyToTail(object shardingKey)
{
return GetWeekTableTail(ConvertToShardingKey(shardingKey));
}
private string GetWeekTableTail(long dateTimeL)
{
var dateTime = dateTimeL.ConvertLongToTime();
var monday = dateTime.GetMonday();
var sunday = monday.AddDays(6);
return $"{monday:yyyyMM}{monday:dd}_{sunday:dd}";
}
protected override Expression<Func<string, bool>> GetRouteToFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
{
switch (shardingOperator)
{
case ShardingOperatorEnum.UnKnown:
_logger.LogWarning($"没有找到对应的匹配需要进行多表扫描:ShardingOperator:[{shardingOperator}]");
return tail => true;
//throw new NotSupportedException(xxxx);
break;
case ShardingOperatorEnum.GreaterThan:
case ShardingOperatorEnum.GreaterThanOrEqual:
//yyyyMMdd
return tail =>String.Compare(tail, GetWeekTableTail(shardingKey), StringComparison.Ordinal) >= 0;
break;
case ShardingOperatorEnum.LessThan:
case ShardingOperatorEnum.LessThanOrEqual:
//yyyyMMdd
return tail =>String.Compare(tail, GetWeekTableTail(shardingKey), StringComparison.Ordinal) <= 0;
break;
case ShardingOperatorEnum.Equal:
//yyyyMMdd
return tail =>tail == GetWeekTableTail(shardingKey);
break;
case ShardingOperatorEnum.NotEqual:
//yyyyMMdd
return tail =>tail != GetWeekTableTail(shardingKey);
break;
default:
throw new ArgumentOutOfRangeException(nameof(shardingOperator), shardingOperator, null);
}
}
}
}

View File

@ -0,0 +1,40 @@
using System;
using System.Linq.Expressions;
using System.Text;
using Microsoft.Extensions.Logging;
namespace ShardingCore.Core.VirtualRoutes.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 19 December 2020 13:52:32
* @Email: 326308290@qq.com
*/
public abstract class SimpleShardingKeyStringModVirtualRoute<T>:AbstractShardingKeyObjectEqualVirtualRoute<T,string> where T:class,IShardingEntity
{
private readonly int _mod;
protected SimpleShardingKeyStringModVirtualRoute(int mod,ILogger<AbstractShardingKeyObjectEqualVirtualRoute<T, string>> _logger) : base(_logger)
{
_mod = mod;
}
protected override Expression<Func<string, bool>> GetRouteEqualToFilter(string shardingKey)
{
var modKey = ShardingKeyToTail(shardingKey);
return s => s == modKey;
}
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey.ToString();
}
public override string ShardingKeyToTail(object shardingKey)
{
var shardingKeyStr = ConvertToShardingKey(shardingKey);
var bytes = Encoding.Default.GetBytes(shardingKeyStr);
return Math.Abs(BitConverter.ToInt32(bytes,0) % _mod).ToString();
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ShardingCore.Core.PhysicTables;
namespace ShardingCore.Core.VirtualRoutes
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 13:59:36
* @Email: 326308290@qq.com
*/
/// <summary>
///
/// </summary>
public interface IVirtualRoute
{
Type ShardingEntityType { get; }
string ShardingKeyToTail(object shardingKey);
/// <summary>
/// 根据查询条件路由返回物理表
/// </summary>
/// <param name="allPhysicTables"></param>
/// <param name="queryable"></param>
/// <returns></returns>
List<IPhysicTable> RouteWithWhere(List<IPhysicTable> allPhysicTables,IQueryable queryable);
/// <summary>
/// 根据值进行路由
/// </summary>
/// <param name="allPhysicTables"></param>
/// <param name="shardingKeyValue"></param>
/// <returns></returns>
IPhysicTable RouteWithValue(List<IPhysicTable> allPhysicTables, object shardingKeyValue);
/// <summary>
/// 获取所有的目前数据库存在的尾巴
/// get all tails in the db
/// </summary>
/// <returns></returns>
List<string> GetAllTails();
}
public interface IVirtualRoute<T> : IVirtualRoute where T : class, IShardingEntity
{
}
}

View File

@ -0,0 +1,67 @@
using System.Linq;
using System.Linq.Expressions;
namespace ShardingCore.Core.VirtualRoutes
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 14:15:02
* @Email: 326308290@qq.com
*/
public class RouteConfig
{
private readonly IQueryable _queryable;
private readonly IShardingEntity _shardingEntity;
private readonly object _shardingKeyValue;
private readonly Expression _predicate;
public RouteConfig(IQueryable queryable=null,IShardingEntity shardingEntity=null,object shardingKeyValue=null,Expression predicate=null)
{
_queryable = queryable;
_shardingEntity = shardingEntity;
_shardingKeyValue = shardingKeyValue;
_predicate = predicate;
}
public IQueryable GetQueryable()
{
return _queryable;
}
public object GetShardingKeyValue()
{
return _shardingKeyValue;
}
public IShardingEntity GetShardingEntity()
{
return _shardingEntity;
}
public Expression GetPredicate()
{
return _predicate;
}
public bool UseQueryable()
{
return _queryable != null;
}
public bool UseValue()
{
return _shardingKeyValue != null;
}
public bool UseEntity()
{
return _shardingEntity != null;
}
public bool UsePredicate()
{
return _predicate != null;
}
}
}

View File

@ -0,0 +1,30 @@
using System.ComponentModel;
namespace ShardingCore.Core.VirtualRoutes
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 19 December 2020 19:56:57
* @Email: 326308290@qq.com
*/
public enum ShardingOperatorEnum
{
[Description("??")]
UnKnown,
[Description(">")]
GreaterThan,
[Description(">=")]
GreaterThanOrEqual,
[Description("<")]
LessThan,
[Description("<=")]
LessThanOrEqual,
[Description("==")]
Equal,
[Description("!=")]
NotEqual,
// [Description("Contains")]
// BeContains
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Core.VirtualRoutes;
namespace ShardingCore.Core.VirtualTables
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 14:06:31
* @Email: 326308290@qq.com
*/
public interface IVirtualTable
{
/// <summary>
/// 分表的类型
/// </summary>
Type EntityType { get; }
/// <summary>
/// 分表配置
/// </summary>
ShardingEntityConfig ShardingConfig { get; }
/// <summary>
/// 获取所有的物理表
/// </summary>
/// <returns></returns>
List<IPhysicTable> GetAllPhysicTables();
/// <summary>
/// 路由到具体的物理表 which physic table route
/// </summary>
/// <param name="routeConfig"></param>
/// <returns></returns>
List<IPhysicTable> RouteTo(RouteConfig routeConfig);
/// <summary>
/// 添加物理表 add physic table
/// </summary>
/// <param name="physicTable"></param>
void AddPhysicTable(IPhysicTable physicTable);
/// <summary>
/// 设置原始表名 get original table name when app start
/// <see cref="ShardingBootstrapper"/>
/// </summary>
/// <param name="originalTableName"></param>
void SetOriginalTableName(string originalTableName);
/// <summary>
/// 获取原始表名 get original table name
/// </summary>
/// <returns></returns>
string GetOriginalTableName();
/// <summary>
/// 获取当前虚拟表的路由 get this virtual table route
/// </summary>
/// <returns></returns>
IVirtualRoute GetVirtualRoute();
/// <summary>
/// 获取启动时已经存在的表后缀 get this virtual table exists tails when app start
/// <see cref="ShardingBootstrapper"/> CreateDateTables
/// </summary>
/// <returns></returns>
List<string> GetTaleAllTails();
}
public interface IVirtualTable<T> : IVirtualTable where T : class, IShardingEntity
{
new IVirtualRoute<T> GetVirtualRoute();
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using ShardingCore.Core.PhysicTables;
namespace ShardingCore.Core.VirtualTables
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 14:10:03
* @Email: 326308290@qq.com
*/
/// <summary>
/// 虚拟表管理者 virtual table manager
/// </summary>
public interface IVirtualTableManager
{
/// <summary>
/// 获取所有的虚拟表 get all virtual table
/// </summary>
/// <returns></returns>
List<IVirtualTable> GetAllVirtualTables();
/// <summary>
/// 获取虚拟表 get virtual table by sharding entity type
/// </summary>
/// <param name="shardingEntityType"></param>
/// <returns></returns>
IVirtualTable GetVirtualTable(Type shardingEntityType);
/// <summary>
/// 获取虚拟表 get virtual table by original table name
/// </summary>
/// <param name="originalTableName"></param>
/// <returns></returns>
IVirtualTable GetVirtualTable(string originalTableName);
/// <summary>
/// 添加虚拟表应用启动时 add virtual table when app start
/// </summary>
/// <param name="virtualTable"></param>
void AddVirtualTable(IVirtualTable virtualTable);
/// <summary>
/// 添加物理表 add physic table
/// </summary>
/// <param name="virtualTable"></param>
/// <param name="physicTable"></param>
void AddPhysicTable(IVirtualTable virtualTable, IPhysicTable physicTable);
/// <summary>
/// 添加物理表 add physic table
/// </summary>
/// <param name="shardingEntityType"></param>
/// <param name="physicTable"></param>
void AddPhysicTable(Type shardingEntityType, IPhysicTable physicTable);
/// <summary>
/// 判断是否是分表字段
/// </summary>
/// <param name="shardingEntityType"></param>
/// <param name="shardingField"></param>
/// <returns></returns>
bool IsShardingKey(Type shardingEntityType, string shardingField);
}
}

View File

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.Core.VirtualTables
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 14:20:12
* @Email: 326308290@qq.com
*/
/// <summary>
/// 同数据库虚拟表
/// </summary>
/// <typeparam name="T"></typeparam>
public class OneDbVirtualTable<T> : IVirtualTable<T> where T : class, IShardingEntity
{
private readonly IVirtualRoute<T> _virtualRoute;
public Type EntityType => typeof(T);
public ShardingEntityConfig ShardingConfig { get; }
private readonly List<IPhysicTable> _physicTables = new List<IPhysicTable>();
public OneDbVirtualTable(IServiceProvider serviceProvider)
{
_virtualRoute = serviceProvider.GetService<IVirtualRoute<T>>() ?? throw new ShardingOwnerNotFoundException($"{EntityType}");
ShardingConfig = ShardingKeyUtil.Parse(EntityType);
}
public List<IPhysicTable> GetAllPhysicTables()
{
return _physicTables;
}
public List<IPhysicTable> RouteTo(RouteConfig routeConfig)
{
var route = _virtualRoute;
if (routeConfig.UseQueryable())
return route.RouteWithWhere(_physicTables, routeConfig.GetQueryable());
if (routeConfig.UsePredicate())
return route.RouteWithWhere(_physicTables, new EnumerableQuery<T>((Expression<Func<T, bool>>) routeConfig.GetPredicate()));
object shardingKeyValue = null;
if (routeConfig.UseValue())
shardingKeyValue = routeConfig.GetShardingKeyValue();
if (routeConfig.UseEntity())
shardingKeyValue = routeConfig.GetShardingEntity().GetPropertyValue(ShardingConfig.ShardingField);
if (shardingKeyValue != null)
{
var routeWithValue = route.RouteWithValue(_physicTables, shardingKeyValue);
return new List<IPhysicTable>(1) {routeWithValue};
}
throw new NotImplementedException(nameof(routeConfig));
}
public void AddPhysicTable(IPhysicTable physicTable)
{
if (_physicTables.All(o => o.Tail != physicTable.Tail))
_physicTables.Add(physicTable);
}
public void SetOriginalTableName(string originalTableName)
{
ShardingConfig.ShardingOriginalTable = originalTableName;
}
public string GetOriginalTableName()
{
return ShardingConfig.ShardingOriginalTable;
}
IVirtualRoute IVirtualTable.GetVirtualRoute()
{
return GetVirtualRoute();
}
public List<string> GetTaleAllTails()
{
return _virtualRoute.GetAllTails();
}
public IVirtualRoute<T> GetVirtualRoute()
{
return _virtualRoute;
}
}
}

View File

@ -0,0 +1,103 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Exceptions;
using ShardingCore.Helpers;
namespace ShardingCore.Core.VirtualTables
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 14:52:42
* @Email: 326308290@qq.com
*/
/// <summary>
/// 同一个数据库下的虚拟表管理者
/// </summary>
public class OneDbVirtualTableManager : IVirtualTableManager
{
private readonly ConcurrentDictionary<Type, IVirtualTable> _virtualTables = new ConcurrentDictionary<Type, IVirtualTable>();
public OneDbVirtualTableManager(IServiceProvider serviceProvider)
{
var shardingEntities = AssemblyHelper.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes())
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => !type.IsAbstract&&type.GetInterfaces()
.Any(it => it.IsInterface &&typeof(IShardingEntity)==it)
);
foreach (var shardingEntity in shardingEntities)
{
Type genericType = typeof(IVirtualTable<>);
Type interfaceType = genericType.MakeGenericType(shardingEntity);
var virtualTable = (IVirtualTable)serviceProvider.GetService(interfaceType);
_virtualTables.TryAdd(virtualTable.EntityType, virtualTable);
}
}
public List<IVirtualTable> GetAllVirtualTables()
{
return _virtualTables.Select(o=>o.Value).ToList();
}
/// <summary>
/// 获取指定类型的虚拟表
/// </summary>
/// <param name="shardingEntityType"></param>
/// <returns></returns>
/// <exception cref="VirtualTableNotFoundException"></exception>
public IVirtualTable GetVirtualTable(Type shardingEntityType)
{
if (!_virtualTables.TryGetValue(shardingEntityType, out var virtualTable) || virtualTable == null)
throw new VirtualTableNotFoundException($"{shardingEntityType}");
return virtualTable;
}
public IVirtualTable GetVirtualTable(string originalTableName)
{
return _virtualTables.Values.FirstOrDefault(o => o.GetOriginalTableName() == originalTableName) ?? throw new CreateSqlVirtualTableNotFoundException(originalTableName);
}
/// <summary>
/// 添加虚拟表
/// </summary>
/// <param name="virtualTable"></param>
public void AddVirtualTable(IVirtualTable virtualTable)
{
_virtualTables.TryAdd(virtualTable.EntityType, virtualTable);
}
/// <summary>
/// 添加物理表
/// </summary>
/// <param name="virtualTable"></param>
/// <param name="physicTable"></param>
public void AddPhysicTable(IVirtualTable virtualTable, IPhysicTable physicTable)
{
AddPhysicTable(virtualTable.EntityType, physicTable);
}
/// <summary>
/// 添加物理表
/// </summary>
/// <param name="shardingEntityType"></param>
/// <param name="physicTable"></param>
public void AddPhysicTable(Type shardingEntityType, IPhysicTable physicTable)
{
var virtualTable = GetVirtualTable(shardingEntityType);
virtualTable.AddPhysicTable(physicTable);
}
/// <summary>
/// 是否是分表字段
/// </summary>
/// <param name="shardingEntityType"></param>
/// <param name="shardingField"></param>
/// <returns></returns>
public bool IsShardingKey(Type shardingEntityType, string shardingField)
{
return _virtualTables.TryGetValue(shardingEntityType, out var virtualTable) && virtualTable.ShardingConfig.ShardingField == shardingField;
}
}
}

View File

@ -0,0 +1,41 @@
using System;
namespace ShardingCore.Core.VirtualTables
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 16 December 2020 13:24:05
* @Email: 326308290@qq.com
*/
/// <summary>
/// 分表配置 sharding config
/// </summary>
public class ShardingEntityConfig
{
/// <summary>
/// 分表类型 sharding entity type
/// </summary>
public Type ShardingEntityType { get; set; }
/// <summary>
/// 分表字段 sharding field
/// </summary>
public string ShardingField { get; set; }
/// <summary>
/// 分表的原表名 original table name in db exclude tail
/// </summary>
public string ShardingOriginalTable { get; set; }
/// <summary>
/// 启动时是否建表 auto create table when start app
/// </summary>
public bool AutoCreateTable { get; set; }
/// <summary>
/// 分表尾巴后缀 table sharding tail prefix
/// </summary>
public string TailPrefix { get; set; } = "_";
}
}

View File

@ -0,0 +1,15 @@
using ShardingCore.DbContexts.ShardingDbContexts;
namespace ShardingCore.DbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 24 December 2020 08:22:23
* @Email: 326308290@qq.com
*/
public interface IShardingDbContextFactory
{
ShardingDbContext Create(ShardingDbContextOptions shardingDbContextOptions);
}
}

View File

@ -0,0 +1,15 @@
using ShardingCore.DbContexts.ShardingDbContexts;
namespace ShardingCore.DbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 29 December 2020 15:22:06
* @Email: 326308290@qq.com
*/
public interface IShardingParallelDbContextFactory
{
ShardingDbContext Create(string tail);
}
}

View File

@ -0,0 +1,18 @@
using ShardingCore.DbContexts.ShardingDbContexts;
namespace ShardingCore.DbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 24 December 2020 08:22:48
* @Email: 326308290@qq.com
*/
public class ShardingDbContextFactory:IShardingDbContextFactory
{
public ShardingDbContext Create(ShardingDbContextOptions shardingDbContextOptions)
{
return new ShardingDbContext(shardingDbContextOptions);
}
}
}

View File

@ -0,0 +1,147 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Extensions;
using ShardingCore.Helpers;
namespace ShardingCore.DbContexts.ShardingDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 16 December 2020 15:28:12
* @Email: 326308290@qq.com
*/
public class ShardingDbContext : DbContext
{
public string Tail { get; }
public List<VirtualTableDbContextConfig> VirtualTableConfigs { get; }
public bool RemoveRemoveShardingEntity { get; }
private static readonly ConcurrentDictionary<Type, Type> _entityTypeConfigurationTypeCaches = new ConcurrentDictionary<Type, Type>();
private static readonly object buildEntityTypeConfigurationLock = new object();
public ShardingDbContext(ShardingDbContextOptions shardingDbContextOptions) : base(shardingDbContextOptions.DbContextOptions)
{
Tail = shardingDbContextOptions.Tail;
VirtualTableConfigs = shardingDbContextOptions.VirtualTableDbContextConfigs;
RemoveRemoveShardingEntity = shardingDbContextOptions.RemoveShardingEntity;
}
/// <summary>
/// 模型构建
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
if (!string.IsNullOrWhiteSpace(Tail))
{
//支持IEntityTypeConfiguration配置
VirtualTableConfigs.ForEach(virtualTable =>
{
var shardingEntityType = virtualTable.ShardingEntityType;
if (!_entityTypeConfigurationTypeCaches.TryGetValue(shardingEntityType, out var entityTypeConfigurationType))
throw new Exception($"未找到对应的类型无法进行IEntityTypeConfiguration配置:[{shardingEntityType.Name}]");
if (entityTypeConfigurationType == null)
throw new NotSupportedException($"{shardingEntityType}的[IBaseEntityTypeConfiguration]未找到");
var method = modelBuilder.GetType()
.GetMethods()
.FirstOrDefault(x => x.Name == nameof(ModelBuilder.ApplyConfiguration)
&& x.GetParameters().Count() == 1
&& x.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));
method.MakeGenericMethod(shardingEntityType).Invoke(modelBuilder, new object[] {Activator.CreateInstance(entityTypeConfigurationType)});
});
VirtualTableConfigs.ForEach(virtualTableConfig =>
{
var shardingEntity = virtualTableConfig.ShardingEntityType;
var tailPrefix = virtualTableConfig.TailPrefix;
var entity = modelBuilder.Entity(shardingEntity);
var tableName = virtualTableConfig.OriginalTableName;
if (string.IsNullOrWhiteSpace(tableName))
throw new ArgumentNullException($"{shardingEntity}:无法找到对应的原始表名。");
#if DEBUG
Console.WriteLine($"映射表:[tableName]-->[{tableName}{tailPrefix}{Tail}]");
#endif
entity.ToTable($"{tableName}{tailPrefix}{Tail}");
});
}
else
{
BuildEntityTypeConfigurationCaches();
//支持IEntityTypeConfiguration配置
foreach (var entityTypeConfigurationType in _entityTypeConfigurationTypeCaches)
{
var shardingEntityType = entityTypeConfigurationType.Key;
if (RemoveRemoveShardingEntity && shardingEntityType.IsShardingEntity())
continue;
var method = modelBuilder.GetType()
.GetMethods()
.FirstOrDefault(x => x.Name == nameof(ModelBuilder.ApplyConfiguration)
&& x.GetParameters().Count() == 1
&& x.GetParameters()[0].ParameterType.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>));
method.MakeGenericMethod(shardingEntityType).Invoke(modelBuilder, new object[] {Activator.CreateInstance(entityTypeConfigurationType.Value)});
}
}
////字段注释,需要开启程序集XML文档
//foreach (var entityType in modelBuilder.Model.GetEntityTypes())
//{
// var comments = XmlHelper.GetPropertyCommentBySummary(entityType.ClrType) ?? new Dictionary<string, string>();
// foreach (var property in entityType.GetProperties())
// {
// if (comments.ContainsKey(property.Name))
// {
// property.SetComment(comments[property.Name]);
// }
// }
//}
//
#if !EFCORE2
//字段注释,需要开启程序集XML文档
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var comments = XmlHelper.GetProperyCommentBySummary(entityType.ClrType) ?? new Dictionary<string, string>();
foreach (var property in entityType.GetProperties())
{
if (comments.ContainsKey(property.Name))
{
property.SetComment(comments[property.Name]);
}
}
}
#endif
}
/// <summary>
/// 构建类型
/// </summary>
public void BuildEntityTypeConfigurationCaches()
{
if (!_entityTypeConfigurationTypeCaches.Any())
{
lock (buildEntityTypeConfigurationLock)
{
if (!_entityTypeConfigurationTypeCaches.Any())
{
var typesToRegister = AssemblyHelper.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes())
.Where(type => !String.IsNullOrEmpty(type.Namespace))
//获取类型namespce不是空的所有接口是范型的当前范型是IEntityTypeConfiguration<>的进行fluent api 映射
.Where(type => !type.IsAbstract && type.GetInterfaces()
.Any(it => it.IsInterface && it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)
&& it.GetGenericArguments().Any())
).ToDictionary(o => o.GetInterfaces().FirstOrDefault(it => it.IsInterface && it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)
&& it.GetGenericArguments().Any())
?.GetGenericArguments()[0], o => o);
foreach (var type in typesToRegister)
{
_entityTypeConfigurationTypeCaches.TryAdd(type.Key, type.Value);
}
}
}
}
}
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.ShardingDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 16 December 2020 16:15:43
* @Email: 326308290@qq.com
*/
public class ShardingDbContextOptions
{
public ShardingDbContextOptions(DbContextOptions dbContextOptions, string tail, List<VirtualTableDbContextConfig> virtualTableDbContextConfigs, bool removeShardingEntity=false)
{
DbContextOptions = dbContextOptions;
Tail = tail;
VirtualTableDbContextConfigs = virtualTableDbContextConfigs;
RemoveShardingEntity = removeShardingEntity;
}
public DbContextOptions DbContextOptions { get; }
public string Tail { get; }
public List<VirtualTableDbContextConfig> VirtualTableDbContextConfigs { get; }
public bool RemoveShardingEntity { get;}
}
}

View File

@ -0,0 +1,34 @@
using System;
namespace ShardingCore.DbContexts.ShardingDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 01 January 2021 16:24:57
* @Email: 326308290@qq.com
*/
public class VirtualTableDbContextConfig
{
public VirtualTableDbContextConfig(Type shardingEntityType, string originalTableName, string tailPrefix)
{
ShardingEntityType = shardingEntityType;
OriginalTableName = originalTableName;
TailPrefix = tailPrefix;
}
/// <summary>
/// 分表实体类型
/// </summary>
public Type ShardingEntityType { get; }
/// <summary>
/// 原始表名不带后缀
/// </summary>
public string OriginalTableName { get; }
/// <summary>
/// 表尾巴前缀
/// </summary>
public string TailPrefix { get; }
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
namespace ShardingCore.DbContexts.Transactions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 10:26:24
* @Email: 326308290@qq.com
*/
public interface IShardingTransaction:IDisposable
{
bool IsOpened { get; }
bool IsUsed { get; }
void Use(DbContext dbContext);
void Open();
void Rollback();
Task RollbackAsync();
void Commit();
Task CommitAsync();
IDbContextTransaction GetDbContextTransaction();
}
}

View File

@ -0,0 +1,101 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore.Exceptions;
namespace ShardingCore.DbContexts.Transactions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 10:23:42
* @Email: 326308290@qq.com
*/
public class ShardingTransaction : IShardingTransaction
{
private IDbContextTransaction _dbTransaction;
private bool _isOpened;
public bool IsOpened => _isOpened;
public bool IsUsed => _dbTransaction != null;
private readonly ISet<DbContext> _dbContextUseTransactions = new HashSet<DbContext>();
public void Use(DbContext dbContext)
{
if (!_isOpened)
throw new ShardingTransactionException($"{nameof(ShardingTransaction)} is not open");
if (!_dbContextUseTransactions.Contains(dbContext))
{
if (!IsUsed)
_dbTransaction = dbContext.Database.BeginTransaction();
else
dbContext.Database.UseTransaction(_dbTransaction.GetDbTransaction());
_dbContextUseTransactions.Add(dbContext);
}
}
public void Open()
{
_isOpened = true;
}
private void Close()
{
_isOpened = false;
_dbContextUseTransactions.Clear();
}
public void Rollback()
{
_dbTransaction?.Rollback();
}
#if EFCORE2
public Task RollbackAsync()
{
_dbTransaction?.Rollback();
return Task.CompletedTask;
}
#endif
#if !EFCORE2
public async Task RollbackAsync()
{
if (_dbTransaction != null)
await _dbTransaction.RollbackAsync();
}
#endif
public void Commit()
{
_dbTransaction?.Commit();
}
#if EFCORE2
public Task CommitAsync()
{
_dbTransaction?.Commit();
return Task.CompletedTask;
}
#endif
#if !EFCORE2
public async Task CommitAsync()
{
if (_dbTransaction != null)
await _dbTransaction.CommitAsync();
}
#endif
public IDbContextTransaction GetDbContextTransaction()
{
return _dbTransaction;
}
public void Dispose()
{
Close();
_dbTransaction?.Dispose();
_dbTransaction = null;
}
}
}

View File

@ -0,0 +1,33 @@
using System;
using Microsoft.Extensions.DependencyInjection;
namespace ShardingCore.DbContexts.VirtualDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 01 January 2021 16:35:19
* @Email: 326308290@qq.com
*/
public abstract class AbstractInjectVirtualDbContext
{
public IServiceProvider ServiceProvider { get; }
public AbstractInjectVirtualDbContext(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
protected TService GetService<TService>() => ServiceProvider.GetService<TService>();
protected TService LazyGet<TService>(ref TService reference)
=> LazyTypeGet(typeof(TService), ref reference);
protected TRef LazyTypeGet<TRef>(Type serviceType, ref TRef reference)
{
return reference ??= (TRef)ServiceProvider.GetService(serviceType);
}
protected IDbContextOptionsProvider DbContextOptionsProvider => LazyGet(ref _dbContextOptionsProvider);
private IDbContextOptionsProvider _dbContextOptionsProvider;
}
}

View File

@ -0,0 +1,16 @@
using System;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.VirtualDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 24 December 2020 10:32:07
* @Email: 326308290@qq.com
*/
public interface IDbContextOptionsProvider:IDisposable
{
DbContextOptions GetDbContextOptions();
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using ShardingCore.DbContexts.Transactions;
namespace ShardingCore.DbContexts.VirtualDbContexts
{
/**
* IVirtualDbContext
*
* Authorxuejiaming
* Created: 2020/6/15 11:42:52
**/
public interface IVirtualDbContext:IDisposable
{
IShardingTransaction BeginTransaction();
void Rollback();
Task RollbackAsync();
IQueryable<T> Set<T>() where T : class;
Task<int> InsertAsync<T>(T entity) where T : class;
Task<int> InsertRangeAsync<T>(ICollection<T> entities) where T : class;
Task<int> UpdateAsync<T>(T entity) where T : class;
Task<int> UpdateRangeAsync<T>(ICollection<T> entities) where T : class;
Task<int> DeleteAsync<T>(T entity) where T : class;
Task<int> DeleteRangeAsync<T>(ICollection<T> entities) where T : class;
Task<int> SaveChangesAsync();
int Insert<T>(T entity) where T : class;
int InsertRange<T>(ICollection<T> entities) where T : class;
int Update<T>(T entity) where T : class;
int UpdateRange<T>(ICollection<T> entities) where T : class;
int Delete<T>(T entity) where T : class;
int DeleteRange<T>(ICollection<T> entities) where T : class;
int SaveChanges();
#region
/// <summary>
/// 批量插入直接提交
/// </summary>
/// <param name="entities"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
ShardingBatchInsertEntry<T> BulkInsert<T>(ICollection<T> entities) where T : class;
/// <summary>
/// 批量更新直接提交
/// </summary>
/// <param name="where"></param>
/// <param name="updateExp"></param>
/// <typeparam name="T"></typeparam>
ShardingBatchUpdateEntry<T> BulkUpdate<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> updateExp) where T : class;
/// <summary>
/// 批量删除直接提交
/// </summary>
/// <param name="where"></param>
/// <typeparam name="T"></typeparam>
ShardingBatchDeleteEntry<T> BulkDelete<T>(Expression<Func<T, bool>> where) where T : class;
#endregion
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.VirtualDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 01 January 2021 20:53:07
* @Email: 326308290@qq.com
*/
public class ShardingBatchDeleteEntry<T>where T:class
{
public ShardingBatchDeleteEntry(Expression<Func<T, bool>> @where, List<DbContext> dbContexts)
{
Where = @where;
DbContexts = dbContexts;
}
public Expression<Func<T, bool>> Where{ get; }
public List<DbContext> DbContexts { get; }
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.VirtualDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 01 January 2021 20:28:53
* @Email: 326308290@qq.com
*/
public class ShardingBatchInsertEntry<T> where T:class
{
public ShardingBatchInsertEntry(Dictionary<DbContext, List<T>> batchGroups)
{
BatchGroups = batchGroups;
}
public Dictionary<DbContext,List<T>> BatchGroups { get; }
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.VirtualDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 01 January 2021 20:45:16
* @Email: 326308290@qq.com
*/
public class ShardingBatchUpdateEntry<T> where T:class
{
public ShardingBatchUpdateEntry(Expression<Func<T, bool>> @where, Expression<Func<T, T>> updateExp, List<DbContext> dbContexts)
{
Where = @where;
UpdateExp = updateExp;
DbContexts = dbContexts;
}
public Expression<Func<T, bool>> Where {get;}
public Expression<Func<T, T>> UpdateExp{get;}
public List<DbContext> DbContexts { get; }
}
}

View File

@ -0,0 +1,404 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Core;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.DbContexts.Transactions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
namespace ShardingCore.DbContexts.VirtualDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 17 December 2020 21:50:49
* @Email: 326308290@qq.com
*/
public class VirtualDbContext : AbstractInjectVirtualDbContext, IVirtualDbContext
{
private readonly string EMPTY_SHARDING_TAIL_ID = Guid.NewGuid().ToString("n");
private readonly IServiceProvider _serviceProvider;
private readonly IVirtualTableManager _virtualTableManager;
private readonly IShardingDbContextFactory _shardingDbContextFactory;
private readonly ConcurrentDictionary<string, DbContext> _dbContextCaches = new ConcurrentDictionary<string, DbContext>();
private IShardingTransaction _dbTransaction = new ShardingTransaction();
public VirtualDbContext(IServiceProvider serviceProvider,
IVirtualTableManager virtualTableManager, IShardingDbContextFactory shardingDbContextFactory) : base(serviceProvider)
{
_serviceProvider = serviceProvider;
_virtualTableManager = virtualTableManager;
_shardingDbContextFactory = shardingDbContextFactory;
}
private bool IsOpenTransaction => _dbTransaction.IsOpened;
public IShardingTransaction BeginTransaction()
{
_dbTransaction.Open();
_dbContextCaches.ForEach(kv => { _dbTransaction.Use(kv.Value); });
return _dbTransaction;
}
public void Rollback()
{
_dbTransaction?.Rollback();
}
public async Task RollbackAsync()
{
if (_dbTransaction != null)
await _dbTransaction.RollbackAsync();
}
public IQueryable<T> Set<T>() where T : class
{
return GetOrCreateShardingDbContext(EMPTY_SHARDING_TAIL_ID).Set<T>().AsNoTracking();
}
public async Task<int> InsertAsync<T>(T entity) where T : class
{
await CreateGenericDbContext(entity).Set<T>().AddAsync(entity);
return 1;
}
public async Task<int> InsertRangeAsync<T>(ICollection<T> entities) where T : class
{
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
await group.Key.AddRangeAsync(group.Select(o=>o.Entity));
}
return entities.Count;
}
public Task<int> UpdateAsync<T>(T entity) where T : class
{
CreateGenericDbContext(entity).Set<T>().Update(entity);
return Task.FromResult(1);
}
public Task<int> UpdateRangeAsync<T>(ICollection<T> entities) where T : class
{
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.UpdateRange(group.Select(o=>o.Entity));
}
return Task.FromResult(entities.Count);
}
public Task<int> DeleteAsync<T>(T entity) where T : class
{
CreateGenericDbContext(entity).Set<T>().Remove(entity);
return Task.FromResult(1);
}
public Task<int> DeleteRangeAsync<T>(ICollection<T> entities) where T : class
{
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.RemoveRange(group.Select(o=>o.Entity));
}
return Task.FromResult(entities.Count);
}
public async Task<int> SaveChangesAsync()
{
var transOpenNow = !_dbTransaction.IsOpened&&_dbContextCaches.Count>1;
if (transOpenNow)
{
BeginTransaction();
}
var effects = 0;
foreach (var dbContextCache in _dbContextCaches)
{
effects += await dbContextCache.Value.SaveChangesAsync();
}
if (transOpenNow)
await _dbTransaction.CommitAsync();
return effects;
}
public int Insert<T>(T entity) where T : class
{
CreateGenericDbContext(entity).Set<T>().Add(entity);
return 1;
}
public int InsertRange<T>(ICollection<T> entities) where T : class
{
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.AddRange(group.Select(o=>o.Entity));
}
return entities.Count;
}
public int Update<T>(T entity) where T : class
{
CreateGenericDbContext(entity).Set<T>().Update(entity);
return 1;
}
public int UpdateRange<T>(ICollection<T> entities) where T : class
{
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.UpdateRange(group.Select(o=>o.Entity));
}
return entities.Count;
}
public int Delete<T>(T entity) where T : class
{
CreateGenericDbContext(entity).Set<T>().Remove(entity);
return 1;
}
public int DeleteRange<T>(ICollection<T> entities) where T : class
{
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext);
foreach (var group in groups)
{
group.Key.RemoveRange(group.Select(o=>o.Entity));
}
return entities.Count;
}
public int SaveChanges()
{
var transOpenNow = !_dbTransaction.IsOpened&&_dbContextCaches.Count>1;
if (transOpenNow)
{
BeginTransaction();
}
var effects = 0;
foreach (var dbContextCache in _dbContextCaches)
{
effects += dbContextCache.Value.SaveChanges();
}
if (transOpenNow)
_dbTransaction.Commit();
return effects;
}
public ShardingBatchInsertEntry<T> BulkInsert<T>(ICollection<T> entities) where T : class
{
var groups = entities.Select(o =>
{
var dbContext = CreateGenericDbContext(o);
return new
{
DbContext = dbContext,
Entity = o
};
}).GroupBy(g => g.DbContext)
.ToDictionary(o => (DbContext) o.Key, o => o.Select(item => item.Entity).ToList());
return new ShardingBatchInsertEntry<T>(groups);
}
public ShardingBatchUpdateEntry<T> BulkUpdate<T>(Expression<Func<T, bool>> @where, Expression<Func<T, T>> updateExp) where T : class
{
List<DbContext> dbContexts = null;
if (typeof(T).IsShardingEntity())
{
var shardingDbContexts = CreateShardingDbContext<IShardingEntity>(new EnumerableQuery<T>(where).AsQueryable());
dbContexts = shardingDbContexts.Select(o => (DbContext) o).ToList();
}
else
{
var dbContext = CreateNoShardingDbContext();
dbContexts = new List<DbContext>(1)
{
dbContext
};
}
return new ShardingBatchUpdateEntry<T>(@where,updateExp,dbContexts);
}
public ShardingBatchDeleteEntry<T> BulkDelete<T>(Expression<Func<T, bool>> @where) where T : class
{
List<DbContext> dbContexts = null;
if (typeof(T).IsShardingEntity())
{
var shardingDbContexts = CreateShardingDbContext<IShardingEntity>(new EnumerableQuery<T>(where).AsQueryable());
dbContexts = shardingDbContexts.Select(o => (DbContext) o).ToList();
}
else
{
var dbContext = CreateNoShardingDbContext();
dbContexts = new List<DbContext>(1)
{
dbContext
};
}
return new ShardingBatchDeleteEntry<T>(@where,dbContexts);
}
private ShardingDbContext CreateGenericDbContext<T>(T entity) where T : class
{
if (entity.IsShardingEntity())
{
return CreateShardingDbContext(entity as IShardingEntity);
}
else
{
return CreateNoShardingDbContext();
}
}
private ShardingDbContext CreateNoShardingDbContext()
{
var shardingDbContext = GetOrCreateShardingDbContext(EMPTY_SHARDING_TAIL_ID);
return shardingDbContext;
}
private ShardingDbContext CreateShardingDbContext<T>(T entity) where T : class, IShardingEntity
{
var physicTable = _virtualTableManager.GetVirtualTable(entity.GetType()).RouteTo(new RouteConfig(null, entity, null))[0];
var shardingDbContext = GetOrCreateShardingDbContext(physicTable.Tail);
return shardingDbContext;
}
private List<ShardingDbContext> CreateShardingDbContext<T>(IQueryable queryable) where T : class, IShardingEntity
{
var physicTables = _virtualTableManager.GetVirtualTable(typeof(T)).RouteTo(new RouteConfig(queryable, null, null));
if (physicTables.Any())
{
var shardingDbContexts = new List<ShardingDbContext>(physicTables.Count);
foreach (var physicTable in physicTables)
{
var shardingDbContext = GetOrCreateShardingDbContext(physicTable.Tail);
shardingDbContexts.Add(shardingDbContext);
}
return shardingDbContexts;
}
throw new QueryableRouteNotMatchException($"{typeof(T)} -> {nameof(queryable)}");
}
private ShardingDbContext GetOrCreateShardingDbContext(string tail)
{
if (!_dbContextCaches.TryGetValue(tail, out var shardingDbContext))
{
var virtualTableConfigs = _virtualTableManager.GetAllVirtualTables().GetVirtualTableDbContextConfigs();
shardingDbContext = _shardingDbContextFactory.Create(new ShardingDbContextOptions(DbContextOptionsProvider.GetDbContextOptions(), tail == EMPTY_SHARDING_TAIL_ID ? string.Empty : tail, virtualTableConfigs));
_dbContextCaches.TryAdd(tail, shardingDbContext);
}
if (IsOpenTransaction)
{
_dbTransaction.Use(shardingDbContext);
}
return (ShardingDbContext) shardingDbContext;
}
public void Dispose()
{
try
{
_dbContextCaches.ForEach(o =>
{
try
{
o.Value.Dispose();
}
catch (Exception e)
{
Console.WriteLine(e);
}
});
_dbContextCaches.Clear();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}

View File

@ -0,0 +1,33 @@
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.DbContexts.ShardingDbContexts;
namespace ShardingCore.EFCores
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 16 December 2020 16:13:05
* @Email: 326308290@qq.com
*/
public class ShardingModelCacheKeyFactory : IModelCacheKeyFactory
{
public object Create(DbContext context)
{
if (context is ShardingDbContext shardingDbContext)
{
//当出现尾巴不一样,本次映射的数据库实体数目不一样就需要重建ef model
var tail = shardingDbContext.Tail;
var removeSharding = shardingDbContext.RemoveRemoveShardingEntity;
var allEntities = string.Join(",",shardingDbContext.VirtualTableConfigs.Select(o=>o.ShardingEntityType.FullName).OrderBy(o=>o).ToList());
return $"{context.GetType()}_{allEntities}_{tail}_{removeSharding}";
}
else
{
return context.GetType();
}
}
}
}

View File

@ -0,0 +1,291 @@
#if !EFCORE2
using System.Linq.Expressions;
using System.Threading;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore.Core.ShardingAccessors;
namespace ShardingCore.EFCores
{
/**
*
*
* Authorxuejiaming
* Created: 2020/12/28 13:58:46
**/
public class ShardingQueryCompiler: QueryCompiler
{
private readonly IQueryContextFactory _queryContextFactory;
private readonly IDatabase _database;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _logger;
private readonly IModel _model;
public ShardingQueryCompiler(IQueryContextFactory queryContextFactory, ICompiledQueryCache compiledQueryCache, ICompiledQueryCacheKeyGenerator compiledQueryCacheKeyGenerator, IDatabase database, IDiagnosticsLogger<DbLoggerCategory.Query> logger, ICurrentDbContext currentContext, IEvaluatableExpressionFilter evaluatableExpressionFilter, IModel model) : base(queryContextFactory, compiledQueryCache, compiledQueryCacheKeyGenerator, database, logger, currentContext, evaluatableExpressionFilter, model)
{
_queryContextFactory = queryContextFactory;
_database = database;
_logger = logger;
_model = model;
}
public override TResult Execute<TResult>(Expression query)
{
var shardingAccessor = ShardingContainer.Services.GetService<IShardingAccessor>();
if (shardingAccessor?.ShardingContext != null)
{
return ShardingExecute<TResult>(query);
}
return base.Execute<TResult>(query);
}
/// <summary>
/// use no compiler
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="query"></param>
/// <returns></returns>
private TResult ShardingExecute<TResult>(Expression query)
{
var queryContext = _queryContextFactory.Create();
query = ExtractParameters(query, queryContext, _logger);
var compiledQuery
= CompileQueryCore<TResult>(_database, query, _model, false);
return compiledQuery(queryContext);
}
public override TResult ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken = new CancellationToken())
{
var shardingAccessor = ShardingContainer.Services.GetService<IShardingAccessor>();
if (shardingAccessor?.ShardingContext != null)
{
var result= ShardingExecuteAsync<TResult>(query, cancellationToken);
return result;
}
return base.ExecuteAsync<TResult>(query, cancellationToken);
}
private TResult ShardingExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken = new CancellationToken())
{
var queryContext = _queryContextFactory.Create();
queryContext.CancellationToken = cancellationToken;
query = ExtractParameters(query, queryContext, _logger);
var compiledQuery
= CompileQueryCore<TResult>(_database, query, _model, true);
return compiledQuery(queryContext);
}
}
}
#endif
#if EFCORE2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Threading.Tasks;
using ShardingCore;
using ShardingCore.Core.ShardingAccessors;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using Remotion.Linq.Clauses.StreamedData;
namespace ShardingCore.EFCores
{
/**
*
*
* Authorxuejiaming
* Created: 2020/12/28 13:58:46
**/
public class ShardingQueryCompiler: QueryCompiler
{
private static MethodInfo CompileQueryMethod { get; }
= typeof(IDatabase).GetTypeInfo()
.GetDeclaredMethod(nameof(IDatabase.CompileQuery));
private readonly IQueryContextFactory _queryContextFactory;
private readonly ICompiledQueryCache _compiledQueryCache;
private readonly ICompiledQueryCacheKeyGenerator _compiledQueryCacheKeyGenerator;
private readonly IDatabase _database;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _logger;
private readonly IQueryModelGenerator _queryModelGenerator;
private readonly Type _contextType;
public ShardingQueryCompiler(IQueryContextFactory queryContextFactory, ICompiledQueryCache compiledQueryCache, ICompiledQueryCacheKeyGenerator compiledQueryCacheKeyGenerator, IDatabase database, IDiagnosticsLogger<DbLoggerCategory.Query> logger, ICurrentDbContext currentContext, IQueryModelGenerator queryModelGenerator) : base(queryContextFactory, compiledQueryCache, compiledQueryCacheKeyGenerator, database, logger, currentContext, queryModelGenerator)
{
_queryContextFactory = queryContextFactory;
_compiledQueryCache = compiledQueryCache;
_compiledQueryCacheKeyGenerator = compiledQueryCacheKeyGenerator;
_database = database;
_logger = logger;
_queryModelGenerator = queryModelGenerator;
_contextType = currentContext.Context.GetType();
}
public override TResult Execute<TResult>(Expression query)
{
var shardingAccessor = ShardingContainer.Services.GetService<IShardingAccessor>();
if (shardingAccessor?.ShardingContext != null)
{
return ShardingExecute<TResult>(query);
}
return base.Execute<TResult>(query);
}
private TResult ShardingExecute<TResult>(Expression query)
{
var queryContext = _queryContextFactory.Create();
query = _queryModelGenerator.ExtractParameters(_logger, query, queryContext);
var compiledQuery
= CompileQueryCore<TResult>(query, _queryModelGenerator, _database, _logger, _contextType);
return compiledQuery(queryContext);
}
public override IAsyncEnumerable<TResult> ExecuteAsync<TResult>(Expression query)
{
var shardingAccessor = ShardingContainer.Services.GetService<IShardingAccessor>();
if (shardingAccessor?.ShardingContext != null)
{
return ShardingExecuteEnumerableAsync<TResult>(query);
}
return base.ExecuteAsync<TResult>(query);
}
private IAsyncEnumerable<TResult> ShardingExecuteEnumerableAsync<TResult>(Expression query)
{
var queryContext = _queryContextFactory.Create();
query = _queryModelGenerator.ExtractParameters(_logger, query, queryContext);
return CompileAsyncQueryCore<TResult>(query,_queryModelGenerator, _database)(queryContext);
}
public override Task<TResult> ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken)
{
return base.ExecuteAsync<TResult>(query, cancellationToken);
}
private Task<TResult> ShardingExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken)
{
var queryContext = _queryContextFactory.Create();
queryContext.CancellationToken = cancellationToken;
query = _queryModelGenerator.ExtractParameters(_logger, query, queryContext);
var compiledQuery = CompileAsyncQueryCore<TResult>(query,_queryModelGenerator, _database);
return ExecuteSingletonAsyncQuery(queryContext, compiledQuery, _logger, _contextType);
}
private static Func<QueryContext, TResult> CompileQueryCore<TResult>(
Expression query,
IQueryModelGenerator queryModelGenerator,
IDatabase database,
IDiagnosticsLogger<DbLoggerCategory.Query> logger,
Type contextType)
{
var queryModel = queryModelGenerator.ParseQuery(query);
var resultItemType
= (queryModel.GetOutputDataInfo()
as StreamedSequenceInfo)?.ResultItemType
?? typeof(TResult);
if (resultItemType == typeof(TResult))
{
var compiledQuery = database.CompileQuery<TResult>(queryModel);
return qc =>
{
try
{
return compiledQuery(qc).First();
}
catch (Exception exception)
{
logger.QueryIterationFailed(contextType, exception);
throw;
}
};
}
try
{
return (Func<QueryContext, TResult>)CompileQueryMethod
.MakeGenericMethod(resultItemType)
.Invoke(database, new object[] { queryModel });
}
catch (TargetInvocationException e)
{
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
throw;
}
}
private static Func<QueryContext, IAsyncEnumerable<TResult>> CompileAsyncQueryCore<TResult>(
Expression query,
IQueryModelGenerator queryModelGenerator,
IDatabase database)
{
var queryModel = queryModelGenerator.ParseQuery(query);
return database.CompileAsyncQuery<TResult>(queryModel);
}
private static async Task<TResult> ExecuteSingletonAsyncQuery<TResult>(
QueryContext queryContext,
Func<QueryContext, IAsyncEnumerable<TResult>> compiledQuery,
IDiagnosticsLogger<DbLoggerCategory.Query> logger,
Type contextType)
{
try
{
var asyncEnumerable = compiledQuery(queryContext);
using (var asyncEnumerator = asyncEnumerable.GetEnumerator())
{
await asyncEnumerator.MoveNext(queryContext.CancellationToken);
return asyncEnumerator.Current;
}
}
catch (Exception exception)
{
logger.QueryIterationFailed(contextType, exception);
throw;
}
}
}
}
#endif

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 28 December 2020 22:34:00
* @Email: 326308290@qq.com
*/
public class CreateSqlVirtualTableNotFoundException:Exception
{
public CreateSqlVirtualTableNotFoundException()
{
}
protected CreateSqlVirtualTableNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public CreateSqlVirtualTableNotFoundException(string? message) : base(message)
{
}
public CreateSqlVirtualTableNotFoundException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 15 January 2021 09:07:34
* @Email: 326308290@qq.com
*/
public class InvalidReplaceQueryRootException:Exception
{
public InvalidReplaceQueryRootException()
{
}
protected InvalidReplaceQueryRootException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public InvalidReplaceQueryRootException(string message) : base(message)
{
}
public InvalidReplaceQueryRootException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 21:36:24
* @Email: 326308290@qq.com
*/
public class QueryableRouteNotMatchException:Exception
{
public QueryableRouteNotMatchException()
{
}
protected QueryableRouteNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public QueryableRouteNotMatchException(string? message) : base(message)
{
}
public QueryableRouteNotMatchException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 10:20:47
* @Email: 326308290@qq.com
*/
public class ShardingCreateException:Exception
{
public ShardingCreateException()
{
}
protected ShardingCreateException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingCreateException(string message) : base(message)
{
}
public ShardingCreateException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 23 December 2020 09:11:57
* @Email: 326308290@qq.com
*/
public class ShardingEntityTypeConfiguration404Exception:Exception
{
public ShardingEntityTypeConfiguration404Exception()
{
}
protected ShardingEntityTypeConfiguration404Exception(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingEntityTypeConfiguration404Exception(string? message) : base(message)
{
}
public ShardingEntityTypeConfiguration404Exception(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 11:09:33
* @Email: 326308290@qq.com
*/
public class ShardingKeyGetValueException:Exception
{
public ShardingKeyGetValueException()
{
}
protected ShardingKeyGetValueException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingKeyGetValueException(string message) : base(message)
{
}
public ShardingKeyGetValueException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 19 December 2020 16:25:21
* @Email: 326308290@qq.com
*/
public class ShardingKeyRouteMoreException:Exception
{
public ShardingKeyRouteMoreException()
{
}
protected ShardingKeyRouteMoreException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingKeyRouteMoreException(string? message) : base(message)
{
}
public ShardingKeyRouteMoreException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 21 December 2020 08:20:56
* @Email: 326308290@qq.com
*/
public class ShardingKeyRouteNotMatchException:Exception
{
public ShardingKeyRouteNotMatchException()
{
}
protected ShardingKeyRouteNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingKeyRouteNotMatchException(string? message) : base(message)
{
}
public ShardingKeyRouteNotMatchException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 21 December 2020 09:32:54
* @Email: 326308290@qq.com
*/
public class ShardingOwnerNotFoundException:Exception
{
public ShardingOwnerNotFoundException()
{
}
protected ShardingOwnerNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingOwnerNotFoundException(string? message) : base(message)
{
}
public ShardingOwnerNotFoundException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 10:40:34
* @Email: 326308290@qq.com
*/
public class ShardingTransactionException:Exception
{
public ShardingTransactionException()
{
}
protected ShardingTransactionException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingTransactionException(string? message) : base(message)
{
}
public ShardingTransactionException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 21 December 2020 09:50:57
* @Email: 326308290@qq.com
*/
public class VirtualRouteNotFoundException:Exception
{
public VirtualRouteNotFoundException()
{
}
protected VirtualRouteNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public VirtualRouteNotFoundException(string? message) : base(message)
{
}
public VirtualRouteNotFoundException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 15:38:02
* @Email: 326308290@qq.com
*/
public class VirtualRouteNotMatchException:Exception
{
public VirtualRouteNotMatchException()
{
}
protected VirtualRouteNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public VirtualRouteNotMatchException(string? message) : base(message)
{
}
public VirtualRouteNotMatchException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 18 December 2020 15:01:16
* @Email: 326308290@qq.com
*/
public class VirtualTableNotFoundException:Exception
{
public VirtualTableNotFoundException()
{
}
protected VirtualTableNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public VirtualTableNotFoundException(string? message) : base(message)
{
}
public VirtualTableNotFoundException(string? message, Exception? innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ShardingCore.Core;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.Utils;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 01 January 2021 19:51:44
* @Email: 326308290@qq.com
*/
public static class CommonExtension
{
/// <summary>
/// 是否基继承至ShardingEntity
/// </summary>
/// <param name="entityType"></param>
/// <returns></returns>
public static bool IsShardingEntity(this Type entityType)
{
if (entityType == null)
throw new ArgumentNullException(nameof(entityType));
return typeof(IShardingEntity).IsAssignableFrom(entityType);
}
/// <summary>
/// 是否基继承至ShardingEntity
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public static bool IsShardingEntity(this object entity)
{
if (entity == null)
throw new ArgumentNullException(nameof(entity));
return typeof(IShardingEntity).IsAssignableFrom(entity.GetType());
}
/// <summary>
/// 虚拟表转换成对应的db配置
/// </summary>
/// <param name="virtualTables"></param>
/// <returns></returns>
public static List<VirtualTableDbContextConfig> GetVirtualTableDbContextConfigs(this List<IVirtualTable> virtualTables)
{
return virtualTables.Select(o => new VirtualTableDbContextConfig(o.EntityType, o.GetOriginalTableName(), o.ShardingConfig.TailPrefix)).ToList();
}
/// <summary>
/// 是否是集合contains方法
/// </summary>
/// <param name="express"></param>
/// <param name="methodName"></param>
/// <returns></returns>
public static bool IsEnumerableContains(this MethodCallExpression express, string methodName)
{
return express.Method.DeclaringType.Namespace.IsIn("System.Linq", "System.Collections.Generic") && methodName == nameof(IList.Contains);
}
public static ISet<Type> ParseQueryableRoute(this IQueryable queryable)
{
return ShardingKeyUtil.GetShardingEntitiesFilter(queryable);
}
}
}

View File

@ -0,0 +1,54 @@
using System;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 02 January 2021 13:10:30
* @Email: 326308290@qq.com
*/
public static class DateTimeExtension
{
#region 13
/// <summary>
/// 将DateTime时间格式转换为本地时间戳格式
/// </summary>
/// Author : Napoleon
/// Created : 2018/4/8 14:19
public static long ConvertTimeToLong(this DateTime time)
{
var start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return (long) (time.AddHours(-8) - start).TotalMilliseconds;
}
/// <summary>
/// 本地时间戳转为C#格式时间
/// </summary>
/// Author : Napoleon
/// Created : 2018/4/8 14:19
public static DateTime ConvertLongToTime(this long timeStamp)
{
var start = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return start.AddMilliseconds(timeStamp).AddHours(8);
}
#endregion
/// <summary>
/// 获取周一
/// </summary>
/// <param name="dateTime"></param>
/// <returns></returns>
public static DateTime GetMonday(this DateTime dateTime)
{
DateTime temp = new DateTime(dateTime.Year, dateTime.Month, dateTime.Day);
int count = dateTime.DayOfWeek - DayOfWeek.Monday;
if (count == -1) count = 6;
var monday = temp.AddDays(-count);
return monday;
}
}
}

View File

@ -0,0 +1,114 @@
using System;
using System.Linq.Expressions;
using System.Reflection;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 14 November 2020 22:06:21
* @Email: 326308290@qq.com
*/
public static class ExpressionExtension
{
public static object GetValueByExpression(this object obj, string propertyExpression)
{
var entityType = obj.GetType();
PropertyInfo property;
Expression propertyAccess;
var parameter = Expression.Parameter(entityType, "o");
if (propertyExpression.Contains("."))
{
String[] childProperties = propertyExpression.Split('.');
property = entityType.GetProperty(childProperties[0]);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
for (int i = 1; i < childProperties.Length; i++)
{
property = property.PropertyType.GetProperty(childProperties[i]);
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
}
}
else
{
property = entityType.GetProperty(propertyExpression);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
}
var lambda = Expression.Lambda(propertyAccess, parameter);
Delegate fn = lambda.Compile();
return fn.DynamicInvoke(obj);
}
/// <summary>
/// 添加And条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="first"></param>
/// <param name="second"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> first,
Expression<Func<T, bool>> second)
{
return first.AndAlso(second, Expression.AndAlso);
}
/// <summary>
/// 添加Or条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="first"></param>
/// <param name="second"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> first,
Expression<Func<T, bool>> second)
{
return first.AndAlso(second, Expression.OrElse);
}
/// <summary>
/// 合并表达式以及参数
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expr1"></param>
/// <param name="expr2"></param>
/// <param name="func"></param>
/// <returns></returns>
private static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2,
Func<Expression, Expression, BinaryExpression> func)
{
var parameter = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
func(left, right), parameter);
}
private class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
}
}

View File

@ -0,0 +1,59 @@
using System.Linq;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Core.Internal.Visitors;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Tuesday, 29 December 2020 13:58:46
* @Email: 326308290@qq.com
*/
/// <summary>
///
/// </summary>
internal static class IShardingQueryableExtension
{
internal static ExtraEntry GetExtraEntry<T>(this IQueryable<T> source)
{
var extraVisitor = new QueryableExtraDiscoverVisitor();
extraVisitor.Visit(source.Expression);
return new ExtraEntry(extraVisitor.GetSkip(), extraVisitor.GetTake(), extraVisitor.GetOrders());
}
/// <summary>
/// 删除Skip表达式
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <param name="source">数据源</param>
/// <returns></returns>
internal static IQueryable<T> RemoveSkip<T>(this IQueryable<T> source)
{
return (IQueryable<T>)source.Provider.CreateQuery(new RemoveSkipVisitor().Visit(source.Expression));
}
/// <summary>
/// 删除Take表达式
/// </summary>
/// <typeparam name="T">实体类型</typeparam>
/// <param name="source">数据源</param>
/// <returns></returns>
internal static IQueryable<T> RemoveTake<T>(this IQueryable<T> source)
{
return (IQueryable<T>) source.Provider.CreateQuery(new RemoveTakeVisitor().Visit(source.Expression));
}
/// <summary>
/// 切换数据源,保留原数据源中的Expression
/// </summary>
/// <param name="source">原数据源</param>
/// <param name="newSource">新数据源</param>
/// <returns></returns>
internal static IQueryable ReplaceDbContextQueryable(this IQueryable source, DbContext dbContext)
{
DbContextReplaceQueryableVisitor replaceQueryableVisitor = new DbContextReplaceQueryableVisitor(dbContext);
var newExpre = replaceQueryableVisitor.Visit(source.Expression);
return replaceQueryableVisitor.Source.Provider.CreateQuery(newExpre);
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 01 January 2021 16:16:11
* @Email: 326308290@qq.com
*/
public static class LinqExtension
{
public static bool IsEmpty<T>(this IEnumerable<T> source)
{
return source == null || !source.Any();
}
public static bool IsNotEmpty<T>(this IEnumerable<T> source)
{
return !source.IsEmpty();
}
public static bool IsIn<T>(this T thisValue, params T[] values)
{
return values.Contains(thisValue);
}
/// <summary>
/// 给IEnumerable拓展ForEach方法
/// </summary>
/// <typeparam name="T">模型类</typeparam>
/// <param name="iEnumberable">数据源</param>
/// <param name="func">方法</param>
public static void ForEach<T>(this IEnumerable<T> iEnumberable, Action<T> func)
{
foreach (var item in iEnumberable)
{
func(item);
}
}
}
}

View File

@ -0,0 +1,77 @@
using System;
using System.Reflection;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 16 December 2020 13:59:46
* @Email: 326308290@qq.com
*/
public static class ObjectExtension
{
private static readonly BindingFlags _bindingFlags
= BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static;
/// <summary>
/// 获取某字段值
/// </summary>
/// <param name="type">类型</param>
/// <param name="obj">对象</param>
/// <param name="fieldName">字段名</param>
/// <returns></returns>
public static object GetTypeFieldValue(this Type type,object obj, string fieldName)
{
return type.GetField(fieldName, _bindingFlags).GetValue(obj);
}
/// <summary>
/// 获取某字段值
/// </summary>
/// <param name="obj">对象</param>
/// <param name="fieldName">字段名</param>
/// <returns></returns>
public static object GetFieldValue(this object obj, string fieldName)
{
return obj.GetType().GetField(fieldName, _bindingFlags).GetValue(obj);
}
/// <summary>
/// 获取某属性值
/// </summary>
/// <param name="obj">对象</param>
/// <param name="propertyName">属性名</param>
/// <returns></returns>
public static object GetPropertyValue(this object obj, string propertyName)
{
var property = obj.GetType().GetProperty(propertyName, _bindingFlags);
if (property != null)
{
return obj.GetType().GetProperty(propertyName, _bindingFlags)?.GetValue(obj);
}
else
{
return null;
}
}
/// <summary>
/// 获取某字段值
/// </summary>
/// <param name="type">类型</param>
/// <param name="obj">对象</param>
/// <param name="propertyName">属性名</param>
/// <returns></returns>
public static object GetTypePropertyValue(this Type type,object obj, string propertyName)
{
var property=type.GetProperty(propertyName, _bindingFlags);
if (property != null)
{
return type.GetProperty(propertyName, _bindingFlags)?.GetValue(obj);
}
else
{
return null;
}
}
}
}

View File

@ -0,0 +1,276 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using ShardingCore.Core;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 28 December 2020 16:50:57
* @Email: 326308290@qq.com
*/
public static class ShardingExtension
{
/// <summary>
/// 转成sharding queryable
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static IShardingQueryable<T> AsSharding<T>(this IQueryable<T> source)
{
return new ShardingQueryable<T>(source);
}
public static async Task<bool> ShardingAnyAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
return await new ShardingQueryable<T>(source.Where(predicate)).AnyAsync();
}
/// <summary>
/// 分页
/// </summary>
/// <param name="source"></param>
/// <param name="pageIndex"></param>
/// <param name="pageSize"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<ShardingPagedResult<T>> ToShardingPageResultAsync<T>(this IQueryable<T> source, int pageIndex, int pageSize)
{
//设置每次获取多少页
var take = pageSize <= 0 ? 1 : pageSize;
//设置当前页码最小1
var index = pageIndex <= 0 ? 1 : pageIndex;
//需要跳过多少页
var skip = (index - 1) * take;
//获取每次总记录数
var count = await new ShardingQueryable<T>(source).CountAsync();
//当数据库数量小于要跳过的条数就说明没数据直接返回不在查询list
if (count <= skip)
return new ShardingPagedResult<T>(new List<T>(0), count);
//获取剩余条数
int remainingCount = count - skip;
//当剩余条数小于take数就取remainingCount
var realTake = remainingCount < take ? remainingCount : take;
var data = await new ShardingQueryable<T>(source.Skip(skip).Take(realTake)).ToListAsync();
return new ShardingPagedResult<T>(data, count);
}
/// <summary>
/// 分页
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static List<T> ToShardingList<T>(this IQueryable<T> source)
{
return new ShardingQueryable<T>(source).ToList();
}
/// <summary>
/// 分页
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<List<T>> ToShardingListAsync<T>(this IQueryable<T> source)
{
return await new ShardingQueryable<T>(source).ToListAsync();
}
/// <summary>
/// 第一条
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T ShardingFirstOrDefault<T>(this IQueryable<T> source)
{
return new ShardingQueryable<T>(source).FirstOrDefault();
}
/// <summary>
/// 第一条
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<T> ShardingFirstOrDefaultAsync<T>(this IQueryable<T> source)
{
return await new ShardingQueryable<T>(source).FirstOrDefaultAsync();
}
/// <summary>
/// 最大
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T ShardingMax<T>(this IQueryable<T> source)
{
return new ShardingQueryable<T>(source).Max();
}
/// <summary>
/// 最大
/// </summary>
/// <param name="source"></param>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static async Task<T> ShardingMaxAsync<T>(this IQueryable<T> source)
{
return await new ShardingQueryable<T>(source).MaxAsync();
}
/// <summary>
/// 最大
/// </summary>
/// <param name="source"></param>
/// <param name="keySelector"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <returns></returns>
public static TResult ShardingMax<T,TResult>(this IQueryable<T> source,Expression<Func<T,TResult>> keySelector)
{
return ShardingMax<TResult>(source.Select(keySelector));
}
/// <summary>
/// 最大
/// </summary>
/// <param name="source"></param>
/// <param name="keySelector"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <returns></returns>
public static async Task<TResult> ShardingMaxAsync<T,TResult>(this IQueryable<T> source,Expression<Func<T,TResult>> keySelector)
{
return await ShardingMaxAsync<TResult>(source.Select(keySelector));
}
public static T ShardingMin<T>(this IQueryable<T> source)
{
return new ShardingQueryable<T>(source).Min();
}
public static async Task<T> ShardingMinAsync<T>(this IQueryable<T> source)
{
return await new ShardingQueryable<T>(source).MinAsync();
}
/// <summary>
/// 最小
/// </summary>
/// <param name="source"></param>
/// <param name="keySelector"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <returns></returns>
public static TResult ShardingMin<T,TResult>(this IQueryable<T> source,Expression<Func<T,TResult>> keySelector)
{
return ShardingMin(source.Select(keySelector));
}
/// <summary>
/// 最小
/// </summary>
/// <param name="source"></param>
/// <param name="keySelector"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <returns></returns>
public static async Task<TResult> ShardingMinAsync<T,TResult>(this IQueryable<T> source,Expression<Func<T,TResult>> keySelector)
{
return await ShardingMinAsync(source.Select(keySelector));
}
public static int ShardingCount<T>(this IQueryable<T> source)
{
return new ShardingQueryable<T>(source).Count();
}
public static async Task<int> ShardingCountAsync<T>(this IQueryable<T> source)
{
return await new ShardingQueryable<T>(source).CountAsync();
}
public static long ShardingLongCount<T>(this IQueryable<T> source)
{
return new ShardingQueryable<T>(source).LongCount();
}
public static async Task<long> ShardingLongCountAsync<T>(this IQueryable<T> source)
{
return await new ShardingQueryable<T>(source).LongCountAsync();
}
public static int ShardingSum(this IQueryable<int> source)
{
return new ShardingQueryable<int>(source).Sum();
}
public static async Task<int> ShardingSumAsync(this IQueryable<int> source)
{
return await new ShardingQueryable<int>(source).SumAsync();
}
public static int ShardingSum<T>(this IQueryable<T> source,Expression<Func<T,int>> keySelector)
{
return ShardingSum(source.Select(keySelector));
}
public static async Task<int> ShardingSumAsync<T>(this IQueryable<T> source,Expression<Func<T,int>> keySelector)
{
return await ShardingSumAsync(source.Select(keySelector));
}
public static long ShardingSum(this IQueryable<long> source)
{
return new ShardingQueryable<long>(source).LongSum();
}
public static async Task<long> ShardingSumAsync(this IQueryable<long> source)
{
return await new ShardingQueryable<long>(source).LongSumAsync();
}
public static long ShardingSum<T>(this IQueryable<T> source,Expression<Func<T,long>> keySelector)
{
return ShardingSum(source.Select(keySelector));
}
public static async Task<long> ShardingSumAsync<T>(this IQueryable<T> source,Expression<Func<T,long>> keySelector)
{
return await ShardingSumAsync(source.Select(keySelector));
}
public static double ShardingSum(this IQueryable<double> source)
{
return new ShardingQueryable<double>(source).DoubleSum();
}
public static async Task<double> ShardingSumAsync(this IQueryable<double> source)
{
return await new ShardingQueryable<double>(source).DoubleSumAsync();
}
public static double ShardingSum<T>(this IQueryable<T> source,Expression<Func<T,double>> keySelector)
{
return ShardingSum(source.Select(keySelector));
}
public static async Task<double> ShardingSumAsync<T>(this IQueryable<T> source,Expression<Func<T,double>> keySelector)
{
return await ShardingSumAsync(source.Select(keySelector));
}
public static decimal ShardingSum(this IQueryable<decimal> source)
{
return new ShardingQueryable<decimal>(source).DecimalSum();
}
public static async Task<decimal> ShardingSumAsync(this IQueryable<decimal> source)
{
return await new ShardingQueryable<decimal>(source).DecimalSumAsync();
}
public static decimal ShardingSum<T>(this IQueryable<T> source,Expression<Func<T,decimal>> keySelector)
{
return ShardingSum(source.Select(keySelector));
}
public static async Task<decimal> ShardingSumAsync<T>(this IQueryable<T> source,Expression<Func<T,decimal>> keySelector)
{
return await ShardingSumAsync(source.Select(keySelector));
}
public static float ShardingSum(this IQueryable<float> source)
{
return new ShardingQueryable<float>(source).FloatSum();
}
public static async Task<float> ShardingSumAsync(this IQueryable<float> source)
{
return await new ShardingQueryable<float>(source).FloatSumAsync();
}
public static float ShardingSum<T>(this IQueryable<T> source,Expression<Func<T,float>> keySelector)
{
return ShardingSum(source.Select(keySelector));
}
public static async Task<float> ShardingSumAsync<T>(this IQueryable<T> source,Expression<Func<T,float>> keySelector)
{
return await ShardingSumAsync(source.Select(keySelector));
}
}
}

View File

@ -0,0 +1,75 @@
using System.Collections.Generic;
using ShardingCore.Core;
using ShardingCore.Helpers;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 30 December 2020 15:22:12
* @Email: 326308290@qq.com
*/
public static class ShardingSyncExtension
{
public static bool Any<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.AnyAsync());
}
public static T FirstOrDefault<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.FirstOrDefaultAsync());
}
public static List<T> ToList<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.ToListAsync());
}
public static int Count<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.CountAsync());
}
public static long LongCount<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.LongCountAsync());
}
public static int Sum<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.SumAsync());
}
public static double DoubleSum<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.DoubleSumAsync());
}
public static decimal DecimalSum<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.DecimalSumAsync());
}
public static long LongSum<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.LongSumAsync());
}
public static float FloatSum<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.FloatSumAsync());
}
public static T Max<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.MaxAsync());
}
public static T Min<T>(this IShardingQueryable<T> queryable)
{
return AsyncHelper.RunSync(() => queryable.MinAsync());
}
}
}

View File

@ -0,0 +1,135 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ShardingCore.Core.Internal.Visitors;
namespace ShardingCore.Extensions
{
/*
* @Author: xjm
* @Description:
* @Date: Wednesday, 13 January 2021 13:57:39
* @Email: 326308290@qq.com
*/
internal static class StringFieldOrderExtension
{
#region Private expression tree helpers
private static LambdaExpression GenerateSelector<TEntity>(String propertyName, out Type resultType)
{
PropertyInfo property;
Expression propertyAccess;
var parameter = Expression.Parameter(typeof(TEntity), "o");
if (propertyName.Contains('.'))
{
String[] childProperties = propertyName.Split('.');
property = typeof(TEntity).GetProperty(childProperties[0]);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
for (int i = 1; i < childProperties.Length; i++)
{
property = property.PropertyType.GetProperty(childProperties[i]);
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
}
}
else
{
property = typeof(TEntity).GetProperty(propertyName);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
}
resultType = property.PropertyType;
return Expression.Lambda(propertyAccess, parameter);
}
private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName)
{
Type type = typeof(TEntity);
Type selectorResultType;
LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType);
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
new Type[] {type, selectorResultType},
source.Expression, Expression.Quote(selector));
return resultExp;
}
#endregion
internal static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string fieldName)
{
MethodCallExpression resultExp = GenerateMethodCall<TEntity>(source, nameof(Queryable.OrderBy), fieldName);
return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
}
internal static IOrderedQueryable<TEntity> OrderByDescending<TEntity>(this IQueryable<TEntity> source, string fieldName)
{
MethodCallExpression resultExp = GenerateMethodCall<TEntity>(source, nameof(Queryable.OrderByDescending), fieldName);
return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
}
internal static IOrderedQueryable<TEntity> ThenBy<TEntity>(this IOrderedQueryable<TEntity> source, string fieldName)
{
MethodCallExpression resultExp = GenerateMethodCall<TEntity>(source, nameof(Queryable.ThenBy), fieldName);
return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
}
internal static IOrderedQueryable<TEntity> ThenByDescending<TEntity>(this IOrderedQueryable<TEntity> source, string fieldName)
{
MethodCallExpression resultExp = GenerateMethodCall<TEntity>(source, nameof(Queryable.ThenByDescending), fieldName);
return source.Provider.CreateQuery<TEntity>(resultExp) as IOrderedQueryable<TEntity>;
}
/// <summary>
/// 排序利用表达式
/// </summary>
/// <param name="source"></param>
/// <param name="sortExpression">"child.name asc,child.age desc"</param>
/// <typeparam name="TEntity"></typeparam>
/// <returns></returns>
internal static IOrderedQueryable<TEntity> OrderWithExpression<TEntity>(this IQueryable<TEntity> source, string sortExpression) where TEntity : class
{
String[] orderFields = sortExpression.Split(',');
IOrderedQueryable<TEntity> result = null;
for (int currentFieldIndex = 0; currentFieldIndex < orderFields.Length; currentFieldIndex++)
{
String[] expressionPart = orderFields[currentFieldIndex].Trim().Split(' ');
String sortField = expressionPart[0];
Boolean sortDescending = (expressionPart.Length == 2) && (expressionPart[1].Equals("DESC", StringComparison.OrdinalIgnoreCase));
if (sortDescending)
{
result = currentFieldIndex == 0 ? source.OrderByDescending(sortField) : result.ThenByDescending(sortField);
}
else
{
result = currentFieldIndex == 0 ? source.OrderBy(sortField) : result.ThenBy(sortField);
}
}
return result;
}
internal static IOrderedQueryable<TEntity> OrderWithExpression<TEntity>(this IQueryable<TEntity> source, IEnumerable<PropertyOrder> propertyOrders)
{
IOrderedQueryable<TEntity> result = null;
var currentIndex = 0;
foreach (var propertyOrder in propertyOrders)
{
String sortField = propertyOrder.PropertyExpression;
if (propertyOrder.IsAsc)
{
result = currentIndex == 0 ? source.OrderBy(sortField) : result.ThenBy(sortField);
}
else
{
result = currentIndex == 0 ? source.OrderByDescending(sortField) : result.ThenByDescending(sortField);
}
currentIndex++;
}
return result;
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.DependencyModel;
using ShardingCore.Core;
namespace ShardingCore.Helpers
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 21 December 2020 12:34:55
* @Email: 326308290@qq.com
*/
/// <summary>
/// https://www.michael-whelan.net/replacing-appdomain-in-dotnet-core/
/// </summary>
public class AssemblyHelper
{
private static readonly string AssemblyRoot = typeof(IShardingEntity).GetTypeInfo().Assembly.GetName().Name;
public static AssemblyHelper CurrentDomain { get; private set; }
static AssemblyHelper()
{
CurrentDomain = new AssemblyHelper();
}
// public Assembly[] GetAssemblies()
// {
// return DependencyContext.Default.RuntimeLibraries.Where(IsCandidateLibrary)
// .SelectMany(l => l.GetDefaultAssemblyNames(DependencyContext.Default))
// .Select(assembly => Assembly.Load(new AssemblyName(assembly.Name)))
// .ToArray();
// }
public Assembly[] GetAssemblies()
{
return AppDomain.CurrentDomain.GetAssemblies();
}
private static bool IsCandidateLibrary(RuntimeLibrary library)
{
return library.Dependencies.Any(dependency => string.Equals(AssemblyRoot, dependency.Name, StringComparison.Ordinal));
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ShardingCore.Helpers
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 17 December 2020 16:10:54
* @Email: 326308290@qq.com
*/
/// <summary>
/// 异步转同步,防止ASP.NET中死锁
/// https://cpratt.co/async-tips-tricks/
/// </summary>
public static class AsyncHelper
{
private static readonly TaskFactory _myTaskFactory =
new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
/// <summary>
/// 同步执行
/// </summary>
/// <param name="func">任务</param>
public static void RunSync(Func<Task> func)
{
_myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
/// <summary>
/// 同步执行
/// </summary>
/// <typeparam name="TResult">返回类型</typeparam>
/// <param name="func">任务</param>
/// <returns></returns>
public static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return _myTaskFactory.StartNew(func).Unwrap().GetAwaiter().GetResult();
}
}
}

View File

@ -0,0 +1,23 @@
namespace ShardingCore.Helpers
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 22 January 2021 13:32:08
* @Email: 326308290@qq.com
*/
public class ShardingCoreHelper
{
private ShardingCoreHelper(){}
public static int GetStringHashCode(string value)
{
int h = 0; // 默认值是0
if (value.Length > 0) {
for (int i = 0; i < value.Length; i++) {
h = 31 * h + value[i]; // val[0]*31^(n-1) + val[1]*31^(n-2) + ... + val[n-1]
}
}
return h;
}
}
}

View File

@ -0,0 +1,88 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;
namespace ShardingCore.Helpers
{
internal static class XmlHelper
{
/// <summary>
/// 通过XML获取类属性注释
/// 参考:https://github.com/dotnetcore/FreeSql/blob/d266446062b1dfcd694f7d213191cd2383410025/FreeSql/Internal/CommonUtils.cs
/// </summary>
/// <param name="type">类型</param>
/// <returns>Dictkey=属性名value=注释</returns>
public static Dictionary<string, string> GetProperyCommentBySummary(Type type)
{
if (type.Assembly.IsDynamic) return null;
//动态生成的程序集,访问不了 Assembly.Location/Assembly.CodeBase
var regex = new Regex(@"\.(dll|exe)", RegexOptions.IgnoreCase);
var xmlPath = regex.Replace(type.Assembly.Location, ".xml");
if (File.Exists(xmlPath) == false)
{
if (string.IsNullOrEmpty(type.Assembly.CodeBase)) return null;
xmlPath = regex.Replace(type.Assembly.CodeBase, ".xml");
if (xmlPath.StartsWith("file:///") && Uri.TryCreate(xmlPath, UriKind.Absolute, out var tryuri))
xmlPath = tryuri.LocalPath;
if (File.Exists(xmlPath) == false) return null;
}
var dic = new Dictionary<string, string>();
var sReader = new StringReader(File.ReadAllText(xmlPath));
using (var xmlReader = XmlReader.Create(sReader))
{
XPathDocument xpath = null;
try
{
xpath = new XPathDocument(xmlReader);
}
catch
{
return null;
}
var xmlNav = xpath.CreateNavigator();
var className = (type.IsNested ? $"{type.Namespace}.{type.DeclaringType.Name}.{type.Name}" : $"{type.Namespace}.{type.Name}").Trim('.');
var node = xmlNav.SelectSingleNode($"/doc/members/member[@name='T:{className}']/summary");
if (node != null)
{
var comment = node.InnerXml.Trim(' ', '\r', '\n', '\t');
if (string.IsNullOrEmpty(comment) == false) dic.Add("", comment); //class注释
}
var props = type.GetPropertiesDictIgnoreCase().Values;
foreach (var prop in props)
{
className = (prop.DeclaringType.IsNested ? $"{prop.DeclaringType.Namespace}.{prop.DeclaringType.DeclaringType.Name}.{prop.DeclaringType.Name}" : $"{prop.DeclaringType.Namespace}.{prop.DeclaringType.Name}").Trim('.');
node = xmlNav.SelectSingleNode($"/doc/members/member[@name='P:{className}.{prop.Name}']/summary");
if (node == null) continue;
var comment = node.InnerXml.Trim(' ', '\r', '\n', '\t');
if (string.IsNullOrEmpty(comment)) continue;
dic.Add(prop.Name, comment);
}
}
return dic;
}
private static ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>> _dicGetPropertiesDictIgnoreCase
= new ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>>();
private static Dictionary<string, PropertyInfo> GetPropertiesDictIgnoreCase(this Type that) => that == null ? null : _dicGetPropertiesDictIgnoreCase.GetOrAdd(that, tp =>
{
var props = that.GetProperties().GroupBy(p => p.DeclaringType).Reverse().SelectMany(p => p); //将基类的属性位置放在前面 #164
var dict = new Dictionary<string, PropertyInfo>(StringComparer.CurrentCultureIgnoreCase);
foreach (var prop in props)
{
if (dict.ContainsKey(prop.Name)) continue;
dict.Add(prop.Name, prop);
}
return dict;
});
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts;
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.DbContexts.VirtualDbContexts;
using ShardingCore.Extensions;
using ShardingCore.TableCreator;
namespace ShardingCore
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 21 December 2020 09:10:07
* @Email: 326308290@qq.com
*/
public class ShardingBootstrapper
{
private readonly IServiceProvider _serviceProvider;
private readonly IVirtualTableManager _virtualTableManager;
private readonly IShardingTableCreator _tableCreator;
private readonly ILogger<ShardingBootstrapper> _logger;
private readonly IShardingDbContextFactory _shardingDbContextFactory;
private readonly ShardingCoreConfig _shardingCoreConfig;
public ShardingBootstrapper(IServiceProvider serviceProvider, IVirtualTableManager virtualTableManager
, IShardingTableCreator tableCreator, ILogger<ShardingBootstrapper> logger,
IShardingDbContextFactory shardingDbContextFactory, ShardingCoreConfig shardingCoreConfig)
{
ShardingContainer.SetServices(serviceProvider);
_serviceProvider = serviceProvider;
_virtualTableManager = virtualTableManager;
_tableCreator = tableCreator;
_logger = logger;
_shardingDbContextFactory = shardingDbContextFactory;
_shardingCoreConfig = shardingCoreConfig;
}
public void Start()
{
EnsureCreated();
var virtualTables = _virtualTableManager.GetAllVirtualTables();
using var scope = _serviceProvider.CreateScope();
var dbContextOptionsProvider = scope.ServiceProvider.GetService<IDbContextOptionsProvider>();
using var context = _shardingDbContextFactory.Create(new ShardingDbContextOptions(dbContextOptionsProvider.GetDbContextOptions(), string.Empty, virtualTables.GetVirtualTableDbContextConfigs()));
foreach (var virtualTable in virtualTables)
{
//获取ShardingEntity的实际表名
#if !EFCORE2
var tableName = context.Model.FindEntityType(virtualTable.EntityType).GetTableName();
#endif
#if EFCORE2
var tableName = context.Model.FindEntityType(virtualTable.EntityType).Relational().TableName;
#endif
virtualTable.SetOriginalTableName(tableName);
CreateDataTable(virtualTable);
}
}
public void EnsureCreated()
{
if (_shardingCoreConfig.EnsureCreated)
{
using var scope = _serviceProvider.CreateScope();
var dbContextOptionsProvider = scope.ServiceProvider.GetService<IDbContextOptionsProvider>();
using var context = _shardingDbContextFactory.Create(new ShardingDbContextOptions(dbContextOptionsProvider.GetDbContextOptions(), string.Empty, new List<VirtualTableDbContextConfig>(), true));
context.Database.EnsureCreated();
}
}
private void CreateDataTable(IVirtualTable virtualTable)
{
var shardingConfig = virtualTable.ShardingConfig;
foreach (var tail in virtualTable.GetTaleAllTails())
{
if (shardingConfig.AutoCreateTable)
{
try
{
_tableCreator.CreateTable(virtualTable.EntityType, tail);
}
catch (Exception e)
{
_logger.LogWarning($"table :{virtualTable.GetOriginalTableName()}{shardingConfig.TailPrefix}{tail} will created");
}
}
//添加物理表
virtualTable.AddPhysicTable(new DefaultPhysicTable(virtualTable.GetOriginalTableName(), virtualTable.ShardingConfig.TailPrefix, tail, virtualTable.EntityType));
}
}
}
}

View File

@ -0,0 +1,23 @@
using System;
namespace ShardingCore
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 02 January 2021 19:37:27
* @Email: 326308290@qq.com
*/
public class ShardingContainer
{
public static IServiceProvider Services { get; private set; }
/// <summary>
/// 静态注入
/// </summary>
/// <param name="services"></param>
public static void SetServices(IServiceProvider services)
{
Services = services;
}
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Version>$(EFCORE5)</Version>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<DefineConstants>TRACE;DEBUG;EFCORE5;</DefineConstants>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,16 @@
namespace ShardingCore
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 14 January 2021 15:50:46
* @Email: 326308290@qq.com
*/
public class ShardingCoreConfig
{
/// <summary>
/// 如果数据库不存在就创建并且创建表
/// </summary>
public bool EnsureCreated { get; set; }
}
}

View File

@ -0,0 +1,36 @@
using System.Collections.Generic;
namespace ShardingCore
{
/// <summary>
/// 分页集合
/// </summary>
/// <typeparam name="T"></typeparam>
public class ShardingPagedResult<T>
{
#region Ctor
/// <summary>
/// 初始化一个新的<c>PagedResult{T}</c>类型的实例。
/// </summary>
/// <param name="total">总记录数。</param>
/// <param name="data">当前页面的数据。</param>
public ShardingPagedResult(List<T> data, int total)
{
this.Total = total;
this.Data = data;
}
#endregion
#region Public Properties
/// <summary>
/// 获取或设置总记录数。
/// </summary>
public int Total { get; set; }
/// <summary>
/// 分页数据
/// </summary>
public List<T> Data { get; set; }
#endregion
}
}

View File

@ -0,0 +1,31 @@
using System;
using ShardingCore.Core;
namespace ShardingCore.TableCreator
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 21 December 2020 11:22:08
* @Email: 326308290@qq.com
*/
/// <summary>
///
/// </summary>
public interface IShardingTableCreator
{
/// <summary>
/// 创建表
/// </summary>
/// <param name="tail"></param>
/// <typeparam name="T"></typeparam>
void CreateTable<T>(string tail) where T : class, IShardingEntity;
/// <summary>
/// 创建表
/// </summary>
/// <param name="shardingEntityType"></param>
/// <param name="tail"></param>
/// <exception cref="ShardingCreateException"></exception>
void CreateTable(Type shardingEntityType,string tail);
}
}

Some files were not shown because too many files have changed in this diff Show More