项目

分层解决方案:数据库配置

本文档中提到的某些功能可能在免费版本中不可用。我们使用 * 符号来表示该功能在 团队版更高级版本 中可用。

ABP Studio的分层解决方案模板包含了预配置的数据库设置。本文档解释了如何在您的解决方案中管理数据库配置。

连接字符串

连接字符串存储在 appsettings.json 文件中。您可以通过修改相应的 appsettings.json 文件来为不同的环境自定义它们。默认情况下,*.DbMigrator 项目和一个Web应用程序项目使用 Default 连接字符串。

要更改 Default 键的连接字符串,请更新项目中的 appsettings.json 文件。连接字符串在 ConnectionStrings 部分下定义,如下所示:

{
  "ConnectionStrings": {
    "Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=Bookstore;Trusted_Connection=True;TrustServerCertificate=true"
  }
}

DbContext 类

*.EntityFrameworkCore 项目中,定义了 DbContext 类。该 DbContext 类派生自 AbpDbContext 类,后者是 ABP 框架的一部分。

[ReplaceDbContext(typeof(IIdentityProDbContext))]
[ReplaceDbContext(typeof(ISaasDbContext))]
[ConnectionStringName("Default")]
public class BookstoreDbContext :
    AbpDbContext<BookstoreDbContext>,
    ISaasDbContext,
    IIdentityProDbContext
{
    #region 来自模块的实体

    // 身份认证
    public DbSet<IdentityUser> Users { get; set; }
    public DbSet<IdentityRole> Roles { get; set; }
    public DbSet<IdentityClaimType> ClaimTypes { get; set; }
    public DbSet<OrganizationUnit> OrganizationUnits { get; set; }
    public DbSet<IdentitySecurityLog> SecurityLogs { get; set; }
    public DbSet<IdentityLinkUser> LinkUsers { get; set; }
    public DbSet<IdentityUserDelegation> UserDelegations { get; set; }
    public DbSet<IdentitySession> Sessions { get; set; }

    // SaaS(软件即服务)
    public DbSet<Tenant> Tenants { get; set; }
    public DbSet<Edition> Editions { get; set; }
    public DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }

    #endregion

    public BookstoreDbContext(DbContextOptions<BookstoreDbContext> 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();
        
        /* 在此处配置您自己的表/实体 */

        //builder.Entity<YourEntity>(b =>
        //{
        //    b.ToTable(BookstoreConsts.DbTablePrefix + "YourEntities", BookstoreConsts.DbSchema);
        //    b.ConfigureByConvention(); // 自动配置基类属性
        //    //...
        //});
    }
}

ConnectionStringName 特性

我们在 BookstoreDbContext 类中使用的是 Default 连接字符串。您可以通过更新 ConnectionStringName 特性来更改连接字符串的名称。

[ConnectionStringName("Default")]

ConnectionStringName 特性定义了该 DbContext 类正在使用的连接字符串的唯一名称。它与 appsettings.json 文件中定义的连接字符串相匹配。该名称还在数据库迁移中用于区分不同的数据库模式,并在为多租户系统存储租户连接字符串时用作键。

ReplaceDbContext 特性

[ReplaceDbContext(typeof(IIdentityProDbContext))]
[ReplaceDbContext(typeof(ISaasDbContext))]

应用程序的 DbContext 利用了 身份认证SaaS * 模块,并创建了一个包含这些模块数据库模式的单一数据库。这些模块通常定义自己的 DbContext 类。但是 ReplaceDbContext 特性 告诉 ABP 使用这个 (BookstoreDbContext) DbContext 类,而不是这些模块定义的 DbContext 类。从技术上讲,它会在运行时替换给定的 DbContext 类。我们这样做是为了确保在使用这些多个模块时,我们拥有一个单一的(合并的)数据库模式、单一的数据库迁移路径和单一的事务操作。当我们替换一个 DbContext 时,我们应该实现它的接口,就像 BookstoreDbContext 类所做的那样:

public class BookstoreDbContext :
    AbpDbContext<BookstoreDbContext>,
    ISaasDbContext,
    IIdentityProDbContext
  • 该类实现了 ISaasDbContextIIdentityProDbContext,因此这些模块可以使用它。

