Skip to content

数据库 9.1.3

Admin.Core源码项目默认使用sqlite数据库,项目启动后,数据库会在 bin\Debug\net9.0 目录下自动生成。如果需要使用其它数据库,可修改ConfigCenter/DbConfig.json配置文件。

Mysql 创建bizdb业务库示例,其它数据库操作类似,以下配置全部在DbConfig.json.DbConfig配置。

安装数据库管理工具

访问 Beekeeper Studio 官网,点击Download for Windows

在下载页面点击点击Skip to the download,选择适合你系统的版本下载并安装。

创建数据库

json
{
    //监听同步结构脚本,默认不监听
    "syncStructureSql": false,
    //开启建库
    "createDb": true,
    //建库连接字符串,修改Server=localhost; Port=3306; Uid=root; Pwd=pwd
    //不要修改Database=mysql,只有通过默认系统库才能连接数据库执行建库脚本
    "createDbConnectionString": "Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;",
    //建库脚本,复杂建库脚本可放到createdbsql.txt中
    "createDbSql": "CREATE DATABASE `bizdb` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci'"
}

提示

FreeSql数据库连接字符串示例

https://freesql.net/guide/#connectionstrings

第三方数据库连接字符串

https://www.connectionstrings.com

Sqlite数据库不需要配置建库,只需配置连接字符串则会自动建库

json
{
    "type": "Sqlite",
    "connectionString": "Data Source=|DataDirectory|\\bizdb.db; Pooling=true;Min Pool Size=1"
}

Sqlite数据库会在 bin\Debug\net9.0 文件夹下创建 bizdb.db

连接数据库

json
{
  //数据库类型
  "type": "MySql",
  //default_authentication_plugin = caching_sha2_password 
  //MySql "Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True"
  //default_authentication_plugin = mysql_native_password
  //MySql "Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;SslMode=none;Min pool size=1;Allow User Variables=True"
  //连接字符串,修改Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd;
  "connectionString": "Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True",
  //指定程序集,连接异常或使用TIDB时需要配置该项
  //FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySqlConnector
  "providerType": ""
}

注意

mysql 8.0 以上版本,default_authentication_plugin默认为caching_sha2_password,选择 "Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True"。

同步结构和初始化数据

json
{
    //程序集名称,自动获取实体表,为空则通过ConfigureFreeSql自定义配置
    "assemblyNames": [ "MyCompanyName.MySys.Api" ],
    //指定实体数据库列表,不填同步所有数据库表实体
    "includeEntityDbs": [],
    //排除实体数据库列表,指定不同步数据库表实体
    "excludeEntityDbs": [],
    //同步结构
    "syncStructure": true,
    //同步结构批次实体数
    "syncStructureEntityBatchSize": 1,
    //同步数据分批处理大小,默认每次处理500条,过大可能会导致内存溢出
    "syncDataBatchSize": 500,
    //同步数据
    "syncData": true,
    //同步更新数据,谨慎开启,不需要可以关闭
    "syncUpdateData": true,
    //同步数据地址,默认InitData/App,一般不调整不修改
    //"SyncDataPath": "InitData/App",
    //同步所有表["base_dict_type", "base_dict", "base_user",  "base_user_staff", "base_org", "base_role", "base_api", "base_view", "base_permission", "base_permission_api", "base_user_role", "base_user_org", "base_role_permission", "base_tenant", "base_tenant_permission"]
    //同步指定表["base_api", "base_view", "base_permission", "base_permission_api"]
    //同步数据包含表,指定表同步,不填同步所有表
    "syncDataIncludeTables": [],
    //同步排除表["base_user"]
    //同步数据排除表,指定表不同步
    "syncDataExcludeTables": [],
    //同步数据操作用户
    "syncDataUser": {
      "id": 161223411986501,
      "userName": "admin",
      "name": "管理员",
      "tenantId": 161223412138053
    },
}

多数据库

多数据库配置

