优化多线程下的数据解析查询

This commit is contained in:
xuejiaming 2021-12-01 15:52:42 +08:00
parent 9ce7617f7f
commit 028d3b08cb
64 changed files with 2006 additions and 257 deletions

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31019.35
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{490FAE47-4476-4508-B216-505FC850447F}"
EndProject
@ -49,7 +49,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShardingCore.Test3x", "test
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShardingCore.Test2x", "test\ShardingCore.Test2x\ShardingCore.Test2x.csproj", "{5ED4AF17-F16D-4857-B19C-018831109991}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCore.Test", "test\ShardingCore.Test\ShardingCore.Test.csproj", "{32EA64CC-0877-4B4D-BFBA-504EDB2237D6}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ShardingCore.Test", "test\ShardingCore.Test\ShardingCore.Test.csproj", "{32EA64CC-0877-4B4D-BFBA-504EDB2237D6}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{B458D737-33C5-4C10-9687-0BED2E7CD346}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShardingCoreBenchmark", "benchmarks\ShardingCoreBenchmark\ShardingCoreBenchmark.csproj", "{8CE5E8AF-DDB7-4989-8AA4-1D47E4226846}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -129,6 +133,10 @@ Global
{32EA64CC-0877-4B4D-BFBA-504EDB2237D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32EA64CC-0877-4B4D-BFBA-504EDB2237D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32EA64CC-0877-4B4D-BFBA-504EDB2237D6}.Release|Any CPU.Build.0 = Release|Any CPU
{8CE5E8AF-DDB7-4989-8AA4-1D47E4226846}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8CE5E8AF-DDB7-4989-8AA4-1D47E4226846}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8CE5E8AF-DDB7-4989-8AA4-1D47E4226846}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8CE5E8AF-DDB7-4989-8AA4-1D47E4226846}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -152,6 +160,7 @@ Global
{E64E09EF-2DC0-4948-A948-256EF5F95C53} = {CC2C88C0-65F2-445D-BE78-973B840FE281}
{5ED4AF17-F16D-4857-B19C-018831109991} = {CC2C88C0-65F2-445D-BE78-973B840FE281}
{32EA64CC-0877-4B4D-BFBA-504EDB2237D6} = {CC2C88C0-65F2-445D-BE78-973B840FE281}
{8CE5E8AF-DDB7-4989-8AA4-1D47E4226846} = {B458D737-33C5-4C10-9687-0BED2E7CD346}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8C07A667-E8B4-43C7-8053-721584BAD291}

View File

@ -0,0 +1,384 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using EFCore.BulkExtensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore;
using ShardingCore.Bootstrapers;
using ShardingCore.Core.VirtualDatabase.VirtualTables;
using ShardingCore.Core.VirtualRoutes.TableRoutes;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
using ShardingCore.Core.VirtualTables;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Sharding.Abstractions;
using ShardingCore6x.NoShardingDbContexts;
using ShardingCore6x.ShardingDbContexts;
namespace ShardingCore6x
{
public class EFCoreCrud
{
private readonly DefaultDbContext _defaultDbContext;
private readonly DefaultShardingDbContext _defaultShardingDbContext;
private readonly IVirtualTableManager<DefaultShardingDbContext> _virtualTableManager;
private readonly IVirtualTable<Order> _virtualTable;
private readonly IRouteTailFactory _routeTailFactory;
private readonly IStreamMergeContextFactory<DefaultShardingDbContext> _streamMergeContextFactory;
public EFCoreCrud()
{
var services = new ServiceCollection();
services.AddDbContext<DefaultDbContext>(o => o.UseSqlServer("Data Source=localhost;Initial Catalog=db1;Integrated Security=True;"), ServiceLifetime.Transient, ServiceLifetime.Transient);
services.AddLogging();
services.AddShardingDbContext<DefaultShardingDbContext>((conStr, builder) => builder.UseSqlServer(conStr), ServiceLifetime.Transient, ServiceLifetime.Transient)
.Begin(o =>
{
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
o.AutoTrackEntity = false;
}).AddShardingTransaction((connection, builder) => builder.UseSqlServer(connection))
.AddDefaultDataSource("ds0", "Data Source=localhost;Initial Catalog=db2;Integrated Security=True;")
.AddShardingTableRoute(op =>
{
op.AddShardingTableRoute<OrderVirtualTableRoute>();
}).End();
var buildServiceProvider = services.BuildServiceProvider();
buildServiceProvider.GetRequiredService<IShardingBootstrapper>().Start();
ICollection<Order> orders = new LinkedList<Order>();
using (var scope = buildServiceProvider.CreateScope())
{
var defaultShardingDbContext = scope.ServiceProvider.GetService<DefaultDbContext>();
defaultShardingDbContext.Database.EnsureCreated();
if (!defaultShardingDbContext.Set<Order>().Any())
{
var begin = DateTime.Now.Date.AddDays(-8);
var now = DateTime.Now;
var current = begin;
int i = 0;
var x = new OrderStatusEnum[] { OrderStatusEnum.Failed, OrderStatusEnum.NotPay, OrderStatusEnum.Succeed };
while (current < now)
{
orders.Add(new Order()
{
Id = i.ToString(),
Amount = i,
Body = $"今天购买了的东西呀:{i}",
CreateTime = current,
Remark = $"这是我的备注哦备注哦备注哦:{i}",
Payer = Guid.NewGuid().ToString("n"),
OrderStatus = x[i % 3]
});
i++;
current = current.AddMilliseconds(100);
}
var sp = Stopwatch.StartNew();
defaultShardingDbContext.BulkInsert<Order>(orders.ToList());
sp.Stop();
Console.WriteLine($"批量插入订单数据:{orders.Count},用时:{sp.ElapsedMilliseconds}");
}
}
using (var scope = buildServiceProvider.CreateScope())
{
var defaultShardingDbContext = scope.ServiceProvider.GetService<DefaultShardingDbContext>();
if (!defaultShardingDbContext.Set<Order>().Any())
{
var sp = Stopwatch.StartNew();
var bulkShardingEnumerable = defaultShardingDbContext.BulkShardingTableEnumerable(orders.ToList());
foreach (var keyValuePair in bulkShardingEnumerable)
{
keyValuePair.Key.BulkInsert(keyValuePair.Value.ToList());
}
sp.Stop();
Console.WriteLine($"批量插入订单数据:{orders.Count},用时:{sp.ElapsedMilliseconds}");
}
}
_defaultDbContext = ShardingContainer.GetService<DefaultDbContext>();
_defaultShardingDbContext = ShardingContainer.GetService<DefaultShardingDbContext>();
_virtualTableManager = ShardingContainer.GetService<IVirtualTableManager<DefaultShardingDbContext>>();
_virtualTable = _virtualTableManager.GetVirtualTable<Order>();
_routeTailFactory= ShardingContainer.GetService<IRouteTailFactory>();
_streamMergeContextFactory =
ShardingContainer.GetService<IStreamMergeContextFactory<DefaultShardingDbContext>>();
}
[Params(10)]
public int N;
//[Benchmark]
//public async Task NoShardingFirstOrDefaultAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1,3000000).ToString();
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Id == next);
// }
//}
//[Benchmark]
//public async Task ShardingFirstOrDefaultAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1, 3000000).ToString();
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Id == next);
// }
//}
//[Benchmark]
//public async Task NoShardingIndexFirstOrDefaultAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1000000, 7000000).ToString();
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Id == next);
// }
//}
//[Benchmark]
//public async Task ShardingIndexFirstOrDefaultAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1000000, 7000000).ToString();
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Id == next);
// }
//}
//[Benchmark]
//public async Task NoShardingNoIndexFirstOrDefaultAsync100w()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Amount == 1000000);
// }
//}
//[Benchmark]
//public async Task ShardingNoIndexFirstOrDefaultAsync100w()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Amount == 1000000);
// }
//}
//[Benchmark]
//public async Task NoShardingNoIndexCountAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().CountAsync(o => o.Amount == 3000000);
// }
//}
//[Benchmark]
//public async Task ShardingNoIndexCountASYNC()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().CountAsync(o => o.Amount == 3000000);
// }
//}
//[Benchmark]
//public async Task NoShardingNoIndexFirstOrDefaultAsync600w()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Amount == 6000000);
// }
//}
//[Benchmark]
//public async Task ShardingNoIndexFirstOrDefaultAsync600w()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().FirstOrDefaultAsync(o => o.Amount == 6000000);
// }
//}
[Benchmark]
public async Task NoShardingNoIndexLikeToListAsync()
{
for (int i = 0; i < N; i++)
{
var next = new Random().Next(5000000, 7000000).ToString();
var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().Where(o => o.Body.Contains(next)).ToListAsync();
}
}
[Benchmark]
public async Task ShardingNoIndexLikeToListAsync()
{
for (int i = 0; i < N; i++)
{
var next = new Random().Next(5000000, 7000000).ToString();
var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().Where(o => o.Body.Contains(next)).ToListAsync();
}
}
//[Benchmark]
//public async Task NoShardingNoIndexToListAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(5000000, 7000000);
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().Where(o => o.Amount == next).ToListAsync();
// }
//}
//[Benchmark]
//public async Task ShardingNoIndexToListAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(5000000, 7000000);
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().Where(o => o.Amount == next).ToListAsync();
// }
//}
//[Benchmark]
//public void ShardingRouteFirstOrDefault()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1000000, 7000000).ToString();
// var queryable = _defaultShardingDbContext.Set<Order>().Where(o => o.Id == next);
// _virtualTable.RouteTo(new ShardingTableRouteConfig(queryable: queryable));
// }
//}
//private static readonly string[] aa = new string[] { "a", "b", "c", "d" };
//[Benchmark]
//public void ShardingCreateDbContextFirstOrDefault()
//{
// for (int i = 0; i < N; i++)
// {
// var routeTail = _routeTailFactory.Create(aa[i % 4]);
// var dbContext = _defaultShardingDbContext.GetDbContext("ds0", true, routeTail);
// }
//}
//[Benchmark]
//public async Task ShardingWhereFirstOrDefaultAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1000000, 7000000).ToString();
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().Where(o => o.Id == next).FirstOrDefaultAsync();
// }
//}
//[Benchmark]
//public async Task ShardingCreateStreamMergeContext()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1000000, 7000000).ToString();
// var queryable = _defaultShardingDbContext.Set<Order>().Where(o => o.Id == next);
// var firstOrDefaultAsync = _streamMergeContextFactory.Create(queryable, _defaultShardingDbContext);
// }
//}
//[Benchmark]
//public async Task ShardingCreateEnumerableQuery()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1000000, 7000000).ToString();
// var queryable = _defaultShardingDbContext.Set<Order>().Where(o => o.Id == next);
// new EnumerableQuery<Order>(queryable.Expression);
// }
//}
//private TResult GenericShardingDbContextMergeExecute<TResult>(IQueryable<Order> queryable)
//{
// //{
// // var queryEntityType = query.GetQueryEntityType();
// //var newStreamMergeEngineType = streamMergeEngineType.MakeGenericType(shardingDbContext.GetType(), queryEntityType);
// //// {
// //// //获取所有需要路由的表后缀
// //// var startNew = Stopwatch.StartNew();
// //// for (int i = 0; i < 10000; i++)
// //// {
// //// var streamEngine = ShardingCreatorHelper.CreateInstance(newStreamMergeEngineType, query, shardingDbContext);
// //// }
// //// startNew.Stop();
// //// var x = startNew.ElapsedMilliseconds;
// //// }
// //{
// // //获取所有需要路由的表后缀
// // var startNew1 = Stopwatch.StartNew();
// // for (int i = 0; i < 10000; i++)
// // {
// // var streamEngine = Activator.CreateInstance(newStreamMergeEngineType, query, shardingDbContext);
// // }
// // startNew1.Stop();
// // var x = startNew1.ElapsedMilliseconds;
// //}
// // var methodName = async ? nameof(IGenericMergeResult.MergeResultAsync) : nameof(IGenericMergeResult.MergeResult);
// // var streamEngineMethod = newStreamMergeEngineType.GetMethod(methodName);
// // if (streamEngineMethod == null)
// // throw new ShardingCoreException($"cant found InMemoryAsyncStreamMergeEngine method [{methodName}]");
// // var @params = async ? new object[] { cancellationToken } : new object[0];
// //}
// {
// var queryEntityType = queryable.Expression.GetQueryEntityType();
// var newStreamMergeEngineType = streamMergeEngineType.MakeGenericType(shardingDbContext.GetType(), queryEntityType);
// var streamEngine = Activator.CreateInstance(newStreamMergeEngineType, query, shardingDbContext);
// var methodName = async ? nameof(IGenericMergeResult.MergeResultAsync) : nameof(IGenericMergeResult.MergeResult);
// var streamEngineMethod = newStreamMergeEngineType.GetMethod(methodName);
// if (streamEngineMethod == null)
// throw new ShardingCoreException($"cant found InMemoryAsyncStreamMergeEngine method [{methodName}]");
// var @params = async ? new object[] { cancellationToken } : new object[0];
// return (TResult)streamEngineMethod.MakeGenericMethod(new Type[] { queryEntityType }).Invoke(streamEngine, @params);
// }
//}
//[Benchmark]
//public async Task NoShardingNoTrackingFirstOrDefaultAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1, 3000000).ToString();
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().AsNoTracking().FirstOrDefaultAsync(o => o.Id == next);
// }
//}
//[Benchmark]
//public async Task ShardingNoTrackingFirstOrDefaultAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var next = new Random().Next(1, 3000000).ToString();
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().AsNoTracking().FirstOrDefaultAsync(o => o.Id == next);
// }
//}
//[Benchmark]
//public async Task NoShardingCountAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultDbContext.Set<Order>().CountAsync();
// }
//}
//[Benchmark]
//public async Task ShardingCountAsync()
//{
// for (int i = 0; i < N; i++)
// {
// var firstOrDefaultAsync = await _defaultShardingDbContext.Set<Order>().CountAsync();
// }
//}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore6x.NoShardingDbContexts
{
internal class DefaultDbContext:DbContext
{
public DefaultDbContext(DbContextOptions<DefaultDbContext> options):base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new OrderMap());
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore6x
{
internal class Order
{
public string Id { get; set; }
public long Amount { get; set; }
public string Body { get; set; }
public DateTime CreateTime { get; set; }
public string Remark { get; set; }
public string Payer { get; set; }
public OrderStatusEnum OrderStatus { get; set; }
}
public enum OrderStatusEnum
{
NotPay=1,
Succeed=1<<1,
Failed=1<<2,
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace ShardingCore6x
{
internal class OrderMap:IEntityTypeConfiguration<Order>
{
public void Configure(EntityTypeBuilder<Order> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Id).IsUnicode(false).HasMaxLength(50);
builder.Property(o => o.Body).IsRequired().HasDefaultValue(string.Empty).HasMaxLength(128);
builder.Property(o => o.Remark).IsRequired().HasDefaultValue(string.Empty).HasMaxLength(128);
builder.Property(o => o.Payer).IsRequired().IsUnicode(false).HasMaxLength(50);
builder.Property(o => o.OrderStatus).HasConversion<int>();
builder.ToTable(nameof(Order));
}
}
}

View File

@ -0,0 +1,8 @@
// See https://aka.ms/new-console-template for more information
using BenchmarkDotNet.Running;
using ShardingCore6x;
var result = BenchmarkRunner.Run<EFCoreCrud>();
Console.ReadLine();

View File

@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>10.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="EFCore.BulkExtensions" Version="6.1.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ShardingCore\ShardingCore.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
using ShardingCore.Sharding;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore6x.ShardingDbContexts
{
internal class DefaultShardingDbContext:AbstractShardingDbContext,IShardingTableDbContext
{
public DefaultShardingDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new OrderMap());
}
public IRouteTail RouteTail { get; set; }
}
}

