虽然不能编译但是我还是要提交

This commit is contained in:
xuejmnet 2021-02-25 20:25:29 +08:00
parent 817fa249aa
commit 9530e1bb85
52 changed files with 2071 additions and 370 deletions

View File

@ -45,7 +45,7 @@ namespace ShardingCore.MySql
foreach (var shardingRoute in options.ShardingRoutes)
{
var genericVirtualRoute = shardingRoute.GetInterfaces().FirstOrDefault(it => it.IsInterface && it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IVirtualRoute<>)
&& it.GetGenericArguments().Any());
&& it.GetGenericArguments().Any());
if (genericVirtualRoute == null)
throw new ArgumentException("add sharding route type error not assignable from IVirtualRoute<>.");
var shardingEntity=genericVirtualRoute.GetGenericArguments()[0];

View File

@ -0,0 +1,16 @@
using System;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.Core.DataSourceAccessors
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 25 February 2021 20:19:44
* @Email: 326308290@qq.com
*/
public interface IDataSourceAccessor
{
DbContext CreateDbContext();
}
}

View File

@ -0,0 +1,18 @@
using System;
namespace ShardingCore.Core
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 12:52:55
* @Email: 326308290@qq.com
*/
/// <summary>
/// 分库接口
/// </summary>
public interface IShardingDataSource
{
}
}

View File