json
{
//多数据库
"dbs": [{
    //数据库注册键:模块库
    "key": "moduledb",
    //程序集名称,自动获取实体表
    "assemblyNames": [ "MyCompanyName.MyModuleName.Api.Contracts" ],
    //指定实体数据库列表,不填同步所有数据库表实体
    "includeEntityDbs": [],
    //排除实体数据库列表,指定不同步数据库表实体
    "excludeEntityDbs": [],

    //监听所有操作
    "monitorCommand": false,
    //监听Curd操作
    "curd": true,
    //监听同步结构脚本
    "syncStructureSql": false,
    //监听同步数据Curd操作
    "syncDataCurd": false,

    //建库
    "createDb": true,
    //SqlServer,PostgreSQL,Oracle,OdbcOracle,OdbcSqlServer,OdbcMySql,OdbcPostgreSQL,Odbc,OdbcDameng,MsAccess
    //建库连接字符串
    //default_authentication_plugin = caching_sha2_password 
    //MySql "Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True"
    //default_authentication_plugin = mysql_native_password
    //MySql "Server=localhost; Port=3306; Database=mysql; Uid=root; Pwd=pwd; Charset=utf8mb4;SslMode=none;Min pool size=1;Allow User Variables=True"
    //SqlServer "Data Source=.;User Id=sa;Password=pwd;Initial Catalog=master;TrustServerCertificate=true;Pooling=true;Min Pool Size=1"
    //PostgreSQL "Host=localhost;Port=5432;Username=postgres;Password=; Database=postgres;Pooling=true;Minimum Pool Size=1"
    //Oracle "user id=SYS;password=pwd; data source=127.0.0.1:1521/XE;Pooling=true;Min Pool Size=1"
    "createDbConnectionString": "",
    //建库脚本,复杂建库脚本可放到createdbsql.txt中
    //MySql "CREATE DATABASE `moduledb` CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci'"
    //SqlServer "CREATE DATABASE [moduledb]"
    //PostgreSQL "CREATE DATABASE \"moduledb\" WITH ENCODING = 'UTF8'"
    "createDbSql": "",

    //同步结构
    "syncStructure": true,
    //同步结构批次实体数
    "syncStructureEntityBatchSize": 1,
    //同步数据分批处理大小,默认每次处理500条,过大可能会导致内存溢出
    "syncDataBatchSize": 500,
    //同步数据
    "syncData": true,
    //同步更新数据,注意生产环境谨慎开启,确定要修改表数据是最新数据再开启。如不想更新某些表的数据,可以先配置同步数据排除表syncDataExcludeTables,再执行数据更新操作
    "syncUpdateData": false,
    //同步数据地址
    "SyncDataPath": "InitData/App",
    //同步所有表["base_dict_type", "base_dict", "base_user",  "base_user_staff", "base_org", "base_role", "base_api", "base_view", "base_permission", "base_permission_api", "base_user_role", "base_user_org", "base_role_permission", "base_tenant", "base_tenant_permission"]
    //同步指定表["base_api", "base_view", "base_permission", "base_permission_api"]
    //同步数据包含表,指定表同步,不填同步所有表
    "syncDataIncludeTables": [],
    //同步排除表["base_user"]
    //同步数据排除表,指定表不同步
    "syncDataExcludeTables": [],
    //同步数据操作用户
    "syncDataUser": {
    "id": 161223411986501,
    "userName": "admin",
    "name": "管理员",
    "tenantId": 161223412138053
    },

    //项目初始化不开启生成数据,发布生产环境前,如果开发环境有配置数据需要更新数据包,可以开启生成数据包,使用完记得关闭
    //开启生成数据前先关闭syncStructure syncData createDb
    //生成数据
    "generateData": false,

    //数据库配置 https:github.com/dotnetcore/FreeSql/wiki/入门
    //连接字符串语法 https:www.connectionstrings.com
    //数据库类型 MySql = 0, SqlServer = 1, PostgreSQL = 2, Oracle = 3, Sqlite = 4, OdbcOracle = 5, OdbcSqlServer = 6, OdbcMySql = 7, OdbcPostgreSQL = 8, Odbc = 9, OdbcDameng = 10, MsAccess = 11, Dameng = 12, OdbcKingbaseES = 13, ShenTong = 14, KingbaseES = 15, Firebird = 16
    "type": "Sqlite",

    //连接字符串
    //default_authentication_plugin = caching_sha2_password 
    //MySql "Server=localhost; Port=3306; Database=moduledb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True"
    //default_authentication_plugin = mysql_native_password
    //MySql "Server=localhost; Port=3306; Database=moduledb; Uid=root; Pwd=pwd; Charset=utf8mb4;SslMode=none;Min pool size=1;Allow User Variables=True"
    //SqlServer "Data Source=.;Integrated Security=True;Initial Catalog=moduledb;Pooling=true;Min Pool Size=1"
    //PostgreSQL "Host=localhost;Port=5432;Username=postgres;Password=; Database=moduledb;Pooling=true;Minimum Pool Size=1"
    //Sqlite "Data Source=|DataDirectory|\\moduledb.db; Pooling=true;Min Pool Size=1"
    //"Oracle" "user id=SYS;password=pwd; data source=127.0.0.1:1521/XE;Pooling=true;Min Pool Size=1",
    "connectionString": "Data Source=|DataDirectory|\\moduledb.db; Pooling=true;Min Pool Size=1",

    //指定程序集
    //FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySqlConnector
    "providerType": "",

    //读写分离从库列表
    "slaveList": [
    //{
    //  权重
    //  "Weight": 1,
    //  连接字符串
    //  "ConnectionString": "Data Source=|DataDirectory|\\moduledb.db; Pooling=true;Min Pool Size=1"
    //}
    ]
}]
}