接下来,BookstoreDbContext 类定义了以下由所实现接口强制的属性:

// 身份认证
public DbSet<IdentityUser> Users { get; set; }
public DbSet<IdentityRole> Roles { get; set; }
public DbSet<IdentityClaimType> ClaimTypes { get; set; }
public DbSet<OrganizationUnit> OrganizationUnits { get; set; }
public DbSet<IdentitySecurityLog> SecurityLogs { get; set; }
public DbSet<IdentityLinkUser> LinkUsers { get; set; }
public DbSet<IdentityUserDelegation> UserDelegations { get; set; }
public DbSet<IdentitySession> Sessions { get; set; }

// SaaS
public DbSet<Tenant> Tenants { get; set; }
public DbSet<Edition> Editions { get; set; }
public DbSet<TenantConnectionString> TenantConnectionStrings { get; set; }

OnModelCreating 方法

OnModelCreating 方法用于配置数据库模式。它调用 ABP 框架的 Configure* 方法来为模块配置数据库模式。您也可以在此方法中配置自己的表/实体。

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();
    
    /* 在此处配置您自己的表/实体 */

    //builder.Entity<YourEntity>(b =>
    //{
    //    b.ToTable(BookstoreConsts.DbTablePrefix + "YourEntities", BookstoreConsts.DbSchema);
    //    b.ConfigureByConvention(); // 自动配置基类属性
    //    //...
    //});
}

Configure* 方法是在每个模块的 EntityFrameworkCore 项目中定义的扩展方法。这些方法用于为其各自的模块配置数据库模式。在运行时,DbContext 类仅针对使用 ReplaceDbContext 特性的 DbContext 类被 BookstoreDbContext 类替换。对于其他模块,它们使用自己专用的 DbContext 类而不进行替换。

IDesignTimeDbContextFactory 的实现

IDesignTimeDbContextFactory 接口用于在设计时创建 DbContext 实例。EF Core 工具使用它来创建迁移和更新数据库。BookstoreDbContextFactory 类实现了 IDesignTimeDbContextFactory 接口,以创建 BookstoreMigrationsDbContext 实例。

public class BookstoreDbContextFactory : IDesignTimeDbContextFactory<BookstoreDbContext>
{
    public BookstoreDbContext CreateDbContext(string[] args)
    {
        var configuration = BuildConfiguration();
        
        BookstoreEfCoreEntityExtensionMappings.Configure();

        var builder = new DbContextOptionsBuilder<BookstoreDbContext>()
            .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();
    }
}

配置

*.EntityFrameworkCore 项目中,BookstoreEntityFrameworkCoreModule 类用于配置数据库上下文。

public override void ConfigureServices(ServiceConfigurationContext context)
{
    context.Services.AddAbpDbContext<BookstoreDbContext>(options =>
    {
            /* 移除 "includeAllEntities: true" 以仅为聚合根创建
              * 默认存储库 */
        options.AddDefaultRepositories(includeAllEntities: true);
    });

    Configure<AbpDbContextOptions>(options =>
    {
            /* 更改 DBMS 的主要位置。
              * 另请参阅用于 EF Core 工具的 BookstoreDbContextFactory。 */
        options.UseSqlServer();
    });
    
}

我们主要是将 SQL Server 设置为该应用程序的默认 DBMS,并将 BookstoreDbContext 类注册到依赖注入系统中。

SaaS 模块:租户管理 UI *

SaaS 模块提供了必要的 UI 来设置和更改租户的连接字符串,并触发数据库迁移。

连接字符串管理模态框

您可以在 SaaS 模块的 租户 页面中,为某个租户点击 操作 下拉按钮中的 数据库连接字符串 命令:

数据库连接字符串

它会打开 数据库连接字符串 模态框,如下图所示:

数据库连接字符串模态框

在这里,我们可以为该租户设置一个 默认连接字符串

当您进行更改并保存对话框后,数据库会自动创建和迁移。如果您稍后更新连接字符串(例如更改数据库名称),它也会再次触发数据库迁移过程。

手动应用数据库迁移

如果您需要为特定租户手动触发数据库迁移,请在 SaaS 模块的 租户管理 页面上,点击相关租户的 操作 下拉菜单,并选择 应用数据库迁移 命令:

应用数据库迁移


在本文档中