View File

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.VirtualRoutes.Days;
using ShardingCore.VirtualRoutes.Mods;
namespace ShardingCore6x.ShardingDbContexts
{
internal class OrderVirtualTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<Order>
{
public OrderVirtualTableRoute() : base(2, 5)
{
}
public override void Configure(EntityMetadataTableBuilder<Order> builder)
{
builder.ShardingProperty(o => o.CreateTime);
}
}
}

View File

@ -17,14 +17,10 @@ namespace Sample.SqlServer.Shardings
*/
public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<SysUserSalary,int>
{
protected override int ConvertToShardingKey(object shardingKey)
{
return Convert.ToInt32(shardingKey);
}
public override string ShardingKeyToTail(object shardingKey)
{
var time = ConvertToShardingKey(shardingKey);
var time = Convert.ToInt32(shardingKey);
return TimeFormatToTail(time);
}

View File

@ -14,14 +14,9 @@ namespace Sample.SqlServer.Shardings
{
public class TestVRoute : AbstractShardingOperatorVirtualTableRoute<SysUserMod, string>
{
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey.ToString();
}
public override string ShardingKeyToTail(object shardingKey)
{
throw new NotImplementedException();
return shardingKey.ToString();
}
//数据库已经存在的tail

View File

@ -15,15 +15,11 @@ namespace Sample.SqlServerShardingAll.VirtualDataSourceRoutes
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
}
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()
{

View File

@ -15,14 +15,10 @@ namespace Sample.SqlServerShardingAll.VirtualDataSourceRoutes
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()

View File

@ -1,4 +1,8 @@
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;
using System.Text;
using Microsoft.EntityFrameworkCore;
using Namotion.Reflection;
using Sample.SqlServerShardingDataSource.Entities;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
using ShardingCore.Sharding;
@ -32,6 +36,34 @@ namespace Sample.SqlServerShardingDataSource
entity.Property(o=>o.Area).IsRequired().IsUnicode(false).HasMaxLength(50);
entity.ToTable(nameof(SysUser));
});
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entityType.GetProperties())
{
if (property.PropertyInfo == null)
continue;
var x = property.GetComment();
if (!string.IsNullOrWhiteSpace(property.GetComment()))
continue;
StringBuilder comment = new StringBuilder(property.PropertyInfo.GetXmlDocsSummary());
if (property.PropertyInfo.PropertyType.IsEnum)
{
foreach (var aValue in Enum.GetValues(property.PropertyInfo.PropertyType))
{
var member = property.PropertyInfo.PropertyType.GetMembers()
.Where(x => x.Name == aValue.ToString())
.FirstOrDefault();
var memberComment = member?.GetXmlDocsSummary();
if (string.IsNullOrWhiteSpace(memberComment))
memberComment = member?.Name;
comment.Append($" {(int)aValue}={memberComment}");
}
}
property.SetComment(comment.ToString());
}
}
}
}

View File

@ -2,10 +2,12 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<DocumentationFile>obj\Debug\net6.0\Sample.SqlServerShardingDataSource.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.0" />
<PackageReference Include="Namotion.Reflection" Version="2.0.5" />
</ItemGroup>
<ItemGroup>

View File

@ -15,14 +15,10 @@ namespace Sample.SqlServerShardingDataSource.VirtualRoutes
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()

View File

@ -15,14 +15,10 @@ namespace Sample.SqlServerShardingDataSource.VirtualRoutes
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()

View File