定义数据库键名

MyCompanyName.MyProjectName.Api.Core/Consts目录下新建DbKeys.cs

cs
using System.ComponentModel;

namespace MyCompanyName.MyProjectName.Api.Core.Consts;

/// <summary>
/// 数据库键名
/// </summary>
public class DbKeys
{
    /// <summary>
    /// 模块库注册键
    /// </summary>
    [Description("模块库注册键")]
    public static string ModuleDb { get; set; } = "moduledb";
}

ModuleDb为模块库注册键,可以自定义命名

新建模块基础仓储类

MyCompanyName.MyProjectName.Api/Core/Repositories目录下新建ModuleRepositoryBase.cs

cs
using MyCompanyName.MySys.Api.Core.Consts;
using ZhonTai.Admin.Core.Db.Transaction;
using ZhonTai.Admin.Core.Repositories;

namespace MyCompanyName.MySys.Api.Core.Repositories;

/// <summary>
/// 模块库基础仓储
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ModuleRepositoryBase<TEntity> : RepositoryBase<TEntity> where TEntity : class
{
    public ModuleRepositoryBase(UnitOfWorkManagerCloud uowm) : base(DbKeys.ModuleDb, uowm)
    {

    }
}

配置宿主应用

Program.cs中配置宿主应用

cs
// 宿主应用实例
new HostApp(new HostAppOptions
{
    //配置前置服务
    ConfigurePreServices = context =>
    {
        //设置模块库注册键
        DbKeys.ModuleDb = "moduledb";
    },
    //配置Autofac容器
    ConfigureAutofacContainer = (builder, context) => 
    {
        //注册泛型模块基础仓储类
        builder.RegisterGeneric(typeof(ModuleRepositoryBase<>)).InstancePerLifetimeScope().PropertiesAutowired();
    }
}

使用多数据库

  • 方式一:使用ModuleRepositoryBase泛型模块基础仓储类
cs
[Order(1010)]
[DynamicApi(Area = ApiConsts.AreaName)]
public class ModuleService : BaseService, IDynamicApi
{
    private readonly ModuleRepositoryBase<Entity> _moduleRepo;

    public ModuleService (ModuleRepositoryBase<Entity> moduleRepo)
    {
        _moduleRepo = moduleRepo;
    }
}
  • 方式二:使用FreeSqlCloud
cs
public class ModuleService : BaseService, IDynamicApi
{
    private readonly FreeSqlCloud _freeSqlCloud;

    public ModuleService(FreeSqlCloud freeSqlCloud)
    {
        _freeSqlCloud = freeSqlCloud;
    }

    public void GetData()
    {
        var fsql = _freeSqlCloud.Use(DbKeys.ModuleDb);
    }
}
  • 方式三:使用模块仓储接口
  1. 定义IModuleRepository模块仓储接口
cs
using MyCompanyName.MySys.Api.Contracts.Domain.Module;
using ZhonTai.Admin.Core.Repositories;

namespace MyCompanyName.MySys.Api.Contracts;

/// <summary>
/// 模块仓储接口
/// </summary>
public interface IModuleRepository : IRepositoryBase<ModuleEntity>
{
}
  1. 定义ModuleRepository模块仓储
cs
using MyCompanyName.MySys.Api.Contracts;
using MyCompanyName.MySys.Api.Contracts.Domain.Module;
using MyCompanyName.MySys.Api.Core.Repositories;
using ZhonTai.Admin.Core.Db.Transaction;

namespace MyCompanyName.MySys.Api.Repositories.Module;

/// <summary>
/// 模块仓储
/// </summary>
public class ModuleRepository : ModuleRepositoryBase<ModuleEntity>, IModuleRepository
{
    public ModuleRepository(UnitOfWorkManagerCloud uowm) : base(uowm)
    {
    }
}
  1. 使用IModuleRepository模块仓储接口
cs
[Order(1010)]
[DynamicApi(Area = ApiConsts.AreaName)]
public class ModuleService : BaseService, IDynamicApi
{
    private readonly IModuleRepository _moduleRepo;

    public ModuleService (IModuleRepository moduleRepo)
    {
        _moduleRepo = moduleRepo;
    }
}

生成数据

生成*.json数据文件到InitData/App文件夹中,配置如下:

json
{
    //建库
    "createDb": false,
    //同步结构
    "syncStructure": false,
    //同步数据
    "syncData": false,
    //同步更新数据
    "syncUpdateData": false,
    //生成数据
    "generateData": true
}

提示

项目开发启动时,不开启生成数据

发布生产环境前,如果开发环境有配置数据需要更新数据包,可以开启生成数据包,使用完记得关闭

项目发布后,如果需要更新数据包,可以开启生成数据包,使用完记得关闭

MyCompanyName.MyProjectName.Api/Core 文件夹下新建 Data 文件夹,新增 CustomGenerateData.cs

cs
using System.Threading.Tasks;
using ZhonTai.Admin.Core.Configs;
using ZhonTai.Admin.Core.Db.Data;
using MyCompanyName.MySys.Api.Contracts.Domain.Module;

namespace MyCompanyName.MySys.Api.Core.Data;

public class CustomGenerateData : GenerateData, IGenerateData
{
    public virtual async Task GenerateDataAsync(IFreeSql db, AppConfig appConfig)
    {
        var isTenant = appConfig.Tenant;

        var modules = await db.Queryable<ModuleEntity>().ToListAsync();

        SaveDataToJsonFile<ModuleEntity>(modules, isTenant, path: "InitData/App");
    }
}

同步数据

同步 InitData/App/*.jsonjson数据到数据库,配置如下:

json
{
    //同步结构
    "syncStructure": true,
    //同步数据
    "syncData": true,
    //同步更新数据
    "syncUpdateData": true,
    //生成数据
    "generateData": false
}

MyCompanyName.MyProjectName.Api/Core 文件夹下新建 Data 文件夹,新增 CustomSyncData.cs 类。

cs
using System;
using System.Threading.Tasks;
using ZhonTai.Admin.Core.Configs;
using ZhonTai.Admin.Core.Db.Data;
using MyCompanyName.MySys.Api.Contracts.Domain.Module;
using System.Linq;

namespace MyCompanyName.MySys.Api.Core.Data;

public class CustomSyncData : SyncData, ISyncData
{
    public virtual async Task SyncDataAsync(IFreeSql db, DbConfig dbConfig = null, AppConfig appConfig = null)
    {
        using var unitOfWork = db.CreateUnitOfWork();

        try
        {
            var isTenant = appConfig.Tenant;

            await SyncEntityAsync<ModuleEntity>(db, unitOfWork, dbConfig, appConfig,
            whereFunc: (select, batchDataList) =>
            {
                if (appConfig.Tenant)
                    return select.Where(a =>
                        batchDataList.Any(b => a.Id == b.Id
                        || (a.TenantId == b.TenantId && !string.IsNullOrWhiteSpace(a.Name) && a.Name == b.Name))
                    );
                else
                    return select.Where(a =>
                        batchDataList.Any(b => a.Id == b.Id
                        || (!string.IsNullOrWhiteSpace(a.Name) && a.Name == b.Name))
                    );
            },
            insertDataFunc: (batchDataList, dbDataList) =>
            {
                return batchDataList.Where(a => !dbDataList.Any(b => a.Id == b.Id));
            });

            unitOfWork.Commit();
        }
        catch (Exception)
        {
            unitOfWork.Rollback();
            throw;
        }
    }
}

数据库事务

使用事务

在服务方法上增加 [AppTransaction] 特性,使用当前项目主库事务

提示

  1. 事务可跨方法,支持同步和异步方法。
  2. 如果服务是动态 Api,开启事务的方法需定义为virtual虚方法,才能正常使用事务。

例如下面的用户更新,存在多个 cud 操作,操作要么全部成功,要么全部失败。

cs
[AppTransaction]
public virtual async Task UpdateAsync(UserUpdateInput input)
{
	if (!(input?.Id > 0))
	{
		//事务回滚
		throw ResultOutput.Exception("请选择用户");
	}

	//查询用户
	var user = await _userRepository.GetAsync(input.Id);
	if (!(user?.Id > 0))
	{
        //事务回滚
		throw ResultOutput.Exception("用户不存在");
		// throw new AppException("用户不存在");
	}

	//数据映射
	_mapper.Map(input, user);

	//更新用户
	await _userRepository.UpdateAsync(user);

	//删除用户角色
	await _userRoleRepository.DeleteAsync(a => a.UserId == user.Id);
	if (input.RoleIds != null && input.RoleIds.Any())
	{
		var roles = input.RoleIds.Select(a => new UserRoleEntity { UserId = user.Id, RoleId = a });
		//批量插入用户角色
		await _userRoleRepository.InsertAsync(roles);
	}
}

事务回滚

抛出友好异常(推荐)

cs
throw ResultOutput.Exception(msg);
cs
throw new AppException(msg);

提交事务

无异常抛出,将自动提交事务

事务属性

参数说明类型默认值
Propagation事务传播方式enumRequired
IsolationLevel事务隔离级别enum-

Propagation 事务传播方式可选值:

  • Requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,默认的选择。
  • Supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • Mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • NotSupported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • Never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • Nested:以嵌套事务方式执行。

IsolationLevel 事务隔离级别可选值:

  • Chaos:无法覆盖隔离级别更高的事务中的挂起的更改。
  • ReadCommitted:在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。
  • ReadUncommitted:可以进行脏读,意思是说,不发布共享锁,也不接受独占锁。
  • RepeatableRead:在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。 防止不可重复的读取,但是仍可以有幻像行。
  • Serializable:在 DataSet 上放置范围锁,以防止在事务完成之前由其他用户更新行或向数据集中插入行。
  • Snapshot:通过在一个应用程序正在修改数据时存储另一个应用程序可以读取的相同数据版本来减少阻止。 表示您无法从一个事务中看到在其他事务中进行的更改,即便重新查询也是如此。
  • Unspecified:正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。