refactor: 魔改工作单元体系

This commit is contained in:
橙子
2024-01-25 01:43:00 +08:00
parent e614935693
commit 6b96231087
6 changed files with 165 additions and 68 deletions

View File

@@ -0,0 +1,19 @@
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.SqlSugarCore
{
public class AsyncLocalDbContextAccessor
{
public static AsyncLocalDbContextAccessor Instance { get; } = new();
public ISqlSugarDbContext? Current
{
get => _currentScope.Value;
set => _currentScope.Value = value;
}
public AsyncLocalDbContextAccessor()
{
_currentScope = new AsyncLocal<ISqlSugarDbContext?>();
}
private readonly AsyncLocal<ISqlSugarDbContext> _currentScope;
}
}

View File

@@ -0,0 +1,29 @@
using System.Data.Common;
using Volo.Abp;
namespace Yi.Framework.SqlSugarCore;
public class SqlSugarDbContextCreationContext
{
public static SqlSugarDbContextCreationContext Current => _current.Value;
private static readonly AsyncLocal<SqlSugarDbContextCreationContext> _current = new AsyncLocal<SqlSugarDbContextCreationContext>();
public string ConnectionStringName { get; }
public string ConnectionString { get; }
public DbConnection ExistingConnection { get; internal set; }
public SqlSugarDbContextCreationContext(string connectionStringName, string connectionString)
{
ConnectionStringName = connectionStringName;
ConnectionString = connectionString;
}
public static IDisposable Use(SqlSugarDbContextCreationContext context)
{
var previousValue = Current;
_current.Value = context;
return new DisposeAction(() => _current.Value = previousValue);
}
}

View File

@@ -13,12 +13,12 @@ namespace Yi.Framework.SqlSugarCore
{ {
public static IServiceCollection AddYiDbContext<DbContext>(this IServiceCollection service) where DbContext : class, ISqlSugarDbContext public static IServiceCollection AddYiDbContext<DbContext>(this IServiceCollection service) where DbContext : class, ISqlSugarDbContext
{ {
service.Replace(new ServiceDescriptor(typeof(ISqlSugarDbContext), typeof(DbContext), ServiceLifetime.Scoped)); service.Replace(new ServiceDescriptor(typeof(ISqlSugarDbContext), typeof(DbContext), ServiceLifetime.Transient));
return service; return service;
} }
public static IServiceCollection TryAddYiDbContext<DbContext>(this IServiceCollection service) where DbContext : class, ISqlSugarDbContext public static IServiceCollection TryAddYiDbContext<DbContext>(this IServiceCollection service) where DbContext : class, ISqlSugarDbContext
{ {
service.TryAdd(new ServiceDescriptor(typeof(ISqlSugarDbContext), typeof(DbContext), ServiceLifetime.Scoped)); service.TryAdd(new ServiceDescriptor(typeof(ISqlSugarDbContext), typeof(DbContext), ServiceLifetime.Transient));
return service; return service;
} }

View File

@@ -13,9 +13,9 @@ namespace Yi.Framework.SqlSugarCore.Uow
} }
public ISqlSugarDbContext GetDbContext() public ISqlSugarDbContext GetDbContext()
{ {
return _sqlsugarDbContext; return _sqlsugarDbContext;
} }
public async Task CommitAsync(CancellationToken cancellationToken = default) public async Task CommitAsync(CancellationToken cancellationToken = default)

View File