@ -29,6 +29,7 @@ namespace ShardingCore.Core.Internal.RoutingRuleEngines
public IEnumerable<RouteResult> Route<T>(RouteRuleContext<T> routeRuleContext)
{
Dictionary<IVirtualTable, ISet<IPhysicTable>> routeMaps = new Dictionary<IVirtualTable, ISet<IPhysicTable>>();
var queryEntities = routeRuleContext.Queryable.ParseQueryableRoute();
//先添加手动路由到当前上下文,之后将不再手动路由里面的自动路由添加到当前上下文
foreach (var kv in routeRuleContext.ManualTails)
{
@ -38,7 +39,8 @@ namespace ShardingCore.Core.Internal.RoutingRuleEngines
if (!routeMaps.ContainsKey(virtualTable))
{
routeMaps.Add(virtualTable,physicTables.ToHashSet());
} else
}
else
{
foreach (var physicTable in physicTables)
{
@ -66,7 +68,7 @@ namespace ShardingCore.Core.Internal.RoutingRuleEngines
if (routeRuleContext.AutoParseRoute)
{
var shardingEntities = routeRuleContext.Queryable.ParseQueryableRoute();
var shardingEntities = queryEntities.Where(o => o.IsShardingEntity());
foreach (var shardingEntity in shardingEntities)
{
var virtualTable = _virtualTableManager.GetVirtualTable(shardingEntity);

View File

@ -18,8 +18,8 @@ namespace ShardingCore.Core.Internal.RoutingRuleEngines
public RouteRuleContext(IQueryable<T> queryable, IVirtualTableManager virtualTableManager)
{
_virtualTableManager = virtualTableManager;
Queryable = queryable;
_virtualTableManager = virtualTableManager;
}
public IQueryable<T> Queryable { get; }

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
namespace ShardingCore.Core.Internal
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 13:34:29
* @Email: 326308290@qq.com
*/
public class ShardingEntityBaseType
{
/// <summary>
/// 数据源
/// </summary>
public Type EntityType { get; set; }
/// <summary>
/// 是否多数据源
/// </summary>
public bool IsMultiDataSourceMapping { get; set; }
/// <summary>
/// 是否分表
/// </summary>
public bool IsMultiTableMapping { get;set; }
/// <summary>
/// 分库字段
/// </summary>
public string ShardingDataSourceField { get;set; }
/// <summary>
/// 分表字段
/// </summary>
public string ShardingTableField { get; set; }
/// <summary>
/// 分表的原表名 original table name in db exclude tail
/// </summary>
public string ShardingOriginalTableName { get; set; }
/// <summary>
/// 启动时是否建表 auto create table when start app
/// </summary>
public bool? AutoCreateTable { get; set; }
/// <summary>
/// 分表尾巴后缀 table sharding tail prefix
/// </summary>
public string TailPrefix { get; set; } = "_";
}
}

View File

@ -15,14 +15,14 @@ namespace ShardingCore.Core.Internal.Visitors
* @Date: Monday, 28 December 2020 22:09:39
* @Email: 326308290@qq.com
*/
public class QueryableRouteDiscoverVisitor<TKey> : ExpressionVisitor
public class QueryableRouteShardingTableDiscoverVisitor<TKey> : ExpressionVisitor
{
private readonly ShardingEntityConfig _shardingConfig;
private readonly Func<object, TKey> _shardingKeyConvert;
private readonly Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> _keyToTailWithFilter;
private Expression<Func<string, bool>> _where = x => true;
public QueryableRouteDiscoverVisitor(ShardingEntityConfig shardingConfig, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailWithFilter)
public QueryableRouteShardingTableDiscoverVisitor(ShardingEntityConfig shardingConfig, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailWithFilter)
{
_shardingConfig = shardingConfig;
_shardingKeyConvert = shardingKeyConvert;

View File

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

View File

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

View File

@ -62,140 +62,4 @@ namespace ShardingCore.Core.Internal.Visitors
}
}
#endif
// internal class ShardingEntitiesVisitor : ExpressionVisitor
// {
// private readonly IVirtualTableManager _virtualTableManager;
// private readonly ISet<Type> _shardingEntities = new HashSet<Type>();
//
// public ShardingEntitiesVisitor(IVirtualTableManager virtualTableManager)
// {
// _virtualTableManager = virtualTableManager;
// }
//
// public ISet<Type> GetShardingEntities()
// {
// return _shardingEntities;
// }
//
// private bool IsShardingKey(Expression expression, out Type shardingEntity)
// {
// if (expression is MemberExpression member
// && _virtualTableManager.IsShardingKey(member.Expression.Type, member.Member.Name))
// {
// shardingEntity = member.Expression.Type;
// return true;
// }
//
// shardingEntity = null;
// return false;
// }
//
// private bool IsMethodShardingKey(MethodCallExpression methodCallExpression, out Type shardingEntity)
// {
// if (methodCallExpression.Arguments.IsNotEmpty())
// {
// for (int i = 0; i < methodCallExpression.Arguments.Count; i++)
// {
// if (methodCallExpression.Arguments[i] is MemberExpression member
// && _virtualTableManager.IsShardingKey(member.Expression.Type, member.Member.Name))
// {
// shardingEntity = member.Expression.Type;
// return true;
// }
// }
// }
//
// shardingEntity = null;
// return false;
// }
//
// protected override Expression VisitMethodCall(MethodCallExpression node)
// {
// var methodName = node.Method.Name;
// switch (methodName)
// {
// case nameof(Queryable.Where):
// ResolveWhere(node);
// break;
// }
//
//
// return base.VisitMethodCall(node);
// }
//
// private void ResolveWhere(MethodCallExpression node)
// {
// if (node.Arguments[1] is UnaryExpression unaryExpression)
// {
// if (unaryExpression.Operand is LambdaExpression lambdaExpression)
// {
// Resolve(lambdaExpression);
// }
// }
// }
//
//
// private void Resolve(Expression expression)
// {
// if (expression is LambdaExpression)
// {
// LambdaExpression lambda = expression as LambdaExpression;
// expression = lambda.Body;
// Resolve(expression);
// }
//
// if (expression is BinaryExpression binaryExpression) //解析二元运算符
// {
// ParseGetWhere(binaryExpression);
// }
//
// if (expression is UnaryExpression) //解析一元运算符
// {
// UnaryExpression unary = expression as UnaryExpression;
// if (unary.Operand is MethodCallExpression methodCall1Expression)
// {
// ResolveInFunc(methodCall1Expression, unary.NodeType != ExpressionType.Not);
// }
// }
//
// if (expression is MethodCallExpression methodCallExpression) //解析扩展方法
// {
// ResolveInFunc(methodCallExpression, true);
// }
// }
//
// private void ResolveInFunc(MethodCallExpression methodCallExpression, bool @in)
// {
// if (methodCallExpression.IsEnumerableContains(methodCallExpression.Method.Name) && IsMethodShardingKey(methodCallExpression, out var shardingEntity))
// {
// _shardingEntities.Add(shardingEntity);
// }
// }
//
// private void ParseGetWhere(BinaryExpression binaryExpression)
// {
// //递归获取
// if (binaryExpression.Left is BinaryExpression)
// ParseGetWhere(binaryExpression.Left as BinaryExpression);
// if (binaryExpression.Left is MethodCallExpression methodCallExpression)
// {
// Resolve(methodCallExpression);
// }
//
// if (binaryExpression.Left is UnaryExpression unaryExpression)
// Resolve(unaryExpression);
//
// if (binaryExpression.Right is BinaryExpression)
// ParseGetWhere(binaryExpression.Right as BinaryExpression);
//
// if (IsShardingKey(binaryExpression.Left, out var shardingEntity1))
// {
// _shardingEntities.Add(shardingEntity1);
// }
// else if (IsShardingKey(binaryExpression.Right, out var shardingEntity2))
// {
// _shardingEntities.Add(shardingEntity2);
// }
// }
// }
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using ShardingCore.Core.Internal;
namespace ShardingCore.Core.PhysicDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 13:05:28
* @Email: 326308290@qq.com
*/
/// <summary>
/// 物理表接口
/// </summary>
public interface IPhysicDataSource
{
/// <summary>
/// 连接字符串
/// </summary>
string GetConnectionString();
/// <summary>
/// 数据源类型
/// </summary>
DataSourceEnum GetDataSourceType();
/// <summary>
/// 添加分库实体
/// </summary>
/// <param name="entityBaseType"></param>
void AddEntity(ShardingEntityBaseType entityBaseType);
/// <summary>
/// 添加分库实体
/// </summary>
/// <typeparam name="T"></typeparam>
void AddEntity<T>() where T : class, IShardingDataSource;
/// <summary>
/// 添加分库实体
/// </summary>
/// <param name="shardingEntity"></param>
void AddEntity(Type shardingEntity);
/// <summary>
/// 是否有对应的实体
/// </summary>
/// <param name="shardingEntity"></param>
/// <returns></returns>
bool HasEntity(Type shardingEntity);
/// <summary>
/// 是否有对应的实体
/// </summary>
/// <returns></returns>
bool HasEntity<T>() where T : class, IShardingDataSource;
}
}

View File

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ShardingCore.Core.Internal;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.Core.PhysicDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 13:47:49
* @Email: 326308290@qq.com
*/
public class PhysicDataSource:IPhysicDataSource
{
public string ConnectionString { get; }
private readonly DataSourceEnum _dataSourceType;
private readonly Dictionary<Type,ShardingEntityBaseType> _entities;
private readonly string _connectionString;
public PhysicDataSource(string connectionString,DataSourceEnum dataSourceType)
{
_connectionString = connectionString;
_dataSourceType = dataSourceType;
_entities = new Dictionary<Type, ShardingEntityBaseType>();
}
public string GetConnectionString()
{
return _connectionString;
}
public DataSourceEnum GetDataSourceType()
{
return _dataSourceType;
}
public void AddEntity(ShardingEntityBaseType entityBaseType)
{
if(!_entities.ContainsKey(entityBaseType.EntityType))
_entities.Add(entityBaseType.EntityType,entityBaseType);
}
public void AddEntity<T>() where T : class, IShardingDataSource
{
var entityType = typeof(T);
AddEntity(entityType);
}
public void AddEntity(Type shardingEntity)
{
if (!shardingEntity.IsShardingDataSource())
throw new InvalidOperationException($"entity:[{shardingEntity}] should from {nameof(IShardingDataSource)}");
var entityBaseType = ShardingUtil.Parse(shardingEntity);
AddEntity(entityBaseType);
}
public bool HasEntity(Type shardingEntity)
{
if (!shardingEntity.IsShardingDataSource())
return false;
return _entities.ContainsKey(shardingEntity);
}
public bool HasEntity<T>() where T : class, IShardingDataSource
{
return HasEntity(typeof(T));
}
}
}

View File

@ -0,0 +1,18 @@
using System;
namespace ShardingCore.Core
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 12:53:46
* @Email: 326308290@qq.com
*/
/// <summary>
/// 数据源分库规则字段
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class ShardingDataSourceKeyAttribute
{
}
}

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.Core.ShardingDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 18 February 2021 17:12:22
* @Email: 326308290@qq.com
*/
/// <summary>
/// 分库数据源管理
/// </summary>
public interface IShardingDataSourceManager
{
/// <summary>
/// 添加对象数据源
/// </summary>
/// <param name="connectKey"></param>
/// <param name="connectionString"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TDbContext"></typeparam>
void AddDataSource<T, TDbContext>(string connectKey,string connectionString) where T : IShardingDataSource
where TDbContext : DbContext;
/// <summary>
/// 是否需要分数据库
/// </summary>
/// <returns></returns>
bool IsShardingDataSource();
public IEnumerable<ShardingDataSourceDbEntry> FilterDataSources(ISet<Type> queryEntities);
}
}

