数据库连接

在使用ClownFish访问数据库时,必须先创建一个 DbContext 实例,它有2个作用:

  • 封装着ADO.NET的数据库连接
  • 所有数据操作方法的入口,可参考:执行命令

本文将介绍这个对象的创建方法。



在独立应用中

using( DbContext dbContext = DbContext.Create(connectionString, providerName) ) {
    // .......
}

参数providerName可以使用下定义的常量

/// <summary>
/// 一些常用的数据访问客户端 ProviderName
/// </summary>
public static class DatabaseClients
{
    public static readonly string SqlClient = "System.Data.SqlClient";

    public static readonly string MySqlClient = "MySql.Data.MySqlClient";

    public static readonly string PostgreSQL = "Npgsql";

    public static readonly string SQLite = "System.Data.SQLite";

    public static readonly string DaMeng = "Dm";
}

或者

using( DbContext dbContext = DbContext.Create(connName) ) {
    // .......
}

参数 connName 表示一个在 ClownFish.App.config 注册的数据库连接名称,例如:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <!-- 数据库连接配置 -->
    <connectionStrings>
        <add name="db1" providerName="System.Data.SqlClient"
			 connectionString="server=MsSqlHost;database=MyNorthwind;uid=user1;pwd=xxxx"/>
        <add name="db2" providerName="MySql.Data.MySqlClient"
			 connectionString="server=MySqlHost;database=MyNorthwind;uid=user1;pwd=xxxx"/>
        <add name="db3" providerName="Npgsql"
            connectionString="Host=PgSqlHost;database=mynorthwind;Username=postgres;Password=xxxx"/>
    </connectionStrings>

    <!-- 或者把数据库连接配置在这里 -->
    <dbConfigs>
        <add name="s1" dbType="SQLSERVER" server="MsSqlHost" database="MyNorthwind" uid="user1" pwd="xxxxxx" args="" />
        <add name="m1" dbType="MySQL" server="MySqlHost" database="MyNorthwind" uid="user1" pwd="xxxxxx" args="" />
        <add name="pg1" dbType="PostgreSQL" server="PgSqlHost" database="mynorthwind" uid="postgres" pwd="xxxxxx" args="" />
    </dbConfigs>
</configuration>

其中,"db1", "db2", "db3", "s1", "m1", "pg1" 都是有效的【数据库连接名称】





在微服务项目中

Nebula采用了多租户的架构设计,因此将数据库按用途分为3大类:

  • 租户库:存储各个租户的业务数据,保证隔离性
  • 主库:存储全局业务数据(不区分租户),例如:租户表
  • 应用库:存储特定应用场景的数据(不区分租户),例如:框架配置

在Controller中,可以调用BaseController提供的方法打开数据库连接

  • CreateMasterConnection(): 打开主库连接
  • CreateTenantConnection(): 打开与当前 已登录 用户关联的租户库连接
  • CreateTenantConnection(string tenantId):打开指定的租户库连接
  • CreateAppDbConnection(string connName):根据连接名称打开连接

在Controller之外(消息处理或者后台任务),可以调用DbConnManager来实现相同的功能:

  • CreateMaster(): 打开主库连接
  • CreateTenant(string tenantId):打开指定的租户库连接
  • CreateAppDb(string connName):根据连接名称打开连接

注意事项:

  • 在执行任何数据库操作之前,都需要先打开数据库连接。
  • 数据库连接用 DbContext 类型封装,它与线程没有关联,跨层使用时,可通过调用参数来传递。
  • DbContext实现了IDisposable接口,在创建后需要使用 using 语句块包起来



连接到 主库

public class ConnDbController : BaseController
{
    public async Task<string> Test1()
    {
        // 连接到【主库】
        using( DbContext dbContext = this.CreateMasterConnection() ) {

            return await dbContext.CPQuery.Create("select now()").ExecuteScalarAsync<string>();
        }
    }
}



连接到 租户库

用户登录场景

public async Task<string> Login()
{
    // 从请求中获取到的租户信息
    // 此时用户还没有经过登录验证,这时候需要在提交登录数据时把 租户ID 一起发送到服务端
    // 服务端根据 租户ID 就可以连接到相应的租户库
    string tenantId = "xxxxxxxxxx";

    using( DbContext dbContext = this.CreateTenantConnection(tenantId) ) {  // 连接到 指定的【租户库】

        User user = await dbContext.CPQuery.Create("select * from users where username = @username and pwd = @pwd").ToSingelAsync<User>();
    }
}

【已登录】用户的后续访问

public async Task<string> Test()
{
    // 连接到  与当前已登录用户对应的 【租户库】
    using( DbContext dbContext = this.CreateTenantConnection() ) {

        return await dbContext.CPQuery.Create("select now()").ExecuteScalarAsync<string>();
    }
}



连接到 应用库

public async Task<string> Test()
{
    // 连接到 某个特定的 【应用库】
    // config 是一个连接名称,在【配置服务】中有对应的连接设置

    using( DbContext dbContext = this.CreateAppDbConnection("notify") ) {

        return await dbContext.CPQuery.Create("select now()").ExecuteScalarAsync<string>();
    }
}



嵌套连接

public async Task<int> TestNestConnection()    // 演示【嵌套连接】
{
    // 根据用户身份信息(租户ID)切换到【当前租户库】,
    using( DbContext dbContext = this.CreateTenantConnection() ) {

        // 在【当前租户库】中执行第一个数据操作
        dbContext.XmlCommand.Create("query1").ExecuteNonQuery();

        // 打开第二个连接,
        using( DbContext dbContext2 = this.CreateAppDbConnection("notify") ) {

            // 在 database2 中执行数据操作
            dbContext2.XmlCommand.Create("query2").ExecuteNonQuery();
        }

        // 继续在【当前租户库】中执行数据操作
        await dbContext.XmlCommand.Create("query3").ExecuteNonQueryAsync();
    }

    return 1;
}



在Controller之外(消息处理或者后台任务) 打开连接

using( DbContext dbContext = DbConnManager.CreateMaster() ) {
    // .......
}

using( DbContext dbContext = DbConnManager.CreateAppDb("connName") ) {
    // .......
}

using( DbContext dbContext = DbConnManager.CreateTenant(tenantId) ) {
    // .......
}