Open Closed

The tenant connection string 'Default' is not valid #9507


User avatar
0
MichelZ created
  • Template: app
  • Created ABP Studio Version: 0.8.1
  • Current ABP Studio Version: 1.0.1
  • Tiered: Yes
  • UI Framework: angular
  • Theme: leptonx (lepton actually?)
  • Theme Style: system
  • Progressive Web App: No
  • Database Provider: ef
  • Database Management System: sqlserver
  • Separate Tenant Schema: Yes
  • Mobile Framework: none
  • Public Website: No
  • Optional Modules:
    • GDPR
    • TextTemplateManagement
    • LanguageManagement
    • AuditLogging
    • SaaS
    • OpenIddictAdmin

We currently got an error when trying to create a Tenant:

EXCEPTION: Volo.Abp.UserFriendlyException: The tenant connection string 'Default' is not valid at Volo.Saas.Host.TenantAppService.CheckConnectionStringAsync(Tenant tenant) at Volo.Saas.Host.TenantAppService.<>c__DisplayClass27_0.ETBg5kcYQAHiA0HJrJL.MoveNext() --- End of stack trace from previous location --- at Volo.Saas.Host.TenantAppService.CreateAsync(SaasTenantCreateDto input)

We think this started happening after upgrading to 9.2.0. We have changed ABP to store connection strings encrypted. For this, we assign an encrypted connection string to SaasTenantCreateDto.ConnectionString.Default. This used to work fine, but now results in the error message above.

We have found a method to override on the ITenantAppService which looked promising:

  public override Task<bool> CheckConnectionStringAsync(string connectionString)
  {
      var decrypted = _stringEncryptionService.Decrypt(connectionString);
      return base.CheckConnectionStringAsync(decrypted);
  }

This however did not lead to success.

Any idea how we can fix this?

For further reference, this is how our overridden CreateAsync looks like:

  public override async Task<SaasTenantDto> CreateAsync(SaasTenantCreateDto input)
  {
      var servers = await _availableSqlServerAppService.GetListAsync(new GetAvailableSqlServersInput {AvailableForTenantDatabase = true});
      var first = servers.Items.OrderBy(i => i.NrOfTenants).First();

      var defaultConnectionString = new SqlConnectionStringBuilder(DbConnectionOptions.ConnectionStrings.Default);
      var baseDBName = string.Join('.', defaultConnectionString.InitialCatalog.Split(".").SkipLast(1));

      var connectionString = new SqlConnectionStringBuilder {InitialCatalog = $"{baseDBName}.Tenant.{input.Name}", DataSource = first.SqlServerName, UserID = first.LoginUser, Password = _stringEncryptionService.Decrypt(first.EncryptedPassword), TrustServerCertificate = true};
      input.ConnectionStrings.Default = _stringEncryptionService.Encrypt(connectionString.ConnectionString);

      await CreateAzureDatabaseIfNotExists(connectionString);
      return await base.CreateAsync(input);
  }

Thx


5 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • In ABP v9.2.0, the SaaS module validates the tenant connection string before saving it. If you are storing the connection string encrypted, the framework will attempt to validate the encrypted value, which will fail because it is not a valid SQL Server connection string in plain text.

    • The correct approach is to ensure that the connection string is decrypted before validation. Overriding CheckConnectionStringAsync is the right idea, but you must also ensure that the decrypted connection string is passed to the base method and to the framework's validation logic.

    • In your CreateAsync override, you are encrypting the connection string before assigning it to input.ConnectionStrings.Default. However, the SaaS module expects the connection string in plain text for validation, and only after validation should it be encrypted for storage.

    Solution:

    1. Assign the plain (decrypted) connection string to input.ConnectionStrings.Default before calling base.CreateAsync(input).
    2. If you want to store the connection string encrypted, you need to customize the storage logic, not the input to the SaaS module's validation.

    Example:

    public override async Task<SaasTenantDto> CreateAsync(SaasTenantCreateDto input)
    {
        var servers = await _availableSqlServerAppService.GetListAsync(new GetAvailableSqlServersInput {AvailableForTenantDatabase = true});
        var first = servers.Items.OrderBy(i => i.NrOfTenants).First();
    
        var defaultConnectionString = new SqlConnectionStringBuilder(DbConnectionOptions.ConnectionStrings.Default);
        var baseDBName = string.Join('.', defaultConnectionString.InitialCatalog.Split(".").SkipLast(1));
    
        var connectionString = new SqlConnectionStringBuilder
        {
            InitialCatalog = $"{baseDBName}.Tenant.{input.Name}",
            DataSource = first.SqlServerName,
            UserID = first.LoginUser,
            Password = _stringEncryptionService.Decrypt(first.EncryptedPassword),
            TrustServerCertificate = true
        };
    
        await CreateAzureDatabaseIfNotExists(connectionString);
    
        // Assign the plain connection string for validation
        input.ConnectionStrings.Default = connectionString.ConnectionString;
    
        var result = await base.CreateAsync(input);
    
        // Optionally, encrypt the connection string after creation for storage
        // (Requires customizing the storage logic, not just the input)
    
        return result;
    }
    
    • If you must store the connection string encrypted, consider customizing the repository or the storage layer, not the DTO passed to the SaaS module.

    References:


    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

  • User Avatar
    0
    MichelZ created

    While I do get what you're telling me bot, I do think that overriding the actual Check logic should work in this case... So please let a human take this one :)

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    EXCEPTION: Volo.Abp.UserFriendlyException: The tenant connection string 'Default' is not valid at Volo.Saas.Host.TenantAppService.CheckConnectionStringAsync(Tenant tenant)

    Can you override the CheckConnectionStringAsync(Tenant tenant) method of TenantAppService?

    protected virtual async Task CheckConnectionStringAsync(Tenant tenant)
    {
        if (!tenant.ConnectionStrings.IsNullOrEmpty())
        {
            foreach (var connectionString in tenant.ConnectionStrings.Where(connectionString => !connectionString.Value.IsNullOrWhiteSpace()))
            {
                if (!(await ConnectionStringChecker.CheckAsync(connectionString.Value!)).Connected)
                {
                    throw new UserFriendlyException(L["TenantHasAnInvalidString", connectionString.Name]);
                }
            }
        }
    }
    
  • User Avatar
    0
    MichelZ created

    Thanks, that nudged me in the right direction. I've opted to replace the IConnectionStringChecker instead, seems to work

        [Dependency(ReplaceServices = true)]
        [ExposeServices(typeof(IConnectionStringChecker), typeof(EncryptedConnectionStringChecker))]
        public class EncryptedConnectionStringChecker : SqlServerConnectionStringChecker, IConnectionStringChecker, ITransientDependency
        {
            private readonly IStringEncryptionService _stringEncryptionService;
    
            public EncryptedConnectionStringChecker(IStringEncryptionService stringEncryptionService)
            {
                _stringEncryptionService = stringEncryptionService ?? throw new ArgumentNullException(nameof(stringEncryptionService));
            }
    
            public override async Task<AbpConnectionStringCheckResult> CheckAsync(string connectionString)
            {
                if (string.IsNullOrWhiteSpace(connectionString))
                {
                    throw new ArgumentException("Connection string cannot be null or empty.", nameof(connectionString));
                }
                try
                {
                    var decryptedConnectionString = _stringEncryptionService.Decrypt(connectionString);
                    return await base.CheckAsync(decryptedConnectionString);
                }
                catch (Exception)
                {
                    return new AbpConnectionStringCheckResult { Connected = false, DatabaseExists = false };
                }
            }
        }
    
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Great

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.0.0-preview. Updated on July 11, 2025, 11:35