完成第一版本的多字段sharding [#82]

This commit is contained in:
xuejiaming 2021-12-24 15:23:55 +08:00
parent c03e679901
commit 167c44be53
131 changed files with 2629 additions and 195 deletions

View File

@ -58,7 +58,6 @@ namespace ShardingCore6x
{
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
o.AutoTrackEntity = false;
}).AddShardingTransaction((connection, builder) => builder
//.UseMySql(connection, new MySqlServerVersion(new Version()))
.UseSqlServer(connection)

View File

@ -11,7 +11,7 @@ namespace ShardingCore6x.ShardingDbContexts
{
internal class OrderVirtualTableRoute:AbstractSimpleShardingModKeyStringVirtualTableRoute<Order>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public OrderVirtualTableRoute() : base(2, 5)
{

View File

@ -39,7 +39,6 @@ namespace ShardingCoreBenchmark5x
{
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 =>

View File

@ -31,7 +31,6 @@ namespace Sample.BulkConsole
{
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
o.AutoTrackEntity = true;
})
.AddShardingTransaction((connection, builder) =>
builder.UseSqlServer(connection).UseLoggerFactory(efLogger))

View File

@ -26,7 +26,6 @@ namespace Sample.Migrations
{
o.CreateShardingTableOnStart = false;
o.EnsureCreatedWithOutShardingTable = false;
o.AutoTrackEntity = true;
})
.AddShardingTransaction((connection, builder) =>
builder.UseSqlServer(connection))

View File

@ -19,7 +19,6 @@ builder.Services.AddControllers();
builder.Services.AddShardingDbContext<DefaultDbContext>((conStr, builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger))
.Begin(o =>
{
o.AutoTrackEntity = true;
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
})

View File

@ -51,7 +51,7 @@ namespace Sample.SqlServer.Shardings
return $"{dateOfMonth:yyyyMM}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -30,7 +30,7 @@ namespace Sample.SqlServer.Shardings
}
public override Expression<Func<string, bool>> GetMainRouteFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = ShardingKeyToTail(shardingKey);
switch (shardingOperator)

View File

@ -36,7 +36,6 @@ namespace Sample.SqlServer
{
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
o.AutoTrackEntity = true;
o.MaxQueryConnectionsLimit = Environment.ProcessorCount;
o.ConnectionMode = ConnectionModeEnum.SYSTEM_AUTO;
//if SysTest entity not exists in db and db is exists

View File

@ -40,7 +40,6 @@ namespace Sample.SqlServerShardingAll
builder.UseSqlServer(conStr).UseLoggerFactory(efLogger);
}).Begin(op =>
{
op.AutoTrackEntity = true;
//如果您使用code-first建议选择false
op.CreateShardingTableOnStart = true;
//如果您使用code-first建议修改为fsle

View File

@ -36,7 +36,6 @@ namespace Sample.SqlServerShardingDataSource
builder.UseSqlServer(conStr).UseLoggerFactory(efLogger);
}).Begin(op =>
{
op.AutoTrackEntity = true;
//如果您使用code-first建议选择false
op.CreateShardingTableOnStart = true;
//如果您使用code-first建议修改为false

View File

@ -0,0 +1,25 @@
using System;
namespace Sample.SqlServerShardingTable.Common
{
public class IdHelper
{
private IdHelper(){}
private static SnowflakeId _snowflakeId;
static IdHelper()
{
_snowflakeId = new SnowflakeId(1,3);
}
public static long NextId()
{
return _snowflakeId.NextId();
}
public static DateTime FromId(long id)
{
return SnowflakeId.AnalyzeIdToDateTime(id);
}
}
}

View File

@ -0,0 +1,196 @@
using System;
using System.Text;
namespace Sample.SqlServerShardingTable.Common
{/// <summary>
/// 雪花ID
/// Twitter_Snowflake
/// SnowFlake的结构如下(每部分用-分开)
/// 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
/// 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0
/// 41位时间截(毫秒级)注意41位时间截不是存储当前时间的时间截而是存储时间截的差值当前时间截 - 开始时间截)得到的值),
/// 41位的时间截可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
/// 这里的的开始时间截一般是我们的id生成器开始使用的时间由我们程序来指定的如下下面程序IdWorker类的startTime属性
/// 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId
/// 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
/// 总共加起来刚好64位为一个Long型。
/// SnowFlake的优点是整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)
/// 并且效率较高经测试SnowFlake单机每秒都能够产生出极限4,096,000个ID来
/// </summary>
public class SnowflakeId
{
// 开始时间截 (new DateTime(2020, 1, 1).ToUniversalTime() - Jan1st1970).TotalMilliseconds
private const long twepoch = 1577808000000L;
// 机器id所占的位数
private const int workerIdBits = 5;
// 数据标识id所占的位数
private const int datacenterIdBits = 5;
// 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private const long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 支持的最大数据标识id结果是31
private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 序列在id中占的位数
private const int sequenceBits = 12;
// 数据标识id向左移17位(12+5)
private const int datacenterIdShift = sequenceBits + workerIdBits;
// 机器ID向左移12位
private const int workerIdShift = sequenceBits;
// 时间截向左移22位(5+5+12)
private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
// 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095)
private const long sequenceMask = -1L ^ (-1L << sequenceBits);
// 数据中心ID(0~31)
public long datacenterId { get; private set; }
// 工作机器ID(0~31)
public long workerId { get; private set; }
// 毫秒内序列(0~4095)
public long sequence { get; private set; }
// 上次生成ID的时间截
public long lastTimestamp { get; private set; }
/// <summary>
/// 雪花ID
/// </summary>
/// <param name="datacenterId">数据中心ID</param>
/// <param name="workerId">工作机器ID</param>
public SnowflakeId(long datacenterId, long workerId)
{
if (datacenterId > maxDatacenterId || datacenterId < 0)
{
throw new Exception(string.Format("datacenter Id can't be greater than {0} or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0)
{
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0", maxWorkerId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = 0L;
this.lastTimestamp = -1L;
}
/// <summary>
/// 获得下一个ID
/// </summary>
/// <returns></returns>
public long NextId()
{
lock (this)
{
long timestamp = GetCurrentTimestamp();
if (timestamp > lastTimestamp) //时间戳改变,毫秒内序列重置
{
sequence = 0L;
}
else if (timestamp == lastTimestamp) //如果是同一时间生成的,则进行毫秒内序列
{
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) //毫秒内序列溢出
{
timestamp = GetNextTimestamp(lastTimestamp); //阻塞到下一个毫秒,获得新的时间戳
}
}
else //当前时间小于上一次ID生成的时间戳证明系统时钟被回拨此时需要做回拨处理
{
sequence = (sequence + 1) & sequenceMask;
if (sequence > 0)
{
timestamp = lastTimestamp; //停留在最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题。
}
else //毫秒内序列溢出
{
timestamp = lastTimestamp + 1; //直接进位到下一个毫秒
}
//throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp));
}
lastTimestamp = timestamp; //上次生成ID的时间截
//移位并通过或运算拼到一起组成64位的ID
var id = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
return id;
}
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static DateTime AnalyzeIdToDateTime(long Id)
{
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
return time.ToLocalTime();
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static string AnalyzeId(long Id)
{
StringBuilder sb = new StringBuilder();
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
sb.Append(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss:fff"));
var datacenterId = (Id ^ (timestamp << timestampLeftShift)) >> datacenterIdShift;
sb.Append("_" + datacenterId);
var workerId = (Id ^ ((timestamp << timestampLeftShift) | (datacenterId << datacenterIdShift))) >> workerIdShift;
sb.Append("_" + workerId);
var sequence = Id & sequenceMask;
sb.Append("_" + sequence);
return sb.ToString();
}
/// <summary>
/// 阻塞到下一个毫秒,直到获得新的时间戳
/// </summary>
/// <param name="lastTimestamp">上次生成ID的时间截</param>
/// <returns>当前时间戳</returns>
private static long GetNextTimestamp(long lastTimestamp)
{
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
/// <summary>
/// 获取当前时间戳
/// </summary>
/// <returns></returns>
private static long GetCurrentTimestamp()
{
return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}
private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
}

View File

@ -0,0 +1,196 @@
using System;
using System.Text;
namespace Sample.SqlServerShardingTable.Common
{/// <summary>
/// 雪花ID
/// Twitter_Snowflake
/// SnowFlake的结构如下(每部分用-分开)
/// 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
/// 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0
/// 41位时间截(毫秒级)注意41位时间截不是存储当前时间的时间截而是存储时间截的差值当前时间截 - 开始时间截)得到的值),
/// 41位的时间截可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
/// 这里的的开始时间截一般是我们的id生成器开始使用的时间由我们程序来指定的如下下面程序IdWorker类的startTime属性
/// 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId
/// 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
/// 总共加起来刚好64位为一个Long型。
/// SnowFlake的优点是整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)
/// 并且效率较高经测试SnowFlake单机每秒都能够产生出极限4,096,000个ID来
/// </summary>
public class SnowflakeIdMock
{
// 开始时间截 (new DateTime(2020, 1, 1).ToUniversalTime() - Jan1st1970).TotalMilliseconds
private const long twepoch = 1577808000000L;
// 机器id所占的位数
private const int workerIdBits = 5;
// 数据标识id所占的位数
private const int datacenterIdBits = 5;
// 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private const long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 支持的最大数据标识id结果是31
private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 序列在id中占的位数
private const int sequenceBits = 12;
// 数据标识id向左移17位(12+5)
private const int datacenterIdShift = sequenceBits + workerIdBits;
// 机器ID向左移12位
private const int workerIdShift = sequenceBits;
// 时间截向左移22位(5+5+12)
private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
// 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095)
private const long sequenceMask = -1L ^ (-1L << sequenceBits);
// 数据中心ID(0~31)
public long datacenterId { get; private set; }
// 工作机器ID(0~31)
public long workerId { get; private set; }
// 毫秒内序列(0~4095)
public long sequence { get; private set; }
// 上次生成ID的时间截
public long lastTimestamp { get; private set; }
/// <summary>
/// 雪花ID
/// </summary>
/// <param name="datacenterId">数据中心ID</param>
/// <param name="workerId">工作机器ID</param>
public SnowflakeIdMock(long datacenterId, long workerId)
{
if (datacenterId > maxDatacenterId || datacenterId < 0)
{
throw new Exception(string.Format("datacenter Id can't be greater than {0} or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0)
{
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0", maxWorkerId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = 0L;
this.lastTimestamp = -1L;
}
/// <summary>
/// 获得下一个ID
/// </summary>
/// <returns></returns>
public long NextId(DateTime utc)
{
lock (this)
{
long timestamp = GetCurrentTimestamp(utc);
if (timestamp > lastTimestamp) //时间戳改变,毫秒内序列重置
{
sequence = 0L;
}
else if (timestamp == lastTimestamp) //如果是同一时间生成的,则进行毫秒内序列
{
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) //毫秒内序列溢出
{
timestamp = GetNextTimestamp(lastTimestamp, utc); //阻塞到下一个毫秒,获得新的时间戳
}
}
else //当前时间小于上一次ID生成的时间戳证明系统时钟被回拨此时需要做回拨处理
{
sequence = (sequence + 1) & sequenceMask;
if (sequence > 0)
{
timestamp = lastTimestamp; //停留在最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题。
}
else //毫秒内序列溢出
{
timestamp = lastTimestamp + 1; //直接进位到下一个毫秒
}
//throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp));
}
lastTimestamp = timestamp; //上次生成ID的时间截
//移位并通过或运算拼到一起组成64位的ID
var id = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
return id;
}
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static DateTime AnalyzeIdToDateTime(long Id)
{
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
return time.ToLocalTime();
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static string AnalyzeId(long Id)
{
StringBuilder sb = new StringBuilder();
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
sb.Append(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss:fff"));
var datacenterId = (Id ^ (timestamp << timestampLeftShift)) >> datacenterIdShift;
sb.Append("_" + datacenterId);
var workerId = (Id ^ ((timestamp << timestampLeftShift) | (datacenterId << datacenterIdShift))) >> workerIdShift;
sb.Append("_" + workerId);
var sequence = Id & sequenceMask;
sb.Append("_" + sequence);
return sb.ToString();
}
/// <summary>
/// 阻塞到下一个毫秒,直到获得新的时间戳
/// </summary>
/// <param name="lastTimestamp">上次生成ID的时间截</param>
/// <returns>当前时间戳</returns>
private static long GetNextTimestamp(long lastTimestamp, DateTime utc)
{
long timestamp = GetCurrentTimestamp(utc);
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp(utc);
}
return timestamp;
}
/// <summary>
/// 获取当前时间戳
/// </summary>
/// <returns></returns>
private static long GetCurrentTimestamp(DateTime utc)
{
return (long)(utc - Jan1st1970).TotalMilliseconds;
}
private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
}

View File

@ -34,6 +34,21 @@ namespace Sample.SqlServerShardingTable.Controllers
sysUsers
});
}
public async Task<IActionResult> Query2()
{
var multiOrder =await _myDbContext.Set<MultiShardingOrder>().Where(o=>o.Id== 232398109278351360).FirstOrDefaultAsync();
var longs = new []{ 232398109278351360 , 255197859283087360 };
var multiOrders = await _myDbContext.Set<MultiShardingOrder>().Where(o => longs.Contains(o.Id)).ToListAsync();
var dateTime = new DateTime(2021, 11, 1);
var multiOrder404 = await _myDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 250345338962063360&&o.CreateTime< dateTime).FirstOrDefaultAsync();
return Ok(new
{
multiOrder,
multiOrders,
multiOrder404
});
}
public async Task<IActionResult> QueryJoin1()
{
var sql= from user in _myDbContext.Set<SysUser>().Where(o => o.Id == "1" || o.Id == "6")

View File

@ -0,0 +1,11 @@
using System;
namespace Sample.SqlServerShardingTable.Entities
{
public class MultiShardingOrder
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
}
}

View File

@ -40,6 +40,13 @@ namespace Sample.SqlServerShardingTable
entity.Property(o=>o.Name).IsRequired().IsUnicode(false).HasMaxLength(50);
entity.ToTable(nameof(Setting));
});
modelBuilder.Entity<MultiShardingOrder>(entity =>
{
entity.HasKey(o => o.Id);
entity.Property(o => o.Id).ValueGeneratedNever();
entity.Property(o=>o.Name).IsRequired().IsUnicode(false).HasMaxLength(50);
entity.ToTable(nameof(MultiShardingOrder));
});
}
/// <summary>
/// empty impl

View File

@ -38,11 +38,12 @@ namespace Sample.SqlServerShardingTable
builder.UseSqlServer(conStr).UseLoggerFactory(efLogger);
}).Begin(op =>
{
op.AutoTrackEntity = true;
//如果您使用code-first建议选择false
op.CreateShardingTableOnStart = true;
//如果您使用code-first建议修改为fsle
op.EnsureCreatedWithOutShardingTable = true;
//当无法获取路由时会返回默认值而不是报错
op.ThrowIfQueryRouteNotMatch = false;
}).AddShardingTransaction((connection, builder) =>
{
builder.UseSqlServer(connection).UseLoggerFactory(efLogger);
@ -52,6 +53,7 @@ namespace Sample.SqlServerShardingTable
{
op.AddShardingTableRoute<SysUserVirtualTableRoute>();
op.AddShardingTableRoute<OrderVirtualTableRoute>();
op.AddShardingTableRoute<MultiShardingOrderVirtualTableRoute>();
}).End();
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Sample.SqlServerShardingTable.Common;
using Sample.SqlServerShardingTable.Entities;
using ShardingCore.Bootstrapers;
@ -66,9 +67,88 @@ namespace Sample.SqlServerShardingTable
};
orders.Add(order);
}
var multiShardingOrders = new List<MultiShardingOrder>(9);
#region
{
var now = new DateTime(2021, 10, 1, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 231765457240207360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 10, 2, 11, 3, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 232095129534607360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 10, 3, 7, 7, 7);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 232398109278351360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 11, 6, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 244811420401807360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 11, 21, 19, 43, 0);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 250345338962063360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 5, 5, 5, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 255197859283087360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 9, 19, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 256860816933007360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 19, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 260394098622607360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
#endregion
myDbContext.AddRange(settings);
myDbContext.AddRange(users);
myDbContext.AddRange(orders);
myDbContext.AddRange(multiShardingOrders);
myDbContext.SaveChanges();
}
}

View File

@ -0,0 +1,79 @@
using System;
using System.Linq.Expressions;
using Sample.SqlServerShardingTable.Common;
using Sample.SqlServerShardingTable.Entities;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Helpers;
using ShardingCore.VirtualRoutes.Months;
namespace Sample.SqlServerShardingTable.VirtualRoutes
{
public class MultiShardingOrderVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<MultiShardingOrder>
{
public override void Configure(EntityMetadataTableBuilder<MultiShardingOrder> builder)
{
builder.ShardingProperty(o => o.CreateTime);
builder.ShardingExtraProperty(o => o.Id);
}
public override Expression<Func<string, bool>> GetExtraRouteFilter(object shardingKey, ShardingOperatorEnum shardingOperator, string shardingPropertyName)
{
switch (shardingPropertyName)
{
case nameof(MultiShardingOrder.Id): return GetIdRouteFilter(shardingKey, shardingOperator);
default: throw new NotImplementedException(shardingPropertyName);
}
}
private Expression<Func<string, bool>> GetIdRouteFilter(object shardingKey,
ShardingOperatorEnum shardingOperator)
{
//解析雪花id 需要考虑异常情况,传入的可能不是雪花id那么可以随机查询一张表
var analyzeIdToDateTime = SnowflakeId.AnalyzeIdToDateTime(Convert.ToInt64(shardingKey));
//当前时间的tail
var t = TimeFormatToTail(analyzeIdToDateTime);
//因为是按月分表所以获取下个月的时间判断id是否是在灵界点创建的
var nextMonthFirstDay = ShardingCoreHelper.GetNextMonthFirstDay(DateTime.Now);
if (analyzeIdToDateTime.AddSeconds(10) > nextMonthFirstDay)
{
var nextT = TimeFormatToTail(nextMonthFirstDay);
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t||tail== nextT;
}
}
var currentMonthFirstDay = ShardingCoreHelper.GetCurrentMonthFirstDay(DateTime.Now);
if (analyzeIdToDateTime.AddSeconds(-10) < currentMonthFirstDay)
{
//上个月tail
var nextT = TimeFormatToTail(analyzeIdToDateTime.AddSeconds(-10));
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t || tail == nextT;
}
}
else
{
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t;
}
}
return tail => true;
}
public override bool AutoCreateTableByTime()
{
return true;
}
public override DateTime GetBeginTime()
{
return new DateTime(2021, 9, 1);
}
}
}

View File

@ -48,7 +48,7 @@ namespace Sample.SqlServerShardingTable.VirtualRoutes
}
public override Expression<Func<string, bool>> GetMainRouteFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
{
//因为hash路由仅支持等于所以仅仅只需要写等于的情况
var t = ShardingKeyToTail(shardingKey);

View File

@ -41,6 +41,18 @@ namespace ShardingCore.Core.EntityMetadatas
_entityMetadata.SetShardingDataSourceProperty(propertyInfo);
return this;
}
public EntityMetadataDataSourceBuilder<TEntity> ShardingExtraProperty<TProperty>(Expression<Func<TEntity, TProperty>> propertyExpression)
{
var propertyAccess = propertyExpression.GetPropertyAccess();
_entityMetadata.AddExtraSharingDataSourceProperty(propertyAccess);
return this;
}
public EntityMetadataDataSourceBuilder<TEntity> ShardingExtraProperty(string propertyName)
{
var propertyInfo = typeof(TEntity).GetProperty(propertyName);
_entityMetadata.AddExtraSharingDataSourceProperty(propertyInfo);
return this;
}
/// <summary>
/// 是否启动建表
/// </summary>

