Open Closed

Identity related tables are not being generated in tenant database #8040


User avatar
0
JimmyLiew created
  • 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)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share your project via https://wetransfer.com/?

    liming.ma@volosoft.com

  • User Avatar
    0
    JimmyLiew created

    Hi, Already sent.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    ok, I will check it asap.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    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);
            }
        }
    }
    
    
  • User Avatar
    0
    JimmyLiew created

    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.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    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,
      },
    

  • User Avatar
    0
    JimmyLiew created

    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
    

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you use the code below to test your connection string and see the exception details?

    https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.EntityFrameworkCore.SqlServer/Volo/Abp/EntityFrameworkCore/ConnectionStrings/SqlServerConnectionStringChecker.cs#L23-L38

  • User Avatar
    0
    JimmyLiew created

    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  
    

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    This should be related to your local docker configuration and is an environmental issue.

  • User Avatar
    0
    JimmyLiew created

    Hi,

    Appreciated your help. Thanks.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    : )

Made with ❤️ on ABP v9.1.0-preview. Updated on October 22, 2024, 09:35