This commit is contained in:
xuejiaming 2021-09-07 13:16:06 +08:00
parent ee0f6c1781
commit 773f11266f
28 changed files with 725 additions and 42 deletions

View File

@ -378,20 +378,31 @@ ctor inject IShardingRouteManager shardingRouteManager
}
```
## 读写分离
该框架目前已经支持单node的读写分离,后续框架将支持多node的读
该框架目前已经支持一主多从的读写分离`UseReadWriteConfiguration`第一个参数返回对应的读数据库链接,默认写数据库链接不会放入其中,并且支持轮询和随机两种读写分离策略,又因为读写分离多链接的时候会导致数据读写不一致,(如分页其实是2步第一步获取count第二部获取list)会导致数据量在最后几页出现缺量的问题,
针对这个问题框架目前实现了自定义读链接获取策略`ReadConnStringGetStrategyEnum.LatestEveryTime`表示为每次都是新的(这个情况下会出现上述问题),`ReadConnStringGetStrategyEnum.LatestFirstTime`表示以dbcontext作为单位获取一次(同dbcontext不会出现问题),
又因为各节点读写分离网络等一系列问题会导致刚刚写入的数据没办法获取到所以系统默认在dbcontext上添加是否支持读写分离如果false默认选择写字符串去读取`DbContext.ReadWriteSupport`
```c#
services.AddShardingDbContext<ShardingDefaultDbContext, DefaultDbContext>(o => o.UseSqlServer(hostBuilderContext.Configuration.GetSection("SqlServer")["ConnectionString"])
,op =>
{
op.EnsureCreatedWithOutShardingTable = true;
op.CreateShardingTableOnStart = true;
op.UseShardingOptionsBuilder((connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger),
(conStr,builder)=> builder.UseSqlServer("read db connection string").UseLoggerFactory(efLogger));
op.AddShardingTableRoute<SysUserModVirtualTableRoute>();
op.AddShardingTableRoute<SysUserSalaryVirtualTableRoute>();
});
services.AddShardingDbContext<DefaultShardingDbContext, DefaultTableDbContext>(
o => o.UseSqlServer("Data Source=localhost;Initial Catalog=ShardingCoreDB;Integrated Security=True;")
, op =>
{
op.EnsureCreatedWithOutShardingTable = true;
op.CreateShardingTableOnStart = true;
op.UseShardingOptionsBuilder(
(connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger),//使用dbconnection创建dbcontext支持事务
(conStr,builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
//.ReplaceService<IQueryTranslationPostprocessorFactory,SqlServer2008QueryTranslationPostprocessorFactory>()//支持sqlserver2008r2
);//使用链接字符串创建dbcontext
op.UseReadWriteConfiguration(sp => new List<string>()
{
"Data Source=localhost;Initial Catalog=ShardingCoreDB1;Integrated Security=True;",
"Data Source=localhost;Initial Catalog=ShardingCoreDB2;Integrated Security=True;"
}, ReadStrategyEnum.Random);
op.AddShardingTableRoute<SysUserModVirtualTableRoute>();
op.AddShardingTableRoute<SysUserSalaryVirtualTableRoute>();
});
```
## 高性能分页

View File

@ -12,7 +12,7 @@ namespace Sample.SqlServer.Shardings
public void Configure(PaginationBuilder<SysUserSalary> builder)
{
builder.PaginationSequence(o => o.Id)
.UseTailCompare(Comparer<string>.Default)
.UseTailComparer(Comparer<string>.Default)
.UseQueryMatch(PaginationMatchEnum.Owner | PaginationMatchEnum.Named | PaginationMatchEnum.PrimaryMatch);
builder.PaginationSequence(o => o.DateOfMonth)
.UseQueryMatch(PaginationMatchEnum.Owner | PaginationMatchEnum.Named | PaginationMatchEnum.PrimaryMatch).UseAppendIfOrderNone(10);

View File

@ -1,3 +1,4 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
@ -11,6 +12,7 @@ using Sample.SqlServer.DbContexts;
using Sample.SqlServer.Shardings;
using ShardingCore;
using ShardingCore.EFCores;
using ShardingCore.Sharding.ReadWriteConfigurations;
namespace Sample.SqlServer
{
@ -36,9 +38,14 @@ namespace Sample.SqlServer
op.CreateShardingTableOnStart = true;
op.UseShardingOptionsBuilder(
(connection, builder) => builder.UseSqlServer(connection).UseLoggerFactory(efLogger),//使用dbconnection创建dbcontext支持事务
(conStr,builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger)
(conStr,builder) => builder.UseSqlServer(conStr).UseLoggerFactory(efLogger).UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)
//.ReplaceService<IQueryTranslationPostprocessorFactory,SqlServer2008QueryTranslationPostprocessorFactory>()//支持sqlserver2008r2
);//使用链接字符串创建dbcontext
op.UseReadWriteConfiguration(sp => new List<string>()
{
"Data Source=localhost;Initial Catalog=ShardingCoreDB1;Integrated Security=True;",
"Data Source=localhost;Initial Catalog=ShardingCoreDB2;Integrated Security=True;"
}, ReadStrategyEnum.Random);
op.AddShardingTableRoute<SysUserModVirtualTableRoute>();
op.AddShardingTableRoute<SysUserSalaryVirtualTableRoute>();
});

View File

@ -146,6 +146,11 @@ namespace Samples.AbpSharding
}
}
public string GetConnectionString()
{
throw new NotImplementedException();
}
public void UseShardingTransaction(DbTransaction transaction)
{
throw new NotImplementedException();

View File

@ -19,6 +19,8 @@ using ShardingCore.Core.ShardingPage;
using ShardingCore.Core.ShardingPage.Abstractions;
using ShardingCore.Core.VirtualRoutes;
using ShardingCore.Core.VirtualRoutes.RouteTails.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
using ShardingCore.Sharding.ShardingQueryExecutors;
namespace ShardingCore
@ -32,7 +34,6 @@ namespace ShardingCore
public static class DIExtension
{
public static IServiceCollection AddShardingDbContext<TShardingDbContext, TActualDbContext>(this IServiceCollection services,
Action<DbContextOptionsBuilder> optionsAction = null,
Action<ShardingConfigOption<TShardingDbContext,TActualDbContext>> configure=null,
@ -49,14 +50,7 @@ namespace ShardingCore
configure?.Invoke(shardingConfigOptions);
services.AddSingleton<IShardingConfigOption, ShardingConfigOption<TShardingDbContext, TActualDbContext>>(sp=> shardingConfigOptions);
//添加创建TActualDbContext 的 创建者
var config = new ShardingDbContextOptionsBuilderConfig<TShardingDbContext>(shardingConfigOptions.SameConnectionConfigure,shardingConfigOptions.DefaultQueryConfigure);
services.AddSingleton<IShardingDbContextOptionsBuilderConfig, ShardingDbContextOptionsBuilderConfig<TShardingDbContext>>(sp=> config);
//添加创建TActualDbContext创建者
services.AddSingleton<IShardingDbContextCreatorConfig,DefaultShardingDbContextCreatorConfig<TShardingDbContext, TActualDbContext>>(sp=> new DefaultShardingDbContextCreatorConfig<TShardingDbContext, TActualDbContext>(typeof(TActualDbContext)));
services.AddShardingBaseOptions(shardingConfigOptions);
Action<DbContextOptionsBuilder> shardingOptionAction = option =>
{
@ -93,12 +87,9 @@ namespace ShardingCore
services.AddSingleton<IShardingConfigOption, ShardingConfigOption<TShardingDbContext, TActualDbContext>>(sp=> shardingConfigOptions);
//添加创建TActualDbContext 的 创建者
var config = new ShardingDbContextOptionsBuilderConfig<TShardingDbContext>(shardingConfigOptions.SameConnectionConfigure,shardingConfigOptions.DefaultQueryConfigure);
services.AddSingleton<IShardingDbContextOptionsBuilderConfig, ShardingDbContextOptionsBuilderConfig<TShardingDbContext>>(sp=> config);
//添加创建TActualDbContext创建者
services.AddSingleton<IShardingDbContextCreatorConfig,DefaultShardingDbContextCreatorConfig<TShardingDbContext, TActualDbContext>>(sp=> new DefaultShardingDbContextCreatorConfig<TShardingDbContext, TActualDbContext>(typeof(TActualDbContext)));
services.AddShardingBaseOptions(shardingConfigOptions);
Action<IServiceProvider, DbContextOptionsBuilder> shardingOptionAction = (sp, option) =>
@ -119,6 +110,49 @@ namespace ShardingCore
return services;
}
internal static void AddShardingBaseOptions<TShardingDbContext, TActualDbContext>(this IServiceCollection services,
ShardingConfigOption<TShardingDbContext, TActualDbContext> shardingConfigOptions)
where TActualDbContext : DbContext, IShardingTableDbContext
where TShardingDbContext : DbContext, IShardingDbContext<TActualDbContext>
{
//添加创建TActualDbContext 的DbContextOptionsBuilder创建者
var config = new ShardingDbContextOptionsBuilderConfig<TShardingDbContext>(shardingConfigOptions.SameConnectionConfigure, shardingConfigOptions.DefaultQueryConfigure);
services.AddSingleton<IShardingDbContextOptionsBuilderConfig, ShardingDbContextOptionsBuilderConfig<TShardingDbContext>>(sp => config);
//添加创建TActualDbContext创建者
services.AddSingleton<IShardingDbContextCreatorConfig, DefaultShardingDbContextCreatorConfig<TShardingDbContext, TActualDbContext>>(sp => new DefaultShardingDbContextCreatorConfig<TShardingDbContext, TActualDbContext>(typeof(TActualDbContext)));
if (!shardingConfigOptions.UseReadWrite)
{
services.AddTransient<IConnectionStringManager, DefaultConnectionStringManager<TShardingDbContext>>();
}
else
{
services.AddTransient<IConnectionStringManager, ReadWriteConnectionStringManager<TShardingDbContext>>();
services.AddSingleton<IReadWriteOptions, ReadWriteOptions<TShardingDbContext>>(sp=>new ReadWriteOptions<TShardingDbContext>(shardingConfigOptions.ReadWriteDefaultPriority, shardingConfigOptions.ReadWriteDefaultEnable, shardingConfigOptions.ReadConnStringGetStrategy));
if (shardingConfigOptions.ReadStrategyEnum == ReadStrategyEnum.Loop)
{
services
.AddSingleton<IShardingConnectionStringResolver,
LoopShardingConnectionStringResolver<TShardingDbContext>>(sp =>
new LoopShardingConnectionStringResolver<TShardingDbContext>(
shardingConfigOptions.ReadConnStringConfigure(sp)));
}else if (shardingConfigOptions.ReadStrategyEnum == ReadStrategyEnum.Random)
{
services
.AddSingleton<IShardingConnectionStringResolver,
RandomShardingConnectionStringResolver<TShardingDbContext>>(sp =>
new RandomShardingConnectionStringResolver<TShardingDbContext>(
shardingConfigOptions.ReadConnStringConfigure(sp)));
}
services.AddSingleton<IShardingReadWriteManager, ShardingReadWriteManager>();
services.AddSingleton<IShardingReadWriteAccessor, ShardingReadWriteAccessor<TShardingDbContext>>();
}
}
internal static IServiceCollection AddInternalShardingCore(this IServiceCollection services)
{

View File

@ -61,6 +61,7 @@ namespace ShardingCore.Extensions
#endif
}
/// <summary>
/// 根据对象集合解析
/// </summary>

View File

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace ShardingCore.Helpers
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 14:33:52
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public static class RandomHelper
{
static int seed = Environment.TickCount;
static readonly ThreadLocal<Random> random =
new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref seed)));
public static int Next()
{
return random.Value.Next();
}
public static int Next(int max)
{
return random.Value.Next(max);
}
public static int Next(int min,int max)
{
return random.Value.Next(min,max);
}
}
}

View File

@ -16,6 +16,7 @@ namespace ShardingCore
{
Type ShardingDbContextType { get;}
Type ActualDbContextType { get;}
bool UseReadWrite { get; }
void AddShardingTableRoute<TRoute>() where TRoute : IVirtualTableRoute;
Type GetVirtualRouteType(Type entityType);

View File

@ -10,6 +10,8 @@ using ShardingCore.DbContexts.ShardingDbContexts;
using ShardingCore.Exceptions;
using ShardingCore.Extensions;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
@ -19,8 +21,6 @@ using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using ShardingCore.EFCores;
namespace ShardingCore.Sharding
{
@ -34,17 +34,19 @@ namespace ShardingCore.Sharding
/// 分表分库的dbcontext
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class AbstractShardingDbContext<T> : DbContext, IShardingDbContext<T>, IShardingTransaction where T : DbContext, IShardingTableDbContext
public abstract class AbstractShardingDbContext<T> : DbContext, IShardingDbContext<T>, IShardingTransaction,IShardingReadWriteSupport where T : DbContext, IShardingTableDbContext
{
private readonly ConcurrentDictionary<string, DbContext> _dbContextCaches = new ConcurrentDictionary<string, DbContext>();
private readonly IShardingConfigOption shardingConfigOption;
private readonly IVirtualTableManager _virtualTableManager;
private readonly IRouteTailFactory _routeTailFactory;
private readonly IShardingDbContextFactory _shardingDbContextFactory;
private readonly IShardingDbContextOptionsBuilderConfig _shardingDbContextOptionsBuilderConfig;
private readonly IReadWriteOptions _readWriteOptions;
private readonly IConnectionStringManager _connectionStringManager;
private DbContextOptions<T> _dbContextOptions;
private readonly object CREATELOCK = new object();
private Guid idid = Guid.NewGuid();
public AbstractShardingDbContext(DbContextOptions options) : base(options)
{
@ -53,7 +55,20 @@ namespace ShardingCore.Sharding
_routeTailFactory = ShardingContainer.GetService<IRouteTailFactory>();
_shardingDbContextOptionsBuilderConfig = ShardingContainer
.GetService<IEnumerable<IShardingDbContextOptionsBuilderConfig>>()
.FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType);
.FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType)??throw new ArgumentNullException(nameof(IShardingDbContextOptionsBuilderConfig));
_connectionStringManager = ShardingContainer.GetService<IEnumerable<IConnectionStringManager>>()
.FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType) ?? throw new ArgumentNullException(nameof(IConnectionStringManager));
shardingConfigOption =ShardingContainer.GetService<IEnumerable<IShardingConfigOption>>().FirstOrDefault(o=>o.ShardingDbContextType==ShardingDbContextType&&o.ActualDbContextType==typeof(T)) ?? throw new ArgumentNullException(nameof(IShardingConfigOption));
if (shardingConfigOption.UseReadWrite)
{
_readWriteOptions = ShardingContainer
.GetService<IEnumerable<IReadWriteOptions>>()
.FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType) ?? throw new ArgumentNullException(nameof(IReadWriteOptions));
ReadWriteSupport = _readWriteOptions.ReadWriteSupport;
ReadWritePriority = _readWriteOptions.ReadWritePriority;
}
}
public abstract Type ShardingDbContextType { get; }
@ -69,6 +84,23 @@ namespace ShardingCore.Sharding
//}
public int ReadWritePriority { get; set; }
public bool ReadWriteSupport { get; set; }
public ReadConnStringGetStrategyEnum GetReadConnStringGetStrategy()
{
return _readWriteOptions.ReadConnStringGetStrategy;
}
public string GetWriteConnectionString()
{
return GetConnectionString();
}
public string GetConnectionString()
{
return Database.GetDbConnection().ConnectionString;
}
private DbContextOptionsBuilder<T> CreateDbContextOptionBuilder()
{
Type type = typeof(DbContextOptionsBuilder<>);
@ -83,10 +115,10 @@ namespace ShardingCore.Sharding
_shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(dbConnection, dbContextOptionBuilder);
return dbContextOptionBuilder.Options;
}
private DbContextOptions<T> CreateMonopolyDbContextOptions()
private DbContextOptions<T> CreateParallelDbContextOptions()
{
var dbContextOptionBuilder = CreateDbContextOptionBuilder();
var connectionString = Database.GetDbConnection().ConnectionString;
var connectionString = _connectionStringManager.GetConnectionString(this);
_shardingDbContextOptionsBuilderConfig.UseDbContextOptionsBuilder(connectionString, dbContextOptionBuilder);
return dbContextOptionBuilder.Options;
}
@ -106,9 +138,9 @@ namespace ShardingCore.Sharding
return new ShardingDbContextOptions(_dbContextOptions, routeTail);
}
private ShardingDbContextOptions CetMonopolyShardingDbContextOptions(IRouteTail routeTail)
private ShardingDbContextOptions CetParallelShardingDbContextOptions(IRouteTail routeTail)
{
return new ShardingDbContextOptions(CreateMonopolyDbContextOptions(), routeTail);
return new ShardingDbContextOptions(CreateParallelDbContextOptions(), routeTail);
}
@ -133,7 +165,7 @@ namespace ShardingCore.Sharding
}
else
{
return _shardingDbContextFactory.Create(ShardingDbContextType, CetMonopolyShardingDbContextOptions(routeTail));
return _shardingDbContextFactory.Create(ShardingDbContextType, CetParallelShardingDbContextOptions(routeTail));
}
}
@ -166,6 +198,7 @@ namespace ShardingCore.Sharding
}
}
public void UseShardingTransaction(DbTransaction transaction)
{
_dbContextCaches.Values.ForEach(o => o.Database.UseTransaction(transaction));

View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
namespace ShardingCore.Sharding.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/7 10:29:38
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public interface IConnectionStringManager
{
Type ShardingDbContextType { get; }
string GetConnectionString(IShardingDbContext shardingDbContext);
}
}

View File

@ -47,7 +47,7 @@ namespace ShardingCore.Sharding.Abstractions
IEnumerable<DbContext> CreateExpressionDbContext<TEntity>(Expression<Func<TEntity, bool>> where)
where TEntity : class;
string GetConnectionString();
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
using ShardingCore.Sharding.ReadWriteConfigurations;
namespace ShardingCore.Sharding.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 20:27:17
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public interface IShardingReadWriteSupport
{
int ReadWritePriority { get; set; }
bool ReadWriteSupport { get; set; }
ReadConnStringGetStrategyEnum GetReadConnStringGetStrategy();
string GetWriteConnectionString();
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.Sharding
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/7 10:32:26
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class DefaultConnectionStringManager<TShardingDbContext>:IConnectionStringManager where TShardingDbContext:DbContext,IShardingDbContext
{
public Type ShardingDbContextType => typeof(TShardingDbContext);
public string GetConnectionString(IShardingDbContext shardingDbContext)
{
return shardingDbContext.GetConnectionString();
}
}
}

View File

@ -25,7 +25,7 @@ namespace ShardingCore.Sharding.PaginationConfigurations
/// </summary>
/// <param name="tailComparer"></param>
/// <returns></returns>
public PaginationOrderPropertyBuilder UseTailCompare(IComparer<string> tailComparer)
public PaginationOrderPropertyBuilder UseTailComparer(IComparer<string> tailComparer)
{
_paginationSequenceConfig.TailComparer= tailComparer ?? throw new ArgumentException(nameof(tailComparer));

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ShardingCore.Sharding.ReadWriteConfigurations.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/7 11:13:52
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public interface IReadWriteOptions
{
Type ShardingDbContextType { get; }
/// <summary>
/// 默认读写配置优先级
/// </summary>
int ReadWritePriority { get; }
/// <summary>
/// 默认是否开启读写分离
/// </summary>
bool ReadWriteSupport { get; }
ReadConnStringGetStrategyEnum ReadConnStringGetStrategy { get; }
}
}

View File

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 13:01:59
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public interface IShardingConnectionStringResolver
{
Type ShardingDbContextType { get; }
string GetConnectionString();
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ShardingCore.Sharding.ReadWriteConfigurations.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 16:30:44
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public interface IShardingReadWriteAccessor
{
Type ShardingDbContextType { get;}
ShardingReadWriteContext ShardingReadWriteContext { get; set; }
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations.Abstractions
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 16:31:32
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public interface IShardingReadWriteManager
{
ShardingReadWriteContext GetCurrent<TShardingDbContext>()
where TShardingDbContext : DbContext, IShardingDbContext;
ShardingReadWriteContext GetCurrent(Type shardingDbContextType);
ShardingReadWriteScope<TShardingDbContext> CreateScope<TShardingDbContext>()
where TShardingDbContext : DbContext, IShardingDbContext;
}
}

View File

@ -0,0 +1,38 @@
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 14:39:23
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class LoopShardingConnectionStringResolver<TShardingDbContext> : IShardingConnectionStringResolver
{
public Type ShardingDbContextType => typeof(TShardingDbContext);
private readonly string[] _connectionStrings;
private readonly int _length;
private long _seed = 0;
public LoopShardingConnectionStringResolver(IEnumerable<string> connectionStrings)
{
_connectionStrings = connectionStrings.ToArray();
_length = _connectionStrings.Length;
}
public string GetConnectionString()
{
Interlocked.Increment(ref _seed);
var next = (int)(_seed % _length);
if (next < 0)
return _connectionStrings[Math.Abs(next)];
return _connectionStrings[next];
}
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Helpers;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 14:22:55
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class RandomShardingConnectionStringResolver<TShardingDbContext> :IShardingConnectionStringResolver
{
public Type ShardingDbContextType => typeof(TShardingDbContext);
private readonly string[] _connectionStrings;
private readonly int _length;
public RandomShardingConnectionStringResolver(IEnumerable<string> connectionStrings)
{
_connectionStrings = connectionStrings.ToArray();
_length = _connectionStrings.Length;
}
public string GetConnectionString()
{
var next = RandomHelper.Next(0, _length);
return _connectionStrings[next];
}
}
}

View File

@ -0,0 +1,31 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 13:08:31
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public enum ReadStrategyEnum
{
Random=1,
Loop=2,
}
public enum ReadConnStringGetStrategyEnum
{
/// <summary>
/// 每次都是最新的
/// </summary>
LatestEveryTime,
/// <summary>
/// 已dbcontext作为缓存条件每次都是第一次获取的
/// </summary>
LatestFirstTime
}
}

View File

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/7 10:37:28
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ReadWriteConnectionStringManager<TShardingDbContext> : IConnectionStringManager where TShardingDbContext : DbContext, IShardingDbContext
{
private readonly IShardingReadWriteManager _shardingReadWriteManager;
public Type ShardingDbContextType => typeof(TShardingDbContext);
private IShardingConnectionStringResolver _shardingConnectionStringResolver;
private string _cacheConnectionString;
public ReadWriteConnectionStringManager(IShardingReadWriteManager shardingReadWriteManager, IEnumerable<IShardingConnectionStringResolver> shardingConnectionStringResolvers)
{
_shardingReadWriteManager = shardingReadWriteManager;
_shardingConnectionStringResolver = shardingConnectionStringResolvers.FirstOrDefault(o => o.ShardingDbContextType == ShardingDbContextType) ?? throw new ArgumentNullException($"{ShardingDbContextType.FullName}:{nameof(shardingConnectionStringResolvers)}");
}
public string GetConnectionString(IShardingDbContext shardingDbContext)
{
if (!(shardingDbContext is IShardingReadWriteSupport shardingReadWriteSupport))
{
return shardingDbContext.GetConnectionString();
}
var shardingReadWriteContext = _shardingReadWriteManager.GetCurrent(ShardingDbContextType);
var support = shardingReadWriteSupport.ReadWriteSupport;
if (shardingReadWriteContext != null)
{
support = (shardingReadWriteSupport.ReadWritePriority >= shardingReadWriteContext.DefaultPriority)
? shardingReadWriteSupport.ReadWriteSupport
: shardingReadWriteContext.DefaultReadEnable;
}
if (support)
{
return GetReadConnectionString0(shardingReadWriteSupport);
}
return shardingReadWriteSupport.GetWriteConnectionString();
}
private string GetReadConnectionString0(IShardingReadWriteSupport shardingReadWriteSupport)
{
var readConnStringGetStrategy = shardingReadWriteSupport.GetReadConnStringGetStrategy();
if (readConnStringGetStrategy == ReadConnStringGetStrategyEnum.LatestFirstTime)
{
if (_cacheConnectionString == null)
_cacheConnectionString = _shardingConnectionStringResolver.GetConnectionString();
return _cacheConnectionString;
}
else if (readConnStringGetStrategy == ReadConnStringGetStrategyEnum.LatestEveryTime)
{
return _shardingConnectionStringResolver.GetConnectionString();
}
else
{
throw new InvalidOperationException($"ReadWriteConnectionStringManager:{readConnStringGetStrategy}");
}
}
}
}

View File

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/7 11:06:40
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ReadWriteOptions<TShardingDbContext> : IReadWriteOptions
where TShardingDbContext : DbContext, IShardingDbContext
{
public ReadWriteOptions(int readWritePriority, bool readWriteSupport, ReadConnStringGetStrategyEnum readConnStringGetStrategy)
{
ReadWritePriority = readWritePriority;
ReadWriteSupport = readWriteSupport;
ReadConnStringGetStrategy = readConnStringGetStrategy;
}
public Type ShardingDbContextType => typeof(TShardingDbContext);
/// <summary>
/// 默认读写配置优先级
/// </summary>
public int ReadWritePriority { get; }
/// <summary>
/// 默认是否开启读写分离
/// </summary>
public bool ReadWriteSupport { get; }
public ReadConnStringGetStrategyEnum ReadConnStringGetStrategy { get; }
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 16:54:23
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingReadWriteAccessor<TShardingDbContext>:IShardingReadWriteAccessor where TShardingDbContext:DbContext,IShardingDbContext
{
private static AsyncLocal<ShardingReadWriteContext> _shardingReadWriteContext = new AsyncLocal<ShardingReadWriteContext>();
public Type ShardingDbContextType => typeof(TShardingDbContext);
/// <summary>
///
/// </summary>
public ShardingReadWriteContext ShardingReadWriteContext
{
get => _shardingReadWriteContext.Value;
set => _shardingReadWriteContext.Value = value;
}
}
}

View File

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 16:52:29
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingReadWriteContext
{
public bool DefaultReadEnable { get; set; }
public int DefaultPriority { get; set; }
private ShardingReadWriteContext()
{
DefaultReadEnable = false;
DefaultPriority = 0;
}
public static ShardingReadWriteContext Create()
{
return new ShardingReadWriteContext();
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Extensions;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 21:02:56
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingReadWriteManager:IShardingReadWriteManager
{
private readonly ConcurrentDictionary<Type, IShardingReadWriteAccessor> _shardingReadWriteAccessors;
public ShardingReadWriteContext GetCurrent<TShardingDbContext>() where TShardingDbContext : DbContext, IShardingDbContext
{
return GetCurrent(typeof(TShardingDbContext));
}
public ShardingReadWriteContext GetCurrent(Type shardingDbContextType)
{
if (!shardingDbContextType.IsShardingDbContext())
throw new InvalidOperationException(shardingDbContextType.FullName);
if (_shardingReadWriteAccessors.TryGetValue(shardingDbContextType, out var accessor))
return accessor.ShardingReadWriteContext;
throw new InvalidOperationException(shardingDbContextType.FullName);
}
public ShardingReadWriteManager(IEnumerable<IShardingReadWriteAccessor> shardingReadWriteAccessors)
{
_shardingReadWriteAccessors = new ConcurrentDictionary<Type,IShardingReadWriteAccessor>(shardingReadWriteAccessors.ToDictionary(o => o.ShardingDbContextType, o => o));
}
public ShardingReadWriteScope<TShardingDbContext> CreateScope<TShardingDbContext>() where TShardingDbContext : DbContext, IShardingDbContext
{
var shardingPageScope = new ShardingReadWriteScope<TShardingDbContext>(_shardingReadWriteAccessors.Values);
shardingPageScope.ShardingReadWriteAccessor.ShardingReadWriteContext = ShardingReadWriteContext.Create();
return shardingPageScope;
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.EntityFrameworkCore;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations.Abstractions;
namespace ShardingCore.Sharding.ReadWriteConfigurations
{
/*
* @Author: xjm
* @Description:
* @Date: 2021/9/6 20:58:57
* @Ver: 1.0
* @Email: 326308290@qq.com
*/
public class ShardingReadWriteScope<TShardingDbContext>:IDisposable
where TShardingDbContext : DbContext, IShardingDbContext
{
public IShardingReadWriteAccessor ShardingReadWriteAccessor { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="shardingReadWriteAccessors"></param>
public ShardingReadWriteScope(IEnumerable<IShardingReadWriteAccessor> shardingReadWriteAccessors)
{
ShardingReadWriteAccessor = shardingReadWriteAccessors.FirstOrDefault(o=>o.ShardingDbContextType==typeof(TShardingDbContext))??throw new ArgumentNullException(nameof(shardingReadWriteAccessors));
}
/// <summary>
/// 回收
/// </summary>
public void Dispose()
{
ShardingReadWriteAccessor.ShardingReadWriteContext = null;
}
}
}

View File

@ -9,6 +9,7 @@ using ShardingCore.Core.VirtualRoutes.TableRoutes;
using ShardingCore.EFCores;
using ShardingCore.Sharding;
using ShardingCore.Sharding.Abstractions;
using ShardingCore.Sharding.ReadWriteConfigurations;
namespace ShardingCore
{
@ -25,13 +26,13 @@ namespace ShardingCore
{
private readonly Dictionary<Type, Type> _virtualRoutes = new Dictionary<Type, Type>();
public Action<DbConnection, DbContextOptionsBuilder> SameConnectionConfigure { get; set; }
public Action<string,DbContextOptionsBuilder> DefaultQueryConfigure { get; set; }
public Action<DbConnection, DbContextOptionsBuilder> SameConnectionConfigure { get;private set; }
public Action<string,DbContextOptionsBuilder> DefaultQueryConfigure { get; private set; }
/// <summary>
/// 配置数据库分表查询和保存时的DbContext创建方式
/// </summary>
/// <param name="sameConnectionConfigure">DbConnection下如何配置因为不同的DbContext支持事务需要使用同一个DbConnection</param>
/// <param name="defaultBuilderConfigure">默认查询DbContext创建的配置</param>
/// <param name="defaultQueryConfigure">默认查询DbContext创建的配置</param>
public void UseShardingOptionsBuilder(Action<DbConnection, DbContextOptionsBuilder> sameConnectionConfigure, Action<string,DbContextOptionsBuilder> defaultQueryConfigure = null)
{
@ -39,6 +40,30 @@ namespace ShardingCore
DefaultQueryConfigure = defaultQueryConfigure ?? throw new ArgumentNullException(nameof(defaultQueryConfigure));
}
public bool UseReadWrite => ReadConnStringConfigure != null;
public Func<IServiceProvider, IEnumerable<string>> ReadConnStringConfigure { get; private set; }
public ReadStrategyEnum ReadStrategyEnum { get; private set; }
public bool ReadWriteDefaultEnable { get; private set; }
public int ReadWriteDefaultPriority { get; private set; }
public ReadConnStringGetStrategyEnum ReadConnStringGetStrategy { get; private set; }
/// <summary>
/// 使用读写分离配置
/// </summary>
/// <param name="readConnStringConfigure"></param>
/// <param name="readStrategyEnum"></param>
/// <param name="defaultEnable">考虑到很多时候读写分离的延迟需要马上用到写入的数据所以默认关闭需要的话自己开启或者通过IShardingReadWriteManager,false表示默认不走读写分离除非你自己开启,true表示默认走读写分离除非你禁用,</param>
/// <param name="defaultPriority">IShardingReadWriteManager.CreateScope()会判断dbcontext的priority然后判断是否启用readwrite</param>
/// <param name="readConnStringGetStrategy">读写分离可能会造成每次查询不一样甚至分表后的分页会有错位问题,因为他不是一个原子操作,所以如果整个请求为一次读写切换大多数更加合适</param>
public void UseReadWriteConfiguration(Func<IServiceProvider, IEnumerable<string>> readConnStringConfigure, ReadStrategyEnum readStrategyEnum,bool defaultEnable=false,int defaultPriority=10,ReadConnStringGetStrategyEnum readConnStringGetStrategy= ReadConnStringGetStrategyEnum.LatestFirstTime)
{
ReadConnStringConfigure = readConnStringConfigure ?? throw new ArgumentNullException(nameof(readConnStringConfigure));
ReadStrategyEnum = readStrategyEnum;
ReadWriteDefaultEnable = defaultEnable;
ReadWriteDefaultPriority = defaultPriority;
ReadConnStringGetStrategy = readConnStringGetStrategy;
}
public Type ShardingDbContextType => typeof(TShardingDbContext);
public Type ActualDbContextType => typeof(TActualDbContext);