View File

@ -38,6 +38,18 @@ namespace ShardingCore.Core.EntityMetadatas
_entityMetadata.SetShardingTableProperty(propertyInfo);
return this;
}
public EntityMetadataTableBuilder<TEntity> ShardingExtraProperty<TProperty>(Expression<Func<TEntity, TProperty>> propertyExpression)
{
var propertyAccess = propertyExpression.GetPropertyAccess();
_entityMetadata.AddExtraSharingTableProperty(propertyAccess);
return this;
}
public EntityMetadataTableBuilder<TEntity> ShardingExtraProperty(string propertyName)
{
var propertyInfo = typeof(TEntity).GetProperty(propertyName);
_entityMetadata.AddExtraSharingTableProperty(propertyInfo);
return this;
}
/// <summary>
/// 分表表和后缀连接器
/// </summary>

View File

@ -16,13 +16,8 @@ namespace ShardingCore.Core.TrackerManagers
*/
public class TrackerManager<TShardingDbContext>: ITrackerManager<TShardingDbContext> where TShardingDbContext : DbContext, IShardingDbContext
{
private readonly IShardingConfigOption<TShardingDbContext> _shardingConfigOption;
private readonly ISet<Type> _dbContextModels = new HashSet<Type>();
public TrackerManager(IShardingConfigOption<TShardingDbContext> shardingConfigOption)
{
_shardingConfigOption = shardingConfigOption;
}
public bool AddDbContextModel(Type entityType)
{
return _dbContextModels.Add(entityType);
@ -30,8 +25,6 @@ namespace ShardingCore.Core.TrackerManagers
public bool EntityUseTrack(Type entityType)
{
if (!_shardingConfigOption.AutoTrackEntity)
return false;
return _dbContextModels.Contains(entityType);
}

View File

@ -18,7 +18,6 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
public abstract class AbstractShardingRouteParseCompileCacheVirtualDataSourceRoute<TEntity, TKey> : AbstractShardingFilterVirtualDataSourceRoute<TEntity, TKey> where TEntity : class
{
private static readonly ConcurrentDictionary<Expression<Func<string, bool>>, Func<string, bool>> _routeCompileCaches = new(new ExtensionExpressionComparer.RouteParseExpressionEqualityComparer());
static AbstractShardingRouteParseCompileCacheVirtualDataSourceRoute()
{
Expression<Func<string, bool>> defaultWhere1 = x => true;
@ -30,7 +29,14 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
/// <summary>
/// 是否启用路由解析编译缓存
/// </summary>
public virtual bool EnableRouteParseCompileCache => false;
public virtual bool? EnableRouteParseCompileCache => null;
public virtual bool EnableCompileCache()
{
if (EnableRouteParseCompileCache.HasValue)
return EnableRouteParseCompileCache.Value;
return ShardingConfigOption.EnableDataSourceRouteCompileCache.GetValueOrDefault();
}
/// <summary>
/// 对表达式进行缓存编译默认永久缓存单个参数表达式且不包含orElse只包含单个AndAlso或者没有AndAlso的,
@ -41,7 +47,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
/// <returns></returns>
public virtual Func<string, bool> CachingCompile(Expression<Func<string, bool>> parseWhere)
{
if (EnableRouteParseCompileCache)
if (EnableCompileCache())
{
var doCachingCompile = DoCachingCompile(parseWhere);
if (doCachingCompile != null)

View File

@ -19,6 +19,7 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
{
public EntityMetadata EntityMetadata { get; private set; }
private readonly DoOnlyOnce _doOnlyOnce = new DoOnlyOnce();
public IShardingConfigOption ShardingConfigOption { get; private set; }
public void Initialize(EntityMetadata entityMetadata)
@ -34,6 +35,9 @@ namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes.Abstractions
paginationConfiguration.Configure(paginationBuilder);
}
ShardingConfigOption =
ShardingContainer.GetRequiredShardingConfigOption(entityMetadata.ShardingDbContextType);
}
public virtual IPaginationConfiguration<T> CreatePaginationConfiguration()
{

View File

@ -21,7 +21,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
protected override List<IPhysicTable> DoRouteWithPredicate(List<IPhysicTable> allPhysicTables, IQueryable queryable)
{
//获取路由后缀表达式
var routeParseExpression = ShardingUtil.GetRouteParseExpression(queryable, EntityMetadata, GetRouteToFilter,true);
var routeParseExpression = ShardingUtil.GetRouteParseExpression(queryable, EntityMetadata, GetRouteFilter,true);
//表达式缓存编译
var filter =CachingCompile(routeParseExpression);
//通过编译结果进行过滤
@ -37,12 +37,12 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
/// <param name="shardingOperator">操作</param>
/// <param name="shardingPropertyName">分表字段</param>
/// <returns>如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表</returns>
public Expression<Func<string, bool>> GetRouteToFilter(object shardingKey,
public Expression<Func<string, bool>> GetRouteFilter(object shardingKey,
ShardingOperatorEnum shardingOperator, string shardingPropertyName)
{
if (EntityMetadata.IsMainShardingTableKey(shardingPropertyName))
{
return GetMainRouteFilter((TKey)shardingKey, shardingOperator);
return GetRouteToFilter((TKey)shardingKey, shardingOperator);
}
else
{
@ -50,7 +50,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
}
}
public abstract Expression<Func<string, bool>> GetMainRouteFilter(TKey shardingKey,
public abstract Expression<Func<string, bool>> GetRouteToFilter(TKey shardingKey,
ShardingOperatorEnum shardingOperator);
public virtual Expression<Func<string, bool>> GetExtraRouteFilter(object shardingKey,

View File

@ -32,7 +32,13 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
/// <summary>
/// 是否启用路由解析编译缓存
/// </summary>
public virtual bool EnableRouteParseCompileCache => false;
public virtual bool? EnableRouteParseCompileCache => null;
public virtual bool EnableCompileCache()
{
if (EnableRouteParseCompileCache.HasValue)
return EnableRouteParseCompileCache.Value;
return ShardingConfigOption.EnableTableRouteCompileCache.GetValueOrDefault();
}
/// <summary>
/// 对表达式进行缓存编译默认永久缓存单个参数表达式且不包含orElse只包含单个AndAlso或者没有AndAlso的,
/// 比如:<![CDATA[o.id==x]]>或者<![CDATA[o.id>x]]>,不会缓存<![CDATA[o=>id>x && o.id<y ]]>等一共大于、等于、小于、大于等于、小于等于(不等于编译成<![CDATA[t=>true]]>)缓存会存在的数量个数上限为
@ -42,7 +48,7 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
/// <returns></returns>
public virtual Func<string, bool> CachingCompile(Expression<Func<string, bool>> parseWhere)
{
if (EnableRouteParseCompileCache)
if (EnableCompileCache())
{
var doCachingCompile = DoCachingCompile(parseWhere);
if (doCachingCompile != null)

View File

@ -22,11 +22,14 @@ namespace ShardingCore.Core.VirtualRoutes.TableRoutes.Abstractions
{
private readonly DoOnlyOnce _doOnlyOnce = new DoOnlyOnce();
public IShardingConfigOption ShardingConfigOption { get; private set; }
public virtual void Initialize(EntityMetadata entityMetadata)
{
if (!_doOnlyOnce.IsUnDo())
throw new ShardingCoreInvalidOperationException("already init");
EntityMetadata = entityMetadata;
ShardingConfigOption =
ShardingContainer.GetRequiredShardingConfigOption(entityMetadata.ShardingDbContextType);
}
public virtual IPaginationConfiguration<T> CreatePaginationConfiguration()
{

View File

@ -43,11 +43,15 @@ namespace ShardingCore.DIExtensions
throw new ArgumentException(
$"{nameof(shardingCoreBeginOptions.MaxQueryConnectionsLimit)} should greater than and equal 1");
ShardingConfigOption.EnsureCreatedWithOutShardingTable = shardingCoreBeginOptions.EnsureCreatedWithOutShardingTable;
ShardingConfigOption.AutoTrackEntity = shardingCoreBeginOptions.AutoTrackEntity;
//ShardingConfigOption.AutoTrackEntity = shardingCoreBeginOptions.AutoTrackEntity;
ShardingConfigOption.CreateShardingTableOnStart = shardingCoreBeginOptions.CreateShardingTableOnStart;
ShardingConfigOption.IgnoreCreateTableError = shardingCoreBeginOptions.IgnoreCreateTableError;
ShardingConfigOption.MaxQueryConnectionsLimit = shardingCoreBeginOptions.MaxQueryConnectionsLimit;
ShardingConfigOption.ConnectionMode = shardingCoreBeginOptions.ConnectionMode;
ShardingConfigOption.ThrowIfQueryRouteNotMatch = shardingCoreBeginOptions.ThrowIfQueryRouteNotMatch;
ShardingConfigOption.EnableTableRouteCompileCache = shardingCoreBeginOptions.EnableTableRouteCompileCache;
ShardingConfigOption.EnableDataSourceRouteCompileCache = shardingCoreBeginOptions.EnableDataSourceRouteCompileCache;
foreach (var entityType in shardingCoreBeginOptions.GetCreateTableEntities())
{
ShardingConfigOption.AddEntityTryCreateTable(entityType);
@ -89,13 +93,17 @@ namespace ShardingCore.DIExtensions
/// 是否需要在启动时创建分表
/// </summary>
public bool? CreateShardingTableOnStart { get; set; }
///// <summary>
///// 是否自动追踪实体
///// 譬如本次查询涉及到a1,a2,a3这三张表会创建3个dbcontext进行查询如果AutoTrackEntity=false那么针对被创建的dbcontext不会有任何变化还是以追踪的形式查询
///// 因为会同时创建3个dbcontext所以针对跨表查询完成后dbcontext会被回收但是查询还是按原先的行为查询所以如果不启用建议在查询的时候使用notracking
///// 如果AutoTrackEntity=true那么被创建的三个dbcontext还是以原先的表现行为进行查询在查询完成后会再次各自创建对应的dbcontext进行对象的追踪
///// </summary>
//public bool AutoTrackEntity { get; set; }
/// <summary>
/// 是否自动追踪实体
/// 譬如本次查询涉及到a1,a2,a3这三张表会创建3个dbcontext进行查询如果AutoTrackEntity=false那么针对被创建的dbcontext不会有任何变化还是以追踪的形式查询
/// 因为会同时创建3个dbcontext所以针对跨表查询完成后dbcontext会被回收但是查询还是按原先的行为查询所以如果不启用建议在查询的时候使用notracking
/// 如果AutoTrackEntity=true那么被创建的三个dbcontext还是以原先的表现行为进行查询在查询完成后会再次各自创建对应的dbcontext进行对象的追踪
/// 当查询遇到没有路由被命中时是否抛出错误
/// </summary>
public bool AutoTrackEntity { get; set; }
public bool ThrowIfQueryRouteNotMatch { get; set; } = true;
/// <summary>
/// 忽略建表时的错误
@ -103,9 +111,7 @@ namespace ShardingCore.DIExtensions
public bool? IgnoreCreateTableError { get; set; } = true;
public int MaxQueryConnectionsLimit { get; set; } = Environment.ProcessorCount;
public ConnectionModeEnum ConnectionMode { get; set; } = ConnectionModeEnum.SYSTEM_AUTO;
[Obsolete]
public bool? EnableTableRouteCompileCache { get; set; }
[Obsolete]
public bool? EnableDataSourceRouteCompileCache { get; set; }
private readonly ISet<Type> _createTableEntities = new HashSet<Type>();

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Exceptions
{
[ExcludeFromCodeCoverage]
public class ShardingCoreDataSourceQueryRouteNotMatchException : ShardingCoreQueryRouteNotMatchException
{
public ShardingCoreDataSourceQueryRouteNotMatchException(string message) : base(message)
{
}
public ShardingCoreDataSourceQueryRouteNotMatchException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Exceptions
{
[ExcludeFromCodeCoverage]
public class ShardingCoreQueryRouteNotMatchException : ShardingCoreException
{
public ShardingCoreQueryRouteNotMatchException(string message) : base(message)
{
}
public ShardingCoreQueryRouteNotMatchException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShardingCore.Exceptions
{
[ExcludeFromCodeCoverage]
public class ShardingCoreTableQueryRouteNotMatchException : ShardingCoreQueryRouteNotMatchException
{
public ShardingCoreTableQueryRouteNotMatchException(string message) : base(message)
{
}
public ShardingCoreTableQueryRouteNotMatchException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -58,10 +58,10 @@ namespace ShardingCore
/// </summary>
public bool? IgnoreCreateTableError { get; set; }
/// <summary>
/// 自动追踪实体
/// </summary>
public bool AutoTrackEntity { get; set; }
///// <summary>
///// 自动追踪实体
///// </summary>
//public bool AutoTrackEntity { get; set; }
/// <summary>
/// 默认数据源名称
/// </summary>
@ -78,18 +78,22 @@ namespace ShardingCore
/// 连接数限制
/// </summary>
public ConnectionModeEnum ConnectionMode { get; set; }
/// <summary>
/// 当查询遇到没有路由被命中时是否抛出错误
/// </summary>
public bool ThrowIfQueryRouteNotMatch { get; set; }
public bool AddParallelTableGroupNode(ParallelTableGroupNode parallelTableGroupNode);
public ISet<ParallelTableGroupNode> GetParallelTableGroupNodes();
///// <summary>
///// 是否启用表路由编译缓存
///// </summary>
//public bool? EnableTableRouteCompileCache { get; set; }
///// <summary>
///// 是否启用分库路由编译缓存
///// </summary>
//public bool? EnableDataSourceRouteCompileCache { get; set; }
/// <summary>
/// 是否启用表路由编译缓存
/// </summary>
public bool? EnableTableRouteCompileCache { get; set; }
/// <summary>
/// 是否启用分库路由编译缓存
/// </summary>
public bool? EnableDataSourceRouteCompileCache { get; set; }
}
public interface IShardingConfigOption<TShardingDbContext>: IShardingConfigOption where TShardingDbContext : DbContext, IShardingDbContext

View File

@ -44,8 +44,11 @@ namespace ShardingCore.Sharding.MergeEngines.Abstractions.InMemoryMerge
public async Task<List<RouteQueryResult<TResult>>> ExecuteAsync<TResult>(Func<IQueryable, Task<TResult>> efQuery, CancellationToken cancellationToken = new CancellationToken())
{
var routeQueryResults = _mergeContext.PreperExecute(() => new List<RouteQueryResult<TResult>>(0));
if (routeQueryResults != null)
return routeQueryResults;
var defaultSqlRouteUnits = GetDefaultSqlRouteUnits();
var waitExecuteQueue = GetDataSourceGroupAndExecutorGroup<RouteQueryResult<TResult>>(true,defaultSqlRouteUnits,
var waitExecuteQueue = GetDataSourceGroupAndExecutorGroup<RouteQueryResult<TResult>>(true, defaultSqlRouteUnits,
async sqlExecutorUnit =>
{
var connectionMode = _mergeContext.RealConnectionMode(sqlExecutorUnit.ConnectionMode);

View File

@ -4,6 +4,7 @@ using System.Threading;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.Enumerators.TrackerEnumerators;
using ShardingCore.Sharding.MergeEngines.EnumeratorStreamMergeEngines.EnumeratorAsync;
using ShardingCore.Sharding.ShardingQueryExecutors;
/*
@ -29,6 +30,9 @@ namespace ShardingCore.Sharding.MergeEngines.EnumeratorStreamMergeEngines
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = new CancellationToken())
{
cancellationToken.ThrowIfCancellationRequested();
var emptyQueryEnumerator = _mergeContext.PreperExecute(() => new EmptyQueryEnumerator<T>());
if (emptyQueryEnumerator != null)
return emptyQueryEnumerator;
var asyncEnumerator = EnumeratorStreamMergeEngineFactory<TShardingDbContext,T>.Create(_mergeContext).GetMergeEngine()
.GetAsyncEnumerator(cancellationToken);
@ -44,6 +48,9 @@ namespace ShardingCore.Sharding.MergeEngines.EnumeratorStreamMergeEngines
#if EFCORE2
IAsyncEnumerator<T> IAsyncEnumerable<T>.GetEnumerator()
{
var emptyQueryEnumerator = _mergeContext.PreperExecute(() => new EmptyQueryEnumerator<T>());
if (emptyQueryEnumerator != null)
return emptyQueryEnumerator;
var asyncEnumerator = ((IAsyncEnumerable<T>)EnumeratorStreamMergeEngineFactory<TShardingDbContext,T>.Create(_mergeContext).GetMergeEngine())
.GetEnumerator();
if (_mergeContext.IsUseShardingTrack(typeof(T)))
@ -57,6 +64,9 @@ namespace ShardingCore.Sharding.MergeEngines.EnumeratorStreamMergeEngines
public IEnumerator<T> GetEnumerator()
{
var emptyQueryEnumerator = _mergeContext.PreperExecute(() => new EmptyQueryEnumerator<T>());
if (emptyQueryEnumerator != null)
return emptyQueryEnumerator;
var enumerator = ((IEnumerable<T>)EnumeratorStreamMergeEngineFactory<TShardingDbContext,T>.Create(_mergeContext).GetMergeEngine())
.GetEnumerator();

View File

@ -52,16 +52,17 @@ namespace ShardingCore.Sharding.ShardingExecutors
private IEnumerable<TableRouteResult> GetTableRouteResults(IEnumerable<TableRouteResult> tableRouteResults)
{
if (_queryCompilerContext.GetQueryEntities().Count > 1)
var routeResults = tableRouteResults as TableRouteResult[] ?? tableRouteResults.ToArray();
if (_queryCompilerContext.GetQueryEntities().Count > 1&& routeResults.Length>0)
{
var entityMetadataManager = _queryCompilerContext.GetEntityMetadataManager();
var queryShardingTables = _queryCompilerContext.GetQueryEntities().Where(o => entityMetadataManager.IsShardingTable(o)).ToArray();
if (queryShardingTables.Length > 1 && _parallelTableManager.IsParallelTableQuery(queryShardingTables))
{
return tableRouteResults.Where(o => o.ReplaceTables.Select(p => p.Tail).ToHashSet().Count == 1);
return routeResults.Where(o => o.ReplaceTables.Select(p => p.Tail).ToHashSet().Count == 1);
}
}
return tableRouteResults;
return routeResults;
}
public static MergeQueryCompilerContext Create(IQueryCompilerContext queryCompilerContext, QueryCombineResult queryCombineResult, DataSourceRouteResult dataSourceRouteResult,IEnumerable<TableRouteResult> tableRouteResults)
@ -101,12 +102,19 @@ namespace ShardingCore.Sharding.ShardingExecutors
{
if (!hasQueryCompilerExecutor.HasValue)
{
hasQueryCompilerExecutor = !IsMergeQuery();
if (hasQueryCompilerExecutor.Value)
if (_dataSourceRouteResult.IntersectDataSources.IsEmpty() || _tableRouteResults.IsEmpty())
{
var routeTailFactory = ShardingContainer.GetService<IRouteTailFactory>();
var dbContext = GetShardingDbContext().GetDbContext(_dataSourceRouteResult.IntersectDataSources.First(), CurrentQueryReadConnection(), routeTailFactory.Create(_tableRouteResults.First()));
_queryCompilerExecutor = new QueryCompilerExecutor(dbContext, GetQueryExpression());
hasQueryCompilerExecutor = false;
}
else
{
hasQueryCompilerExecutor = !IsMergeQuery();
if (hasQueryCompilerExecutor.Value)
{
var routeTailFactory = ShardingContainer.GetService<IRouteTailFactory>();
var dbContext = GetShardingDbContext().GetDbContext(_dataSourceRouteResult.IntersectDataSources.First(), CurrentQueryReadConnection(), routeTailFactory.Create(_tableRouteResults.First()));
_queryCompilerExecutor = new QueryCompilerExecutor(dbContext, GetQueryExpression());
}
}
}

View File

@ -45,8 +45,9 @@ namespace ShardingCore.Sharding.ShardingExecutors
var queryCombineResult = queryableCombine.Combine(queryCompilerContext);
var dataSourceRouteResult = dataSourceRouteRuleEngineFactory.Route(queryCombineResult.GetCombineQueryable());
var tableRouteResults = tableRouteRuleEngineFactory.Route(queryCombineResult.GetCombineQueryable());
var routeResults = tableRouteResults as TableRouteResult[] ?? tableRouteResults.ToArray();
var mergeCombineCompilerContext = MergeQueryCompilerContext.Create(queryCompilerContext, queryCombineResult, dataSourceRouteResult,
tableRouteResults);
routeResults);
return mergeCombineCompilerContext;
}

View File

@ -17,6 +17,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ShardingCore.Exceptions;
using ShardingCore.Sharding.StreamMergeEngines;
namespace ShardingCore.Sharding
@ -233,7 +235,7 @@ namespace ShardingCore.Sharding
/// <returns></returns>
public bool IsParallelQuery()
{
return !_shardingConfigOption.AutoTrackEntity || MergeQueryCompilerContext.IsCrossTable() || MergeQueryCompilerContext.CurrentQueryReadConnection();
return MergeQueryCompilerContext.IsCrossTable() || MergeQueryCompilerContext.CurrentQueryReadConnection();
}
/// <summary>
@ -266,6 +268,49 @@ namespace ShardingCore.Sharding
{
return _shardingComparer;
}
public TResult PreperExecute<TResult>(Func<TResult> emptyFunc)
{
if (IsRouteNotMatch())
{
if (ThrowIfQueryRouteNotMatch())
{
if (IsDataSourceRouteNotMatch())
{
throw new ShardingCoreDataSourceQueryRouteNotMatchException(MergeQueryCompilerContext.GetQueryExpression().ShardingPrint());
}
else
{
throw new ShardingCoreTableQueryRouteNotMatchException(MergeQueryCompilerContext.GetQueryExpression().ShardingPrint());
}
}
else
{
return emptyFunc();
}
}
return default;
}
/// <summary>
/// 无路由匹配
/// </summary>
/// <returns></returns>
private bool IsRouteNotMatch()
{
return DataSourceRouteResult.IntersectDataSources.IsEmpty() || TableRouteResults.IsEmpty();
}
private bool IsDataSourceRouteNotMatch()
{
return DataSourceRouteResult.IntersectDataSources.IsEmpty();
}
private bool ThrowIfQueryRouteNotMatch()
{
return _shardingConfigOption.ThrowIfQueryRouteNotMatch;
}
public void Dispose()
{
foreach (var dbContext in _parallelDbContexts.Keys)

View File

@ -232,15 +232,21 @@ namespace ShardingCore
/// 忽略建表时的错误
/// </summary>
public bool? IgnoreCreateTableError { get; set; } = true;
/// <summary>
/// 自动追踪实体
/// </summary>
public bool AutoTrackEntity { get; set; }
///// <summary>
///// 自动追踪实体
///// </summary>
//public bool AutoTrackEntity { get; set; }
public string DefaultDataSourceName { get; set; }
public string DefaultConnectionString { get; set; }
public int MaxQueryConnectionsLimit { get; set; } = Environment.ProcessorCount;
public ConnectionModeEnum ConnectionMode { get; set; } = ConnectionModeEnum.SYSTEM_AUTO;
/// <summary>
/// 当查询遇到没有路由被命中时是否抛出错误
/// </summary>
public bool ThrowIfQueryRouteNotMatch { get; set; } = true;
public bool? EnableTableRouteCompileCache { get; set; }
public bool? EnableDataSourceRouteCompileCache { get; set; }
public bool AddParallelTableGroupNode(ParallelTableGroupNode parallelTableGroupNode)

View File

@ -53,7 +53,7 @@ namespace ShardingCore.VirtualRoutes.Abstractions
/// </summary>
/// <returns></returns>
public abstract string[] GetCronExpressions();
public Task ExecuteAsync()
public virtual Task ExecuteAsync()
{
var virtualTableManager = (IVirtualTableManager)ShardingContainer.GetService(typeof(IVirtualTableManager<>).GetGenericType0(EntityMetadata.ShardingDbContextType));
var virtualTable = virtualTableManager.GetVirtualTable(typeof(TEntity));

View File

@ -49,7 +49,7 @@ namespace ShardingCore.VirtualRoutes.Days
return $"{time:yyyyMMdd}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -41,7 +41,7 @@ namespace ShardingCore.VirtualRoutes.Days
return $"{dateTime:yyyyMMdd}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -48,7 +48,7 @@ namespace ShardingCore.VirtualRoutes.Mods
return Enumerable.Range(0, Mod).Select(o => o.ToString().PadLeft(TailLength, PaddingChar)).ToList();
}
public override Expression<Func<string, bool>> GetMainRouteFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = ShardingKeyToTail(shardingKey);

View File

@ -65,7 +65,7 @@ namespace ShardingCore.VirtualRoutes.Mods
/// <param name="shardingKey"></param>
/// <param name="shardingOperator"></param>
/// <returns></returns>
public override Expression<Func<string, bool>> GetMainRouteFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(string shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = ShardingKeyToTail(shardingKey);
switch (shardingOperator)

View File

@ -39,7 +39,7 @@ namespace ShardingCore.VirtualRoutes.Months
return $"{time:yyyyMM}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -41,7 +41,7 @@ namespace ShardingCore.VirtualRoutes.Months
var datetime = ShardingCoreHelper.ConvertLongToDateTime(time);
return $"{datetime:yyyyMM}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -41,7 +41,7 @@ namespace ShardingCore.VirtualRoutes.Weeks
return $"{currentMonday:yyyyMM}{currentMonday:dd}_{currentSunday:dd}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -50,7 +50,7 @@ namespace ShardingCore.VirtualRoutes.Weeks
return $"{currentMonday:yyyyMM}{currentMonday:dd}_{currentSunday:dd}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -37,7 +37,7 @@ namespace ShardingCore.VirtualRoutes.Years
{
return $"{time:yyyy}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(DateTime shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -60,7 +60,7 @@ namespace ShardingCore.VirtualRoutes.Years
/// <param name="shardingKey"></param>
/// <param name="shardingOperator"></param>
/// <returns>当传入表后缀你告诉框架这个后缀是否需要被返回,分片字段如何筛选出后缀</returns>
public override Expression<Func<string, bool>> GetMainRouteFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(long shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -0,0 +1,196 @@
using System;
using System.Text;
namespace ShardingCore.Test.Common
{/// <summary>
/// 雪花ID
/// Twitter_Snowflake
/// SnowFlake的结构如下(每部分用-分开)
/// 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
/// 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0
/// 41位时间截(毫秒级)注意41位时间截不是存储当前时间的时间截而是存储时间截的差值当前时间截 - 开始时间截)得到的值),
/// 41位的时间截可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
/// 这里的的开始时间截一般是我们的id生成器开始使用的时间由我们程序来指定的如下下面程序IdWorker类的startTime属性
/// 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId
/// 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
/// 总共加起来刚好64位为一个Long型。
/// SnowFlake的优点是整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)
/// 并且效率较高经测试SnowFlake单机每秒都能够产生出极限4,096,000个ID来
/// </summary>
public class SnowflakeId
{
// 开始时间截 (new DateTime(2020, 1, 1).ToUniversalTime() - Jan1st1970).TotalMilliseconds
private const long twepoch = 1577808000000L;
// 机器id所占的位数
private const int workerIdBits = 5;
// 数据标识id所占的位数
private const int datacenterIdBits = 5;
// 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private const long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 支持的最大数据标识id结果是31
private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 序列在id中占的位数
private const int sequenceBits = 12;
// 数据标识id向左移17位(12+5)
private const int datacenterIdShift = sequenceBits + workerIdBits;
// 机器ID向左移12位
private const int workerIdShift = sequenceBits;
// 时间截向左移22位(5+5+12)
private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
// 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095)
private const long sequenceMask = -1L ^ (-1L << sequenceBits);
// 数据中心ID(0~31)
public long datacenterId { get; private set; }
// 工作机器ID(0~31)
public long workerId { get; private set; }
// 毫秒内序列(0~4095)
public long sequence { get; private set; }
// 上次生成ID的时间截
public long lastTimestamp { get; private set; }
/// <summary>
/// 雪花ID
/// </summary>
/// <param name="datacenterId">数据中心ID</param>
/// <param name="workerId">工作机器ID</param>
public SnowflakeId(long datacenterId, long workerId)
{
if (datacenterId > maxDatacenterId || datacenterId < 0)
{
throw new Exception(string.Format("datacenter Id can't be greater than {0} or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0)
{
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0", maxWorkerId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = 0L;
this.lastTimestamp = -1L;
}
/// <summary>
/// 获得下一个ID
/// </summary>
/// <returns></returns>
public long NextId()
{
lock (this)
{
long timestamp = GetCurrentTimestamp();
if (timestamp > lastTimestamp) //时间戳改变,毫秒内序列重置
{
sequence = 0L;
}
else if (timestamp == lastTimestamp) //如果是同一时间生成的,则进行毫秒内序列
{
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) //毫秒内序列溢出
{
timestamp = GetNextTimestamp(lastTimestamp); //阻塞到下一个毫秒,获得新的时间戳
}
}
else //当前时间小于上一次ID生成的时间戳证明系统时钟被回拨此时需要做回拨处理
{
sequence = (sequence + 1) & sequenceMask;
if (sequence > 0)
{
timestamp = lastTimestamp; //停留在最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题。
}
else //毫秒内序列溢出
{
timestamp = lastTimestamp + 1; //直接进位到下一个毫秒
}
//throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp));
}
lastTimestamp = timestamp; //上次生成ID的时间截
//移位并通过或运算拼到一起组成64位的ID
var id = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
return id;
}
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static DateTime AnalyzeIdToDateTime(long Id)
{
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
return time.ToLocalTime();
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static string AnalyzeId(long Id)
{
StringBuilder sb = new StringBuilder();
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
sb.Append(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss:fff"));
var datacenterId = (Id ^ (timestamp << timestampLeftShift)) >> datacenterIdShift;
sb.Append("_" + datacenterId);
var workerId = (Id ^ ((timestamp << timestampLeftShift) | (datacenterId << datacenterIdShift))) >> workerIdShift;
sb.Append("_" + workerId);
var sequence = Id & sequenceMask;
sb.Append("_" + sequence);
return sb.ToString();
}
/// <summary>
/// 阻塞到下一个毫秒,直到获得新的时间戳
/// </summary>
/// <param name="lastTimestamp">上次生成ID的时间截</param>
/// <returns>当前时间戳</returns>
private static long GetNextTimestamp(long lastTimestamp)
{
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
/// <summary>
/// 获取当前时间戳
/// </summary>
/// <returns></returns>
private static long GetCurrentTimestamp()
{
return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}
private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace ShardingCore.Test.Domain.Entities
{
public class MultiShardingOrder
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using ShardingCore.Test.Domain.Entities;
namespace ShardingCore.Test.Domain.Maps
{
public class MultiShardingOrderMap:IEntityTypeConfiguration<MultiShardingOrder>
{
public void Configure(EntityTypeBuilder<MultiShardingOrder> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Id).ValueGeneratedNever();
builder.Property(o => o.Name).IsRequired().IsUnicode(false).HasMaxLength(50);
builder.ToTable(nameof(MultiShardingOrder));
}
}
}

View File

@ -34,6 +34,7 @@ namespace ShardingCore.Test
modelBuilder.ApplyConfiguration(new LogYearLongMap());
modelBuilder.ApplyConfiguration(new SysUserModIntMap());
modelBuilder.ApplyConfiguration(new LogDayLongMap());
modelBuilder.ApplyConfiguration(new MultiShardingOrderMap());
}
public IRouteTail RouteTail { get; set; }

View File

@ -86,29 +86,29 @@ namespace ShardingCore.Test
var xxxx = "202102";
var queryable1 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202102);
var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable2 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= 202102);
var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var xxxx1 = 202102;
var queryable3 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= xxxx1);
var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable4 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202101);
var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable5 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth > 202101);
var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable6 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth == 202101);
var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable7 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 <= o.DateOfMonth);
var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable8 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 == o.DateOfMonth);
var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2));
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3));
Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4));
@ -235,6 +235,20 @@ namespace ShardingCore.Test
public string Id { get; set; }
public string T { get; set; }
}
[Fact]
public async Task TestMultiShardingProperty()
{
var multiOrder = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 232398109278351360).FirstOrDefaultAsync();
Assert.NotNull(multiOrder);
var longs = new[] { 232398109278351360, 255197859283087360 };
var multiOrders = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => longs.Contains(o.Id)).ToListAsync();
Assert.Equal(2, multiOrders.Count);
var dateTime = new DateTime(2021, 11, 1);
var multiOrder404 = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefaultAsync();
Assert.Null(multiOrder404);
}
[Fact]
public void TestEntityMetadataManager()
{

View File

@ -65,29 +65,29 @@ namespace ShardingCore.Test
var queryable1 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202102);
var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable2 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= 202102);
var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var xxxx1 = 202102;
var queryable3 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= xxxx1);
var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable4 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202101);
var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable5 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth > 202101);
var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable6 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth == 202101);
var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable7 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 <= o.DateOfMonth);
var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable8 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 == o.DateOfMonth);
var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2));
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3));
Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4));
@ -190,6 +190,20 @@ namespace ShardingCore.Test
public string Id { get; set; }
public string T { get; set; }
}
[Fact]
public void TestMultiShardingProperty()
{
var multiOrder = _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 232398109278351360).FirstOrDefault();
Assert.NotNull(multiOrder);
var longs = new[] { 232398109278351360, 255197859283087360 };
var multiOrders = _virtualDbContext.Set<MultiShardingOrder>().Where(o => longs.Contains(o.Id)).ToList();
Assert.Equal(2, multiOrders.Count);
var dateTime = new DateTime(2021, 11, 1);
var multiOrder404 = _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefault();
Assert.Null(multiOrder404);
}
[Fact]
public void TestEntityMetadataManager()
{

View File

@ -12,7 +12,7 @@ namespace ShardingCore.Test.Shardings
{
public class LogDayLongVirtualRoute:AbstractSimpleShardingDayKeyLongVirtualTableRoute<LogDayLong>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute => true;
public override void Configure(EntityMetadataTableBuilder<LogDayLong> builder)

View File

@ -10,7 +10,7 @@ namespace ShardingCore.Test.Shardings
{
public class LogDayVirtualTableRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute<LogDay>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute => true;
public override DateTime GetBeginTime()

View File

@ -12,7 +12,7 @@ namespace ShardingCore.Test.Shardings
{
public class LogMonthLongvirtualRoute:AbstractSimpleShardingMonthKeyLongVirtualTableRoute<LogMonthLong>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute => true;
public override bool AutoCreateTableByTime()

View File

@ -7,7 +7,7 @@ namespace ShardingCore.Test.Shardings
{
public class LogWeekDateTimeVirtualTableRoute:AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute<LogWeekDateTime>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute => true;
public override bool AutoCreateTableByTime()

View File

@ -11,7 +11,7 @@ namespace ShardingCore.Test.Shardings
{
public class LogWeekTimeLongVirtualTableRoute : AbstractSimpleShardingWeekKeyLongVirtualTableRoute<LogWeekTimeLong>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute => true;
public override bool AutoCreateTableByTime()

View File

@ -11,7 +11,7 @@ namespace ShardingCore.Test.Shardings
{
public class LogYearDateTimeVirtualRoute:AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute<LogYearDateTime>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute => true;
public override bool AutoCreateTableByTime()

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Extensions;
using ShardingCore.Test.Domain.Entities;
using ShardingCore.VirtualRoutes.Years;
@ -12,7 +13,7 @@ namespace ShardingCore.Test.Shardings
{
public class LogYearLongVirtualRoute:AbstractSimpleShardingYearKeyLongVirtualTableRoute<LogYearLong>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute => true;
public override void Configure(EntityMetadataTableBuilder<LogYearLong> builder)

View File

@ -0,0 +1,79 @@
using System;
using System.Linq.Expressions;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Helpers;
using ShardingCore.Test.Common;
using ShardingCore.Test.Domain.Entities;
using ShardingCore.VirtualRoutes.Months;
namespace ShardingCore.Test.Shardings
{
public class MultiShardingOrderVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<MultiShardingOrder>
{
public override void Configure(EntityMetadataTableBuilder<MultiShardingOrder> builder)
{
builder.ShardingProperty(o => o.CreateTime);
builder.ShardingExtraProperty(o => o.Id);
}
public override Expression<Func<string, bool>> GetExtraRouteFilter(object shardingKey, ShardingOperatorEnum shardingOperator, string shardingPropertyName)
{
switch (shardingPropertyName)
{
case nameof(MultiShardingOrder.Id): return GetIdRouteFilter(shardingKey, shardingOperator);
default: throw new NotImplementedException(shardingPropertyName);
}
}
private Expression<Func<string, bool>> GetIdRouteFilter(object shardingKey,
ShardingOperatorEnum shardingOperator)
{
//解析雪花id 需要考虑异常情况,传入的可能不是雪花id那么可以随机查询一张表
var analyzeIdToDateTime = SnowflakeId.AnalyzeIdToDateTime(Convert.ToInt64(shardingKey));
//当前时间的tail
var t = TimeFormatToTail(analyzeIdToDateTime);
//因为是按月分表所以获取下个月的时间判断id是否是在灵界点创建的
var nextMonthFirstDay = ShardingCoreHelper.GetNextMonthFirstDay(DateTime.Now);
if (analyzeIdToDateTime.AddSeconds(10) > nextMonthFirstDay)
{
var nextT = TimeFormatToTail(nextMonthFirstDay);
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t||tail== nextT;
}
}
var currentMonthFirstDay = ShardingCoreHelper.GetCurrentMonthFirstDay(DateTime.Now);
if (analyzeIdToDateTime.AddSeconds(-10) < currentMonthFirstDay)
{
//上个月tail
var nextT = TimeFormatToTail(analyzeIdToDateTime.AddSeconds(-10));
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t || tail == nextT;
}
}
else
{
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t;
}
}
return tail => true;
}
public override bool AutoCreateTableByTime()
{
return true;
}
public override DateTime GetBeginTime()
{
return new DateTime(2021, 9, 1);
}
}
}

View File

@ -11,7 +11,7 @@ namespace ShardingCore.Test.Shardings
{
public class OrderAreaShardingVirtualDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<Order,string>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
protected override bool EnableHintRoute =>true;
private readonly List<string> _dataSources = new List<string>()

View File

@ -9,7 +9,7 @@ namespace ShardingCore.Test.Shardings
{
public class OrderCreateTimeVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override DateTime GetBeginTime()
{
return new DateTime(2021, 1, 1);

View File

@ -12,7 +12,7 @@ namespace ShardingCore.Test.Shardings
public class SysUserModIntVirtualRoute:AbstractSimpleShardingModKeyIntVirtualTableRoute<SysUserModInt>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public SysUserModIntVirtualRoute() : base(2, 3)
{

View File

@ -13,7 +13,7 @@ namespace ShardingCore.Test.Shardings
public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute<SysUserMod>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public SysUserModVirtualTableRoute() : base(2,3)
{

View File

@ -16,7 +16,7 @@ namespace ShardingCore.Test.Shardings
*/
public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<SysUserSalary,int>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override string ShardingKeyToTail(object shardingKey)
{
@ -46,7 +46,7 @@ namespace ShardingCore.Test.Shardings
return $"{dateOfMonth:yyyyMM}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -41,7 +41,7 @@ namespace ShardingCore.Test
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
o.AutoTrackEntity = true;
o.ThrowIfQueryRouteNotMatch = false;
//o.AddParallelTables(typeof(SysUserMod), typeof(SysUserSalary));
})
.AddShardingTransaction((connection, builder) =>
@ -72,6 +72,7 @@ namespace ShardingCore.Test
op.AddShardingTableRoute<LogYearLongVirtualRoute>();
op.AddShardingTableRoute<SysUserModIntVirtualRoute>();
op.AddShardingTableRoute<LogDayLongVirtualRoute>();
op.AddShardingTableRoute<MultiShardingOrderVirtualTableRoute>();
}).AddReadWriteSeparation(sp =>
{
return new Dictionary<string, IEnumerable<string>>()
@ -269,6 +270,82 @@ namespace ShardingCore.Test
});
begin6 = begin6.AddDays(1);
}
var multiShardingOrders = new List<MultiShardingOrder>(9);
#region
{
var now = new DateTime(2021, 10, 1, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 231765457240207360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 10, 2, 11, 3, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 232095129534607360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 10, 3, 7, 7, 7);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 232398109278351360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 11, 6, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 244811420401807360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 11, 21, 19, 43, 0);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 250345338962063360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 5, 5, 5, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 255197859283087360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 9, 19, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 256860816933007360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 19, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 260394098622607360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
#endregion
using (var tran = virtualDbContext.Database.BeginTransaction())
{
await virtualDbContext.AddRangeAsync(userMods);
@ -282,6 +359,7 @@ namespace ShardingCore.Test
await virtualDbContext.AddRangeAsync(logYears);
await virtualDbContext.AddRangeAsync(logMonthLongs);
await virtualDbContext.AddRangeAsync(logYearkLongs);
await virtualDbContext.AddRangeAsync(multiShardingOrders);
await virtualDbContext.SaveChangesAsync();
tran.Commit();

View File

@ -0,0 +1,196 @@
using System;
using System.Text;
namespace ShardingCore.Test2x.Common
{/// <summary>
/// 雪花ID
/// Twitter_Snowflake
/// SnowFlake的结构如下(每部分用-分开)
/// 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
/// 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0
/// 41位时间截(毫秒级)注意41位时间截不是存储当前时间的时间截而是存储时间截的差值当前时间截 - 开始时间截)得到的值),
/// 41位的时间截可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
/// 这里的的开始时间截一般是我们的id生成器开始使用的时间由我们程序来指定的如下下面程序IdWorker类的startTime属性
/// 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId
/// 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
/// 总共加起来刚好64位为一个Long型。
/// SnowFlake的优点是整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)
/// 并且效率较高经测试SnowFlake单机每秒都能够产生出极限4,096,000个ID来
/// </summary>
public class SnowflakeId
{
// 开始时间截 (new DateTime(2020, 1, 1).ToUniversalTime() - Jan1st1970).TotalMilliseconds
private const long twepoch = 1577808000000L;
// 机器id所占的位数
private const int workerIdBits = 5;
// 数据标识id所占的位数
private const int datacenterIdBits = 5;
// 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private const long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 支持的最大数据标识id结果是31
private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 序列在id中占的位数
private const int sequenceBits = 12;
// 数据标识id向左移17位(12+5)
private const int datacenterIdShift = sequenceBits + workerIdBits;
// 机器ID向左移12位
private const int workerIdShift = sequenceBits;
// 时间截向左移22位(5+5+12)
private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
// 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095)
private const long sequenceMask = -1L ^ (-1L << sequenceBits);
// 数据中心ID(0~31)
public long datacenterId { get; private set; }
// 工作机器ID(0~31)
public long workerId { get; private set; }
// 毫秒内序列(0~4095)
public long sequence { get; private set; }
// 上次生成ID的时间截
public long lastTimestamp { get; private set; }
/// <summary>
/// 雪花ID
/// </summary>
/// <param name="datacenterId">数据中心ID</param>
/// <param name="workerId">工作机器ID</param>
public SnowflakeId(long datacenterId, long workerId)
{
if (datacenterId > maxDatacenterId || datacenterId < 0)
{
throw new Exception(string.Format("datacenter Id can't be greater than {0} or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0)
{
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0", maxWorkerId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = 0L;
this.lastTimestamp = -1L;
}
/// <summary>
/// 获得下一个ID
/// </summary>
/// <returns></returns>
public long NextId()
{
lock (this)
{
long timestamp = GetCurrentTimestamp();
if (timestamp > lastTimestamp) //时间戳改变,毫秒内序列重置
{
sequence = 0L;
}
else if (timestamp == lastTimestamp) //如果是同一时间生成的,则进行毫秒内序列
{
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) //毫秒内序列溢出
{
timestamp = GetNextTimestamp(lastTimestamp); //阻塞到下一个毫秒,获得新的时间戳
}
}
else //当前时间小于上一次ID生成的时间戳证明系统时钟被回拨此时需要做回拨处理
{
sequence = (sequence + 1) & sequenceMask;
if (sequence > 0)
{
timestamp = lastTimestamp; //停留在最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题。
}
else //毫秒内序列溢出
{
timestamp = lastTimestamp + 1; //直接进位到下一个毫秒
}
//throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp));
}
lastTimestamp = timestamp; //上次生成ID的时间截
//移位并通过或运算拼到一起组成64位的ID
var id = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
return id;
}
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static DateTime AnalyzeIdToDateTime(long Id)
{
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
return time.ToLocalTime();
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static string AnalyzeId(long Id)
{
StringBuilder sb = new StringBuilder();
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
sb.Append(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss:fff"));
var datacenterId = (Id ^ (timestamp << timestampLeftShift)) >> datacenterIdShift;
sb.Append("_" + datacenterId);
var workerId = (Id ^ ((timestamp << timestampLeftShift) | (datacenterId << datacenterIdShift))) >> workerIdShift;
sb.Append("_" + workerId);
var sequence = Id & sequenceMask;
sb.Append("_" + sequence);
return sb.ToString();
}
/// <summary>
/// 阻塞到下一个毫秒,直到获得新的时间戳
/// </summary>
/// <param name="lastTimestamp">上次生成ID的时间截</param>
/// <returns>当前时间戳</returns>
private static long GetNextTimestamp(long lastTimestamp)
{
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
/// <summary>
/// 获取当前时间戳
/// </summary>
/// <returns></returns>
private static long GetCurrentTimestamp()
{
return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}
private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace ShardingCore.Test2x.Domain.Entities
{
public class MultiShardingOrder
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using ShardingCore.Test2x.Domain.Entities;
namespace ShardingCore.Test2x.Domain.Maps
{
public class MultiShardingOrderMap:IEntityTypeConfiguration<MultiShardingOrder>
{
public void Configure(EntityTypeBuilder<MultiShardingOrder> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Id).ValueGeneratedNever();
builder.Property(o => o.Name).IsRequired().IsUnicode(false).HasMaxLength(50);
builder.ToTable(nameof(MultiShardingOrder));
}
}
}

View File

@ -34,6 +34,7 @@ namespace ShardingCore.Test2x
modelBuilder.ApplyConfiguration(new LogYearLongMap());
modelBuilder.ApplyConfiguration(new SysUserModIntMap());
modelBuilder.ApplyConfiguration(new LogDayLongMap());
modelBuilder.ApplyConfiguration(new MultiShardingOrderMap());
}
public IRouteTail RouteTail { get; set; }

View File

@ -74,29 +74,29 @@ namespace ShardingCore.Test2x
var queryable1 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202102);
var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable2 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= 202102);
var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var xxxx1 = 202102;
var queryable3 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= xxxx1);
var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable4 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202101);
var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable5 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth > 202101);
var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable6 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth == 202101);
var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable7 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 <= o.DateOfMonth);
var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable8 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 == o.DateOfMonth);
var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2));
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3));
Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4));
@ -218,6 +218,20 @@ namespace ShardingCore.Test2x
public string Id { get; set; }
public string T { get; set; }
}
[Fact]
public async Task TestMultiShardingProperty()
{
var multiOrder = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 232398109278351360).FirstOrDefaultAsync();
Assert.NotNull(multiOrder);
var longs = new[] { 232398109278351360, 255197859283087360 };
var multiOrders = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => longs.Contains(o.Id)).ToListAsync();
Assert.Equal(2, multiOrders.Count);
var dateTime = new DateTime(2021, 11, 1);
var multiOrder404 = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefaultAsync();
Assert.Null(multiOrder404);
}
[Fact]
public void TestEntityMetadataManager()
{

View File

@ -64,29 +64,29 @@ namespace ShardingCore.Test2x
var queryable1 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202102);
var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable2 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= 202102);
var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var xxxx1 = 202102;
var queryable3 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= xxxx1);
var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable4 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202101);
var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable5 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth > 202101);
var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable6 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth == 202101);
var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable7 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 <= o.DateOfMonth);
var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable8 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 == o.DateOfMonth);
var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2));
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3));
Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4));
@ -189,6 +189,19 @@ namespace ShardingCore.Test2x
public string T { get; set; }
}
[Fact]
public void TestMultiShardingProperty()
{
var multiOrder = _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 232398109278351360).FirstOrDefault();
Assert.NotNull(multiOrder);
var longs = new[] { 232398109278351360, 255197859283087360 };
var multiOrders = _virtualDbContext.Set<MultiShardingOrder>().Where(o => longs.Contains(o.Id)).ToList();
Assert.Equal(2, multiOrders.Count);
var dateTime = new DateTime(2021, 11, 1);
var multiOrder404 = _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefault();
Assert.Null(multiOrder404);
}
[Fact]
public void TestEntityMetadataManager()
{
var objMetadata0 = _entityMetadataManager.TryGet(typeof(object));

View File

@ -10,7 +10,7 @@ namespace ShardingCore.Test2x.Shardings
public class LogDayLongVirtualRoute:AbstractSimpleShardingDayKeyLongVirtualTableRoute<LogDayLong>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override void Configure(EntityMetadataTableBuilder<LogDayLong> builder)
{

View File

@ -11,7 +11,7 @@ namespace ShardingCore.Test2x.Shardings
public class LogDayVirtualTableRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute<LogDay>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override DateTime GetBeginTime()
{

View File

@ -8,7 +8,7 @@ namespace ShardingCore.Test2x.Shardings
public class LogMonthLongvirtualRoute:AbstractSimpleShardingMonthKeyLongVirtualTableRoute<LogMonthLong>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override bool AutoCreateTableByTime()
{

View File

@ -8,7 +8,7 @@ namespace ShardingCore.Test2x.Shardings
public class LogWeekDateTimeVirtualTableRoute:AbstractSimpleShardingWeekKeyDateTimeVirtualTableRoute<LogWeekDateTime>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override bool AutoCreateTableByTime()
{

View File

@ -8,7 +8,7 @@ namespace ShardingCore.Test2x.Shardings
public class LogWeekTimeLongVirtualTableRoute : AbstractSimpleShardingWeekKeyLongVirtualTableRoute<LogWeekTimeLong>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override bool AutoCreateTableByTime()
{

View File

@ -8,7 +8,7 @@ namespace ShardingCore.Test2x.Shardings
public class LogYearDateTimeVirtualRoute:AbstractSimpleShardingYearKeyDateTimeVirtualTableRoute<LogYearDateTime>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override bool AutoCreateTableByTime()
{

View File

@ -8,7 +8,7 @@ namespace ShardingCore.Test2x.Shardings
public class LogYearLongVirtualRoute:AbstractSimpleShardingYearKeyLongVirtualTableRoute<LogYearLong>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override void Configure(EntityMetadataTableBuilder<LogYearLong> builder)
{

View File

@ -0,0 +1,79 @@
using System;
using System.Linq.Expressions;
using ShardingCore.Core.EntityMetadatas;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Helpers;
using ShardingCore.Test2x.Common;
using ShardingCore.Test2x.Domain.Entities;
using ShardingCore.VirtualRoutes.Months;
namespace ShardingCore.Test2x.Shardings
{
public class MultiShardingOrderVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<MultiShardingOrder>
{
public override void Configure(EntityMetadataTableBuilder<MultiShardingOrder> builder)
{
builder.ShardingProperty(o => o.CreateTime);
builder.ShardingExtraProperty(o => o.Id);
}
public override Expression<Func<string, bool>> GetExtraRouteFilter(object shardingKey, ShardingOperatorEnum shardingOperator, string shardingPropertyName)
{
switch (shardingPropertyName)
{
case nameof(MultiShardingOrder.Id): return GetIdRouteFilter(shardingKey, shardingOperator);
default: throw new NotImplementedException(shardingPropertyName);
}
}
private Expression<Func<string, bool>> GetIdRouteFilter(object shardingKey,
ShardingOperatorEnum shardingOperator)
{
//解析雪花id 需要考虑异常情况,传入的可能不是雪花id那么可以随机查询一张表
var analyzeIdToDateTime = SnowflakeId.AnalyzeIdToDateTime(Convert.ToInt64(shardingKey));
//当前时间的tail
var t = TimeFormatToTail(analyzeIdToDateTime);
//因为是按月分表所以获取下个月的时间判断id是否是在灵界点创建的
var nextMonthFirstDay = ShardingCoreHelper.GetNextMonthFirstDay(DateTime.Now);
if (analyzeIdToDateTime.AddSeconds(10) > nextMonthFirstDay)
{
var nextT = TimeFormatToTail(nextMonthFirstDay);
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t||tail== nextT;
}
}
var currentMonthFirstDay = ShardingCoreHelper.GetCurrentMonthFirstDay(DateTime.Now);
if (analyzeIdToDateTime.AddSeconds(-10) < currentMonthFirstDay)
{
//上个月tail
var nextT = TimeFormatToTail(analyzeIdToDateTime.AddSeconds(-10));
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t || tail == nextT;
}
}
else
{
if (shardingOperator == ShardingOperatorEnum.Equal)
{
return tail => tail == t;
}
}
return tail => true;
}
public override bool AutoCreateTableByTime()
{
return true;
}
public override DateTime GetBeginTime()
{
return new DateTime(2021, 9, 1);
}
}
}

View File

@ -12,7 +12,7 @@ namespace ShardingCore.Test2x.Shardings
public class OrderAreaShardingVirtualDataSourceRoute:AbstractShardingOperatorVirtualDataSourceRoute<Order,string>
{
protected override bool EnableHintRoute =>true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
private readonly List<string> _dataSources = new List<string>()
{

View File

@ -9,7 +9,7 @@ namespace ShardingCore.Test2x.Shardings
{
public class OrderCreateTimeVirtualTableRoute:AbstractSimpleShardingMonthKeyDateTimeVirtualTableRoute<Order>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override DateTime GetBeginTime()
{
return new DateTime(2021, 1, 1);

View File

@ -7,7 +7,7 @@ namespace ShardingCore.Test2x.Shardings
public class SysUserModIntVirtualRoute:AbstractSimpleShardingModKeyIntVirtualTableRoute<SysUserModInt>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public SysUserModIntVirtualRoute() : base(2, 3)
{

View File

@ -13,7 +13,7 @@ namespace ShardingCore.Test2x.Shardings
public class SysUserModVirtualTableRoute : AbstractSimpleShardingModKeyStringVirtualTableRoute<SysUserMod>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public SysUserModVirtualTableRoute() : base(2,3)
{

View File

@ -16,7 +16,7 @@ namespace ShardingCore.Test2x.Shardings
*/
public class SysUserSalaryVirtualTableRoute:AbstractShardingOperatorVirtualTableRoute<SysUserSalary,int>
{
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override string ShardingKeyToTail(object shardingKey)
{
var time = Convert.ToInt32(shardingKey);
@ -45,7 +45,7 @@ namespace ShardingCore.Test2x.Shardings
return $"{dateOfMonth:yyyyMM}";
}
public override Expression<Func<string, bool>> GetMainRouteFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
public override Expression<Func<string, bool>> GetRouteToFilter(int shardingKey, ShardingOperatorEnum shardingOperator)
{
var t = TimeFormatToTail(shardingKey);
switch (shardingOperator)

View File

@ -40,7 +40,7 @@ namespace ShardingCore.Test2x
{
o.CreateShardingTableOnStart = true;
o.EnsureCreatedWithOutShardingTable = true;
o.AutoTrackEntity = true;
o.ThrowIfQueryRouteNotMatch = false;
})
.AddShardingTransaction((connection, builder) =>
builder.UseSqlServer(connection).UseLoggerFactory(efLogger))
@ -70,6 +70,7 @@ namespace ShardingCore.Test2x
op.AddShardingTableRoute<LogYearLongVirtualRoute>();
op.AddShardingTableRoute<SysUserModIntVirtualRoute>();
op.AddShardingTableRoute<LogDayLongVirtualRoute>();
op.AddShardingTableRoute<MultiShardingOrderVirtualTableRoute>();
}).AddReadWriteSeparation(sp =>
{
return new Dictionary<string, IEnumerable<string>>()
@ -267,6 +268,82 @@ namespace ShardingCore.Test2x
});
begin6 = begin6.AddDays(1);
}
var multiShardingOrders = new List<MultiShardingOrder>(9);
#region
{
var now = new DateTime(2021, 10, 1, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 231765457240207360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 10, 2, 11, 3, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 232095129534607360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 10, 3, 7, 7, 7);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 232398109278351360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 11, 6, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 244811420401807360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 11, 21, 19, 43, 0);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 250345338962063360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 5, 5, 5, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 255197859283087360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 9, 19, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 256860816933007360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
{
var now = new DateTime(2021, 12, 19, 13, 13, 11);
multiShardingOrders.Add(new MultiShardingOrder()
{
Id = 260394098622607360,
Name = $"{now:yyyy/MM/dd HH:mm:ss}",
CreateTime = now
});
}
#endregion
using (var tran = virtualDbContext.Database.BeginTransaction())
{
await virtualDbContext.AddRangeAsync(userMods);
@ -280,6 +357,7 @@ namespace ShardingCore.Test2x
await virtualDbContext.AddRangeAsync(logYears);
await virtualDbContext.AddRangeAsync(logMonthLongs);
await virtualDbContext.AddRangeAsync(logYearkLongs);
await virtualDbContext.AddRangeAsync(multiShardingOrders);
await virtualDbContext.SaveChangesAsync();
tran.Commit();

View File

@ -0,0 +1,196 @@
using System;
using System.Text;
namespace ShardingCore.Test3x.Common
{/// <summary>
/// 雪花ID
/// Twitter_Snowflake
/// SnowFlake的结构如下(每部分用-分开)
/// 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
/// 1位标识由于long基本类型在Java中是带符号的最高位是符号位正数是0负数是1所以id一般是正数最高位是0
/// 41位时间截(毫秒级)注意41位时间截不是存储当前时间的时间截而是存储时间截的差值当前时间截 - 开始时间截)得到的值),
/// 41位的时间截可以使用69年年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69
/// 这里的的开始时间截一般是我们的id生成器开始使用的时间由我们程序来指定的如下下面程序IdWorker类的startTime属性
/// 10位的数据机器位可以部署在1024个节点包括5位datacenterId和5位workerId
/// 12位序列毫秒内的计数12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号
/// 总共加起来刚好64位为一个Long型。
/// SnowFlake的优点是整体上按照时间自增排序并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分)
/// 并且效率较高经测试SnowFlake单机每秒都能够产生出极限4,096,000个ID来
/// </summary>
public class SnowflakeId
{
// 开始时间截 (new DateTime(2020, 1, 1).ToUniversalTime() - Jan1st1970).TotalMilliseconds
private const long twepoch = 1577808000000L;
// 机器id所占的位数
private const int workerIdBits = 5;
// 数据标识id所占的位数
private const int datacenterIdBits = 5;
// 支持的最大机器id结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
private const long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 支持的最大数据标识id结果是31
private const long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
// 序列在id中占的位数
private const int sequenceBits = 12;
// 数据标识id向左移17位(12+5)
private const int datacenterIdShift = sequenceBits + workerIdBits;
// 机器ID向左移12位
private const int workerIdShift = sequenceBits;
// 时间截向左移22位(5+5+12)
private const int timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
// 生成序列的掩码这里为4095 (0b111111111111=0xfff=4095)
private const long sequenceMask = -1L ^ (-1L << sequenceBits);
// 数据中心ID(0~31)
public long datacenterId { get; private set; }
// 工作机器ID(0~31)
public long workerId { get; private set; }
// 毫秒内序列(0~4095)
public long sequence { get; private set; }
// 上次生成ID的时间截
public long lastTimestamp { get; private set; }
/// <summary>
/// 雪花ID
/// </summary>
/// <param name="datacenterId">数据中心ID</param>
/// <param name="workerId">工作机器ID</param>
public SnowflakeId(long datacenterId, long workerId)
{
if (datacenterId > maxDatacenterId || datacenterId < 0)
{
throw new Exception(string.Format("datacenter Id can't be greater than {0} or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0)
{
throw new Exception(string.Format("worker Id can't be greater than {0} or less than 0", maxWorkerId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
this.sequence = 0L;
this.lastTimestamp = -1L;
}
/// <summary>
/// 获得下一个ID
/// </summary>
/// <returns></returns>
public long NextId()
{
lock (this)
{
long timestamp = GetCurrentTimestamp();
if (timestamp > lastTimestamp) //时间戳改变,毫秒内序列重置
{
sequence = 0L;
}
else if (timestamp == lastTimestamp) //如果是同一时间生成的,则进行毫秒内序列
{
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) //毫秒内序列溢出
{
timestamp = GetNextTimestamp(lastTimestamp); //阻塞到下一个毫秒,获得新的时间戳
}
}
else //当前时间小于上一次ID生成的时间戳证明系统时钟被回拨此时需要做回拨处理
{
sequence = (sequence + 1) & sequenceMask;
if (sequence > 0)
{
timestamp = lastTimestamp; //停留在最后一次时间戳上,等待系统时间追上后即完全度过了时钟回拨问题。
}
else //毫秒内序列溢出
{
timestamp = lastTimestamp + 1; //直接进位到下一个毫秒
}
//throw new Exception(string.Format("Clock moved backwards. Refusing to generate id for {0} milliseconds", lastTimestamp - timestamp));
}
lastTimestamp = timestamp; //上次生成ID的时间截
//移位并通过或运算拼到一起组成64位的ID
var id = ((timestamp - twepoch) << timestampLeftShift)
| (datacenterId << datacenterIdShift)
| (workerId << workerIdShift)
| sequence;
return id;
}
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static DateTime AnalyzeIdToDateTime(long Id)
{
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
return time.ToLocalTime();
}
/// <summary>
/// 解析雪花ID
/// </summary>
/// <returns></returns>
public static string AnalyzeId(long Id)
{
StringBuilder sb = new StringBuilder();
var timestamp = (Id >> timestampLeftShift);
var time = Jan1st1970.AddMilliseconds(timestamp + twepoch);
sb.Append(time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss:fff"));
var datacenterId = (Id ^ (timestamp << timestampLeftShift)) >> datacenterIdShift;
sb.Append("_" + datacenterId);
var workerId = (Id ^ ((timestamp << timestampLeftShift) | (datacenterId << datacenterIdShift))) >> workerIdShift;
sb.Append("_" + workerId);
var sequence = Id & sequenceMask;
sb.Append("_" + sequence);
return sb.ToString();
}
/// <summary>
/// 阻塞到下一个毫秒,直到获得新的时间戳
/// </summary>
/// <param name="lastTimestamp">上次生成ID的时间截</param>
/// <returns>当前时间戳</returns>
private static long GetNextTimestamp(long lastTimestamp)
{
long timestamp = GetCurrentTimestamp();
while (timestamp <= lastTimestamp)
{
timestamp = GetCurrentTimestamp();
}
return timestamp;
}
/// <summary>
/// 获取当前时间戳
/// </summary>
/// <returns></returns>
private static long GetCurrentTimestamp()
{
return (long)(DateTime.UtcNow - Jan1st1970).TotalMilliseconds;
}
private static readonly DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
}

View File

@ -0,0 +1,11 @@
using System;
namespace ShardingCore.Test3x.Domain.Entities
{
public class MultiShardingOrder
{
public long Id { get; set; }
public string Name { get; set; }
public DateTime CreateTime { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using ShardingCore.Test3x.Domain.Entities;
namespace ShardingCore.Test3x.Domain.Maps
{
public class MultiShardingOrderMap:IEntityTypeConfiguration<MultiShardingOrder>
{
public void Configure(EntityTypeBuilder<MultiShardingOrder> builder)
{
builder.HasKey(o => o.Id);
builder.Property(o => o.Id).ValueGeneratedNever();
builder.Property(o => o.Name).IsRequired().IsUnicode(false).HasMaxLength(50);
builder.ToTable(nameof(MultiShardingOrder));
}
}
}

View File

@ -34,6 +34,7 @@ namespace ShardingCore.Test3x
modelBuilder.ApplyConfiguration(new LogYearLongMap());
modelBuilder.ApplyConfiguration(new SysUserModIntMap());
modelBuilder.ApplyConfiguration(new LogDayLongMap());
modelBuilder.ApplyConfiguration(new MultiShardingOrderMap());
}
public IRouteTail RouteTail { get; set; }

View File

@ -74,29 +74,29 @@ namespace ShardingCore.Test3x
var queryable1 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202102);
var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable2 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= 202102);
var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var xxxx1 = 202102;
var queryable3 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= xxxx1);
var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable4 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202101);
var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable5 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth > 202101);
var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable6 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth == 202101);
var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable7 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 <= o.DateOfMonth);
var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable8 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 == o.DateOfMonth);
var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2));
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3));
Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4));
@ -217,6 +217,20 @@ namespace ShardingCore.Test3x
public string Id { get; set; }
public string T { get; set; }
}
[Fact]
public async Task TestMultiShardingProperty()
{
var multiOrder = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 232398109278351360).FirstOrDefaultAsync();
Assert.NotNull(multiOrder);
var longs = new[] { 232398109278351360, 255197859283087360 };
var multiOrders = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => longs.Contains(o.Id)).ToListAsync();
Assert.Equal(2, multiOrders.Count);
var dateTime = new DateTime(2021, 11, 1);
var multiOrder404 = await _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefaultAsync();
Assert.Null(multiOrder404);
}
[Fact]
public void TestEntityMetadataManager()
{

View File

@ -65,29 +65,29 @@ namespace ShardingCore.Test3x
var queryable1 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202102);
var routeParseExpression1 = ShardingUtil.GetRouteParseExpression(queryable1, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable2 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= 202102);
var routeParseExpression2 = ShardingUtil.GetRouteParseExpression(queryable2, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var xxxx1 = 202102;
var queryable3 = _virtualDbContext.Set<SysUserSalary>().Where(ox => ox.DateOfMonth >= xxxx1);
var routeParseExpression3 = ShardingUtil.GetRouteParseExpression(queryable3, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable4 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth >= 202101);
var routeParseExpression4 = ShardingUtil.GetRouteParseExpression(queryable4, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable5 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth > 202101);
var routeParseExpression5 = ShardingUtil.GetRouteParseExpression(queryable5, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable6 = _virtualDbContext.Set<SysUserSalary>().Where(o => o.DateOfMonth == 202101);
var routeParseExpression6 = ShardingUtil.GetRouteParseExpression(queryable6, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable7 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 <= o.DateOfMonth);
var routeParseExpression7 = ShardingUtil.GetRouteParseExpression(queryable7, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
var queryable8 = _virtualDbContext.Set<SysUserSalary>().Where(o => 202101 == o.DateOfMonth);
var routeParseExpression8 = ShardingUtil.GetRouteParseExpression(queryable8, virtualTableRoute.EntityMetadata,
(i, op,propertyName) => virtualTableRoute.GetRouteToFilter(i, op,propertyName), true);
(i, op,propertyName) => virtualTableRoute.GetRouteFilter(i, op,propertyName), true);
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression2));
Assert.Equal(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression3));
Assert.NotEqual(expressionEqualityComparer.GetHashCode(routeParseExpression1), expressionEqualityComparer.GetHashCode(routeParseExpression4));
@ -191,6 +191,19 @@ namespace ShardingCore.Test3x
public string T { get; set; }
}
[Fact]
public void TestMultiShardingProperty()
{
var multiOrder = _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 232398109278351360).FirstOrDefault();
Assert.NotNull(multiOrder);
var longs = new[] { 232398109278351360, 255197859283087360 };
var multiOrders = _virtualDbContext.Set<MultiShardingOrder>().Where(o => longs.Contains(o.Id)).ToList();
Assert.Equal(2, multiOrders.Count);
var dateTime = new DateTime(2021, 11, 1);
var multiOrder404 = _virtualDbContext.Set<MultiShardingOrder>().Where(o => o.Id == 250345338962063360 && o.CreateTime < dateTime).FirstOrDefault();
Assert.Null(multiOrder404);
}
[Fact]
public void TestEntityMetadataManager()
{
var objMetadata0 = _entityMetadataManager.TryGet(typeof(object));

View File

@ -13,7 +13,7 @@ namespace ShardingCore.Test3x.Shardings
public class LogDayLongVirtualRoute:AbstractSimpleShardingDayKeyLongVirtualTableRoute<LogDayLong>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override void Configure(EntityMetadataTableBuilder<LogDayLong> builder)
{

View File

@ -11,7 +11,7 @@ namespace ShardingCore.Test3x.Shardings
public class LogDayVirtualTableRoute:AbstractSimpleShardingDayKeyDateTimeVirtualTableRoute<LogDay>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override DateTime GetBeginTime()
{

View File

@ -12,7 +12,7 @@ namespace ShardingCore.Test3x.Shardings
public class LogMonthLongvirtualRoute:AbstractSimpleShardingMonthKeyLongVirtualTableRoute<LogMonthLong>
{
protected override bool EnableHintRoute => true;
public override bool EnableRouteParseCompileCache => true;
public override bool? EnableRouteParseCompileCache => true;
public override bool AutoCreateTableByTime()
{

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