diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/AsyncLocalDbContextAccessor .cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/AsyncLocalDbContextAccessor .cs new file mode 100644 index 00000000..09b214cc --- /dev/null +++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/AsyncLocalDbContextAccessor .cs @@ -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(); + } + private readonly AsyncLocal _currentScope; + } +} diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContextCreationContext.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContextCreationContext.cs new file mode 100644 index 00000000..fc868c8c --- /dev/null +++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContextCreationContext.cs @@ -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 _current = new AsyncLocal(); + + 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); + } +} diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlsugarCoreExtensions.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlsugarCoreExtensions.cs index 8facee27..7269e9d0 100644 --- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlsugarCoreExtensions.cs +++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlsugarCoreExtensions.cs @@ -13,12 +13,12 @@ namespace Yi.Framework.SqlSugarCore { public static IServiceCollection AddYiDbContext(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; } public static IServiceCollection TryAddYiDbContext(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; } diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/SqlSugarTransactionApi.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/SqlSugarTransactionApi.cs index 0a9a0c43..44ea1419 100644 --- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/SqlSugarTransactionApi.cs +++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/SqlSugarTransactionApi.cs @@ -13,9 +13,9 @@ namespace Yi.Framework.SqlSugarCore.Uow } public ISqlSugarDbContext GetDbContext() - { - - return _sqlsugarDbContext; + { + + return _sqlsugarDbContext; } public async Task CommitAsync(CancellationToken cancellationToken = default) diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/UnitOfWorkSqlsugarDbContextProvider.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/UnitOfWorkSqlsugarDbContextProvider.cs index 3b600607..2f6b4d6f 100644 --- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/UnitOfWorkSqlsugarDbContextProvider.cs +++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/UnitOfWorkSqlsugarDbContextProvider.cs @@ -1,17 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using SqlSugar; using Volo.Abp.Data; using Volo.Abp.MultiTenancy; using Volo.Abp.Threading; using Volo.Abp.Uow; -using Volo.Abp; -using Microsoft.Extensions.DependencyInjection; -using SqlSugar; using Yi.Framework.SqlSugarCore.Abstractions; namespace Yi.Framework.SqlSugarCore.Uow @@ -21,7 +15,9 @@ namespace Yi.Framework.SqlSugarCore.Uow private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator; private readonly string MasterTenantDbDefaultName = DbConnOptions.MasterTenantDbDefaultName; public ILogger> Logger { get; set; } + public IServiceProvider ServiceProvider { get; set; } + private static AsyncLocalDbContextAccessor ContextInstance => AsyncLocalDbContextAccessor.Instance; protected readonly IUnitOfWorkManager UnitOfWorkManager; protected readonly IConnectionStringResolver ConnectionStringResolver; protected readonly ICancellationTokenProvider CancellationTokenProvider; @@ -43,53 +39,74 @@ namespace Yi.Framework.SqlSugarCore.Uow _dbConnectionCreator = dbConnectionCreator; } + private static object _databaseApiLock = new object(); public virtual async Task GetDbContextAsync() { var unitOfWork = UnitOfWorkManager.Current; - if (unitOfWork == null) + if (unitOfWork == null|| unitOfWork.Options.IsTransactional==false) { - UnitOfWorkManager.Begin(true); - unitOfWork = UnitOfWorkManager.Current; - //取消工作单元强制性 + if (ContextInstance.Current is null) + { + ContextInstance.Current = (TDbContext)ServiceProvider.GetRequiredService(); + } + //提高体验,取消工作单元强制性 //throw new AbpException("A DbContext can only be created inside a unit of work!"); + //如果不启用工作单元,创建一个新的db,不开启事务即可 + return (TDbContext)ContextInstance.Current; } + + var connectionStringName = ConnectionStrings.DefaultConnectionStringName; + + //获取当前连接字符串,未多租户时,默认为空 var connectionString = await ResolveConnectionStringAsync(connectionStringName); - // var dbContextKey = $"{this.GetType().FullName}_{connectionString}"; - var dbContextKey = "Default"; - var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey); + var dbContextKey = $"{this.GetType().FullName}_{connectionString}"; - if (databaseApi == null) + + lock (_databaseApiLock) { - databaseApi = new SqlSugarDatabaseApi( - await CreateDbContextAsync(unitOfWork, connectionStringName, connectionString) - ); - unitOfWork.AddDatabaseApi(dbContextKey, databaseApi); + //尝试当前工作单元获取db + var databaseApi = unitOfWork.FindDatabaseApi(dbContextKey); + //当前没有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 CreateDbContextAsync(IUnitOfWork unitOfWork, string connectionStringName, string connectionString) { - - var dbContext = await CreateDbContextAsync(unitOfWork); - - //没有检测到使用多租户功能,默认使用默认库即可 - if (string.IsNullOrWhiteSpace(connectionString)) + var creationContext = new SqlSugarDbContextCreationContext(connectionStringName, connectionString); + //将连接key进行传值 + using (SqlSugarDbContextCreationContext.Use(creationContext)) { - connectionString = dbContext.Options.Url; - connectionStringName = DbConnOptions.TenantDbDefaultName; + var dbContext = await CreateDbContextAsync(unitOfWork); + + //没有检测到使用多租户功能,默认使用默认库即可 + 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) @@ -138,38 +155,35 @@ namespace Yi.Framework.SqlSugarCore.Uow protected virtual async Task CreateDbContextAsync(IUnitOfWork unitOfWork) { - return unitOfWork.ServiceProvider.GetRequiredService(); - //return unitOfWork.Options.IsTransactional - // ? await CreateDbContextWithTransactionAsync(unitOfWork) - // : unitOfWork.ServiceProvider.GetRequiredService(); + return unitOfWork.Options.IsTransactional + ? await CreateDbContextWithTransactionAsync(unitOfWork) + : unitOfWork.ServiceProvider.GetRequiredService(); } protected virtual async Task CreateDbContextWithTransactionAsync(IUnitOfWork unitOfWork) { - var transactionApiKey = $"Sqlsugar_Default" + Guid.NewGuid().ToString(); + //事务key + var transactionApiKey = $"SqlsugarCore_{SqlSugarDbContextCreationContext.Current.ConnectionString}"; + + //尝试查找事务 var activeTransaction = unitOfWork.FindTransactionApi(transactionApiKey) as SqlSugarTransactionApi; - //if (activeTransaction==null|| activeTransaction.Equals(default(SqlSugarTransactionApi))) - //{ - var dbContext = unitOfWork.ServiceProvider.GetRequiredService(); - var transaction = new SqlSugarTransactionApi( - dbContext - ); - unitOfWork.AddTransactionApi(transactionApiKey, transaction); + //该db还没有进行开启事务 + if (activeTransaction == null) + { + //获取到db添加事务即可 + var dbContext = unitOfWork.ServiceProvider.GetRequiredService(); + var transaction = new SqlSugarTransactionApi( + dbContext + ); + unitOfWork.AddTransactionApi(transactionApiKey, transaction); - - //await Console.Out.WriteLineAsync("开始新的事务"); - // Console.WriteLine(dbContext.SqlSugarClient.ContextID); - await dbContext.SqlSugarClient.Ado.BeginTranAsync(); - return dbContext; - //} - //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(); - //} + await dbContext.SqlSugarClient.Ado.BeginTranAsync(); + return dbContext; + } + else + { + return (TDbContext)activeTransaction.GetDbContext(); + } } diff --git a/Yi.Abp.Net8/src/Yi.Abp.Application/Services/TestService.cs b/Yi.Abp.Net8/src/Yi.Abp.Application/Services/TestService.cs index 3ae82863..a82cdf46 100644 --- a/Yi.Abp.Net8/src/Yi.Abp.Application/Services/TestService.cs +++ b/Yi.Abp.Net8/src/Yi.Abp.Application/Services/TestService.cs @@ -1,12 +1,18 @@ 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 { + /// + /// 这是一个示例 + /// public class TestService : ApplicationService { + public ISqlSugarRepository sqlSugarRepository { get; set; } /// - /// 你好世界 + /// 你好世界,动态Api /// /// /// @@ -14,5 +20,34 @@ namespace Yi.Abp.Application.Services { return name ?? "HelloWord"; } + + /// + /// 工作单元魔改 + /// 用户体验优先,万金油模式,支持多线程并发安全,支持多线程工作单元,支持无工作单元,支持。。。 + /// 自动在各个情况处理db客户端最优解之一 + /// + /// + public async Task GetUowAsync() + { + int i = 10; + List tasks = new List(); + + 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); + } } }