diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs
index 4aeb7766..e8044cdf 100644
--- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs
+++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore.Abstractions/DbConnOptions.cs
@@ -66,11 +66,20 @@ namespace Yi.Framework.SqlSugarCore.Abstractions
public static string MasterTenantDbDefaultName = "Master";
public static string TenantDbDefaultName = "Default";
+ ///
+ /// 获取默认数据库
+ ///
+ ///
public SaasMultiTenancyOptions GetDefaultSaasMultiTenancy()
{
return new SaasMultiTenancyOptions { Name = TenantDbDefaultName, Url = Url };
}
- public SaasMultiTenancyOptions? GetDefaultMasterSaasMultiTenancy()
+
+ ///
+ /// 获取主数据库
+ ///
+ ///
+ public SaasMultiTenancyOptions? GetMasterSaasMultiTenancy()
{
if (EnabledSaasMultiTenancy == false)
{
diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbConnectionCreator.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbConnectionCreator.cs
index 54637f83..1dd4a823 100644
--- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbConnectionCreator.cs
+++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbConnectionCreator.cs
@@ -24,6 +24,9 @@ namespace Yi.Framework.SqlSugarCore
currentDb.Aop.DataExecuted = this.DataExecuted;
OnSqlSugarClientConfig(currentDb);
}
+
+
+
public ConnectionConfig Build(Action? action=null)
{
var dbConnOptions = Options;
diff --git a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContext.cs b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContext.cs
index 7efaa086..6b57fcd6 100644
--- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContext.cs
+++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/SqlSugarDbContext.cs
@@ -24,7 +24,7 @@ namespace Yi.Framework.SqlSugarCore
///
public ISqlSugarClient SqlSugarClient { get; private set; }
public ICurrentUser CurrentUser => LazyServiceProvider.GetRequiredService();
-
+ private readonly string MasterTenantDbDefaultName = DbConnOptions.MasterTenantDbDefaultName;
private IAbpLazyServiceProvider LazyServiceProvider { get; }
private IGuidGenerator GuidGenerator => LazyServiceProvider.LazyGetRequiredService();
@@ -37,7 +37,7 @@ namespace Yi.Framework.SqlSugarCore
public IEntityChangeEventHelper EntityChangeEventHelper => LazyServiceProvider.LazyGetService(NullEntityChangeEventHelper.Instance);
public DbConnOptions Options => LazyServiceProvider.LazyGetRequiredService>().Value;
-
+ private ISqlSugarDbConnectionCreator _dbConnectionCreator;
public void SetSqlSugarClient(ISqlSugarClient sqlSugarClient)
{
@@ -47,16 +47,77 @@ namespace Yi.Framework.SqlSugarCore
{
LazyServiceProvider = lazyServiceProvider;
var connectionCreator = LazyServiceProvider.LazyGetRequiredService();
+ _dbConnectionCreator = connectionCreator;
connectionCreator.OnSqlSugarClientConfig = OnSqlSugarClientConfig;
connectionCreator.EntityService = EntityService;
connectionCreator.DataExecuting = DataExecuting;
connectionCreator.DataExecuted = DataExecuted;
connectionCreator.OnLogExecuting = OnLogExecuting;
connectionCreator.OnLogExecuted = OnLogExecuted;
- SqlSugarClient = new SqlSugarClient(connectionCreator.Build());
- connectionCreator.SetDbAop(SqlSugarClient);
+ SqlSugarClient = new SqlSugarClient(connectionCreator.Build());
+ var connectionStringResolver = LazyServiceProvider.LazyGetRequiredService();
+ var connectionStr = connectionStringResolver.ResolveAsync().Result;
+ var changedDb = DatabaseChange(this, connectionStr);
+ SqlSugarClient = changedDb.SqlSugarClient;
}
+ ///
+ /// db切换多库支持
+ ///
+ ///
+ ///
+ ///
+ protected virtual SqlSugarDbContext DatabaseChange(SqlSugarDbContext dbContext, string connectionString)
+ {
+ string configId = string.Empty;
+ //没有检测到使用多租户功能,默认使用默认库即可
+ if (string.IsNullOrWhiteSpace(connectionString))
+ {
+ connectionString = dbContext.Options.Url;
+ configId = CurrentTenant.Name;
+ }
+
+ var dbOption = dbContext.Options;
+ var db = dbContext.SqlSugarClient.AsTenant();
+
+ //主库的Db切换,当操作的是租户表的时候
+ if (CurrentTenant.Name == MasterTenantDbDefaultName)
+ {
+ //直接切换
+ configId = MasterTenantDbDefaultName;
+ var conStrOrNull = dbOption.GetMasterSaasMultiTenancy();
+ Volo.Abp.Check.NotNull(conStrOrNull, "租户主库未找到");
+ connectionString = conStrOrNull.Url;
+ }
+
+ //租户Db的动态切换
+ //二级缓存
+ var changed = false;
+ if (!db.IsAnyConnection(configId))
+ {
+ var config = _dbConnectionCreator.Build(options =>
+ {
+ options.DbType = dbOption.DbType!.Value;
+ options.ConfigId = configId;//设置库的唯一标识
+ options.IsAutoCloseConnection = true;
+ options.ConnectionString = connectionString;
+ });
+ //添加一个db到当前上下文 (Add部分不线上下文不会共享)
+ db.AddConnection(config);
+ changed = true;
+ }
+ var currentDb = db.GetConnection(configId) as ISqlSugarClient;
+ //设置Aop
+ if (changed)
+ {
+ _dbConnectionCreator.SetDbAop(currentDb);
+ }
+ dbContext.SetSqlSugarClient(currentDb);
+
+ return dbContext;
+ }
+
+
///
/// 上下文对象扩展
///
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 44ea1419..6e49fa9f 100644
--- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/SqlSugarTransactionApi.cs
+++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/SqlSugarTransactionApi.cs
@@ -20,7 +20,7 @@ namespace Yi.Framework.SqlSugarCore.Uow
public async Task CommitAsync(CancellationToken cancellationToken = default)
{
- await _sqlsugarDbContext.SqlSugarClient.Ado.CommitTranAsync();
+ // await _sqlsugarDbContext.SqlSugarClient.Ado.CommitTranAsync();
}
public void Dispose()
@@ -29,7 +29,7 @@ namespace Yi.Framework.SqlSugarCore.Uow
public async Task RollbackAsync(CancellationToken cancellationToken = default)
{
- await _sqlsugarDbContext.SqlSugarClient.Ado.RollbackTranAsync();
+ // await _sqlsugarDbContext.SqlSugarClient.Ado.RollbackTranAsync();
}
}
}
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 c04531a1..4c5317e9 100644
--- a/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/UnitOfWorkSqlsugarDbContextProvider.cs
+++ b/Yi.Abp.Net8/framework/Yi.Framework.SqlSugarCore/Uow/UnitOfWorkSqlsugarDbContextProvider.cs
@@ -14,7 +14,7 @@ namespace Yi.Framework.SqlSugarCore.Uow
public class UnitOfWorkSqlsugarDbContextProvider : ISugarDbContextProvider where TDbContext : ISqlSugarDbContext
{
private readonly ISqlSugarDbConnectionCreator _dbConnectionCreator;
- private readonly string MasterTenantDbDefaultName = DbConnOptions.MasterTenantDbDefaultName;
+
public ILogger> Logger { get; set; }
public IServiceProvider ServiceProvider { get; set; }
@@ -59,11 +59,10 @@ namespace Yi.Framework.SqlSugarCore.Uow
ContextInstance.Current = (TDbContext)ServiceProvider.GetRequiredService();
}
var dbContext = (TDbContext)ContextInstance.Current;
- var output = DatabaseChange(dbContext, connectionStringName, connectionString);
//提高体验,取消工作单元强制性
//throw new AbpException("A DbContext can only be created inside a unit of work!");
//如果不启用工作单元,创建一个新的db,不开启事务即可
- return output;
+ return dbContext;
}
@@ -100,64 +99,10 @@ namespace Yi.Framework.SqlSugarCore.Uow
using (SqlSugarDbContextCreationContext.Use(creationContext))
{
var dbContext = await CreateDbContextAsync(unitOfWork);
-
- //获取到DB之后,对多租户多库进行处理
- var changedDbContext = DatabaseChange(dbContext, connectionStringName, connectionString);
- return changedDbContext;
+ return dbContext;
}
}
- protected virtual TDbContext DatabaseChange(TDbContext dbContext, string configId, string connectionString)
- {
- //没有检测到使用多租户功能,默认使用默认库即可
- if (string.IsNullOrWhiteSpace(connectionString))
- {
- connectionString = dbContext.Options.Url;
- configId = DbConnOptions.TenantDbDefaultName;
- }
-
- var dbOption = dbContext.Options;
- var db = dbContext.SqlSugarClient.AsTenant();
- //主库的Db切换,当操作的是租户表的时候
- if (CurrentTenant.Name == MasterTenantDbDefaultName)
- {
- //直接切换
- configId = MasterTenantDbDefaultName;
- var conStrOrNull = dbOption.GetDefaultMasterSaasMultiTenancy();
- Volo.Abp.Check.NotNull(conStrOrNull, "租户主库未找到");
- connectionString = conStrOrNull.Url;
- }
-
- //租户Db的动态切换
- //二级缓存
- var changed = false;
- if (!db.IsAnyConnection(configId))
- {
- var config = _dbConnectionCreator.Build(options =>
- {
- options.DbType = dbOption.DbType!.Value;
- options.ConfigId = configId;//设置库的唯一标识
- options.IsAutoCloseConnection = true;
- options.ConnectionString = connectionString;
- });
- //添加一个db到当前上下文 (Add部分不线上下文不会共享)
- db.AddConnection(config);
- changed = true;
- }
- var currentDb = db.GetConnection(configId) as ISqlSugarClient;
-
- //设置Aop
- if (changed)
- {
- _dbConnectionCreator.SetDbAop(currentDb);
- }
-
-
- dbContext.SetSqlSugarClient(currentDb);
- return dbContext;
- }
-
-
protected virtual async Task CreateDbContextAsync(IUnitOfWork unitOfWork)
{
return unitOfWork.Options.IsTransactional
@@ -182,7 +127,7 @@ namespace Yi.Framework.SqlSugarCore.Uow
);
unitOfWork.AddTransactionApi(transactionApiKey, transaction);
- await dbContext.SqlSugarClient.Ado.BeginTranAsync();
+ // await dbContext.SqlSugarClient.Ado.BeginTranAsync();
return dbContext;
}
else
diff --git a/Yi.Abp.Net8/module/tenant-management/Yi.Framework.TenantManagement.Application/TenantService.cs b/Yi.Abp.Net8/module/tenant-management/Yi.Framework.TenantManagement.Application/TenantService.cs
index f215cdad..87739eac 100644
--- a/Yi.Abp.Net8/module/tenant-management/Yi.Framework.TenantManagement.Application/TenantService.cs
+++ b/Yi.Abp.Net8/module/tenant-management/Yi.Framework.TenantManagement.Application/TenantService.cs
@@ -110,17 +110,18 @@ namespace Yi.Framework.TenantManagement.Application
[HttpPut("tenant/init/{id}")]
public async Task InitAsync([FromRoute]Guid id)
{
- using (CurrentTenant.Change(id))
+ using (CurrentTenant.Change(id,"test"))
{
- CodeFirst(await _repository.GetDbContextAsync());
+ await CodeFirst(this.LazyServiceProvider);
await _dataSeeder.SeedAsync(id);
}
+
}
- private void CodeFirst(ISqlSugarClient db)
+ private async Task CodeFirst(IServiceProvider service)
{
-
- var moduleContainer = ServiceProvider.GetRequiredService();
+ var moduleContainer = service.GetRequiredService();
+ var db = await _repository.GetDbContextAsync();
//尝试创建数据库
db.DbMaintenance.CreateDatabase();
@@ -131,12 +132,11 @@ namespace Yi.Framework.TenantManagement.Application
types.AddRange(module.Assembly.GetTypes()
.Where(x => x.GetCustomAttribute() == null)
.Where(x => x.GetCustomAttribute() != null)
- .Where(x=>x.GetCustomAttribute()==null)
.Where(x => x.GetCustomAttribute() is null));
}
if (types.Count > 0)
{
- db.CodeFirst.InitTables(types.ToArray());
+ db.CopyNew().CodeFirst.InitTables(types.ToArray());
}
}
diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/DefaultConnectionStringResolver2.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/DefaultConnectionStringResolver2.cs
new file mode 100644
index 00000000..a285d577
--- /dev/null
+++ b/Yi.Abp.Net8/src/Yi.Abp.Web/DefaultConnectionStringResolver2.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
+using Volo.Abp.Data;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Abp.MultiTenancy;
+
+[Dependency(ReplaceServices = true)]
+public class MultiTenantConnectionStringResolver2 : DefaultConnectionStringResolver
+{
+ private readonly ICurrentTenant _currentTenant;
+ private readonly IServiceProvider _serviceProvider;
+
+ public MultiTenantConnectionStringResolver2(
+ IOptionsMonitor options,
+ ICurrentTenant currentTenant,
+ IServiceProvider serviceProvider)
+ : base(options)
+ {
+ _currentTenant = currentTenant;
+ _serviceProvider = serviceProvider;
+ }
+
+ public override async Task ResolveAsync(string? connectionStringName = null)
+ {
+ if (_currentTenant.Id == null)
+ {
+ //No current tenant, fallback to default logic
+ return await base.ResolveAsync(connectionStringName);
+ }
+
+ var tenant = await FindTenantConfigurationAsync(_currentTenant.Id.Value);
+
+ if (tenant == null || tenant.ConnectionStrings.IsNullOrEmpty())
+ {
+ //Tenant has not defined any connection string, fallback to default logic
+ return await base.ResolveAsync(connectionStringName);
+ }
+
+ var tenantDefaultConnectionString = tenant.ConnectionStrings?.Default;
+
+ //Requesting default connection string...
+ //if (connectionStringName == null ||
+ // connectionStringName == ConnectionStrings.DefaultConnectionStringName)
+ //{
+ // //Return tenant's default or global default
+ // return !tenantDefaultConnectionString.IsNullOrWhiteSpace()
+ // ? tenantDefaultConnectionString!
+ // : Options.ConnectionStrings.Default!;
+ //}
+
+ //Requesting specific connection string...
+ var connString = tenant.ConnectionStrings?.FirstOrDefault().Value;
+ if (!connString.IsNullOrWhiteSpace())
+ {
+ //Found for the tenant
+ return connString!;
+ }
+
+ //Fallback to the mapped database for the specific connection string
+ var database = Options.Databases.GetMappedDatabaseOrNull(connectionStringName);
+ if (database != null && database.IsUsedByTenants)
+ {
+ connString = tenant.ConnectionStrings?.GetOrDefault(database.DatabaseName);
+ if (!connString.IsNullOrWhiteSpace())
+ {
+ //Found for the tenant
+ return connString!;
+ }
+ }
+
+ //Fallback to tenant's default connection string if available
+ if (!tenantDefaultConnectionString.IsNullOrWhiteSpace())
+ {
+ return tenantDefaultConnectionString!;
+ }
+
+ return await base.ResolveAsync(connectionStringName);
+ }
+
+ [Obsolete("Use ResolveAsync method.")]
+ public override string Resolve(string? connectionStringName = null)
+ {
+ if (_currentTenant.Id == null)
+ {
+ //No current tenant, fallback to default logic
+ return base.Resolve(connectionStringName);
+ }
+
+ var tenant = FindTenantConfiguration(_currentTenant.Id.Value);
+
+ if (tenant == null || tenant.ConnectionStrings.IsNullOrEmpty())
+ {
+ //Tenant has not defined any connection string, fallback to default logic
+ return base.Resolve(connectionStringName);
+ }
+
+ var tenantDefaultConnectionString = tenant.ConnectionStrings?.Default;
+
+ //Requesting default connection string...
+ if (connectionStringName == null ||
+ connectionStringName == ConnectionStrings.DefaultConnectionStringName)
+ {
+ //Return tenant's default or global default
+ return !tenantDefaultConnectionString.IsNullOrWhiteSpace()
+ ? tenantDefaultConnectionString!
+ : Options.ConnectionStrings.Default!;
+ }
+
+ //Requesting specific connection string...
+ var connString = tenant.ConnectionStrings?.GetOrDefault(connectionStringName);
+ if (!connString.IsNullOrWhiteSpace())
+ {
+ //Found for the tenant
+ return connString!;
+ }
+
+ //Fallback to tenant's default connection string if available
+ if (!tenantDefaultConnectionString.IsNullOrWhiteSpace())
+ {
+ return tenantDefaultConnectionString!;
+ }
+
+ //Try to find the specific connection string for given name
+ var connStringInOptions = Options.ConnectionStrings.GetOrDefault(connectionStringName);
+ if (!connStringInOptions.IsNullOrWhiteSpace())
+ {
+ return connStringInOptions!;
+ }
+
+ //Fallback to the global default connection string
+ var defaultConnectionString = Options.ConnectionStrings.Default;
+ if (!defaultConnectionString.IsNullOrWhiteSpace())
+ {
+ return defaultConnectionString!;
+ }
+
+ throw new AbpException("No connection string defined!");
+ }
+
+ protected virtual async Task FindTenantConfigurationAsync(Guid tenantId)
+ {
+ using (var serviceScope = _serviceProvider.CreateScope())
+ {
+ var tenantStore = serviceScope
+ .ServiceProvider
+ .GetRequiredService();
+
+ return await tenantStore.FindAsync(tenantId);
+ }
+ }
+
+ [Obsolete("Use FindTenantConfigurationAsync method.")]
+ protected virtual TenantConfiguration? FindTenantConfiguration(Guid tenantId)
+ {
+ using (var serviceScope = _serviceProvider.CreateScope())
+ {
+ var tenantStore = serviceScope
+ .ServiceProvider
+ .GetRequiredService();
+
+ return tenantStore.Find(tenantId);
+ }
+ }
+}
diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/Program.cs b/Yi.Abp.Net8/src/Yi.Abp.Web/Program.cs
index 0feeab19..f8c9f615 100644
--- a/Yi.Abp.Net8/src/Yi.Abp.Web/Program.cs
+++ b/Yi.Abp.Net8/src/Yi.Abp.Web/Program.cs
@@ -1,5 +1,8 @@
+using Microsoft.Extensions.DependencyInjection.Extensions;
using Serilog;
using Serilog.Events;
+using Volo.Abp.Data;
+using Volo.Abp.MultiTenancy;
using Yi.Abp.Web;
//创建日志,可使用{SourceContext}记录
@@ -23,6 +26,7 @@ try
builder.Host.UseAutofac();
builder.Host.UseSerilog();
await builder.Services.AddApplicationAsync();
+ builder.Services.Replace(new ServiceDescriptor(typeof(IConnectionStringResolver), typeof(MultiTenantConnectionStringResolver2),ServiceLifetime.Transient));
var app = builder.Build();
await app.InitializeApplicationAsync();
await app.RunAsync();
diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db b/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db
index f46d3d4e..6899f4ca 100644
Binary files a/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db and b/Yi.Abp.Net8/src/Yi.Abp.Web/yi-abp-dev.db differ
diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/yi-test.db b/Yi.Abp.Net8/src/Yi.Abp.Web/yi-test.db
new file mode 100644
index 00000000..7d73f400
Binary files /dev/null and b/Yi.Abp.Net8/src/Yi.Abp.Web/yi-test.db differ
diff --git a/Yi.Abp.Net8/src/Yi.Abp.Web/yi-test2.db b/Yi.Abp.Net8/src/Yi.Abp.Web/yi-test2.db
new file mode 100644
index 00000000..c88deb02
Binary files /dev/null and b/Yi.Abp.Net8/src/Yi.Abp.Web/yi-test2.db differ