View File

@ -0,0 +1,53 @@
using System;
namespace ShardingCore.Core.ShardingDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 20 February 2021 09:13:56
* @Email: 326308290@qq.com
*/
public class ShardingDataSourceDbEntry
{
public ShardingDataSourceDbEntry(string connectKey, Type dbContextType, string connectionString)
{
ConnectKey = connectKey;
DbContextType = dbContextType;
ConnectionString = connectionString;
}
/// <summary>
/// 连接标识
/// </summary>
public string ConnectKey { get; }
/// <summary>
/// dbcontext类型
/// </summary>
public Type DbContextType { get; }
/// <summary>
/// 连接字符串
/// </summary>
public string ConnectionString { get; }
public override int GetHashCode()
{
return this.ConnectKey.GetHashCode() ^ 31;
}
public override bool Equals(object obj)
{
if (!(obj is ShardingDataSourceDbEntry))
return false;
if (ReferenceEquals(this, obj))
return true;
ShardingDataSourceDbEntry item = (ShardingDataSourceDbEntry) obj;
return item.ConnectKey == this.ConnectKey;
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.Core.ShardingDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 18 February 2021 17:13:16
* @Email: 326308290@qq.com
*/
/// <summary>
/// 分库对象
/// </summary>
public class ShardingDataSourceEntry
{
/// <summary>
/// 构造函数
/// </summary>
/// <param name="entityType"></param>
/// <param name="dataSourceDbEntry"></param>
public ShardingDataSourceEntry(Type entityType,ShardingDataSourceDbEntry dataSourceDbEntry)
{
EntityType = entityType;
DataSourceDbEntry = dataSourceDbEntry;
}
/// <summary>
/// 分库类型
/// </summary>
public Type EntityType { get; }
/// <summary>
/// 分库对应的数据库对象
/// </summary>
public ShardingDataSourceDbEntry DataSourceDbEntry { get; }
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.Core.ShardingDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 19 February 2021 08:07:27
* @Email: 326308290@qq.com
*/
public class ShardingDataSourceManager : IShardingDataSourceManager
{
private readonly Dictionary<Type, List<ShardingDataSourceEntry>> _entityMaps = new Dictionary<Type, List<ShardingDataSourceEntry>>();
private readonly Dictionary<ShardingDataSourceDbEntry, List<ShardingDataSourceEntry>> _dbContextMaps = new Dictionary<ShardingDataSourceDbEntry, List<ShardingDataSourceEntry>>();
/// <summary>
/// 添加数据源
/// </summary>
/// <param name="connectKey"></param>
/// <param name="connectionString"></param>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TDbContext"></typeparam>
public void AddDataSource<T, TDbContext>(string connectKey, string connectionString) where T : IShardingDataSource where TDbContext : DbContext
{
var entityType = typeof(T);
var dbContextType = typeof(TDbContext);
var dataSourceDbEntry = new ShardingDataSourceDbEntry(connectKey, dbContextType, connectionString);
if (!_entityMaps.ContainsKey(entityType))
{
_entityMaps.Add(dbContextType, new List<ShardingDataSourceEntry>());
}
if (!_dbContextMaps.ContainsKey(dataSourceDbEntry))
{
_dbContextMaps.Add(dataSourceDbEntry, new List<ShardingDataSourceEntry>());
}
_entityMaps[entityType].Add(new ShardingDataSourceEntry(entityType, dataSourceDbEntry));
_dbContextMaps[dataSourceDbEntry].Add(new ShardingDataSourceEntry(entityType, dataSourceDbEntry));
}
public bool IsShardingDataSource()
{
return _dbContextMaps.Count > 1;
}
public IEnumerable<ShardingDataSourceDbEntry> FilterDataSources(ISet<Type> queryEntities)
{
var list = _entityMaps.Where(o => queryEntities.Contains(o.Key)).Select(o => o.Value.Select(o => o.DataSourceDbEntry).ToList()).ToList();
if (list.Count <= 0)
return new List<ShardingDataSourceDbEntry>(0);
if (list.Count == 1)
return list[0];
return list.Aggregate((pre, next) => pre.Except(next).ToList());
}
}
}

View File

@ -0,0 +1,26 @@
using System;
namespace ShardingCore.Core
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 13:07:31
* @Email: 326308290@qq.com
*/
/// <summary>
/// 数据源类型
/// </summary>
public enum DataSourceEnum
{
/// <summary>
/// SqlServer 1
/// </summary>
SqlServer = 1,
/// <summary>
/// MySql 2
/// </summary>
MySql = 1 << 1
}
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using ShardingCore.Core.PhysicDataSources;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualRoutes.DataSourceRoutes;
namespace ShardingCore.Core.VirtualDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 13:01:39
* @Email: 326308290@qq.com
*/
/// <summary>
/// 虚拟数据源 连接所有的实际数据源
/// </summary>
public interface IVirtualDataSource
{
Type EntityType{get;}
/// <summary>
/// 获取所有的物理数据源
/// </summary>
/// <returns></returns>
List<IPhysicDataSource> GetAllDataSources();
/// <summary>
/// 路由到具体的物理数据源
/// </summary>
/// <returns></returns>
List<IPhysicDataSource> RouteTo(VirutalDataSourceConfig routeConfig);
/// <summary>
/// 添加物理表 add physic table
/// </summary>
/// <param name="physicTable"></param>
void AddDataSource(IPhysicDataSource physicTable);
/// <summary>
/// 获取当前数据源的路由
/// </summary>
/// <returns></returns>
IVirtualDataSourceRoute GetRoute();
}
public interface IVirtualDataSource<T> : IVirtualDataSource where T : class, IShardingDataSource
{
new IVirtualDataSourceRoute<T> GetRoute();
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using ShardingCore.Core.PhysicDataSources;
namespace ShardingCore.Core.VirtualDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 06 February 2021 14:24:01
* @Email: 326308290@qq.com
*/
public interface IVirtualDataSourceManager
{
/// <summary>
/// 获取所有的虚拟连接 get all virtual table
/// </summary>
/// <returns></returns>
List<IVirtualDataSource> GetAllVirtualDataSources();
/// <summary>
/// 获取虚拟表 get virtual table by sharding entity type
/// </summary>
/// <param name="shardingEntityType"></param>
/// <returns></returns>
IVirtualDataSource GetVirtualDataSource(Type shardingEntityType);
/// <summary>
/// 获取虚拟表 get virtual table by sharding entity type
/// </summary>
/// <returns></returns>
IVirtualDataSource<T> GetVirtualDataSource<T>() where T:class,IShardingDataSource;
/// <summary>
/// 添加虚拟数据源应用启动时 add virtual table when app start
/// </summary>
/// <param name="virtualDataSource"></param>
void AddVirtualDataSource(IVirtualDataSource virtualDataSource);
/// <summary>
/// 添加物理表 add physic table
/// </summary>
/// <param name="virtualDataSource"></param>
/// <param name="physicDataSource"></param>
void AddPhysicDataSource(IVirtualDataSource virtualDataSource, IPhysicDataSource physicDataSource);
/// <summary>
/// 添加物理表 add physic table
/// </summary>
/// <param name="shardingEntityType"></param>
/// <param name="physicDataSource"></param>
void AddPhysicDataSource(Type shardingEntityType, IPhysicDataSource physicDataSource);
}
}

View File

@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ShardingCore.Core.PhysicDataSources;
using ShardingCore.Core.VirtualRoutes.DataSourceRoutes;
using Microsoft.Extensions.DependencyInjection;
using ShardingCore.Core.Internal;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.Core.VirtualDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 15:21:04
* @Email: 326308290@qq.com
*/
public class VirtualDataSource<T>:IVirtualDataSource<T> where T:class,IShardingDataSource
{
private readonly List<IPhysicDataSource> _physicDataSources;
private readonly IVirtualDataSourceRoute<T> _virtualDataSourceRoute;
public ShardingEntityBaseType ShardingEntityType { get; }
public Type EntityType { get; }
public VirtualDataSource(IServiceProvider serviceProvider)
{
_physicDataSources = new List<IPhysicDataSource>();
_virtualDataSourceRoute = serviceProvider.GetService<IVirtualDataSourceRoute<T>>() ?? throw new ArgumentNullException($"{typeof(T)}");
EntityType = typeof(T);
ShardingEntityType = ShardingUtil.Parse(EntityType);
}
public List<IPhysicDataSource> GetAllDataSources()
{
return _physicDataSources;
}
public List<IPhysicDataSource> RouteTo(VirutalDataSourceConfig routeConfig)
{
if (routeConfig.UseQueryable())
return _virtualDataSourceRoute.RouteWithWhere(_physicDataSources, routeConfig.GetQueryable());
if (routeConfig.UsePredicate())
return _virtualDataSourceRoute.RouteWithWhere(_physicDataSources, new EnumerableQuery<T>((Expression<Func<T, bool>>) routeConfig.GetPredicate()));
object shardingKeyValue = null;
if (routeConfig.UseValue())
shardingKeyValue = routeConfig.GetShardingKeyValue();
if (routeConfig.UseEntity())
shardingKeyValue = routeConfig.GetShardingDataSource().GetPropertyValue(ShardingEntityType.ShardingDataSourceField);
if (shardingKeyValue != null)
{
var routeWithValue = _virtualDataSourceRoute.RouteWithValue(_physicDataSources, shardingKeyValue);
return new List<IPhysicDataSource>(1) {routeWithValue};
}
throw new NotImplementedException(nameof(VirutalDataSourceConfig));
}
public void AddDataSource(IPhysicDataSource physicTable)
{
if (_physicDataSources.Any(o => o.GetConnectionString() == physicTable.GetConnectionString()))
return;
_physicDataSources.Add(physicTable);
}
IVirtualDataSourceRoute<T> IVirtualDataSource<T>.GetRoute()
{
return _virtualDataSourceRoute;
}
public IVirtualDataSourceRoute GetRoute()
{
return _virtualDataSourceRoute;
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using ShardingCore.Core.PhysicDataSources;
using ShardingCore.Exceptions;
using ShardingCore.Helpers;
namespace ShardingCore.Core.VirtualDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 06 February 2021 15:24:08
* @Email: 326308290@qq.com
*/
public class VirtualDataSourceManager:IVirtualDataSourceManager
{
private readonly ConcurrentDictionary<Type, IVirtualDataSource> _virtualDataSources = new ConcurrentDictionary<Type, IVirtualDataSource>();
public VirtualDataSourceManager(IServiceProvider serviceProvider)
{
var shardingEntities = AssemblyHelper.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes())
.Where(type => !String.IsNullOrEmpty(type.Namespace))
.Where(type => !type.IsAbstract&&type.GetInterfaces()
.Any(it => it.IsInterface &&typeof(IShardingDataSource)==it)
);
foreach (var shardingEntity in shardingEntities)
{
Type genericType = typeof(IVirtualDataSource<>);
Type interfaceType = genericType.MakeGenericType(shardingEntity);
var virtualDataSource = (IVirtualDataSource)serviceProvider.GetService(interfaceType);
_virtualDataSources.TryAdd(virtualDataSource.EntityType, virtualDataSource);
}
}
public List<IVirtualDataSource> GetAllVirtualDataSources()
{
return _virtualDataSources.Select(o => o.Value).ToList();
}
public IVirtualDataSource GetVirtualDataSource(Type shardingEntityType)
{
if (!_virtualDataSources.TryGetValue(shardingEntityType, out var virtualTable) || virtualTable == null)
throw new VirtualDataSourceNotFoundException($"{shardingEntityType}");
return virtualTable;
}
public IVirtualDataSource<T> GetVirtualDataSource<T>() where T : class, IShardingDataSource
{
return (IVirtualDataSource<T>)GetVirtualDataSource(typeof(T));
}
public void AddVirtualDataSource(IVirtualDataSource virtualDataSource)
{
_virtualDataSources.TryAdd(virtualDataSource.EntityType, virtualDataSource);
}
public void AddPhysicDataSource(IVirtualDataSource virtualDataSource, IPhysicDataSource physicDataSource)
{
AddPhysicDataSource(virtualDataSource.EntityType, physicDataSource);
}
public void AddPhysicDataSource(Type shardingEntityType, IPhysicDataSource physicDataSource)
{
var virtualTable = GetVirtualDataSource(shardingEntityType);
virtualTable.AddDataSource(physicDataSource);
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Linq;
using System.Linq.Expressions;
namespace ShardingCore.Core.VirtualDataSources
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 06 February 2021 11:28:33
* @Email: 326308290@qq.com
*/
public class VirutalDataSourceConfig
{
private readonly IQueryable _queryable;
private readonly IShardingDataSource _shardingDataSource;
private readonly object _shardingKeyValue;
private readonly Expression _predicate;
public VirutalDataSourceConfig(IQueryable queryable=null,IShardingDataSource shardingDataSource=null,object shardingKeyValue=null,Expression predicate=null)
{
_queryable = queryable;
_shardingDataSource = shardingDataSource;
_shardingKeyValue = shardingKeyValue;
_predicate = predicate;
}
public IQueryable GetQueryable()
{
return _queryable;
}
public object GetShardingKeyValue()
{
return _shardingKeyValue;
}
public IShardingDataSource GetShardingDataSource()
{
return _shardingDataSource;
}
public Expression GetPredicate()
{
return _predicate;
}
public bool UseQueryable()
{
return _queryable != null;
}
public bool UseValue()
{
return _shardingKeyValue != null;
}
public bool UseEntity()
{
return _shardingDataSource != null;
}
public bool UsePredicate()
{
return _predicate != null;
}
}
}

View File

@ -20,7 +20,7 @@ namespace ShardingCore.Core.VirtualRoutes.Abstractions
protected override List<IPhysicTable> DoRouteWithWhere(List<IPhysicTable> allPhysicTables, IQueryable queryable)
{
//获取所有需要路由的表后缀
var filter = ShardingKeyUtil.GetRouteObjectOperatorFilter(queryable, ShardingKeyUtil.Parse(typeof(T)), ConvertToShardingKey, GetRouteToFilter);
var filter = ShardingKeyUtil.GetRouteShardingTableFilter(queryable, ShardingKeyUtil.Parse(typeof(T)), ConvertToShardingKey, GetRouteToFilter);
var physicTables = allPhysicTables.Where(o => filter(o.Tail)).ToList();
return physicTables;
}

View File

@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ShardingCore.Core.PhysicDataSources;
namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 17:06:49
* @Email: 326308290@qq.com
*/
public abstract class AbstractDataSourceVirtualRoute<T,TKey>:IVirtualDataSourceRoute<T> where T:class,IShardingDataSource
{
public Type ShardingEntityType => typeof(T);
public abstract IPhysicDataSource RouteWithValue(List<IPhysicDataSource> allPhysicDataSources, object shardingKey);
protected abstract TKey ConvertToShardingKey(object shardingKey);
/// <summary>
/// 对外路由方法
/// </summary>
/// <param name="allPhysicDataSources"></param>
/// <param name="queryable"></param>
/// <returns></returns>
public List<IPhysicDataSource> RouteWithWhere(List<IPhysicDataSource> allPhysicDataSources, IQueryable queryable)
{
return AfterFilter(allPhysicDataSources,DoRouteWithWhere(allPhysicDataSources,queryable));
}
/// <summary>
/// 实际路由
/// </summary>
/// <param name="allPhysicDataSources"></param>
/// <param name="queryable"></param>
/// <returns></returns>
protected abstract List<IPhysicDataSource> DoRouteWithWhere(List<IPhysicDataSource> allPhysicDataSources, IQueryable queryable);
/// <summary>
/// 物理表过滤后
/// </summary>
/// <param name="allPhysicDataSources">所有的数据源</param>
/// <param name="filterPhysicDataSources">过滤后的数据源</param>
/// <returns></returns>
public virtual List<IPhysicDataSource> AfterFilter(List<IPhysicDataSource> allPhysicDataSources,List<IPhysicDataSource> filterPhysicDataSources)
{
return filterPhysicDataSources;
}
}
}

View File

@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using ShardingCore.Core.PhysicDataSources;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Utils;
namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 17:21:48
* @Email: 326308290@qq.com
*/
public abstract class AbstractShardingDataSourceOperatorVirtualRoute<T, TKey> : AbstractDataSourceVirtualRoute<T, TKey> where T : class, IShardingDataSource
{
/// <summary>
/// 如何路由到具体表 shardingKeyValue:分表的值, 返回结果:如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表
/// </summary>
/// <param name="shardingKey">分表的值</param>
/// <param name="shardingOperator">操作</param>
/// <returns>如果返回true表示返回该表 第一个参数 tail 第二参数是否返回该物理表</returns>
protected abstract Expression<Func<IPhysicDataSource, bool>> GetRouteToFilter(TKey shardingKey, ShardingOperatorEnum shardingOperator);
/// <summary>
/// 通过iqueryable来解析本次路由到的具体数据源
/// </summary>
/// <param name="allPhysicDataSources"></param>
/// <param name="queryable"></param>
/// <returns></returns>
protected override List<IPhysicDataSource> DoRouteWithWhere(List<IPhysicDataSource> allPhysicDataSources, IQueryable queryable)
{
//获取所有需要路由的表后缀
var filter = ShardingUtil.GetRouteDataSourceFilter(queryable, ShardingUtil.Parse(typeof(T)), ConvertToShardingKey, GetRouteToFilter);
var physicTables = allPhysicDataSources.Where(o => filter(o)).ToList();
return physicTables;
}
/// <summary>
/// 根据具体值路由到对应的数据源
/// </summary>
/// <param name="allPhysicDataSources"></param>
/// <param name="shardingKey"></param>
/// <returns></returns>
/// <exception cref="ShardingDataSourceRouteNotMatchException"></exception>
/// <exception cref="ShardingDataSourceRouteMatchMoreException"></exception>
public override IPhysicDataSource RouteWithValue(List<IPhysicDataSource> allPhysicDataSources, object shardingKey)
{
var filter = GetRouteToFilter(ConvertToShardingKey(shardingKey), ShardingOperatorEnum.Equal).Compile();
var physicDataSources = allPhysicDataSources.Where(o => filter(o)).ToList();
if (physicDataSources.IsEmpty())
{
var shardingEntityBaseType = ShardingUtil.Parse(typeof(T));
throw new ShardingDataSourceRouteNotMatchException($"{shardingEntityBaseType.EntityType} -> [{shardingEntityBaseType.ShardingDataSourceField}] ->【{shardingKey}】");
}
if (physicDataSources.Count > 1)
throw new ShardingDataSourceRouteMatchMoreException($"data source :{string.Join(",", physicDataSources.Select(o => $"[{o.GetDataSourceType()}]"))}");
return physicDataSources[0];
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ShardingCore.Core.PhysicDataSources;
namespace ShardingCore.Core.VirtualRoutes.DataSourceRoutes
{
/*
* @Author: xjm
* @Description:
* @Date: Friday, 05 February 2021 13:03:58
* @Email: 326308290@qq.com
*/
public interface IVirtualDataSourceRoute
{
Type ShardingEntityType { get; }
/// <summary>
/// 根据查询条件路由返回物理表
/// </summary>
/// <param name="allPhysicDataSources"></param>
/// <param name="queryable"></param>
/// <returns></returns>
List<IPhysicDataSource> RouteWithWhere(List<IPhysicDataSource> allPhysicDataSources,IQueryable queryable);
/// <summary>
/// 根据值进行路由
/// </summary>
/// <param name="allPhysicDataSources"></param>
/// <param name="shardingKeyValue"></param>
/// <returns></returns>
IPhysicDataSource RouteWithValue(List<IPhysicDataSource> allPhysicDataSources, object shardingKeyValue);
}
public interface IVirtualDataSourceRoute<T> : IVirtualDataSourceRoute where T : class, IShardingDataSource
{
}
}

View File

@ -29,7 +29,6 @@ namespace ShardingCore.Core.VirtualTables
/// <summary>
/// 获取虚拟表 get virtual table by sharding entity type
/// </summary>
/// <param name="shardingEntityType"></param>
/// <returns></returns>
IVirtualTable<T> GetVirtualTable<T>() where T:class,IShardingEntity;

View File

@ -60,7 +60,7 @@ namespace ShardingCore.Core.VirtualTables
return new List<IPhysicTable>(1) {routeWithValue};
}
throw new NotImplementedException(nameof(routeConfig));
throw new NotImplementedException(nameof(RouteConfig));
}

View File

@ -0,0 +1,24 @@
using System;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Sunday, 07 February 2021 15:24:41
* @Email: 326308290@qq.com
*/
public class AbstractionShardingDbContext:DbContext
{
public AbstractionShardingDbContext(ShardingDbContextOptionBuilder<AbstractionShardingDbContext> optionBuilder)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
}

View File

@ -0,0 +1,15 @@
using System;
namespace ShardingCore.DbContexts.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 18 February 2021 14:29:28
* @Email: 326308290@qq.com
*/
public interface IShardingDbSource
{
}
}

View File

@ -0,0 +1,46 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.Extensions.Logging;
using ShardingCore.EFCores;
namespace ShardingCore.DbContexts.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: Sunday, 07 February 2021 15:25:36
* @Email: 326308290@qq.com
*/
public class ShardingDbContextOptionBuilder
{
private readonly DbContextOptionsBuilder _builder;
public ShardingDbContextOptionBuilder():this(new DbContextOptionsBuilder())
{
}
public ShardingDbContextOptionBuilder(DbContextOptionsBuilder optionsBuilder)
{
_builder = optionsBuilder
.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
.ReplaceService<IQueryCompiler, ShardingQueryCompiler>()
.ReplaceService<IModelCacheKeyFactory, ShardingModelCacheKeyFactory>();
}
public ShardingDbContextOptionBuilder UseLoggerFactory(ILoggerFactory loggerFactory)
{
_builder.UseLoggerFactory(loggerFactory);
return this;
}
public DbContextOptionsBuilder GetOptionsBuilder()
{
return _builder;
}
}
public class ShardingDbContextOptionBuilder<T>:ShardingDbContextOptionBuilder where T:DbContext
{
}
}

View File

@ -1,4 +1,4 @@
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.DbContexts.ShardingTableDbContexts;
namespace ShardingCore.DbContexts
{
@ -10,6 +10,6 @@ namespace ShardingCore.DbContexts
*/
public interface IShardingDbContextFactory
{
ShardingDbContext Create(ShardingDbContextOptions shardingDbContextOptions);
ShardingTableDbContext Create(ShardingTableDbContextOptions shardingDbContextOptions);
}
}

View File

@ -1,4 +1,4 @@
using ShardingCore.DbContexts.ShardingDbContexts;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts
{
@ -10,6 +10,6 @@ namespace ShardingCore.DbContexts
*/
public interface IShardingParallelDbContextFactory
{
ShardingDbContext Create(string tail);
DbContext Create(string tail);
}
}

View File

@ -0,0 +1,16 @@
using System;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 20 February 2021 15:04:25
* @Email: 326308290@qq.com
*/
public interface IShardingParallelDbContextFactoryManager
{
public DbContext CreateDbContext(string connectKey, string tail);
}
}

