优化多线程下的数据解析查询
This commit is contained in:
parent
9ce7617f7f
commit
028d3b08cb
|
@ -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}
|
||||
|
|
|
@ -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();
|
||||
// }
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -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>
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.RouteRuleEngine
|
|||
public DataSourceRouteRuleContext(IQueryable<T> queryable)
|
||||
{
|
||||
Queryable = queryable;
|
||||
QueryEntities = queryable.ParseQueryableRoute();
|
||||
QueryEntities = queryable.ParseQueryableEntities();
|
||||
}
|
||||
/// <summary>
|
||||
/// 查询条件
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions
|
|||
{
|
||||
throw new ShardingCoreParallelQueryTimeOutException(_executeExpression.ShardingPrint());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
@ -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>();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Diagnostics;
|
||||
using ShardingCore.Core.VirtualRoutes.TableRoutes.RoutingRuleEngine;
|
||||
using ShardingCore.Sharding.Abstractions;
|
||||
using System.Linq;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
})
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue