diff --git a/docs/en/solution-templates/layered-web-application/database-configurations.md b/docs/en/solution-templates/layered-web-application/database-configurations.md new file mode 100644 index 0000000000..c46222be50 --- /dev/null +++ b/docs/en/solution-templates/layered-web-application/database-configurations.md @@ -0,0 +1,242 @@ +# Layered Solution: Database configurations + +```json +//[doc-nav] +{ + "Previous": { + "Name": "Authentication", + "Path": "solution-templates/layered-web-application/authentication" + }, + "Next": { + "Name": "Logging (with Serilog)", + "Path": "solution-templates/layered-web-application/logging" + } +} +``` + +> Some of the features mentioned in this document may not be available in the free version. We're using the **\*** symbol to indicate that a feature is available in the **[Team](https://abp.io/pricing)** and **[Higher](https://abp.io/pricing)** licenses. + +ABP Studio's Layered Solution Template includes pre-configured database settings. This document explains how to manage database configurations in your solution. + +## Connection Strings + +Connection strings are stored in the `appsettings.json` file. You can customize them for different environments by modifying the respective `appsettings.json` files. The `*.DbMigrator` project and one of the [Web Application](web-applications.md) projects use the `Default` connection string by default. + +To change the connection string for the `Default` key, update the `appsettings.json` file in your project. Connection strings are defined under the `ConnectionStrings` section, as shown below: + +```json +{ + "ConnectionStrings": { + "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=Bookstore;Trusted_Connection=True;TrustServerCertificate=true" + } +} +``` + +### The DbContext Class + +In the `*.EntityFrameworkCore` project, the `DbContext` class is defined. The `DbContext` class is derived from the `AbpDbContext` class, which is a part of the ABP Framework. + +```csharp +[ReplaceDbContext(typeof(IIdentityProDbContext))] +[ReplaceDbContext(typeof(ISaasDbContext))] +[ConnectionStringName("Default")] +public class BookstoreDbContext : + AbpDbContext, + ISaasDbContext, + IIdentityProDbContext +{ + #region Entities from the modules + + // Identity + public DbSet Users { get; set; } + public DbSet Roles { get; set; } + public DbSet ClaimTypes { get; set; } + public DbSet OrganizationUnits { get; set; } + public DbSet SecurityLogs { get; set; } + public DbSet LinkUsers { get; set; } + public DbSet UserDelegations { get; set; } + public DbSet Sessions { get; set; } + + // SaaS + public DbSet Tenants { get; set; } + public DbSet Editions { get; set; } + public DbSet TenantConnectionStrings { get; set; } + + #endregion + + public BookstoreDbContext(DbContextOptions options) + : base(options) + { + + } + + protected override void OnModelCreating(ModelBuilder builder) + { + base.OnModelCreating(builder); + + builder.ConfigurePermissionManagement(); + builder.ConfigureSettingManagement(); + builder.ConfigureBackgroundJobs(); + builder.ConfigureAuditLogging(); + builder.ConfigureFeatureManagement(); + builder.ConfigureIdentityPro(); + builder.ConfigureOpenIddictPro(); + builder.ConfigureLanguageManagement(); + builder.ConfigureSaas(); + builder.ConfigureTextTemplateManagement(); + builder.ConfigureGdpr(); + builder.ConfigureCmsKit(); + builder.ConfigureCmsKitPro(); + builder.ConfigureBlobStoring(); + + /* Configure your own tables/entities inside here */ + + //builder.Entity(b => + //{ + // b.ToTable(BookstoreConsts.DbTablePrefix + "YourEntities", BookstoreConsts.DbSchema); + // b.ConfigureByConvention(); //auto configure for the base class props + // //... + //}); + } +} +``` + +#### ConnectionStringName Attribute + +We're using the *Default* connection string in the `BookstoreDbContext` class. You can change the connection string name by updating the `ConnectionStringName` attribute. + +```csharp +[ConnectionStringName("Default")] +``` + +[The `ConnectionStringName` attribute](../../framework/fundamentals/connection-strings.md#set-the-connection-string-name) defines the unique name of the connection string that is being used by that `DbContext` class. It matches with the connection string defined in the `appsettings.json` file. That name is also used in database migrations to distinguish different database schemas, and used as the key while storing tenant connection strings for a multi-tenant system. + +#### ReplaceDbContext Attribute + +```csharp +[ReplaceDbContext(typeof(IIdentityProDbContext))] +[ReplaceDbContext(typeof(ISaasDbContext))] +``` + +The application DbContext utilizes the [Identity](../../modules/identity.md) and [Saas **\***](../../modules/saas.md) modules and creates a single database that contains these modules database schemas. These modules define their own `DbContext` class normally. But [the `ReplaceDbContext` attribute](../../framework/data/entity-framework-core/index.md#replace-other-dbcontextes) tells to ABP to use this (`BookstoreDbContext`) `DbContext` class instead of the `DbContext` classes defined by these modules. Technically, it replaces the given `DbContext` classes on runtime. We are doing that to ensure that we have a single (merged) database schema, single database migration path and a single database transaction operation when we work these multiple modules. When we replace a `DbContext`, we should implement its interface as done with the `BookstoreDbContext` class: + +````csharp +public class BookstoreDbContext : + AbpDbContext, + ISaasDbContext, + IIdentityProDbContext +```` + +* That class implements `ISaasDbContext` and `IIdentityProDbContext`, so these modules can use it. + +As the next part, the `BookstoreDbContext` class defines the following properties those are forced by the implemented interfaces: + +```csharp +// Identity +public DbSet Users { get; set; } +public DbSet Roles { get; set; } +public DbSet ClaimTypes { get; set; } +public DbSet OrganizationUnits { get; set; } +public DbSet SecurityLogs { get; set; } +public DbSet LinkUsers { get; set; } +public DbSet UserDelegations { get; set; } +public DbSet Sessions { get; set; } + +// SaaS +public DbSet Tenants { get; set; } +public DbSet Editions { get; set; } +public DbSet TenantConnectionStrings { get; set; } +``` + +#### OnModelCreating Method + +The `OnModelCreating` method is used to configure the database schema. It calls the `Configure*` methods of the ABP Framework to configure the database schema for the modules. You can also configure your own tables/entities inside this method. + +```csharp +protected override void OnModelCreating(ModelBuilder builder) +{ + base.OnModelCreating(builder); + + builder.ConfigurePermissionManagement(); + builder.ConfigureSettingManagement(); + builder.ConfigureBackgroundJobs(); + builder.ConfigureAuditLogging(); + builder.ConfigureFeatureManagement(); + builder.ConfigureIdentityPro(); + builder.ConfigureOpenIddictPro(); + builder.ConfigureLanguageManagement(); + builder.ConfigureSaas(); + builder.ConfigureTextTemplateManagement(); + builder.ConfigureGdpr(); + builder.ConfigureCmsKit(); + builder.ConfigureCmsKitPro(); + builder.ConfigureBlobStoring(); + + /* Configure your own tables/entities inside here */ + + //builder.Entity(b => + //{ + // b.ToTable(BookstoreConsts.DbTablePrefix + "YourEntities", BookstoreConsts.DbSchema); + // b.ConfigureByConvention(); //auto configure for the base class props + // //... + //}); +} +``` + +> The `Configure*` methods are extension methods defined in each module's `EntityFrameworkCore` project. These methods are used to configure the database schema for their respective modules. At runtime, the `DbContext` class is replaced by the `BookstoreDbContext` class only for the `DbContext` classes that use the `ReplaceDbContext` attribute. For other modules, their own dedicated `DbContext` classes are used without replacement. + +### The `IDesignTimeDbContextFactory` Implementation + +The `IDesignTimeDbContextFactory` interface is used to create a `DbContext` instance at design time. It is used by EF Core tools to create migrations and update the database. The `BookstoreDbContextFactory` class implements the `IDesignTimeDbContextFactory` interface to create a `BookstoreMigrationsDbContext` instance. + +```csharp +public class BookstoreDbContextFactory : IDesignTimeDbContextFactory +{ + public BookstoreDbContext CreateDbContext(string[] args) + { + var configuration = BuildConfiguration(); + + BookstoreEfCoreEntityExtensionMappings.Configure(); + + var builder = new DbContextOptionsBuilder() + .UseSqlServer(configuration.GetConnectionString("Default")); + + return new BookstoreDbContext(builder.Options); + } + + private static IConfigurationRoot BuildConfiguration() + { + var builder = new ConfigurationBuilder() + .SetBasePath(Path.Combine(Directory.GetCurrentDirectory(), "../Acme.Bookstore.DbMigrator/")) + .AddJsonFile("appsettings.json", optional: false); + + return builder.Build(); + } +} +``` + +### Configuration + +In the `*.EntityFrameworkCore` project, the `BookstoreEntityFrameworkCoreModule` class is used to configure the database context. + +```csharp +public override void ConfigureServices(ServiceConfigurationContext context) +{ + context.Services.AddAbpDbContext(options => + { + /* Remove "includeAllEntities: true" to create + * default repositories only for aggregate roots */ + options.AddDefaultRepositories(includeAllEntities: true); + }); + + Configure(options => + { + /* The main point to change your DBMS. + * See also BookstoreDbContextFactory for EF Core tooling. */ + options.UseSqlServer(); + }); + +} +``` + +We are basically setting the SQL Server as the default DBMS for this application. and registering the `BookstoreDbContext` class to the [dependency injection](../../framework/fundamentals/dependency-injection.md) system. \ No newline at end of file