View File

@ -1,4 +1,5 @@
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.DbContexts.ShardingTableDbContexts;
namespace ShardingCore.DbContexts
{
@ -10,7 +11,7 @@ namespace ShardingCore.DbContexts
*/
public class ShardingDbContextFactory:IShardingDbContextFactory
{
public ShardingDbContext Create(ShardingDbContextOptions shardingDbContextOptions)
public ShardingTableDbContext Create(ShardingTableDbContextOptions shardingDbContextOptions)
{
return new ShardingDbContext(shardingDbContextOptions);
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Monday, 22 February 2021 16:45:11
* @Email: 326308290@qq.com
*/
public class ShardingParallelDbContextFactoryManager:IShardingParallelDbContextFactoryManager
{
private readonly Dictionary<>
public DbContext CreateDbContext(string connectKey, string tail)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.ShardingTableDbContexts
{
/*
* @Author: xjm
* @Description: 使 使dbcontext
* @Date: Thursday, 18 February 2021 15:06:46
* @Email: 326308290@qq.com
*/
/// <summary>
///
/// </summary>
public abstract class ShardingTableDbContext:DbContext
{
public string Tail { get; }
public Dictionary<Type,VirtualTableDbContextConfig> VirtualTableConfigs { get; }
public bool RemoveRemoveShardingEntity { get; }
public ShardingTableDbContext(ShardingTableDbContextOptions options)
{
Tail = options.Tail;
VirtualTableConfigs = options.VirtualTableDbContextConfigs.ToDictionary(o=>o.ShardingEntityType,o=>o);
RemoveRemoveShardingEntity = options.RemoveShardingEntity;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
OnModelCreatingAfter(modelBuilder);
}
protected virtual void OnModelCreatingAfter(ModelBuilder modelBuilder)
{
if (!string.IsNullOrWhiteSpace(Tail))
{
var mutableEntityTypes = modelBuilder.Model.GetEntityTypes().Where(o=>VirtualTableConfigs.ContainsKey(o.ClrType));
foreach (var entityType in mutableEntityTypes)
{
var virtualTableConfig = VirtualTableConfigs[entityType.ClrType];
var shardingEntity = virtualTableConfig.ShardingEntityType;
var tailPrefix = virtualTableConfig.TailPrefix;
var entity = modelBuilder.Entity(shardingEntity);
var tableName = virtualTableConfig.OriginalTableName;
if (string.IsNullOrWhiteSpace(tableName))
throw new ArgumentNullException($"{shardingEntity}: not found original table name。");
#if DEBUG
Console.WriteLine($"映射表:[tableName]-->[{tableName}{tailPrefix}{Tail}]");
#endif
entity.ToTable($"{tableName}{tailPrefix}{Tail}");
}
}
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.DbContexts.ShardingTableDbContexts
{
/*
* @Author: xjm
* @Description:
* @Date: Thursday, 18 February 2021 15:22:46
* @Email: 326308290@qq.com
*/
public class ShardingTableDbContextOptions
{
public ShardingTableDbContextOptions(DbContextOptions dbContextOptions, string tail, List<VirtualTableDbContextConfig> virtualTableDbContextConfigs, bool removeShardingEntity=false)
{
DbContextOptions = dbContextOptions;
Tail = tail;
VirtualTableDbContextConfigs = virtualTableDbContextConfigs;
RemoveShardingEntity = removeShardingEntity;
}
public DbContextOptions DbContextOptions { get; }
public string Tail { get; }
public List<VirtualTableDbContextConfig> VirtualTableDbContextConfigs { get; }
public bool RemoveShardingEntity { get;}
}
}

View File

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

View File

@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore;
using ShardingCore.Core;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.DbContexts.ShardingTableDbContexts;
using ShardingCore.DbContexts.Transactions;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
@ -311,7 +311,7 @@ namespace ShardingCore.DbContexts.VirtualDbContexts
return new ShardingBatchDeleteEntry<T>(where,dbContexts);
}
private ShardingDbContext CreateGenericDbContext<T>(T entity) where T : class
private DbContext CreateGenericDbContext<T>(T entity) where T : class
{
var tail = EMPTY_SHARDING_TAIL_ID;
if (entity.IsShardingEntity())
@ -341,12 +341,12 @@ namespace ShardingCore.DbContexts.VirtualDbContexts
throw new QueryableRouteNotMatchException($"{typeof(T)} -> {nameof(queryable)}");
}
private ShardingDbContext GetOrCreateShardingDbContext(string tail)
private DbContext GetOrCreateShardingDbContext(string tail)
{
if (!_dbContextCaches.TryGetValue(tail, out var shardingDbContext))
{
var virtualTableConfigs = _virtualTableManager.GetAllVirtualTables().GetVirtualTableDbContextConfigs();
shardingDbContext = _shardingDbContextFactory.Create(new ShardingDbContextOptions(DbContextOptionsProvider.GetDbContextOptions(), tail == EMPTY_SHARDING_TAIL_ID ? string.Empty : tail, virtualTableConfigs));
shardingDbContext = _shardingDbContextFactory.Create(new ShardingTableDbContextOptions(DbContextOptionsProvider.GetDbContextOptions(), tail == EMPTY_SHARDING_TAIL_ID ? string.Empty : tail, virtualTableConfigs));
_dbContextCaches.TryAdd(tail, shardingDbContext);
}
@ -355,7 +355,7 @@ namespace ShardingCore.DbContexts.VirtualDbContexts
_dbTransaction.Use(shardingDbContext);
}
return (ShardingDbContext) shardingDbContext;
return shardingDbContext;
}

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 06 February 2021 09:08:49
* @Email: 326308290@qq.com
*/
public class ShardingDataSourceRouteMatchMoreException:Exception
{
public ShardingDataSourceRouteMatchMoreException()
{
}
protected ShardingDataSourceRouteMatchMoreException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public ShardingDataSourceRouteMatchMoreException(string message) : base(message)
{
}
public ShardingDataSourceRouteMatchMoreException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

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

View File

@ -0,0 +1,30 @@
using System;
using System.Runtime.Serialization;
namespace ShardingCore.Exceptions
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 06 February 2021 15:41:19
* @Email: 326308290@qq.com
*/
public class VirtualDataSourceNotFoundException:Exception
{
public VirtualDataSourceNotFoundException()
{
}
protected VirtualDataSourceNotFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
public VirtualDataSourceNotFoundException(string message) : base(message)
{
}
public VirtualDataSourceNotFoundException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@ -19,7 +19,18 @@ namespace ShardingCore.Extensions
public static class CommonExtension
{
/// <summary>
/// 是否基继承至ShardingEntity
/// 是否基继承至IShardingDataSource
/// </summary>
/// <param name="entityType"></param>
/// <returns></returns>
public static bool IsShardingDataSource(this Type entityType)
{
if (entityType == null)
throw new ArgumentNullException(nameof(entityType));
return typeof(IShardingDataSource).IsAssignableFrom(entityType);
}
/// <summary>
/// 是否基继承至IShardingEntity
/// </summary>
/// <param name="entityType"></param>
/// <returns></returns>
@ -62,7 +73,7 @@ namespace ShardingCore.Extensions
}
public static ISet<Type> ParseQueryableRoute(this IQueryable queryable)
{
return ShardingKeyUtil.GetShardingEntitiesFilter(queryable);
return ShardingKeyUtil.GetQueryEntitiesFilter(queryable);
}
}
}

View File

@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging;
using ShardingCore.Core.PhysicTables;
using ShardingCore.Core.VirtualTables;
using ShardingCore.DbContexts;
using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.DbContexts.ShardingTableDbContexts;
using ShardingCore.DbContexts.VirtualDbContexts;
using ShardingCore.Extensions;
using ShardingCore.TableCreator;
@ -47,7 +47,7 @@ namespace ShardingCore
var virtualTables = _virtualTableManager.GetAllVirtualTables();
using var scope = _serviceProvider.CreateScope();
var dbContextOptionsProvider = scope.ServiceProvider.GetService<IDbContextOptionsProvider>();
using var context = _shardingDbContextFactory.Create(new ShardingDbContextOptions(dbContextOptionsProvider.GetDbContextOptions(), string.Empty, virtualTables.GetVirtualTableDbContextConfigs()));
using var context = _shardingDbContextFactory.Create(new ShardingTableDbContextOptions(dbContextOptionsProvider.GetDbContextOptions(), string.Empty, virtualTables.GetVirtualTableDbContextConfigs()));
foreach (var virtualTable in virtualTables)
{
@ -69,7 +69,7 @@ namespace ShardingCore
{
using var scope = _serviceProvider.CreateScope();
var dbContextOptionsProvider = scope.ServiceProvider.GetService<IDbContextOptionsProvider>();
using var context = _shardingDbContextFactory.Create(new ShardingDbContextOptions(dbContextOptionsProvider.GetDbContextOptions(), string.Empty, new List<VirtualTableDbContextConfig>(), true));
using var context = _shardingDbContextFactory.Create(new ShardingTableDbContextOptions(dbContextOptionsProvider.GetDbContextOptions(), string.Empty, new List<VirtualTableDbContextConfig>(), true));
context.Database.EnsureCreated();
}
}

View File

@ -16,5 +16,7 @@ namespace ShardingCore
/// 是否需要在启动时创建分表
/// </summary>
public bool? CreateShardingTableOnStart { get; set; }
}
}

View File

@ -6,6 +6,7 @@ using System.Linq.Expressions;
using System.Reflection;
using ShardingCore.Core;
using ShardingCore.Core.Internal.Visitors;
using ShardingCore.Core.Internal.Visitors.Querys;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualTables;
@ -56,9 +57,9 @@ namespace ShardingCore.Utils
return shardingEntityConfig;
}
public static Func<string, bool> GetRouteObjectOperatorFilter<TKey>(IQueryable queryable, ShardingEntityConfig shardingConfig, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailExpression)
public static Func<string, bool> GetRouteShardingTableFilter<TKey>(IQueryable queryable, ShardingEntityConfig shardingConfig, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<string, bool>>> keyToTailExpression)
{
QueryableRouteDiscoverVisitor<TKey> visitor = new QueryableRouteDiscoverVisitor<TKey>(shardingConfig, shardingKeyConvert, keyToTailExpression);
QueryableRouteShardingTableDiscoverVisitor<TKey> visitor = new QueryableRouteShardingTableDiscoverVisitor<TKey>(shardingConfig, shardingKeyConvert, keyToTailExpression);
visitor.Visit(queryable.Expression);
@ -74,6 +75,14 @@ namespace ShardingCore.Utils
return visitor.GetShardingEntities();
}
public static ISet<Type> GetQueryEntitiesFilter(IQueryable queryable)
{
QueryEntitiesVisitor visitor = new QueryEntitiesVisitor();
visitor.Visit(queryable.Expression);
return visitor.GetQueryEntities();
}
}
}

