Admin Core

Admin.Core 数据库配置说明

Admin.Core 源码项目默认使用 SQLite 数据库。项目启动后,数据库文件会自动生成在以下目录:

Admin.Core 源码项目默认使用 SQLite 数据库。项目启动后,数据库文件会自动生成在以下目录:

1
bin\Debug\net10.0

如需使用其他数据库,可修改配置文件:

1
ConfigCenter/DbConfig.json

下面以 MySQL 创建 bizdb 业务库 为例说明配置方式。其他数据库的操作方式类似。以下配置均位于:

1
DbConfig.json -> DbConfig

安装数据库管理工具

推荐使用 Beekeeper Studio 作为数据库管理工具。

  1. 访问 Beekeeper Studio 官网。
  2. 点击 Download for Windows
  3. 在下载页面点击 Skip to the download
  4. 选择适合当前系统的版本,下载并安装。

创建数据库

以 MySQL 创建 bizdb 数据库为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
// 监听同步结构脚本,默认不监听
"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 数据库配置

SQLite 数据库不需要配置建库,只需配置连接字符串,项目启动时会自动创建数据库文件。

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

SQLite 数据库文件会创建在以下目录:

1
bin\Debug\net10.0\bizdb.db

连接数据库

以 MySQL 为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
// 数据库类型
"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,推荐使用以下连接字符串:

1
Server=localhost; Port=3306; Database=bizdb; Uid=root; Pwd=pwd; Charset=utf8mb4;Min pool size=1;Allow User Variables=True

同步结构和初始化数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
// 程序集名称,自动获取实体表;为空则通过 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
}
}

多数据库配置

配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
{
// 多数据库
"dbs": [
{
// 数据库注册键:模块库
"key": "moduledb",

// 程序集名称,自动获取实体表
"assemblyNames": [ "MyCompanyName.MyModuleName.Api.Contracts" ],

// 指定实体数据库列表,不填则同步所有数据库表实体
"includeEntityDbs": [],

// 排除实体数据库列表,指定不同步的数据库表实体
"excludeEntityDbs": [],

// 监听所有操作
"monitorCommand": false,

// 监听 CRUD 操作
"curd": true,

// 监听同步结构脚本
"syncStructureSql": false,

// 监听同步数据 CRUD 操作
"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"
// }
]
}
]
}

定义数据库键名

在以下目录中新建 DbKeys.cs 类:

1
MyCompanyName.MyProjectName.Api.Core/Consts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 为模块库注册键,可根据需要自定义命名。


新建模块基础仓储类

在以下目录中新建 ModuleRepositoryBase.cs 类:

1
MyCompanyName.MyProjectName.Api/Core/Repositories
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 中配置宿主应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 宿主应用实例
new HostApp(new HostAppOptions
{
// 配置前置服务
ConfigurePreServices = context =>
{
// 设置模块库注册键
DbKeys.ModuleDb = "moduledb";
},

// 配置 Autofac 容器
ConfigureAutofacContainer = (builder, context) =>
{
// 注册泛型模块基础仓储类
builder.RegisterGeneric(typeof(ModuleRepositoryBase<>))
.InstancePerLifetimeScope()
.PropertiesAutowired();
}
});

使用多数据库

方式一:使用 ModuleRepositoryBase 泛型模块基础仓储类

1
2
3
4
5
6
7
8
9
10
11
[Order(1010)]
[DynamicApi(Area = ApiConsts.AreaName)]
public class ModuleService : BaseService, IDynamicApi
{
private readonly ModuleRepositoryBase<Entity> _moduleRepo;

public ModuleService(ModuleRepositoryBase<Entity> moduleRepo)
{
_moduleRepo = moduleRepo;
}
}

方式二:使用 FreeSqlCloud

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class ModuleService : BaseService, IDynamicApi
{
private readonly FreeSqlCloud _freeSqlCloud;

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

public void GetData()
{
var fsql = _freeSqlCloud.Use(DbKeys.ModuleDb);
}
}

方式三:使用模块仓储接口

定义 IModuleRepository 模块仓储接口

1
2
3
4
5
6
7
8
9
10
11
using MyCompanyName.MySys.Api.Contracts.Domain.Module;
using ZhonTai.Admin.Core.Repositories;

namespace MyCompanyName.MySys.Api.Contracts;

/// <summary>
/// 模块仓储接口
/// </summary>
public interface IModuleRepository : IRepositoryBase<ModuleEntity>
{
}

定义 ModuleRepository 模块仓储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
{
}
}

使用 IModuleRepository 模块仓储接口

1
2
3
4
5
6
7
8
9
10
11
[Order(1010)]
[DynamicApi(Area = ApiConsts.AreaName)]
public class ModuleService : BaseService, IDynamicApi
{
private readonly IModuleRepository _moduleRepo;

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

生成数据

*.json 数据文件生成到以下目录:

1
InitData/App

配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
// 建库
"createDb": false,

// 同步结构
"syncStructure": false,

// 同步数据
"syncData": false,

// 同步更新数据
"syncUpdateData": false,

// 生成数据
"generateData": true
}

提示

  • 项目开发启动时,不建议开启生成数据。
  • 发布生产环境前,如果开发环境存在配置数据需要更新数据包,可以开启生成数据包。
  • 项目发布后,如需更新数据包,也可以开启生成数据包。
  • 使用完成后,请及时关闭 generateData

自定义生成数据

在以下目录中新建 Data 文件夹,并新增 CustomGenerateData.cs 类:

1
MyCompanyName.MyProjectName.Api/Core/Data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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");
}
}

同步数据

将以下目录中的 JSON 数据同步到数据库:

1
InitData/App/*.json

配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
// 同步结构
"syncStructure": true,

// 同步数据
"syncData": true,

// 同步更新数据
"syncUpdateData": true,

// 生成数据
"generateData": false
}

自定义同步数据

在以下目录中新建 Data 文件夹,并新增 CustomSyncData.cs 类:

1
MyCompanyName.MyProjectName.Api/Core/Data
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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
)
)
);
}

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
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
[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);
}
}

提示

  • 事务可跨方法使用。
  • 支持同步方法和异步方法。
  • 如果服务是动态 API,开启事务的方法需要定义为 virtual 虚方法,事务才能正常生效。
  • 存在多个 CUD 操作时,事务会确保操作要么全部成功,要么全部失败。

事务回滚

推荐抛出友好异常触发事务回滚:

1
throw ResultOutput.Exception(msg);

也可以使用:

1
throw new AppException(msg);

提交事务

如果方法执行过程中没有异常抛出,事务将自动提交。


事务属性

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

Propagation 事务传播方式

说明
Required 如果当前没有事务,则新建事务;如果已存在事务,则加入当前事务。默认选择。
Supports 支持当前事务;如果没有当前事务,则以非事务方式执行。
Mandatory 使用当前事务;如果没有当前事务,则抛出异常。
NotSupported 以非事务方式执行;如果当前存在事务,则挂起当前事务。
Never 以非事务方式执行;如果当前存在事务,则抛出异常。
Nested 以嵌套事务方式执行。

IsolationLevel 事务隔离级别

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

#中台 #中台/.NET模板 #中台/搭建项目框架 #中台/数据库配置 #中台/多数据库配置