Connection Strings

Connection string system is especially needed when you want to create or use a modular system. If you have a monolithic application with a single database, you can go with the ABP startup solution template, which is properly configured for you.

ABP Framework is designed to be modular and multi-tenancy aware. Connection string management is also designed to support these scenarios;

  • Allows to set separate connection strings for every module, so every module can have its own physical database. Modules even might be configured to use different database providers.
  • Allows to set separate connection string and use a separate database per tenant (in a SaaS application).

It also supports hybrid scenarios;

  • Allows to group modules into databases (e.g., all modules into a single shared database or two modules to database A, three modules to database B, one module to database C and rest of the modules to database D)
  • Allows to group tenants into databases, just like the modules.
  • Allows to separate databases per tenant per module (which might be hard to maintain for you because of too many databases, but the ABP framework supports it).

All the pre-built application modules are designed to be compatible these scenarios.

Configure the Connection Strings

See the following configuration:

"ConnectionStrings": {
  "Default": "Server=localhost;Database=MyMainDb;Trusted_Connection=True;",
  "AbpIdentityServer": "Server=localhost;Database=MyIdsDb;Trusted_Connection=True;",
  "AbpPermissionManagement": "Server=localhost;Database=MyPermissionDb;Trusted_Connection=True;"
}

ABP uses the IConfiguration service to get the application configuration. While the simplest way to write configuration into the appsettings.json file, it is not limited to this file. You can use environment variables, user secrets, Azure Key Vault... etc. See the configuration document for more.

This configuration defines three different connection strings:

  • MyMainDb (the Default connection string) is the main connection string of the application. If you don't specify a connection string for a module, it fallbacks to the Default connection string. The application startup template is configured to use a single connection string, so all the modules uses a single, shared database.
  • MyIdsDb (the AbpIdentityServer connection string) is used by the IdentityServer module.
  • MyPermissionDb (the AbpPermissionManagement connection string) is used by the Permission Management module.

Pre-built application modules define constants for the connection string names. For example, the IdentityServer module defines a ConnectionStringName constant in the AbpIdentityServerDbProperties class (located in the Volo.Abp.IdentityServer namespace). Other modules similarly define constants, so you can investigate the connection string name.

AbpDbConnectionOptions

AbpDbConnectionOptions is the options class that is used to set the connection strings and configure database structures.

Setting the connection strings

ABP uses the AbpDbConnectionOptions to get the connection strings. If you configure the connection strings as explained above, AbpDbConnectionOptions is automatically filled. However, you can set or override the connection strings using the options pattern. You can configure the AbpDbConnectionOptions in the ConfigureServices method of your module as shown below:

public override void ConfigureServices(ServiceConfigurationContext context)
{
    Configure<AbpDbConnectionOptions>(options =>
    {
        options.ConnectionStrings.Default = "...";
        options.ConnectionStrings["AbpPermissionManagement"] = "...";
    });
}

Configuring the database structures

Databases property of the AbpDbConnectionOptions class is used to group multiple connection strings (of multiple modules) to a single connection string.

See the following connection strings:

"ConnectionStrings": {
  "Default": "Server=localhost;Database=MyMainDb;Trusted_Connection=True;",
  "AbpIdentity": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;",
  "AbpIdentityServer": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;",
  "AbpPermissionManagement": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;"
}

In this example, we've defined four connection strings, but the last three of them are the same; AbpIdentity, AbpIdentityServer and AbpPermissionManagement uses the same database, named MySecondaryDb. The main application and the other modules use the Default connection string, hence the MyMainDb database.

What we want to do here is to group three modules (AbpIdentity, AbpIdentityServer and AbpPermissionManagement) in a single database, but we needed to specify each one manually. Because the fallback connection string is the Default one, if we don't specify it for a module.

To eliminate the repetitive connection string definition, we can configure the AbpDbConnectionOptions.Databases property to group these connection strings, as shown in the following code (we place that in the ConfigureServices method of our module class):

Configure<AbpDbConnectionOptions>(options =>
{
    options.Databases.Configure("MySecondaryDb", db =>
    {
        db.MappedConnections.Add("AbpIdentity");
        db.MappedConnections.Add("AbpIdentityServer");
        db.MappedConnections.Add("AbpPermissionManagement");
    });
});

Then we can change the appsettings.json file as shown in the following code block:

"ConnectionStrings": {
  "Default": "Server=localhost;Database=MyMainDb;Trusted_Connection=True;",
  "MySecondaryDb": "Server=localhost;Database=MySecondaryDb;Trusted_Connection=True;"
}

MySecondaryDb becomes the new connection string for the mapped connections.

ABP first looks for the module-specific connection string, then looks if a database mapping is available, finally fallbacks to the Default connection string.

Set the Connection String Name

A module typically has a unique connection string name associated to its DbContext class using the ConnectionStringName attribute. Example:

[ConnectionStringName("AbpIdentityServer")]
public class IdentityServerDbContext
    : AbpDbContext<IdentityServerDbContext>, IIdentityServerDbContext
{
}

For Entity Framework Core and MongoDB, write this to your DbContext class (and the interface if it has). In this way, ABP uses the specified connection string for the related DbContext instances.

Database Migrations for the Entity Framework Core

Relational databases require to create the database and the database schema (tables, views... etc.) before using it.

The startup template (with EF Core ORM) comes with a single database and a .EntityFrameworkCore project that contains related classes and the migration files for that database. This project mainly defines a YourProjectNameDbContext class that calls the Configure...() methods of the used modules, like builder.ConfigurePermissionManagement().

Once you want to separate a module's database, you typically will need to create a second migration path. See the EF Core Migrations document to learn how to create and use a different database for a desired module.

Multi-Tenancy

See the multi-tenancy document to learn how to use separate databases for tenants.

Replace the Connection String Resolver

ABP defines the IConnectionStringResolver and uses it whenever it needs a connection string. It has two pre-built implementations:

  • DefaultConnectionStringResolver uses the AbpDbConnectionOptions to select the connection string based on the rules defined in the "Configure the Connection Strings" section above.
  • MultiTenantConnectionStringResolver used for multi-tenant applications and tries to get the configured connection string for the current tenant if available. It uses the ITenantStore to find the connection strings. It inherits from the DefaultConnectionStringResolver and fallbacks to the base logic if no connection string specified for the current tenant.

If you need a custom logic to determine the connection string, implement the IConnectionStringResolver interface (optionally derive from the existing implementations) and replace the existing implementation using the dependency injection system.

In this document