View File

@ -0,0 +1,108 @@
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using ShardingCore.Core;
using ShardingCore.Core.Internal;
using ShardingCore.Core.Internal.Visitors;
using ShardingCore.Core.PhysicDataSources;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualTables;
using ShardingCore.Extensions;
namespace ShardingCore.Utils
{
/*
* @Author: xjm
* @Description:
* @Date: Saturday, 06 February 2021 08:28:07
* @Email: 326308290@qq.com
*/
public class ShardingUtil
{
private static readonly ConcurrentDictionary<Type, ShardingEntityBaseType> _caches = new ConcurrentDictionary<Type, ShardingEntityBaseType>();
private ShardingUtil()
{
}
public static ShardingEntityBaseType Parse(Type entityType)
{
if (_caches.TryGetValue(entityType, out var baseType))
{
return baseType;
}
var isShardingDataSource = entityType.IsShardingDataSource();
var isShardingTable = entityType.IsShardingEntity();
baseType = new ShardingEntityBaseType()
{
EntityType = entityType,
IsMultiDataSourceMapping = isShardingDataSource,
IsMultiTableMapping = isShardingTable
};
if (!isShardingDataSource && isShardingTable)
throw new NotSupportedException(entityType.ToString());
PropertyInfo[] shardingProperties = entityType.GetProperties();
if (isShardingTable)
{
var shardingTables = shardingProperties.SelectMany(p => p.GetCustomAttributes(true).Where(o => o.GetType() == typeof(ShardingKeyAttribute))).ToList();
if (shardingTables.Count != 1)
throw new NotSupportedException($"{entityType} From IShardingEntity should use single attribute [ShardingKey]");
}
var shardingDataSourceCount = 0;
var shardingTableCount = 0;
foreach (var shardingProperty in shardingProperties)
{
var attributes = shardingProperty.GetCustomAttributes(true);
if (isShardingDataSource)
{
if (attributes.FirstOrDefault(x => x.GetType() == typeof(ShardingDataSourceKeyAttribute)) is ShardingDataSourceKeyAttribute shardingDataSourceKey)
{
if (shardingDataSourceCount > 1)
throw new NotSupportedException($"{entityType} From IShardingDataSource should use single attribute [{nameof(ShardingDataSourceKeyAttribute)}]");
baseType.ShardingDataSourceField = shardingProperty.Name;
shardingDataSourceCount++;
}
}
if (isShardingTable)
{
if (attributes.FirstOrDefault(x => x.GetType() == typeof(ShardingKeyAttribute)) is ShardingKeyAttribute shardingKey)
{
if (shardingTableCount > 1)
throw new NotSupportedException($"{entityType} From IShardingEntity should use single attribute [{nameof(ShardingKeyAttribute)}]");
baseType.ShardingTableField = shardingProperty.Name;
baseType.AutoCreateTable = shardingKey.AutoCreateTableOnStart == ShardingKeyAutoCreateTableEnum.UnKnown ? (bool?) null : (shardingKey.AutoCreateTableOnStart == ShardingKeyAutoCreateTableEnum.Create);
baseType.TailPrefix = shardingKey.TailPrefix;
shardingTableCount++;
}
}
}
_caches.TryAdd(entityType, baseType);
return baseType;
}
public static Func<IPhysicDataSource, bool> GetRouteDataSourceFilter<TKey>(IQueryable queryable, ShardingEntityBaseType shardingEntityBaseType, Func<object, TKey> shardingKeyConvert, Func<TKey, ShardingOperatorEnum, Expression<Func<IPhysicDataSource, bool>>> keyToTailExpression)
{
QueryableRouteShardingDataSourceDiscoverVisitor<TKey> visitor = new QueryableRouteShardingDataSourceDiscoverVisitor<TKey>(shardingEntityBaseType, shardingKeyConvert, keyToTailExpression);
visitor.Visit(queryable.Expression);
return visitor.GetDataSourceFilter();
}
}
}