@@ -1,17 +1,11 @@
using System; using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using SqlSugar;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading; using Volo.Abp.Threading;
using Volo.Abp.Uow; using Volo.Abp.Uow;
using Volo.Abp;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
using Yi.Framework.SqlSugarCore.Abstractions; using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Framework.SqlSugarCore.Uow namespace Yi.Framework.SqlSugarCore.Uow
@@ -21,7 +15,9 @@ namespace Yi.Framework.SqlSugarCore.Uow
private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator; private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator;
private readonly string MasterTenantDbDefaultName = DbConnOptions.MasterTenantDbDefaultName; private readonly string MasterTenantDbDefaultName = DbConnOptions.MasterTenantDbDefaultName;
public ILogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>> Logger { get; set; } public ILogger<UnitOfWorkSqlsugarDbContextProvider<TDbContext>> Logger { get; set; }
public IServiceProvider ServiceProvider { get; set; }
private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance;
protected readonly IUnitOfWorkManager UnitOfWorkManager; protected readonly IUnitOfWorkManager UnitOfWorkManager;
protected readonly IConnectionStringResolver ConnectionStringResolver; protected readonly IConnectionStringResolver ConnectionStringResolver;
protected readonly ICancellationTokenProvider CancellationTokenProvider; protected readonly ICancellationTokenProvider CancellationTokenProvider;
@@ -43,53 +39,74 @@ namespace Yi.Framework.SqlSugarCore.Uow
_dbConnectionCreator = dbConnectionCreator; _dbConnectionCreator = dbConnectionCreator;
} }
private static object _databaseApiLock = new object();
public virtual async Task<TDbContext> GetDbContextAsync() public virtual async Task<TDbContext> GetDbContextAsync()
{ {
var unitOfWork = UnitOfWorkManager.Current; var unitOfWork = UnitOfWorkManager.Current;
if (unitOfWork == null) if (unitOfWork == null|| unitOfWork.Options.IsTransactional==false)
{ {
UnitOfWorkManager.Begin(true); if (ContextInstance.Current is null)
unitOfWork = UnitOfWorkManager.Current; {
//取消工作单元强制性 ContextInstance.Current = (TDbContext)ServiceProvider.GetRequiredService<ISqlSugarDbContext>();
}
//提高体验,取消工作单元强制性
//throw new AbpException("A DbContext can only be created inside a unit of work!"); //throw new AbpException("A DbContext can only be created inside a unit of work!");
//如果不启用工作单元创建一个新的db不开启事务即可
return (TDbContext)ContextInstance.Current;
} }
var connectionStringName = ConnectionStrings.DefaultConnectionStringName; var connectionStringName = ConnectionStrings.DefaultConnectionStringName;
//获取当前连接字符串,未多租户时,默认为空
var connectionString = await ResolveConnectionStringAsync(connectionStringName); var connectionString = await ResolveConnectionStringAsync(connectionStringName);
// var dbContextKey = $"{this.GetType().FullName}_{connectionString}"; var dbContextKey = $"{this.GetType().FullName}_{connectionString}";
var dbContextKey = "Default";
var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
if (databaseApi == null)
lock (_databaseApiLock)
{ {
databaseApi = new SqlSugarDatabaseApi( //尝试当前工作单元获取db
await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString) var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey);
);
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
//当前没有db创建一个新的db
if (databaseApi == null)
{
//db根据连接字符串来创建
databaseApi = new SqlSugarDatabaseApi(
CreateDbContextAsync(unitOfWork, connectionStringName, connectionString).Result
);
//创建的db加入到当前工作单元中
unitOfWork.AddDatabaseApi(dbContextKey, databaseApi);
}
return (TDbContext)((SqlSugarDatabaseApi)databaseApi).DbContext;
} }
return (TDbContext)((SqlSugarDatabaseApi)databaseApi).DbContext; ;
} }
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName, string connectionString) protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName, string connectionString)
{ {
var creationContext = new SqlSugarDbContextCreationContext(connectionStringName, connectionString);
var dbContext = await CreateDbContextAsync(unitOfWork); //将连接key进行传值
using (SqlSugarDbContextCreationContext.Use(creationContext))
//没有检测到使用多租户功能,默认使用默认库即可
if (string.IsNullOrWhiteSpace(connectionString))
{ {
connectionString = dbContext.Options.Url; var dbContext = await CreateDbContextAsync(unitOfWork);
connectionStringName = DbConnOptions.TenantDbDefaultName;
//没有检测到使用多租户功能,默认使用默认库即可
if (string.IsNullOrWhiteSpace(connectionString))
{
connectionString = dbContext.Options.Url;
connectionStringName = DbConnOptions.TenantDbDefaultName;
}
//获取到DB之后对多租户多库进行处理
var changedDbContext = DatabaseChange(dbContext, connectionStringName, connectionString);
return changedDbContext;
} }
//获取到DB之后对多租户多库进行处理
var changedDbContext = DatabaseChange(dbContext, connectionStringName, connectionString);
return changedDbContext;
} }
protected virtual TDbContext DatabaseChange(TDbContext dbContext, string configId, string connectionString) protected virtual TDbContext DatabaseChange(TDbContext dbContext, string configId, string connectionString)
@@ -138,38 +155,35 @@ namespace Yi.Framework.SqlSugarCore.Uow
protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork) protected virtual async Task<TDbContext> CreateDbContextAsync(IUnitOfWork unitOfWork)
{ {
return unitOfWork.ServiceProvider.GetRequiredService<TDbContext>(); return unitOfWork.Options.IsTransactional
//return unitOfWork.Options.IsTransactional ? await CreateDbContextWithTransactionAsync(unitOfWork)
// ? await CreateDbContextWithTransactionAsync(unitOfWork) : unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
// : unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
} }
protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork) protected virtual async Task<TDbContext> CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork)
{ {
var transactionApiKey = $"Sqlsugar_Default" + Guid.NewGuid().ToString(); //事务key
var transactionApiKey = $"SqlsugarCore_{SqlSugarDbContextCreationContext.Current.ConnectionString}";
//尝试查找事务
var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi; var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi;
//if (activeTransaction==null|| activeTransaction.Equals(default(SqlSugarTransactionApi)))
//{
var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>(); //该db还没有进行开启事务
var transaction = new SqlSugarTransactionApi( if (activeTransaction == null)
dbContext {
); //获取到db添加事务即可
unitOfWork.AddTransactionApi(transactionApiKey, transaction); var dbContext = unitOfWork.ServiceProvider.GetRequiredService<TDbContext>();
var transaction = new SqlSugarTransactionApi(
dbContext
);
unitOfWork.AddTransactionApi(transactionApiKey, transaction);
await dbContext.SqlSugarClient.Ado.BeginTranAsync();
//await Console.Out.WriteLineAsync("开始新的事务"); return dbContext;
// Console.WriteLine(dbContext.SqlSugarClient.ContextID); }
await dbContext.SqlSugarClient.Ado.BeginTranAsync(); else
return dbContext; {
//} return (TDbContext)activeTransaction.GetDbContext();
//else }
//{
// var db= activeTransaction.GetDbContext().SqlSugarClient;
// // await Console.Out.WriteLineAsync("继续老的事务");
// // Console.WriteLine(activeTransaction.DbContext.SqlSugarClient);
// await activeTransaction.GetDbContext().SqlSugarClient.Ado.BeginTranAsync();
// return (TDbContext)activeTransaction.GetDbContext();
//}
} }

View File

@@ -1,12 +1,18 @@
using Volo.Abp.Application.Services; using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection; using Volo.Abp.Uow;
using Yi.Framework.Bbs.Domain.Entities.Forum;
using Yi.Framework.SqlSugarCore.Abstractions;
namespace Yi.Abp.Application.Services namespace Yi.Abp.Application.Services
{ {
/// <summary>
/// 这是一个示例
/// </summary>
public class TestService : ApplicationService public class TestService : ApplicationService
{ {
public ISqlSugarRepository<BannerEntity> sqlSugarRepository { get; set; }
/// <summary> /// <summary>
/// 你好世界 /// 你好世界动态Api
/// </summary> /// </summary>
/// <param name="name"></param> /// <param name="name"></param>
/// <returns></returns> /// <returns></returns>
@@ -14,5 +20,34 @@ namespace Yi.Abp.Application.Services
{ {
return name ?? "HelloWord"; return name ?? "HelloWord";
} }
/// <summary>
/// 工作单元魔改
/// 用户体验优先,万金油模式,支持多线程并发安全,支持多线程工作单元,支持无工作单元,支持。。。
/// 自动在各个情况处理db客户端最优解之一
/// </summary>
/// <returns></returns>
public async Task GetUowAsync()
{
int i = 10;
List<Task> tasks = new List<Task>();
while (i > 0)
{
tasks.Add(Task.Run(async () =>
{
using (var uow = UnitOfWorkManager.Begin(true, true))
{
await sqlSugarRepository.InsertAsync(new BannerEntity { Name = "插入1" });
await uow.CompleteAsync();
}
await sqlSugarRepository.InsertAsync(new BannerEntity { Name = "插入2" });
}));
await sqlSugarRepository.InsertAsync(new BannerEntity { Name = "插入3" });
i--;
}
await Task.WhenAll(tasks);
}
} }
} }