- ABP Framework version: v8.3.0
- UI Type: Angular
- Database System: EF Core (SQL Server)
- Tiered (for MVC) or Auth Server Separated (for Angular): yes
- Exception message and full stack trace:
- Steps to reproduce the issue: Create a microservice startup template using ABP studio and create a new tenant with module specified connection string(please refer screenshot below). Click on Run > Build & Start All to run all the solutions using ABP studio.
Hi, I have encountered some problems when trying login to the tenant that created. I have consolidated some of the problems at below.
Notice that, some identity related tables are not being generated in tenant database.(E.g AbpUser table). I have tried manually apply the migration by clicking "Apply database migrations" button on the UI, but the missing tables still not being generated.
Notice that, login with this tenant option is missing in the Host UI.
Encountered "Invalid object name "AbpLanguageTexts" error when login to new created tenant . I have checked the **AbpLanguageTexts **table is existed in tenant administrator database.
Your advice would be greatly appreciated. Thank You.
37 Answer(s)
-
0
hi
Can you share your project via https://wetransfer.com/?
liming.ma@volosoft.com
-
0
Hi, Already sent.
-
0
hi
ok, I will check it asap.
-
0
hi
Add this
MyMultiTenantConnectionStringResolver
class to your project. We will fix this problem. Sorry for that.using Microsoft.Extensions.Options; using Volo.Abp; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.MultiTenancy; namespace AbpMultiTenant.AuthServer; [Dependency(ReplaceServices = true)] public class MyMultiTenantConnectionStringResolver : DefaultConnectionStringResolver { private readonly ICurrentTenant _currentTenant; private readonly IServiceProvider _serviceProvider; public MyMultiTenantConnectionStringResolver( IOptionsMonitor<AbpDbConnectionOptions> options, ICurrentTenant currentTenant, IServiceProvider serviceProvider) : base(options) { _currentTenant = currentTenant; _serviceProvider = serviceProvider; } public override async Task<string> 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?.GetOrDefault(connectionStringName); 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 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 base.Resolve(connectionStringName); } protected virtual async Task<TenantConfiguration?> FindTenantConfigurationAsync(Guid tenantId) { using (var serviceScope = _serviceProvider.CreateScope()) { var tenantStore = serviceScope .ServiceProvider .GetRequiredService<ITenantStore>(); 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<ITenantStore>(); return tenantStore.Find(tenantId); } } }
-
0
Hi, Thanks your prompt response. Invalid object name "AbpLanguageTexts" problem already solved. However still encountered "invalid Connection String" issue. And also found that, Login to this tenant option is missing. As i remember this option was there in previous version. May need your help to advise.
Thanks.
-
0
hi
invalid Connection String
It works on my machine. What is the exception when you test connection string?
https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/ConnectionStrings/SqlServerConnectionStringChecker.cs#L35
Login to this tenant option is missing
Try to add to your
oAuthConfig
impersonation: { userImpersonation: true, tenantImpersonation: true, },
-
0
Hi, I tested connection string below.
Server=localhost,1434; User Id=sa; Password=myPassw@rd; Database=Tenant-F; TrustServerCertificate=true
The check connection string API was returning false. I could not found any exception shown in saas log.file. Attached the log info for your reference.
2024-10-11 16:56:58.605 +08:00 [INF] Request starting HTTP/1.1 GET http://localhost:44355/api/saas/demo/check?connectionstring=Server%3Dlocalhost%2C1434%3B%20User%20Id%3Dsa%3B%20Password%3DmyPassw%40rd%3B%20Database%3DTenant-F%3B%20TrustServerCertificate%3Dtrue - null null 2024-10-11 16:56:58.609 +08:00 [INF] Executing endpoint 'AbpMultiTenant.SaasService.Controllers.DemoController.CheckConnectionString (AbpMultiTenant.SaasService)' 2024-10-11 16:56:58.609 +08:00 [INF] Route matched with {action = "CheckConnectionString", controller = "Demo", area = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[System.String] CheckConnectionString(System.String) on controller AbpMultiTenant.SaasService.Controllers.DemoController (AbpMultiTenant.SaasService). 2024-10-11 16:56:59.633 +08:00 [INF] Executing ObjectResult, writing value of type 'System.String'. 2024-10-11 16:56:59.634 +08:00 [INF] Executed action AbpMultiTenant.SaasService.Controllers.DemoController.CheckConnectionString (AbpMultiTenant.SaasService) in 1024.905ms 2024-10-11 16:56:59.634 +08:00 [INF] Executed endpoint 'AbpMultiTenant.SaasService.Controllers.DemoController.CheckConnectionString (AbpMultiTenant.SaasService)' 2024-10-11 16:56:59.635 +08:00 [INF] Request finished HTTP/1.1 GET http://localhost:44355/api/saas/demo/check?connectionstring=Server%3Dlocalhost%2C1434%3B%20User%20Id%3Dsa%3B%20Password%3DmyPassw%40rd%3B%20Database%3DTenant-F%3B%20TrustServerCertificate%3Dtrue - 200 null text/plain; charset=utf-8 1029.7245ms 2024-10-11 16:57:00.683 +08:00 [INF] Request starting HTTP/1.1 GET http://host.docker.internal:44355/metrics - null null 2024-10-11 16:57:00.685 +08:00 [INF] Executing endpoint 'Prometheus metrics' 2024-10-11 16:57:00.690 +08:00 [INF] Executed endpoint 'Prometheus metrics' 2024-10-11 16:57:00.691 +08:00 [INF] Request finished HTTP/1.1 GET http://host.docker.internal:44355/metrics - 200 null application/openmetrics-text; version=1.0.0; charset=utf-8 8.1178ms
-
0
-
0
Hi, below the details of the exception. However, I found that after i have replaced the localhost to 127.0.0.1 in connection string then it's working. Is there any SQL configuration i have missed out in order to make this localhost works?
Server=127.0.0.1,1434; User Id=sa; Password=myPassw@rd; Database=Tenant-F; TrustServerCertificate=true
"Microsoft.Data.SqlClient.SqlException (0x80131904): Connection Timeout Expired. The timeout period elapsed while attempting to consume the pre-login handshake acknowledgement. This could be because the pre-login handshake failed or the server was unable to respond back in time. The duration spent while attempting to connect to this server was - [Pre-Login] initialization=1044; handshake=0; \r\n ---> System.ComponentModel.Win32Exception (258): The wait operation timed out.\r\n at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)\r\n at Microsoft.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)\r\n at Microsoft.Data.SqlClient.TdsParserStateObject.ThrowExceptionAndWarning(Boolean callerHasConnectionLock, Boolean asyncClose)\r\n at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)\r\n at Microsoft.Data.SqlClient.TdsParserStateObject.ReadSniSyncOverAsync()\r\n at Microsoft.Data.SqlClient.TdsParserStateObject.TryReadNetworkPacket()\r\n at Microsoft.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(SqlConnectionEncryptOption encrypt, Boolean trustServerCert, Boolean integratedSecurity, Boolean& marsCapable, Boolean& fedAuthRequired, Boolean tlsFirst, String serverCert)\r\n at Microsoft.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, SqlConnectionString connectionOptions, Boolean withFailover)\r\n at Microsoft.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, Boolean withFailover)\r\n at Microsoft.Data.SqlClient.SqlInternalConnectionTds.LoginNoFailover(ServerInfo serverInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString connectionOptions, SqlCredential credential, TimeoutTimer timeout)\r\n at Microsoft.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(TimeoutTimer timeout, SqlConnectionString connectionOptions, SqlCredential credential, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance)\r\n at Microsoft.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, SqlCredential credential, Object providerInfo, String newPassword, SecureString newSecurePassword, Boolean redirectedUserInstance, SqlConnectionString userConnectionOptions, SessionData reconnectSessionData, Boolean applyTransientFaultHandling, String accessToken, DbConnectionPool pool)\r\n at Microsoft.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, DbConnectionPoolKey poolKey, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection, DbConnectionOptions userOptions)\r\n at Microsoft.Data.ProviderBase.DbConnectionFactory.CreatePooledConnection(DbConnectionPool pool, DbConnection owningObject, DbConnectionOptions options, DbConnectionPoolKey poolKey, DbConnectionOptions userOptions)\r\n at Microsoft.Data.ProviderBase.DbConnectionPool.CreateObject(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)\r\n at Microsoft.Data.ProviderBase.DbConnectionPool.UserCreateRequest(DbConnection owningObject, DbConnectionOptions userOptions, DbConnectionInternal oldConnection)\r\n at Microsoft.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)\r\n at Microsoft.Data.ProviderBase.DbConnectionPool.WaitForPendingOpen()\r\n--- End of stack trace from previous location ---\r\n
-
0
This should be related to your local docker configuration and is an environmental issue.
-
0
Hi,
Appreciated your help. Thanks.
-
0
: )