@ -14,10 +14,6 @@ namespace Sample.SqlServerShardingTable.VirtualRoutes
{
public class OrderHashRangeVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<Order,string>
{
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey.ToString();
}
public override string ShardingKeyToTail(object shardingKey)
{

View File

@ -0,0 +1,32 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualRoutes.TableRoutes;
namespace ShardingCore.Core
{
internal class RouteFilterComparer : IEqualityComparer<Expression<Func<string, bool>>>
{
public int Compare(Expression<Func<string, bool>>? x, Expression<Func<string, bool>>? y)
{
if (LambdaCompare.Eq(x, y))
return 0;
return - 1;
}
public bool Equals(Expression<Func<string, bool>>? x, Expression<Func<string, bool>>? y)
{
return LambdaCompare.Eq(x, y);
}
public int GetHashCode(Expression<Func<string, bool>> obj)
{
return 0;
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Core
{
internal class ShardingCreatorHelper
{
private static ConcurrentDictionary<Type, Func<object[], object>> _creator;
private ShardingCreatorHelper() { }
static ShardingCreatorHelper()
{
_creator = new ConcurrentDictionary<Type, Func<object[], object>>();
}
public static object CreateInstance(Type targetType,params object[] args)
{
var creator = _creator.GetOrAdd(targetType,key=> GetActivator(key));
return creator(args);
}
private static Func<object[],object> GetActivator(Type targetType)
{
ConstructorInfo ctor = targetType.GetConstructors().First();
Type type = ctor.DeclaringType;
ParameterInfo[] paramsInfo = ctor.GetParameters();
//create a single param of type object[]
ParameterExpression param =
Expression.Parameter(typeof(object[]), "args");
Expression[] argsExp =
new Expression[paramsInfo.Length];
//pick each arg from the params array
//and create a typed expression of them
for (int i = 0; i < paramsInfo.Length; i++)
{
Expression index = Expression.Constant(i);
Type paramType = paramsInfo[i].ParameterType;
Expression paramAccessorExp =
Expression.ArrayIndex(param, index);
Expression paramCastExp =
Expression.Convert(paramAccessorExp, paramType);
argsExp[i] = paramCastExp;
}
//make a NewExpression that calls the
//ctor with the args we just created
NewExpression newExp = Expression.New(ctor, argsExp);
//create a lambda with the New
//Expression as body and our param object[] as arg
//compile it
var compiled =
Expression.Lambda<Func<object[],object>>(newExp, param).Compile();
return compiled;
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using ShardingCore.Core.EntityMetadatas;
@ -26,7 +27,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
protected override List<string> DoRouteWithPredicate(List<string> allDataSourceNames, IQueryable queryable)
{
//获取所有需要路由的表后缀
var filter = ShardingUtil.GetRouteShardingTableFilter(queryable, EntityMetadata, ConvertToShardingKey, GetRouteToFilter,false);
var filter = ShardingUtil.GetRouteShardingTableFilter<TKey>(queryable, EntityMetadata, GetRouteToFilter,false);
var dataSources = allDataSourceNames.Where(o => filter(o)).ToList();
return dataSources;
}

View File

@ -43,12 +43,6 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
public new PaginationMetadata PaginationMetadata { get; protected set; }
public bool EnablePagination => PaginationMetadata != null;
/// <summary>
/// 分库字段object类型的如何转成对应的泛型类型how convert sharding key to generic type key value
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
protected abstract TKey ConvertToShardingKey(object shardingKey);
/// <summary>
/// 分库字段如何转成对应的数据源名称 how convert sharding data source key to data source name
/// </summary>

View File

@ -23,7 +23,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine
public DataSourceRouteRuleContext(IQueryable<T> queryable)
{
Queryable = queryable;
QueryEntities = queryable.ParseQueryableRoute();
QueryEntities = queryable.ParseQueryableEntities();
}
/// <summary>
/// 查询条件

View File

@ -0,0 +1,374 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Core.VirtualRoutes
{
public sealed class ExpressionComparer : IEqualityComparer<Expression>
{
public NameComparison CompareLambdaNames { get; set; }
public NameComparison CompareParameterNames { get; set; }
public ConstantComparison CompareConstants { get; set; }
public ExpressionComparer()
{
this.CompareConstants = ConstantComparison.ByCurrentValueValue;
}
public bool Equals(Expression x, Expression y)
{
return EqualsImpl(x, y, null);
}
private bool EqualsImpl(Expression x, Expression y, ParameterContext parameterContext)
{
if (x == y) return true;
if (x == null || y == null || x.NodeType != y.NodeType || x.Type != y.Type) return false;
switch (x.NodeType)
{
case ExpressionType.Add:
case ExpressionType.AddChecked:
case ExpressionType.Subtract:
case ExpressionType.SubtractChecked:
case ExpressionType.Multiply:
case ExpressionType.MultiplyChecked:
case ExpressionType.Divide:
case ExpressionType.Modulo:
case ExpressionType.Power:
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.ExclusiveOr:
case ExpressionType.Coalesce:
case ExpressionType.ArrayIndex:
case ExpressionType.RightShift:
case ExpressionType.LeftShift:
case ExpressionType.Assign:
case ExpressionType.AddAssign:
case ExpressionType.AndAssign:
case ExpressionType.DivideAssign:
case ExpressionType.ExclusiveOrAssign:
case ExpressionType.LeftShiftAssign:
case ExpressionType.ModuloAssign:
case ExpressionType.MultiplyAssign:
case ExpressionType.OrAssign:
case ExpressionType.PowerAssign:
case ExpressionType.RightShiftAssign:
case ExpressionType.SubtractAssign:
case ExpressionType.AddAssignChecked:
case ExpressionType.SubtractAssignChecked:
case ExpressionType.MultiplyAssignChecked:
{
var xt = (BinaryExpression)x;
var yt = (BinaryExpression)y;
return xt.Method == yt.Method && EqualsImpl(xt.Left, yt.Left, parameterContext) && EqualsImpl(xt.Right, yt.Right, parameterContext) && EqualsImpl(xt.Conversion, yt.Conversion, parameterContext);
}
case ExpressionType.Block:
{
var xt = (BlockExpression)x;
var yt = (BlockExpression)y;
if (xt.Expressions.Count != yt.Expressions.Count || xt.Variables.Count != yt.Variables.Count) return false;
for (var i = 0; i < xt.Variables.Count; i++)
if (!EqualsImpl(xt.Variables[i], yt.Variables[i], parameterContext)) return false;
for (var i = 0; i < xt.Expressions.Count; i++)
if (!EqualsImpl(xt.Expressions[i], yt.Expressions[i], parameterContext)) return false;
return true;
}
case ExpressionType.Conditional:
{
var xt = (ConditionalExpression)x;
var yt = (ConditionalExpression)y;
return EqualsImpl(xt.Test, yt.Test, parameterContext) && EqualsImpl(xt.IfTrue, yt.IfTrue, parameterContext) && EqualsImpl(xt.IfFalse, yt.IfFalse, parameterContext);
}
case ExpressionType.Constant:
{
switch (this.CompareConstants)
{
case ConstantComparison.ByCurrentValueValue:
return Equals(((ConstantExpression)x).Value, ((ConstantExpression)y).Value);
case ConstantComparison.ByCurrentValueReference:
return ((ConstantExpression)x).Value == ((ConstantExpression)y).Value;
case ConstantComparison.ByExpressionReference:
return x == y;
default:
throw new InvalidEnumArgumentException("CompareConstants", (int)this.CompareConstants, typeof(ConstantComparison));
}
}
case ExpressionType.Default:
{
return true;
}
case ExpressionType.Dynamic:
{
var xt = (DynamicExpression)x;
var yt = (DynamicExpression)y;
if (xt.Binder != yt.Binder || xt.DelegateType != yt.DelegateType || xt.Arguments.Count != yt.Arguments.Count) return false;
for (var i = 0; i < xt.Arguments.Count; i++)
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
return true;
}
case ExpressionType.Index:
{
var xt = (IndexExpression)x;
var yt = (IndexExpression)y;
if (xt.Arguments.Count != yt.Arguments.Count || xt.Indexer != yt.Indexer || !EqualsImpl(xt.Object, yt.Object, parameterContext)) return false;
for (var i = 0; i < xt.Arguments.Count; i++)
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
return true;
}
case ExpressionType.Invoke:
{
var xt = (InvocationExpression)x;
var yt = (InvocationExpression)y;
if (xt.Arguments.Count != yt.Arguments.Count || !EqualsImpl(xt.Expression, yt.Expression, parameterContext)) return false;
for (var i = 0; i < xt.Arguments.Count; i++)
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
return true;
}
case ExpressionType.Lambda:
{
var xt = (LambdaExpression)x;
var yt = (LambdaExpression)y;
if (!CompareNames(xt.Name, yt.Name, this.CompareLambdaNames) || xt.Parameters.Count != yt.Parameters.Count) return false;
for (var i = 0; i < xt.Parameters.Count; i++)
if (!EqualsImpl(xt.Parameters[i], yt.Parameters[i], null)) return false; // This and the catch parameter are the only cases where we compare parameters by value instead of positionally
return EqualsImpl(xt.Body, yt.Body, new ParameterContext(parameterContext, xt.Parameters.ToArray(), yt.Parameters.ToArray()));
}
case ExpressionType.ListInit:
{
var xt = (ListInitExpression)x;
var yt = (ListInitExpression)y;
return EqualsImpl(xt.NewExpression, yt.NewExpression, parameterContext) && EqualsImpl(xt.Initializers, yt.Initializers, parameterContext);
}
case ExpressionType.MemberAccess:
{
var xt = (MemberExpression)x;
var yt = (MemberExpression)y;
return xt.Member == yt.Member && EqualsImpl(xt.Expression, yt.Expression, parameterContext);
}
case ExpressionType.MemberInit:
{
var xt = (MemberInitExpression)x;
var yt = (MemberInitExpression)y;
if (xt.Bindings.Count != yt.Bindings.Count || !EqualsImpl(xt.NewExpression, yt.NewExpression, parameterContext)) return false;
for (var i = 0; i < xt.Bindings.Count; i++)
if (!EqualsImpl(xt.Bindings[i], yt.Bindings[i], parameterContext)) return false;
return true;
}
case ExpressionType.Call:
{
var xt = (MethodCallExpression)x;
var yt = (MethodCallExpression)y;
if (xt.Arguments.Count != yt.Arguments.Count || xt.Method != yt.Method || !EqualsImpl(xt.Object, yt.Object, parameterContext)) return false;
for (var i = 0; i < xt.Arguments.Count; i++)
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
return true;
}
case ExpressionType.NewArrayBounds:
case ExpressionType.NewArrayInit:
{
var xt = (NewArrayExpression)x;
var yt = (NewArrayExpression)y;
if (xt.Expressions.Count != yt.Expressions.Count) return false;
for (var i = 0; i < xt.Expressions.Count; i++)
if (!EqualsImpl(xt.Expressions[i], yt.Expressions[i], parameterContext)) return false;
return true;
}
case ExpressionType.New:
{
var xt = (NewExpression)x;
var yt = (NewExpression)y;
// I believe NewExpression.Members is guaranteed to be the same if NewExpression.Constructor is the same.
if (xt.Arguments.Count != yt.Arguments.Count || xt.Constructor == yt.Constructor) return false;
for (var i = 0; i < xt.Arguments.Count; i++)
if (!EqualsImpl(xt.Arguments[i], yt.Arguments[i], parameterContext)) return false;
return true;
}
case ExpressionType.Parameter:
{
var xt = (ParameterExpression)x;
var yt = (ParameterExpression)y;
if (parameterContext != null)
{
int xIndex;
var currentContext = parameterContext;
while (true)
{
xIndex =Array.IndexOf(currentContext.XParameters,xt);
if (xIndex != -1) break;
currentContext = currentContext.ParentContext;
if (currentContext == null) throw new InvalidOperationException("X parameter " + xt + " is not contained in a parent lambda context or catch block variable context. Since parameter equality is determined positionally, the equality is ambiguous.");
}
var yIndex = Array.IndexOf(currentContext.YParameters,yt);
if (yIndex == -1) throw new InvalidOperationException("Y parameter " + yt + " is not defined in the same parent lambda context or catch block variable context as the x parameter " + xt + ".");
return xIndex == yIndex;
}
return CompareNames(xt.Name, yt.Name, this.CompareParameterNames) && xt.IsByRef == yt.IsByRef;
}
case ExpressionType.Switch:
{
var xt = (SwitchExpression)x;
var yt = (SwitchExpression)y;
if (xt.Comparison != yt.Comparison || xt.Cases.Count != yt.Cases.Count || !EqualsImpl(xt.SwitchValue, yt.SwitchValue, parameterContext) || !EqualsImpl(xt.DefaultBody, yt.DefaultBody, parameterContext)) return false;
for (var i = 0; i < xt.Cases.Count; i++)
{
var xCase = xt.Cases[i];
var yCase = yt.Cases[i];
if (xCase.TestValues.Count != yCase.TestValues.Count || !EqualsImpl(xCase.Body, yCase.Body, parameterContext)) return false;
for (var ti = 0; ti < xCase.TestValues.Count; ti++)
if (!EqualsImpl(xCase.TestValues[ti], yCase.TestValues[ti], parameterContext)) return false;
}
return true;
}
case ExpressionType.Try:
{
var xt = (TryExpression)x;
var yt = (TryExpression)y;
if (xt.Handlers.Count != yt.Handlers.Count || !EqualsImpl(xt.Body, yt.Body, parameterContext) || !EqualsImpl(xt.Fault, yt.Fault, parameterContext) || !EqualsImpl(xt.Finally, yt.Finally, parameterContext)) return false;
for (var i = 0; i < xt.Handlers.Count; i++)
{
var xHandler = xt.Handlers[i];
var yHandler = yt.Handlers[i];
var newParameterContext = new ParameterContext(parameterContext, new[] { xHandler.Variable }, new[] { yHandler.Variable });
if (xHandler.Test != yHandler.Test || !EqualsImpl(xHandler.Body, yHandler.Body, newParameterContext) || !EqualsImpl(xHandler.Filter, yHandler.Filter, newParameterContext) || !EqualsImpl(xHandler.Variable, yHandler.Variable, null)) return false; // This and the lambda definition are the only cases where we compare parameters by value instead of positionally
}
return true;
}
case ExpressionType.TypeIs:
{
var xt = (TypeBinaryExpression)x;
var yt = (TypeBinaryExpression)y;
return xt.TypeOperand == yt.TypeOperand && EqualsImpl(xt.Expression, yt.Expression, parameterContext);
}
case ExpressionType.Negate:
case ExpressionType.NegateChecked:
case ExpressionType.Not:
case ExpressionType.IsFalse:
case ExpressionType.IsTrue:
case ExpressionType.OnesComplement:
case ExpressionType.ArrayLength:
case ExpressionType.Convert:
case ExpressionType.ConvertChecked:
case ExpressionType.Throw:
case ExpressionType.TypeAs:
case ExpressionType.Quote:
case ExpressionType.UnaryPlus:
case ExpressionType.Unbox:
case ExpressionType.Increment:
case ExpressionType.Decrement:
case ExpressionType.PreIncrementAssign:
case ExpressionType.PostIncrementAssign:
case ExpressionType.PreDecrementAssign:
case ExpressionType.PostDecrementAssign:
{
var xt = (UnaryExpression)x;
var yt = (UnaryExpression)y;
return xt.Method == yt.Method && EqualsImpl(xt.Operand, yt.Operand, parameterContext);
}
default:
throw new NotImplementedException(x.NodeType.ToString());
}
}
private bool EqualsImpl(MemberBinding x, MemberBinding y, ParameterContext parameterContext)
{
if (x.Member != y.Member || x.BindingType != y.BindingType) return false;
switch (x.BindingType)
{
case MemberBindingType.Assignment:
return EqualsImpl(((MemberAssignment)x).Expression, ((MemberAssignment)y).Expression, parameterContext);
case MemberBindingType.MemberBinding:
{
var xtBinding = (MemberMemberBinding)x;
var ytBinding = (MemberMemberBinding)y;
if (xtBinding.Bindings.Count != ytBinding.Bindings.Count) return false;
for (var i = 0; i < xtBinding.Bindings.Count; i++)
if (!EqualsImpl(xtBinding.Bindings[i], ytBinding.Bindings[i], parameterContext)) return false;
return true;
}
case MemberBindingType.ListBinding:
return EqualsImpl(((MemberListBinding)x).Initializers, ((MemberListBinding)y).Initializers, parameterContext);
default:
throw new NotImplementedException(x.BindingType.GetType() + " " + x.BindingType);
}
}
private bool EqualsImpl(IList<ElementInit> x, IList<ElementInit> y, ParameterContext parameterContext)
{
if (x.Count != y.Count) return false;
for (var i = 0; i < x.Count; i++)
{
var xInitializer = x[i];
var yInitializer = y[i];
if (xInitializer.AddMethod != yInitializer.AddMethod || xInitializer.Arguments.Count != yInitializer.Arguments.Count) return false;
for (var ai = 0; ai < xInitializer.Arguments.Count; ai++)
if (!EqualsImpl(xInitializer.Arguments[ai], yInitializer.Arguments[ai], parameterContext)) return false;
}
return true;
}
private sealed class ParameterContext
{
public readonly ParameterContext ParentContext;
public readonly ParameterExpression[] XParameters;
public readonly ParameterExpression[] YParameters;
public ParameterContext(ParameterContext parentContext, ParameterExpression[] xParameters, ParameterExpression[] yParameters)
{
ParentContext = parentContext;
XParameters = xParameters;
YParameters = yParameters;
}
}
private bool CompareNames(string name1, string name2, NameComparison comparison)
{
switch (comparison)
{
case NameComparison.None:
return true;
case NameComparison.CaseSensitive:
return StringComparer.Ordinal.Equals(name1, name2);
case NameComparison.CaseInsensitive:
return StringComparer.OrdinalIgnoreCase.Equals(name1, name2);
default:
throw new InvalidEnumArgumentException("comparison", (int)comparison, typeof(NameComparison));
}
}
public int GetHashCode(Expression obj)
{
// Better to put everything in one bin than to let the default reference-based GetHashCode cause a false negative.
return 0;
}
}
public enum NameComparison
{
None,
CaseSensitive,
CaseInsensitive
}
public enum ConstantComparison
{
ByExpressionReference,
ByCurrentValueReference,
ByCurrentValueValue
}
}

View File

@ -0,0 +1,236 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Core.VirtualRoutes
{
public static class LambdaCompare
{
public static bool Eq<TSource, TValue>(
Expression<Func<TSource, TValue>> x,
Expression<Func<TSource, TValue>> y)
{
return ExpressionsEqual(x, y, null, null);
}
public static bool Eq<TSource1, TSource2, TValue>(
Expression<Func<TSource1, TSource2, TValue>> x,
Expression<Func<TSource1, TSource2, TValue>> y)
{
return ExpressionsEqual(x, y, null, null);
}
public static Expression<Func<Expression<Func<TSource, TValue>>, bool>> Eq<TSource, TValue>(Expression<Func<TSource, TValue>> y)
{
return x => ExpressionsEqual(x, y, null, null);
}
private static bool ExpressionsEqual(Expression x, Expression y, LambdaExpression rootX, LambdaExpression rootY)
{
if (ReferenceEquals(x, y)) return true;
if (x == null || y == null) return false;
var valueX = TryCalculateConstant(x);
var valueY = TryCalculateConstant(y);
if (valueX.IsDefined && valueY.IsDefined)
return ValuesEqual(valueX.Value, valueY.Value);
if (x.NodeType != y.NodeType
|| x.Type != y.Type)
{
if (IsAnonymousType(x.Type) && IsAnonymousType(y.Type))
throw new NotImplementedException("Comparison of Anonymous Types is not supported");
return false;
}
if (x is LambdaExpression)
{
var lx = (LambdaExpression)x;
var ly = (LambdaExpression)y;
var paramsX = lx.Parameters;
var paramsY = ly.Parameters;
return CollectionsEqual(paramsX, paramsY, lx, ly) && ExpressionsEqual(lx.Body, ly.Body, lx, ly);
}
if (x is MemberExpression)
{
var mex = (MemberExpression)x;
var mey = (MemberExpression)y;
return Equals(mex.Member, mey.Member) && ExpressionsEqual(mex.Expression, mey.Expression, rootX, rootY);
}
if (x is BinaryExpression)
{
var bx = (BinaryExpression)x;
var by = (BinaryExpression)y;
return bx.Method == @by.Method && ExpressionsEqual(bx.Left, @by.Left, rootX, rootY) &&
ExpressionsEqual(bx.Right, @by.Right, rootX, rootY);
}
if (x is UnaryExpression)
{
var ux = (UnaryExpression)x;
var uy = (UnaryExpression)y;
return ux.Method == uy.Method && ExpressionsEqual(ux.Operand, uy.Operand, rootX, rootY);
}
if (x is ParameterExpression)
{
var px = (ParameterExpression)x;
var py = (ParameterExpression)y;
return rootX.Parameters.IndexOf(px) == rootY.Parameters.IndexOf(py);
}
if (x is MethodCallExpression)
{
var cx = (MethodCallExpression)x;
var cy = (MethodCallExpression)y;
return cx.Method == cy.Method
&& ExpressionsEqual(cx.Object, cy.Object, rootX, rootY)
&& CollectionsEqual(cx.Arguments, cy.Arguments, rootX, rootY);
}
if (x is MemberInitExpression)
{
var mix = (MemberInitExpression)x;
var miy = (MemberInitExpression)y;
return ExpressionsEqual(mix.NewExpression, miy.NewExpression, rootX, rootY)
&& MemberInitsEqual(mix.Bindings, miy.Bindings, rootX, rootY);
}
if (x is NewArrayExpression)
{
var nx = (NewArrayExpression)x;
var ny = (NewArrayExpression)y;
return CollectionsEqual(nx.Expressions, ny.Expressions, rootX, rootY);
}
if (x is NewExpression)
{
var nx = (NewExpression)x;
var ny = (NewExpression)y;
return
Equals(nx.Constructor, ny.Constructor)
&& CollectionsEqual(nx.Arguments, ny.Arguments, rootX, rootY)
&& (nx.Members == null && ny.Members == null
|| nx.Members != null && ny.Members != null && CollectionsEqual(nx.Members, ny.Members));
}
if (x is ConditionalExpression)
{
var cx = (ConditionalExpression)x;
var cy = (ConditionalExpression)y;
return
ExpressionsEqual(cx.Test, cy.Test, rootX, rootY)
&& ExpressionsEqual(cx.IfFalse, cy.IfFalse, rootX, rootY)
&& ExpressionsEqual(cx.IfTrue, cy.IfTrue, rootX, rootY);
}
throw new NotImplementedException(x.ToString());
}
private static Boolean IsAnonymousType(Type type)
{
Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any();
Boolean nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;
return isAnonymousType;
}
private static bool MemberInitsEqual(ICollection<MemberBinding> bx, ICollection<MemberBinding> by, LambdaExpression rootX, LambdaExpression rootY)
{
if (bx.Count != by.Count)
{
return false;
}
if (bx.Concat(by).Any(b => b.BindingType != MemberBindingType.Assignment))
throw new NotImplementedException("Only MemberBindingType.Assignment is supported");
return
bx.Cast<MemberAssignment>().OrderBy(b => b.Member.Name).Select((b, i) => new { Expr = b.Expression, b.Member, Index = i })
.Join(
by.Cast<MemberAssignment>().OrderBy(b => b.Member.Name).Select((b, i) => new { Expr = b.Expression, b.Member, Index = i }),
o => o.Index, o => o.Index, (xe, ye) => new { XExpr = xe.Expr, XMember = xe.Member, YExpr = ye.Expr, YMember = ye.Member })
.All(o => Equals(o.XMember, o.YMember) && ExpressionsEqual(o.XExpr, o.YExpr, rootX, rootY));
}
private static bool ValuesEqual(object x, object y)
{
if (ReferenceEquals(x, y))
return true;
if (x is ICollection && y is ICollection)
return CollectionsEqual((ICollection)x, (ICollection)y);
return Equals(x, y);
}
private static ConstantValue TryCalculateConstant(Expression e)
{
if (e is ConstantExpression)
return new ConstantValue(true, ((ConstantExpression)e).Value);
if (e is MemberExpression)
{
var me = (MemberExpression)e;
var parentValue = TryCalculateConstant(me.Expression);
if (parentValue.IsDefined)
{
var result =
me.Member is FieldInfo
? ((FieldInfo)me.Member).GetValue(parentValue.Value)
: ((PropertyInfo)me.Member).GetValue(parentValue.Value);
return new ConstantValue(true, result);
}
}
if (e is NewArrayExpression)
{
var ae = ((NewArrayExpression)e);
var result = ae.Expressions.Select(TryCalculateConstant);
if (result.All(i => i.IsDefined))
return new ConstantValue(true, result.Select(i => i.Value).ToArray());
}
if (e is ConditionalExpression)
{
var ce = (ConditionalExpression)e;
var evaluatedTest = TryCalculateConstant(ce.Test);
if (evaluatedTest.IsDefined)
{
return TryCalculateConstant(Equals(evaluatedTest.Value, true) ? ce.IfTrue : ce.IfFalse);
}
}
return default(ConstantValue);
}
private static bool CollectionsEqual(IEnumerable<Expression> x, IEnumerable<Expression> y, LambdaExpression rootX, LambdaExpression rootY)
{
return x.Count() == y.Count()
&& x.Select((e, i) => new { Expr = e, Index = i })
.Join(y.Select((e, i) => new { Expr = e, Index = i }),
o => o.Index, o => o.Index, (xe, ye) => new { X = xe.Expr, Y = ye.Expr })
.All(o => ExpressionsEqual(o.X, o.Y, rootX, rootY));
}
private static bool CollectionsEqual(ICollection x, ICollection y)
{
return x.Count == y.Count
&& x.Cast<object>().Select((e, i) => new { Expr = e, Index = i })
.Join(y.Cast<object>().Select((e, i) => new { Expr = e, Index = i }),
o => o.Index, o => o.Index, (xe, ye) => new { X = xe.Expr, Y = ye.Expr })
.All(o => Equals(o.X, o.Y));
}
private struct ConstantValue
{
public ConstantValue(bool isDefined, object value)
: this()
{
IsDefined = isDefined;
Value = value;
}
public bool IsDefined { get; private set; }
public object Value { get; private set; }
}
}
}

View File

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using ShardingCore.Core.EntityMetadatas;
namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
{
@ -22,7 +21,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
protected override List<IPhysicTable> DoRouteWithPredicate(List<IPhysicTable> allPhysicTables, IQueryable queryable)
{
//获取所有需要路由的表后缀
var filter = ShardingUtil.GetRouteShardingTableFilter(queryable, EntityMetadata, ConvertToShardingKey, GetRouteToFilter,true);
var filter = ShardingUtil.GetRouteShardingTableFilter<TKey>(queryable, EntityMetadata, GetRouteToFilter,true);
var physicTables = allPhysicTables.Where(o => filter(o.Tail)).ToList();
return physicTables;
}

View File

@ -33,14 +33,6 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
return null;
}
/// <summary>
/// 如何将分表字段转成对应的类型
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
protected abstract TKey ConvertToShardingKey(object shardingKey);
public EntityMetadata EntityMetadata { get; private set; }
/// <summary>

View File

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Core.VirtualRoutes.TableRoutes
{
internal static class ExpressionHasher
{
private const int NullHashCode = 0x61E04917;
[ThreadStatic]
private static HashVisitor _visitor;
private static HashVisitor Visitor
{
get
{
if (_visitor == null)
_visitor = new HashVisitor();
return _visitor;
}
}
public static int GetHashCode(Expression e)
{
if (e == null)
return NullHashCode;
var visitor = Visitor;
visitor.Reset();
visitor.Visit(e);
return visitor.Hash;
}
private sealed class HashVisitor : ExpressionVisitor
{
private int _hash;
internal int Hash
{
get { return _hash; }
}
internal void Reset()
{
_hash = 0;
}
private void UpdateHash(int value)
{
_hash = (_hash * 397) ^ value;
}
private void UpdateHash(object component)
{
int componentHash;
if (component == null)
{
componentHash = NullHashCode;
}
else
{
var member = component as MemberInfo;
if (member != null)
{
componentHash = member.Name.GetHashCode();
var declaringType = member.DeclaringType;
if (declaringType != null && declaringType.AssemblyQualifiedName != null)
componentHash = (componentHash * 397) ^ declaringType.AssemblyQualifiedName.GetHashCode();
}
else
{
componentHash = component.GetHashCode();
}
}
_hash = (_hash * 397) ^ componentHash;
}
public override Expression Visit(Expression node)
{
UpdateHash((int)node.NodeType);
return base.Visit(node);
}
protected override Expression VisitConstant(ConstantExpression node)
{
UpdateHash(node.Value);
return base.VisitConstant(node);
}
protected override Expression VisitMember(MemberExpression node)
{
UpdateHash(node.Member);
return base.VisitMember(node);
}
protected override MemberAssignment VisitMemberAssignment(MemberAssignment node)
{
UpdateHash(node.Member);
return base.VisitMemberAssignment(node);
}
protected override MemberBinding VisitMemberBinding(MemberBinding node)
{
UpdateHash((int)node.BindingType);
UpdateHash(node.Member);
return base.VisitMemberBinding(node);
}
protected override MemberListBinding VisitMemberListBinding(MemberListBinding node)
{
UpdateHash((int)node.BindingType);
UpdateHash(node.Member);
return base.VisitMemberListBinding(node);
}
protected override MemberMemberBinding VisitMemberMemberBinding(MemberMemberBinding node)
{
UpdateHash((int)node.BindingType);
UpdateHash(node.Member);
return base.VisitMemberMemberBinding(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
UpdateHash(node.Method);
return base.VisitMethodCall(node);
}
protected override Expression VisitNew(NewExpression node)
{
UpdateHash(node.Constructor);
return base.VisitNew(node);
}
protected override Expression VisitNewArray(NewArrayExpression node)
{
UpdateHash(node.Type);
return base.VisitNewArray(node);
}
protected override Expression VisitParameter(ParameterExpression node)
{
UpdateHash(node.Type);
return base.VisitParameter(node);
}
protected override Expression VisitTypeBinary(TypeBinaryExpression node)
{
UpdateHash(node.Type);
return base.VisitTypeBinary(node);
}
}
}
}

View File

@ -0,0 +1,315 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Core.VirtualRoutes.TableRoutes
{
public static class QueryResultCache
{
private static readonly ConcurrentDictionary<object, Func<string, bool>> _routeFilter =
new ConcurrentDictionary<object, Func<string, bool>>();
/// <summary>
/// Returns the result of the query; if possible from the cache, otherwise
/// the query is materialized and the result cached before being returned.
/// </summary>
public static Func<string, bool> FromCache(this Expression<Func<string,bool>> query)
{
var startNew = Stopwatch.StartNew();
for (int i = 0; i < 300; i++)
{
string key1 = query.GetCacheKey();
}
startNew.Stop();
var x = startNew.ElapsedMilliseconds;
string key = query.GetCacheKey();
return _routeFilter.GetOrAdd(key, k => query.Compile());
}
/// <summary>
/// Gets a cache key for a query.
/// </summary>
public static string GetCacheKey(this Expression expression)
{
// locally evaluate as much of the query as possible
expression = Evaluator.PartialEval(expression, QueryResultCache.CanBeEvaluatedLocally);
// support local collections
expression = LocalCollectionExpander.Rewrite(expression);
// use the string representation of the expression for the cache key
string key = expression.ToString();
// the key is potentially very long, so use an md5 fingerprint
// (fine if the query result data isn't critically sensitive)
key = key.ToMd5Fingerprint();
return key;
}
static Func<Expression, bool> CanBeEvaluatedLocally
{
get
{
return expression =>
{
// don't evaluate parameters
if (expression.NodeType == ExpressionType.Parameter)
return false;
// can't evaluate queries
if (typeof(IQueryable).IsAssignableFrom(expression.Type))
return false;
return true;
};
}
}
}
/// <summary>
/// Enables the partial evaluation of queries.
/// </summary>
/// <remarks>
/// From http://msdn.microsoft.com/en-us/library/bb546158.aspx
/// Copyright notice http://msdn.microsoft.com/en-gb/cc300389.aspx#O
/// </remarks>
public static class Evaluator
{
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
{
return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
}
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression PartialEval(Expression expression)
{
return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
}
private static bool CanBeEvaluatedLocally(Expression expression)
{
return expression.NodeType != ExpressionType.Parameter;
}
/// <summary>
/// Evaluates & replaces sub-trees when first candidate is reached (top-down)
/// </summary>
class SubtreeEvaluator : ExpressionVisitor
{
HashSet<Expression> candidates;
internal SubtreeEvaluator(HashSet<Expression> candidates)
{
this.candidates = candidates;
}
internal Expression Eval(Expression exp)
{
return this.Visit(exp);
}
public override Expression Visit(Expression exp)
{
if (exp == null)
{
return null;
}
if (this.candidates.Contains(exp))
{
return this.Evaluate(exp);
}
return base.Visit(exp);
}
private Expression Evaluate(Expression e)
{
if (e.NodeType == ExpressionType.Constant)
{
return e;
}
LambdaExpression lambda = Expression.Lambda(e);
Delegate fn = lambda.Compile();
return Expression.Constant(fn.DynamicInvoke(null), e.Type);
}
}
/// <summary>
/// Performs bottom-up analysis to determine which nodes can possibly
/// be part of an evaluated sub-tree.
/// </summary>
class Nominator : ExpressionVisitor
{
Func<Expression, bool> fnCanBeEvaluated;
HashSet<Expression> candidates;
bool cannotBeEvaluated;
internal Nominator(Func<Expression, bool> fnCanBeEvaluated)
{
this.fnCanBeEvaluated = fnCanBeEvaluated;
}
internal HashSet<Expression> Nominate(Expression expression)
{
this.candidates = new HashSet<Expression>();
this.Visit(expression);
return this.candidates;
}
public override Expression Visit(Expression expression)
{
if (expression != null)
{
bool saveCannotBeEvaluated = this.cannotBeEvaluated;
this.cannotBeEvaluated = false;
base.Visit(expression);
if (!this.cannotBeEvaluated)
{
if (this.fnCanBeEvaluated(expression))
{
this.candidates.Add(expression);
}
else
{
this.cannotBeEvaluated = true;
}
}
this.cannotBeEvaluated |= saveCannotBeEvaluated;
}
return expression;
}
}
}
/// <summary>
/// Enables cache key support for local collection values.
/// </summary>
public class LocalCollectionExpander : ExpressionVisitor
{
public static Expression Rewrite(Expression expression)
{
return new LocalCollectionExpander().Visit(expression);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
// pair the method's parameter types with its arguments
var map = node.Method.GetParameters()
.Zip(node.Arguments, (p, a) => new { Param = p.ParameterType, Arg = a })
.ToLinkedList();
// deal with instance methods
var instanceType = node.Object == null ? null : node.Object.Type;
map.AddFirst(new { Param = instanceType, Arg = node.Object });
// for any local collection parameters in the method, make a
// replacement argument which will print its elements
var replacements = (from x in map
where x.Param != null && x.Param.IsGenericType
let g = x.Param.GetGenericTypeDefinition()
where g == typeof(IEnumerable<>) || g == typeof(List<>)
where x.Arg.NodeType == ExpressionType.Constant
let elementType = x.Param.GetGenericArguments().Single()
let printer = MakePrinter((ConstantExpression)x.Arg, elementType)
select new { x.Arg, Replacement = printer }).ToList();
if (replacements.Any())
{
var args = map.Select(x => (from r in replacements
where r.Arg == x.Arg
select r.Replacement).SingleOrDefault() ?? x.Arg).ToList();
node = node.Update(args.First(), args.Skip(1));
}
return base.VisitMethodCall(node);
}
ConstantExpression MakePrinter(ConstantExpression enumerable, Type elementType)
{
var value = (IEnumerable)enumerable.Value;
var printerType = typeof(Printer<>).MakeGenericType(elementType);
var printer = Activator.CreateInstance(printerType, value);
return Expression.Constant(printer);
}
/// <summary>
/// Overrides ToString to print each element of a collection.
/// </summary>
/// <remarks>
/// Inherits List in order to support List.Contains instance method as well
/// as standard Enumerable.Contains/Any extension methods.
/// </remarks>
class Printer<T> : List<T>
{
public Printer(IEnumerable collection)
{
this.AddRange(collection.Cast<T>());
}
public override string ToString()
{
return "{" + this.ToConcatenatedString(t => t.ToString(), "|") + "}";
}
}
}
public static class Utility
{
/// <summary>
/// Creates an MD5 fingerprint of the string.
/// </summary>
public static string ToMd5Fingerprint(this string s)
{
var bytes = Encoding.Unicode.GetBytes(s.ToCharArray());
var hash = new MD5CryptoServiceProvider().ComputeHash(bytes);
// concat the hash bytes into one long string
return hash.Aggregate(new StringBuilder(32),
(sb, b) => sb.Append(b.ToString("X2")))
.ToString();
}
public static string ToConcatenatedString<T>(this IEnumerable<T> source, Func<T, string> selector, string separator)
{
var b = new StringBuilder();
bool needSeparator = false;
foreach (var item in source)
{
if (needSeparator)
b.Append(separator);
b.Append(selector(item));
needSeparator = true;
}
return b.ToString();
}
public static LinkedList<T> ToLinkedList<T>(this IEnumerable<T> source)
{
return new LinkedList<T>(source);
}
}
}

View File

@ -10,9 +10,6 @@ using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Sharding.Abstractions;
#if !EFCORE5
using ShardingCore.Extensions;
#endif
namespace ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine
{
@ -36,7 +33,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine
public IEnumerable<TableRouteResult> Route<T>(TableRouteRuleContext<T> tableRouteRuleContext)
{
Dictionary<IVirtualTable, ISet<IPhysicTable>> routeMaps = new Dictionary<IVirtualTable, ISet<IPhysicTable>>();
var queryEntities = tableRouteRuleContext.Queryable.ParseQueryableRoute();
var queryEntities = tableRouteRuleContext.Queryable.ParseQueryableEntities();
var shardingEntities = queryEntities.Where(o => _entityMetadataManager.IsShardingTable(o));

View File

@ -25,11 +25,16 @@ using ShardingCore.Sharding.ShardingQueryExecutors;
using ShardingCore.TableCreator;
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore.Storage;
using ShardingCore.Bootstrapers;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.EFCores.OptionsExtensions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Jobs;
using ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge;
using ShardingCore.Sharding.StreamMergeEngines;
namespace ShardingCore
{

View File

@ -91,7 +91,7 @@ namespace ShardingCore.Extensions
return express.Method.DeclaringType.Namespace.IsIn("System.Linq", "System.Collections.Generic") && methodName == nameof(IList.Contains);
}
public static ISet<Type> ParseQueryableRoute(this IQueryable queryable)
public static ISet<Type> ParseQueryableEntities(this IQueryable queryable)
{
return ShardingUtil.GetQueryEntitiesFilter(queryable);
}

View File

@ -78,11 +78,10 @@ namespace ShardingCore.Extensions
/// </summary>
/// <param name="source">元数据源</param>
/// <param name="dbContext">新数据源</param>
/// <param name="isParallelQuery">是否是并行查询,是的话直接启用asnotracking</param>
/// <returns></returns>
internal static IQueryable ReplaceDbContextQueryable(this IQueryable source, DbContext dbContext,bool isParallelQuery)
internal static IQueryable ReplaceDbContextQueryable(this IQueryable source, DbContext dbContext)
{
DbContextReplaceQueryableVisitor replaceQueryableVisitor = new DbContextReplaceQueryableVisitor(dbContext,isParallelQuery);
DbContextReplaceQueryableVisitor replaceQueryableVisitor = new DbContextReplaceQueryableVisitor(dbContext);
var newExpression = replaceQueryableVisitor.Visit(source.Expression);
return replaceQueryableVisitor.Source.Provider.CreateQuery(newExpression);
}

View File

@ -40,7 +40,7 @@ namespace ShardingCore.Extensions
{
var entityMetadataManager = ShardingContainer.GetService<IEntityMetadataManager<TShardingDbContext>>();
var queryEntities = streamMergeContext.GetOriginalQueryable().ParseQueryableRoute();
var queryEntities = streamMergeContext.GetOriginalQueryable().ParseQueryableEntities();
//仅一个对象支持分库或者分表的组合
return queryEntities.Count(o=>(entityMetadataManager.IsShardingDataSource(o) &&!entityMetadataManager.IsShardingTable(o)) ||(entityMetadataManager.IsShardingDataSource(o)&& entityMetadataManager.IsShardingTable(o))|| (!entityMetadataManager.IsShardingDataSource(o) && entityMetadataManager.IsShardingTable(o))) ==1;
}

View File

@ -67,6 +67,7 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions
{
throw new ShardingCoreParallelQueryTimeOutException(_executeExpression.ShardingPrint());
}
}
public void Dispose()

View File

@ -22,6 +22,11 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge.Abstract
public override TResult MergeResult<TResult>()
{
var current = DoMergeResult<TResult>();
return ProcessTrackResult(current);
}
private TResult ProcessTrackResult<TResult>(TResult current)
{
if (current != null)
{
if (GetStreamMergeContext().IsUseShardingTrack(current.GetType()))
@ -40,27 +45,11 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge.Abstract
}
return current;
}
public override async Task<TResult> MergeResultAsync<TResult>(CancellationToken cancellationToken = new CancellationToken())
{
cancellationToken.ThrowIfCancellationRequested();
var current = await DoMergeResultAsync<TResult>(cancellationToken);
if (current != null)
{
if (GetStreamMergeContext().IsUseShardingTrack(current.GetType()))
{
var c = (object)current;
var genericDbContext = GetStreamMergeContext().GetShardingDbContext().CreateGenericDbContext(c);
var attachedEntity = genericDbContext.GetAttachedEntity(c);
if (attachedEntity == null)
genericDbContext.Attach(current);
else
{
return (TResult)attachedEntity;
}
}
}
return current;
return ProcessTrackResult(current);
}
public abstract TResult DoMergeResult<TResult>();

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
@ -53,6 +54,7 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge
{
if (_secondExpression == null)
throw new ShardingCoreInvalidOperationException(methodCallExpression.ShardingPrint());
// ReSharper disable once VirtualMemberCallInConstructor
_queryable = CombineQueryable(_queryable, _secondExpression);
}
@ -72,7 +74,7 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge
{
var shardingDbContext = _mergeContext.CreateDbContext(dsname, tableRouteResult);
var newQueryable = (IQueryable<TEntity>)GetStreamMergeContext().GetReWriteQueryable()
.ReplaceDbContextQueryable(shardingDbContext,_mergeContext.IsParallelQuery());
.ReplaceDbContextQueryable(shardingDbContext);
var newCombineQueryable = DoCombineQueryable<TResult>(newQueryable);
return newCombineQueryable
;
@ -86,9 +88,13 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge
{
return _mergeContext.TableRouteResults.Select(routeResult =>
{
var asyncExecuteQueryable = CreateAsyncExecuteQueryable<TResult>(dataSourceName, routeResult);
return Task.Run(async () =>
{
var asyncExecuteQueryable = CreateAsyncExecuteQueryable<TResult>(dataSourceName, routeResult);
return AsyncParallelResultExecute(asyncExecuteQueryable,dataSourceName,routeResult,efQuery, cancellationToken);
return await AsyncParallelResultExecute(asyncExecuteQueryable, dataSourceName, routeResult, efQuery,
cancellationToken);
});
});
}).ToArray();

View File

@ -37,7 +37,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
_routeQueryResults = routeQueryResults;
}
public override IStreamMergeAsyncEnumerator<TEntity>[] GetRouteQueryStreamMergeAsyncEnumerators(bool async,CancellationToken cancellationToken=new CancellationToken())
public override IStreamMergeAsyncEnumerator<TEntity>[] GetRouteQueryStreamMergeAsyncEnumerators(bool async, CancellationToken cancellationToken = new CancellationToken())
{
cancellationToken.ThrowIfCancellationRequested();
var noPaginationQueryable = StreamMergeContext.GetOriginalQueryable().RemoveSkip().RemoveTake();
@ -64,7 +64,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
//if sharding data source
var appendAsc = _dataSourceSequenceOrderConfig.AppendAsc;
//if sharding table
var useThenBy = _tableSequenceOrderConfig != null;
var useThenBy = _tableSequenceOrderConfig != null;
if (appendAsc)
{
sortRouteResults = sortRouteResults.OrderBy(o => o.DataSourceName,
@ -104,8 +104,11 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
StreamMergeContext.ReSetOrders(reSetOrders);
var enumeratorTasks = sequenceResults.Select(sequenceResult =>
{
var newQueryable = CreateAsyncExecuteQueryable(sequenceResult.DSName, noPaginationQueryable, sequenceResult, reSetOrders);
return AsyncParallelEnumerator(newQueryable, async, cancellationToken);
return Task.Run(async () =>
{
var newQueryable = CreateAsyncExecuteQueryable(sequenceResult.DSName, noPaginationQueryable, sequenceResult, reSetOrders);
return await AsyncParallelEnumerator(newQueryable, async, cancellationToken);
});
}).ToArray();
var streamEnumerators = Task.WhenAll(enumeratorTasks).WaitAndUnwrapException();
@ -116,7 +119,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
{
var shardingDbContext = StreamMergeContext.CreateDbContext(dsname, sequenceResult.TableRouteResult);
var newQueryable = (IQueryable<TEntity>)(noPaginationQueryable.Skip(sequenceResult.Skip).Take(sequenceResult.Take).OrderWithExpression(reSetOrders))
.ReplaceDbContextQueryable(shardingDbContext,StreamMergeContext.IsParallelQuery());
.ReplaceDbContextQueryable(shardingDbContext);
return newQueryable;
}

View File

@ -34,8 +34,11 @@ namespace ShardingCore.Sharding.MergeEngines.EnumeratorStreamMergeEngines.Enumer
var tableRouteResults = StreamMergeContext.TableRouteResults;
return tableRouteResults.Select(routeResult =>
{
var newQueryable = CreateAsyncExecuteQueryable(dataSourceName, routeResult);
return AsyncParallelEnumerator(newQueryable, async,cancellationToken);
return Task.Run(async () =>
{
var newQueryable = CreateAsyncExecuteQueryable(dataSourceName, routeResult);
return await AsyncParallelEnumerator(newQueryable, async, cancellationToken);
});
});
}).ToArray();
@ -48,7 +51,7 @@ namespace ShardingCore.Sharding.MergeEngines.EnumeratorStreamMergeEngines.Enumer
{
var shardingDbContext = StreamMergeContext.CreateDbContext(dsname,tableRouteResult);
var newQueryable = (IQueryable<TEntity>)StreamMergeContext.GetReWriteQueryable()
.ReplaceDbContextQueryable(shardingDbContext,StreamMergeContext.IsParallelQuery());
.ReplaceDbContextQueryable(shardingDbContext);
return newQueryable;
}

View File

@ -50,8 +50,12 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
{
return StreamMergeContext.TableRouteResults.Select(routeResult =>
{
var newQueryable = CreateAsyncExecuteQueryable(dataSourceName, reverseOrderQueryable, routeResult);
return AsyncParallelEnumerator(newQueryable, async,cancellationToken);
return Task.Run(async () =>
{
var newQueryable =
CreateAsyncExecuteQueryable(dataSourceName, reverseOrderQueryable, routeResult);
return await AsyncParallelEnumerator(newQueryable, async, cancellationToken);
});
});
}).ToArray();;
@ -63,7 +67,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
{
var shardingDbContext = StreamMergeContext.CreateDbContext(dsname,tableRouteResult);
var newQueryable = (IQueryable<TEntity>)reverseOrderQueryable
.ReplaceDbContextQueryable(shardingDbContext,StreamMergeContext.IsParallelQuery());
.ReplaceDbContextQueryable(shardingDbContext);
return newQueryable;
}

View File

@ -23,7 +23,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
internal class SequenceEnumeratorAsyncStreamMergeEngine<TShardingDbContext,TEntity> : AbstractEnumeratorStreamMergeEngine<TEntity>
internal class SequenceEnumeratorAsyncStreamMergeEngine<TShardingDbContext, TEntity> : AbstractEnumeratorStreamMergeEngine<TEntity>
where TShardingDbContext : DbContext, IShardingDbContext
{
private readonly PaginationSequenceConfig _dataSourceSequenceMatchOrderConfig;
@ -38,7 +38,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
_isAsc = isAsc;
}
public override IStreamMergeAsyncEnumerator<TEntity>[] GetRouteQueryStreamMergeAsyncEnumerators(bool async,CancellationToken cancellationToken=new CancellationToken())
public override IStreamMergeAsyncEnumerator<TEntity>[] GetRouteQueryStreamMergeAsyncEnumerators(bool async, CancellationToken cancellationToken = new CancellationToken())
{
cancellationToken.ThrowIfCancellationRequested();
var noPaginationQueryable = StreamMergeContext.GetOriginalQueryable().RemoveSkip().RemoveTake();
@ -53,7 +53,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
var dataSourceOrderMain = _dataSourceSequenceMatchOrderConfig != null;
var sortRouteResults = _routeQueryResults.Select(o => new
{
DataSourceName=o.DataSourceName,
DataSourceName = o.DataSourceName,
Tail = o.TableRouteResult.ReplaceTables.First().Tail,
RouteQueryResult = o
});
@ -64,7 +64,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
if (_isAsc)
{
sortRouteResults = sortRouteResults.OrderBy(o => o.DataSourceName,
_dataSourceSequenceMatchOrderConfig.RouteComparer).ThenByIf(o=>o.Tail, useThenBy, _tableSequenceMatchOrderConfig.RouteComparer);
_dataSourceSequenceMatchOrderConfig.RouteComparer).ThenByIf(o => o.Tail, useThenBy, _tableSequenceMatchOrderConfig.RouteComparer);
}
else
{
@ -91,19 +91,23 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
var enumeratorTasks = sequenceResults.Select(sequenceResult =>
{
var newQueryable = CreateAsyncExecuteQueryable(sequenceResult.DSName, noPaginationQueryable, sequenceResult);
return AsyncParallelEnumerator(newQueryable, async,cancellationToken);
return Task.Run(async () =>
{
var newQueryable =
CreateAsyncExecuteQueryable(sequenceResult.DSName, noPaginationQueryable, sequenceResult);
return await AsyncParallelEnumerator(newQueryable, async, cancellationToken);
});
}).ToArray();
var streamEnumerators = Task.WhenAll(enumeratorTasks).WaitAndUnwrapException();
return streamEnumerators;
}
private IQueryable<TEntity> CreateAsyncExecuteQueryable(string dsname,IQueryable<TEntity> noPaginationQueryable, SequenceResult sequenceResult)
private IQueryable<TEntity> CreateAsyncExecuteQueryable(string dsname, IQueryable<TEntity> noPaginationQueryable, SequenceResult sequenceResult)
{
var shardingDbContext = StreamMergeContext.CreateDbContext(dsname,sequenceResult.TableRouteResult);
var shardingDbContext = StreamMergeContext.CreateDbContext(dsname, sequenceResult.TableRouteResult);
var newQueryable = (IQueryable<TEntity>)(noPaginationQueryable.Skip(sequenceResult.Skip).Take(sequenceResult.Take))
.ReplaceDbContextQueryable(shardingDbContext,StreamMergeContext.IsParallelQuery());
.ReplaceDbContextQueryable(shardingDbContext);
return newQueryable;
}

View File

@ -28,7 +28,7 @@ namespace ShardingCore.Sharding.StreamMergeEngines.EnumeratorStreamMergeEngines.
var dataSourceName = StreamMergeContext.DataSourceRouteResult.IntersectDataSources.First();
var routeResult = StreamMergeContext.TableRouteResults.First();
var shardingDbContext = StreamMergeContext.CreateDbContext(dataSourceName,routeResult);
var newQueryable = (IQueryable<TEntity>) StreamMergeContext.GetOriginalQueryable().ReplaceDbContextQueryable(shardingDbContext,StreamMergeContext.IsParallelQuery());
var newQueryable = (IQueryable<TEntity>) StreamMergeContext.GetOriginalQueryable().ReplaceDbContextQueryable(shardingDbContext);
if (async)
{
var asyncEnumerator = GetAsyncEnumerator0(newQueryable).WaitAndUnwrapException();

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
@ -13,6 +14,7 @@ using ShardingCore.Helpers;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.Enumerators;
using ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge.AbstractGenericMergeEngines;
using ShardingCore.Utils;
namespace ShardingCore.Sharding.StreamMergeEngines
{
@ -36,8 +38,10 @@ namespace ShardingCore.Sharding.StreamMergeEngines
public override async Task<TResult> DoMergeResultAsync<TResult>(CancellationToken cancellationToken = new CancellationToken())
{
var result = await base.ExecuteAsync( queryable => ((IQueryable<TResult>)queryable).FirstOrDefaultAsync(cancellationToken), cancellationToken);
var notNullResult = result.Where(o => o != null&&o.QueryResult!=null).Select(o=>o.QueryResult).ToList();
var notNullResult = result.Where(o => o != null && o.QueryResult != null).Select(o => o.QueryResult).ToList();
if (notNullResult.IsEmpty())
return default;

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.Logging;
using ShardingCore.Core;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
using ShardingCore.DbContexts;
using ShardingCore.DbContexts.ShardingDbContexts;

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@ -7,6 +8,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.Core;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Sharding.Abstractions;
@ -29,6 +31,10 @@ namespace ShardingCore.Sharding.ShardingQueryExecutors
*/
public class DefaultShardingQueryExecutor : IShardingQueryExecutor
{
static DefaultShardingQueryExecutor()
{
}
public TResult Execute<TResult>(ICurrentDbContext currentContext, Expression query)
{
@ -145,19 +151,64 @@ namespace ShardingCore.Sharding.ShardingQueryExecutors
return (TResult)Activator.CreateInstance(streamMergeEngineType, streamMergeContext);
}
public class AAA
{
private readonly IShardingDbContext _shardingDbContext;
private readonly MethodCallExpression _query;
public AAA(IShardingDbContext shardingDbContext, MethodCallExpression query)
{
_shardingDbContext = shardingDbContext;
_query = query;
}
}
private TResult GenericShardingDbContextMergeExecute<TResult>(Type streamMergeEngineType, IShardingDbContext shardingDbContext, MethodCallExpression query, bool async, CancellationToken cancellationToken)
{
var queryEntityType = query.GetQueryEntityType();
var resultEntityType = query.GetResultType();
streamMergeEngineType = streamMergeEngineType.MakeGenericType(shardingDbContext.GetType(), queryEntityType);
var streamEngine = Activator.CreateInstance(streamMergeEngineType, query, shardingDbContext);
var methodName = async ? nameof(IGenericMergeResult.MergeResultAsync) : nameof(IGenericMergeResult.MergeResult);
var streamEngineMethod = streamMergeEngineType.GetMethod(methodName);
if (streamEngineMethod == null)
throw new ShardingCoreException($"cant found InMemoryAsyncStreamMergeEngine method [{methodName}]");
var @params = async ? new object[] { cancellationToken } : new object[0];
return (TResult)streamEngineMethod.MakeGenericMethod(new Type[] { queryEntityType }).Invoke(streamEngine, @params);
//{
// var queryEntityType = query.GetQueryEntityType();
// var newStreamMergeEngineType = streamMergeEngineType.MakeGenericType(shardingDbContext.GetType(), queryEntityType);
// //{
// // //获取所有需要路由的表后缀
// // var startNew = Stopwatch.StartNew();
// // for (int i = 0; i < 10000; i++)
// // {
// // var streamEngine = ShardingCreatorHelper.CreateInstance(newStreamMergeEngineType, query, shardingDbContext);
// // }
// // startNew.Stop();
// // var x = startNew.ElapsedMilliseconds;
// //}
// {
// //获取所有需要路由的表后缀
// var startNew1 = Stopwatch.StartNew();
// for (int i = 0; i < 1; i++)
// {
// var streamEngine = Activator.CreateInstance(typeof(AAA), shardingDbContext, query);
// }
// startNew1.Stop();
// var x = startNew1.ElapsedMilliseconds;
// }
// var methodName = async ? nameof(IGenericMergeResult.MergeResultAsync) : nameof(IGenericMergeResult.MergeResult);
// var streamEngineMethod = newStreamMergeEngineType.GetMethod(methodName);
// if (streamEngineMethod == null)
// throw new ShardingCoreException($"cant found InMemoryAsyncStreamMergeEngine method [{methodName}]");
// var @params = async ? new object[] { cancellationToken } : new object[0];
//}
{
var queryEntityType = query.GetQueryEntityType();
var newStreamMergeEngineType = streamMergeEngineType.MakeGenericType(shardingDbContext.GetType(), queryEntityType);
var streamEngine = Activator.CreateInstance(newStreamMergeEngineType, query, shardingDbContext);
var methodName = async ? nameof(IGenericMergeResult.MergeResultAsync) : nameof(IGenericMergeResult.MergeResult);
var streamEngineMethod = newStreamMergeEngineType.GetMethod(methodName);
if (streamEngineMethod == null)
throw new ShardingCoreException($"cant found InMemoryAsyncStreamMergeEngine method [{methodName}]");
var @params = async ? new object[] { cancellationToken } : new object[0];
return (TResult)streamEngineMethod.MakeGenericMethod(new Type[] { queryEntityType }).Invoke(streamEngine, @params);
}
}
private TResult GenericMergeExecute2<TResult>(Type streamMergeEngineType, IShardingDbContext shardingDbContext, MethodCallExpression query, bool async, CancellationToken cancellationToken)
{

View File

@ -26,17 +26,17 @@ namespace ShardingCore.Sharding
* @Date: Monday, 25 January 2021 11:38:27
* @Email: 326308290@qq.com
*/
public class StreamMergeContext<T>:IDisposable
public class StreamMergeContext<TEntity>:IDisposable
#if !EFCORE2
,IAsyncDisposable
#endif
{
//private readonly IShardingScopeFactory _shardingScopeFactory;
private readonly IQueryable<T> _source;
private readonly IQueryable<TEntity> _source;
private readonly IShardingDbContext _shardingDbContext;
private readonly IRouteTailFactory _routeTailFactory;
private readonly IQueryable<T> _reWriteSource;
private readonly IQueryable<TEntity> _reWriteSource;
//public IEnumerable<TableRouteResult> RouteResults { get; }
//public DataSourceRouteResult RoutingResult { get; }
public int? Skip { get; private set; }
@ -71,7 +71,7 @@ namespace ShardingCore.Sharding
private readonly IShardingComparer _shardingComparer;
public StreamMergeContext(IQueryable<T> source, IShardingDbContext shardingDbContext,
public StreamMergeContext(IQueryable<TEntity> source, IShardingDbContext shardingDbContext,
DataSourceRouteResult dataSourceRouteResult,
IEnumerable<TableRouteResult> tableRouteResults,
IRouteTailFactory routeTailFactory)
@ -84,7 +84,7 @@ namespace ShardingCore.Sharding
TableRouteResults = tableRouteResults;
IsCrossDataSource = dataSourceRouteResult.IntersectDataSources.Count > 1;
IsCrossTable = tableRouteResults.Count() > 1;
var reWriteResult = new ReWriteEngine<T>(source).ReWrite();
var reWriteResult = new ReWriteEngine<TEntity>(source).ReWrite();
Skip = reWriteResult.Skip;
Take = reWriteResult.Take;
Orders = reWriteResult.Orders ?? Enumerable.Empty<PropertyOrder>();
@ -92,7 +92,7 @@ namespace ShardingCore.Sharding
SelectContext = reWriteResult.SelectContext;
GroupByContext = reWriteResult.GroupByContext;
_reWriteSource = reWriteResult.ReWriteQueryable;
QueryEntities = source.ParseQueryableRoute();
QueryEntities = source.ParseQueryableEntities();
_trackerManager =
(ITrackerManager)ShardingContainer.GetService(
typeof(ITrackerManager<>).GetGenericType0(shardingDbContext.GetType()));
@ -151,11 +151,11 @@ namespace ShardingCore.Sharding
// return _routeTailFactory.Create(tableRouteResult);
//}
public IQueryable<T> GetReWriteQueryable()
public IQueryable<TEntity> GetReWriteQueryable()
{
return _reWriteSource;
}
public IQueryable<T> GetOriginalQueryable()
public IQueryable<TEntity> GetOriginalQueryable()
{
return _source;
}

View File

@ -1,3 +1,4 @@
using System.Diagnostics;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine;
using ShardingCore.Sharding.Abstractions;
using System.Linq;

View File

@ -19,13 +19,11 @@ namespace ShardingCore.Core.Internal.Visitors
internal class DbContextReplaceQueryableVisitor : ExpressionVisitor
{
private readonly DbContext _dbContext;
private readonly bool _isParallelQuery;
public IQueryable Source;
public DbContextReplaceQueryableVisitor(DbContext dbContext, bool isParallelQuery)
public DbContextReplaceQueryableVisitor(DbContext dbContext)
{
_dbContext = dbContext;
_isParallelQuery = isParallelQuery;
}
protected override Expression VisitConstant(ConstantExpression node)
@ -35,9 +33,9 @@ namespace ShardingCore.Core.Internal.Visitors
var dbContextDependencies = typeof(DbContext).GetTypePropertyValue(_dbContext, "DbContextDependencies") as IDbContextDependencies;
var targetIQ = (IQueryable)((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, queryable.ElementType);
IQueryable newQueryable = null;
if (_isParallelQuery)
newQueryable = targetIQ.Provider.CreateQuery((Expression)Expression.Call((Expression)null, typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(EntityFrameworkQueryableExtensions.AsNoTracking)).MakeGenericMethod(queryable.ElementType), targetIQ.Expression));
else
//if (_isParallelQuery)
// newQueryable = targetIQ.Provider.CreateQuery((Expression)Expression.Call((Expression)null, typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(EntityFrameworkQueryableExtensions.AsNoTracking)).MakeGenericMethod(queryable.ElementType), targetIQ.Expression));
//else
newQueryable = targetIQ.Provider.CreateQuery(targetIQ.Expression);
Source = newQueryable;
// return base.Visit(Expression.Constant(newQueryable));
@ -53,13 +51,11 @@ namespace ShardingCore.Core.Internal.Visitors
internal class DbContextReplaceQueryableVisitor : ExpressionVisitor
{
private readonly DbContext _dbContext;
private readonly bool _isParallelQuery;
public IQueryable Source;
public DbContextReplaceQueryableVisitor(DbContext dbContext, bool isParallelQuery)
public DbContextReplaceQueryableVisitor(DbContext dbContext)
{
_dbContext = dbContext;
_isParallelQuery = isParallelQuery;
}
protected override Expression VisitExtension(Expression node)
@ -70,9 +66,9 @@ namespace ShardingCore.Core.Internal.Visitors
var targetIQ = (IQueryable)((IDbSetCache)_dbContext).GetOrAddSet(dbContextDependencies.SetSource, queryRootExpression.EntityType.ClrType);
//AsNoTracking
IQueryable newQueryable = null;
if (_isParallelQuery)
newQueryable = targetIQ.Provider.CreateQuery((Expression)Expression.Call((Expression)null, typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(EntityFrameworkQueryableExtensions.AsNoTracking)).MakeGenericMethod(queryRootExpression.EntityType.ClrType), targetIQ.Expression));
else
//if (_isParallelQuery)
// newQueryable = targetIQ.Provider.CreateQuery((Expression)Expression.Call((Expression)null, typeof(EntityFrameworkQueryableExtensions).GetTypeInfo().GetDeclaredMethod(nameof(EntityFrameworkQueryableExtensions.AsNoTracking)).MakeGenericMethod(queryRootExpression.EntityType.ClrType), targetIQ.Expression));
//else
newQueryable = targetIQ.Provider.CreateQuery(targetIQ.Expression);
Source = newQueryable;
//如何替换ef5的set

View File

@ -1,27 +1,34 @@
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Query;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.VirtualDatabase;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualRoutes.TableRoutes;
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
*/
/*
* @Author: xjm
* @Description:
* @Date: Monday, 28 December 2020 22:09:39
* @Email: 326308290@qq.com
*/
public class QueryableRouteShardingTableDiscoverVisitor<TKey> : ExpressionVisitor
{
private static readonly ConcurrentDictionary<Expression<Func<string, bool>>, Func<string, bool>> _routeFilter =
new ConcurrentDictionary<Expression<Func<string, bool>>, Func<string, bool>>(new RouteFilterComparer());
private readonly EntityMetadata _entityMetadata;
private readonly Func<object, TKey> _shardingKeyConvert;
private readonly Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> _keyToTailWithFilter;
/// <summary>
/// 是否是分表路由
@ -29,16 +36,32 @@ namespace ShardingCore.Core.Internal.Visitors
private readonly bool _shardingTableRoute;
private Expression<Func<string, bool>> _where = x => true;
public QueryableRouteShardingTableDiscoverVisitor(EntityMetadata entityMetadata, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailWithFilter,bool shardingTableRoute)
public QueryableRouteShardingTableDiscoverVisitor(EntityMetadata entityMetadata, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailWithFilter, bool shardingTableRoute)
{
_entityMetadata = entityMetadata;
_shardingKeyConvert = shardingKeyConvert;
_keyToTailWithFilter = keyToTailWithFilter;
_shardingTableRoute = shardingTableRoute;
}
public Func<string, bool> GetStringFilterTail()
{
//if (_lastExpression == null)
//{
// _lastExpression = _where;
//}
//else
//{
// var startNew = Stopwatch.StartNew();
// for (int i = 0; i < 300; i++)
// {
// var exxq = LambdaCompare.Eq(_lastExpression, _where);
// }
// startNew.Stop();
// var x = startNew.ElapsedMilliseconds;
// var eq = LambdaCompare.Eq(_lastExpression, _where);
// _lastExpression = _where;
//}
//return _routeFilter.GetOrAdd(_where, key => _where.Compile());
return _where.Compile();
}
@ -46,7 +69,7 @@ namespace ShardingCore.Core.Internal.Visitors
{
return expression is MemberExpression member
&& member.Expression.Type == _entityMetadata.EntityType
&& member.Member.Name == (_shardingTableRoute?_entityMetadata.ShardingTableProperty.Name:_entityMetadata.ShardingDataSourceProperty.Name);
&& member.Member.Name == (_shardingTableRoute ? _entityMetadata.ShardingTableProperty.Name : _entityMetadata.ShardingDataSourceProperty.Name);
}
/// <summary>
/// 方法是否包含shardingKey
@ -80,31 +103,28 @@ namespace ShardingCore.Core.Internal.Visitors
return expression is MethodCallExpression;
}
private object GetShardingKeyValue(Expression expression)
private TKey GetShardingKeyValue(Expression expression)
{
if (expression is ConstantExpression)
return (expression as ConstantExpression).Value;
if (expression is UnaryExpression)
if (expression is ConstantExpression constantExpression)
return (TKey)constantExpression.Value;
if (expression is UnaryExpression unaryExpression)
{
UnaryExpression unary = expression as UnaryExpression;
LambdaExpression lambda = Expression.Lambda(unary.Operand);
Delegate fn = lambda.Compile();
return fn.DynamicInvoke(null);
return Expression.Lambda<Func<TKey>>(unaryExpression.Operand).Compile()();
}
if (expression is MemberExpression member1Expression)
{
var target = GetShardingKeyValue(member1Expression.Expression);
if (member1Expression.Member is FieldInfo field)
return field.GetValue(target);
if (member1Expression.Member is PropertyInfo property)
return property.GetValue(target);
return Expression.Lambda(member1Expression).Compile().DynamicInvoke();
//var target = GetShardingKeyValue(member1Expression.Expression);
//if (member1Expression.Member is FieldInfo field)
// return field.GetValue(target);
//if (member1Expression.Member is PropertyInfo property)
// return property.GetValue(target);
return Expression.Lambda<Func<TKey>>(member1Expression).Compile()();
}
if (expression is MethodCallExpression methodCallExpression)
{
return Expression.Lambda(methodCallExpression).Compile().DynamicInvoke();
return Expression.Lambda<Func<TKey>>(methodCallExpression).Compile()();
//return methodCallExpression.Method.Invoke(
// GetShardingKeyValue(methodCallExpression.Object),
// methodCallExpression.Arguments
@ -192,14 +212,13 @@ namespace ShardingCore.Core.Internal.Visitors
if (arrayObject != null)
{
var enumerable = (IEnumerable) arrayObject;
var enumerable = (IEnumerable<TKey>)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);
var eq = _keyToTailWithFilter(item, @in ? ShardingOperatorEnum.Equal : ShardingOperatorEnum.NotEqual);
if (@in)
contains = contains.Or(eq);
else
@ -243,12 +262,13 @@ namespace ShardingCore.Core.Internal.Visitors
else
{
//条件在右边
bool conditionOnRight=false;
object value = null;
bool conditionOnRight = false;
TKey value = default;
if (IsShardingKey(binaryExpression.Left)&&IsConstantOrMember(binaryExpression.Right))
if (IsShardingKey(binaryExpression.Left) && IsConstantOrMember(binaryExpression.Right))
{
conditionOnRight = true;
value = GetShardingKeyValue(binaryExpression.Right);
}
else if (IsConstantOrMember(binaryExpression.Left) && IsShardingKey(binaryExpression.Right))
@ -270,12 +290,11 @@ namespace ShardingCore.Core.Internal.Visitors
_ => ShardingOperatorEnum.UnKnown
};
if (value == null)
if (EqualityComparer<TKey>.Default.Equals(value, default))
return x => true;
var keyValue = _shardingKeyConvert(value);
return _keyToTailWithFilter(keyValue, op);
return _keyToTailWithFilter(value, op);
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
@ -51,9 +52,10 @@ namespace ShardingCore.Utils
/// <param name="keyToTailExpression"></param>
/// <param name="shardingTableRoute">sharding table or data source</param>
/// <returns></returns>
public static Func<string, bool> GetRouteShardingTableFilter<TKey>(IQueryable queryable, EntityMetadata entityMetadata, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailExpression,bool shardingTableRoute)
public static Func<string, bool> GetRouteShardingTableFilter<TKey>(IQueryable queryable, EntityMetadata entityMetadata, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailExpression,bool shardingTableRoute)
{
QueryableRouteShardingTableDiscoverVisitor<TKey> visitor = new QueryableRouteShardingTableDiscoverVisitor<TKey>(entityMetadata, shardingKeyConvert, keyToTailExpression, shardingTableRoute);
QueryableRouteShardingTableDiscoverVisitor<TKey> visitor = new QueryableRouteShardingTableDiscoverVisitor<TKey>(entityMetadata, keyToTailExpression, shardingTableRoute);
visitor.Visit(queryable.Expression);

View File

@ -14,15 +14,6 @@ namespace ShardingCore.VirtualRoutes.Abstractions
/// <typeparam name="TEntity"></typeparam>
public abstract class AbstractShardingTimeKeyDateTimeVirtualTableRoute<TEntity> : AbstractShardingAutoCreateOperatorVirtualTableRoute<TEntity, DateTime> where TEntity : class
{
/// <summary>
/// how convert object to date time
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
protected override DateTime ConvertToShardingKey(object shardingKey)
{
return Convert.ToDateTime(shardingKey);
}
/// <summary>
/// how convert sharding key to tail
/// </summary>
@ -30,7 +21,7 @@ namespace ShardingCore.VirtualRoutes.Abstractions
/// <returns></returns>
public override string ShardingKeyToTail(object shardingKey)
{
var time = ConvertToShardingKey(shardingKey);
var time = Convert.ToDateTime(shardingKey);
return TimeFormatToTail(time);
}
/// <summary>

View File

@ -12,15 +12,6 @@ namespace ShardingCore.VirtualRoutes.Abstractions
/// <typeparam name="TEntity">entity</typeparam>
public abstract class AbstractShardingTimeKeyLongVirtualTableRoute<TEntity> : AbstractShardingAutoCreateOperatorVirtualTableRoute<TEntity, long> where TEntity : class
{
/// <summary>
/// how convert object to long
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
protected override long ConvertToShardingKey(object shardingKey)
{
return (long)shardingKey;
}
/// <summary>
/// how convert sharding key to tail
/// </summary>
@ -28,7 +19,7 @@ namespace ShardingCore.VirtualRoutes.Abstractions
/// <returns></returns>
public override string ShardingKeyToTail(object shardingKey)
{
var time = ConvertToShardingKey(shardingKey);
var time = (long)shardingKey;
return TimeFormatToTail(time);
}
/// <summary>

View File

@ -36,14 +36,10 @@ namespace ShardingCore.VirtualRoutes.Mods
Mod = mod;
PaddingChar = paddingChar;
}
protected override int ConvertToShardingKey(object shardingKey)
{
return Convert.ToInt32(shardingKey);
}
public override string ShardingKeyToTail(object shardingKey)
{
var shardingKeyInt = ConvertToShardingKey(shardingKey);
var shardingKeyInt = Convert.ToInt32(shardingKey);
return Math.Abs(shardingKeyInt % Mod).ToString().PadLeft(TailLength,PaddingChar);
}

View File

@ -48,19 +48,10 @@ namespace ShardingCore.VirtualRoutes.Mods
/// <returns></returns>
public override string ShardingKeyToTail(object shardingKey)
{
var shardingKeyStr = ConvertToShardingKey(shardingKey);
var shardingKeyStr = shardingKey.ToString();
return Math.Abs(ShardingCoreHelper.GetStringHashCode(shardingKeyStr) % Mod).ToString().PadLeft(TailLength,PaddingChar);
}
/// <summary>
/// 将shardingKey转成对应的字符串
/// </summary>
/// <param name="shardingKey"></param>
/// <returns></returns>
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey.ToString();
}
/// <summary>
/// 获取对应类型在数据库中的所有后缀
/// </summary>
/// <returns></returns>

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
@ -11,6 +12,7 @@ using ShardingCore.Core.VirtualDatabase.VirtualDataSources;
using ShardingCore.Core.VirtualDatabase.VirtualDataSources.PhysicDataSources;
using ShardingCore.Core.VirtualDatabase.VirtualTables;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails;
using ShardingCore.Core.VirtualRoutes.TableRoutes.RouteTails.Abstractions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Extensions.ShardingPageExtensions;
@ -19,6 +21,7 @@ using ShardingCore.Sharding;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
using ShardingCore.Sharding.ShardingComparision.Abstractions;
using ShardingCore.Sharding.ShardingDbContextExecutors;
using ShardingCore.Sharding.StreamMergeEngines;
using ShardingCore.TableCreator;
using ShardingCore.Test.Domain.Entities;
using Xunit;
@ -43,12 +46,13 @@ namespace ShardingCore.Test
private readonly IVirtualTableManager<ShardingDefaultDbContext> _virtualTableManager;
private readonly IShardingTableCreator<ShardingDefaultDbContext> _shardingTableCreator;
private readonly IShardingReadWriteManager _shardingReadWriteManager;
private readonly IRouteTailFactory _routeTailFactory;
public ShardingTest(ShardingDefaultDbContext virtualDbContext, IShardingRouteManager shardingRouteManager, IConfiguration configuration,
IEntityMetadataManager<ShardingDefaultDbContext> entityMetadataManager,
IShardingComparer<ShardingDefaultDbContext> shardingComparer, IVirtualDataSource<ShardingDefaultDbContext> virtualDataSource,
IVirtualTableManager<ShardingDefaultDbContext> virtualTableManager,
IShardingTableCreator<ShardingDefaultDbContext> shardingTableCreator, IShardingReadWriteManager shardingReadWriteManager)
IShardingTableCreator<ShardingDefaultDbContext> shardingTableCreator, IShardingReadWriteManager shardingReadWriteManager,IRouteTailFactory routeTailFactory)
{
_virtualDbContext = virtualDbContext;
_shardingRouteManager = shardingRouteManager;
@ -60,6 +64,7 @@ namespace ShardingCore.Test
_virtualTableManager = virtualTableManager;
_shardingTableCreator = shardingTableCreator;
_shardingReadWriteManager = shardingReadWriteManager;
_routeTailFactory = routeTailFactory;
}
[Fact]
@ -435,9 +440,9 @@ namespace ShardingCore.Test
var sysUserModDesc = await _virtualDbContext.Set<SysUserMod>().OrderByDescending(o => o.Id).FirstOrDefaultAsync();
Assert.True(sysUserModDesc != null && sysUserModDesc.Id == "999");
}
}
[Fact]
[Fact]
public async Task FirstOrDefault2()
{
var sysUserMod = await _virtualDbContext.Set<SysUserMod>().Where(o => o.Id == "1").FirstOrDefaultAsync();

View File

@ -16,14 +16,10 @@ namespace ShardingCore.Test.Shardings
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()

View File

@ -16,14 +16,10 @@ namespace ShardingCore.Test.Shardings
*/
public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<SysUserSalary,int>
{
protected override int ConvertToShardingKey(object shardingKey)
{
return Convert.ToInt32(shardingKey);
}
public override string ShardingKeyToTail(object shardingKey)
{
var time = ConvertToShardingKey(shardingKey);
var time = Convert.ToInt32(shardingKey);
return TimeFormatToTail(time);
}

View File

@ -38,8 +38,8 @@ namespace ShardingCore.Test
.Begin(o =>
{
#if DEBUG
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
//o.CreateShardingTableOnStart = true;
//o.EnsureCreatedWithOutShardingTable = true;
#endif
o.AutoTrackEntity = true;
})

View File

@ -16,14 +16,10 @@ namespace ShardingCore.Test2x.Shardings
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()

View File

@ -16,14 +16,9 @@ namespace ShardingCore.Test2x.Shardings
*/
public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<SysUserSalary,int>
{
protected override int ConvertToShardingKey(object shardingKey)
{
return Convert.ToInt32(shardingKey);
}
public override string ShardingKeyToTail(object shardingKey)
{
var time = ConvertToShardingKey(shardingKey);
var time = Convert.ToInt32(shardingKey);
return TimeFormatToTail(time);
}

View File

@ -16,14 +16,10 @@ namespace ShardingCore.Test3x.Shardings
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()

View File

@ -16,14 +16,10 @@ namespace ShardingCore.Test3x.Shardings
*/
public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<SysUserSalary,int>
{
protected override int ConvertToShardingKey(object shardingKey)
{
return Convert.ToInt32(shardingKey);
}
public override string ShardingKeyToTail(object shardingKey)
{
var time = ConvertToShardingKey(shardingKey);
var time = Convert.ToInt32(shardingKey);
return TimeFormatToTail(time);
}

View File

@ -16,14 +16,10 @@ namespace ShardingCore.Test5x.Shardings
{
"A", "B", "C"
};
protected override string ConvertToShardingKey(object shardingKey)
{
return shardingKey?.ToString() ?? string.Empty;
}
//我们设置区域就是数据库
public override string ShardingKeyToDataSourceName(object shardingKey)
{
return ConvertToShardingKey(shardingKey);
return shardingKey?.ToString() ?? string.Empty;
}
public override List<string> GetAllDataSourceNames()

View File

@ -16,14 +16,9 @@ namespace ShardingCore.Test5x.Shardings
*/
public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<SysUserSalary,int>
{
protected override int ConvertToShardingKey(object shardingKey)
{
return Convert.ToInt32(shardingKey);
}
public override string ShardingKeyToTail(object shardingKey)
{
var time = ConvertToShardingKey(shardingKey);
var time = Convert.ToInt32(shardingKey);
return TimeFormatToTail(time);
}