提交初始化项目
This commit is contained in:
parent
8d5bc4cb28
commit
878c658cda
|
@ -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
|
|
@ -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
|
|
@ -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: 2020年4月7日 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ShardingCore.Core.VirtualRoutes;
|
||||
using ShardingCore.Extensions;
|
||||
|
||||
namespace ShardingCore.SqlServer
|
||||
{
|
||||
/*
|
||||
* @Author: xjm
|
||||
* @Description:
|
||||
* @Date: 2020年4月7日 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; }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<T>的类型</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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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")}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; } = "_";
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*
|
||||
* Author:xuejiaming
|
||||
* 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
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
/**
|
||||
* 描述:
|
||||
*
|
||||
* Author:xuejiaming
|
||||
* 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
|
||||
{
|
||||
/**
|
||||
* 描述:
|
||||
*
|
||||
* Author:xuejiaming
|
||||
* 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
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>Dict:key=属性名,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;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in New Issue