# 数据访问 (02_Data)
最后更新: 2024-09-20
# 📚 概述
DarkM 数据访问层是基于 Dapper 封装的轻量级 ORM,支持 SqlServer、MySql、SQLite、PostgreSQL、Oracle 五种数据库,提供完整的 CRUD、批量操作、分表操作、工作单元等功能。
源代码位置: DarkM/src/Framework/Data
# 🏗️ 模块架构
# 完整目录结构
Data/
├── Core/ # 核心层
│ └── Data.Abstractions/ # 抽象层
│ ├── IDbContext.cs # 数据库上下文接口
│ ├── IDbSet.cs # 数据集接口
│ ├── IRepository.cs # 仓储接口
│ ├── IEntity.cs # 实体接口
│ ├── IUnitOfWork.cs # 工作单元接口
│ ├── Options/
│ │ ├── DbOptions.cs # 数据库配置
│ │ └── DbModuleOptions.cs # 模块配置
│ ├── Pagination/
│ │ ├── Paging.cs # 分页模型
│ │ └── Sort.cs # 排序模型
│ ├── Attributes/
│ │ ├── TableAttribute.cs # 表特性
│ │ ├── ColumnAttribute.cs # 列特性
│ │ └── KeyAttribute.cs # 主键特性
│ └── Enums/
│ └── SqlDialect.cs # 数据库类型
│
├── Db/ # 数据库驱动
│ ├── Data.SqlServer/
│ ├── Data.MySql/
│ ├── Data.SQLite/
│ ├── Data.PostgreSQL/
│ └── Data.Oracle/
│
└── Extend/ # 扩展层
├── Data.Query/ # 查询扩展
│ ├── QueryModel.cs # 查询模型
│ └── QueryPagingModel.cs # 分页查询模型
└── Data.Integration/ # 集成层
└── ServiceCollectionExtensions.cs # DI 扩展
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
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
# 🔧 支持的数据库
| 数据库 | Dialect 值 | 默认端口 | 说明 |
|---|---|---|---|
| SqlServer | 0 | 1433 | 功能强大,Windows 友好 |
| MySql | 1 | 3306 | 开源流行,性能好 |
| SQLite | 2 | - | 嵌入式,零配置 |
| PostgreSQL | 3 | 5432 | 功能强大,开源 |
| Oracle | 4 | 1521 | 企业级,稳定 |
# 💡 配置说明
# appsettings.json 配置
{
"Db": {
// 是否开启 SQL 日志(开发环境建议开启)
"Logging": false,
// 数据库类型:0=SqlServer, 1=MySql, 2=SQLite, 3=PostgreSQL, 4=Oracle
"Dialect": 2,
// 数据库地址(SQLite 为文件路径)
"Server": "../../data/SQLite",
// 端口号
"Port": 0,
// 用户名
"UserId": "",
// 密码
"Password": "",
// 是否自动创建数据库和表
"CreateDatabase": true,
// 是否自动初始化数据
"InitData": true,
// 模块列表
"Modules": [
{
// 模块名称(必须与模块 Code 一致)
"Name": "Admin",
// 数据库名称
"Database": "Nm_Admin",
// 表前缀
"Prefix": "",
// 自定义连接字符串(可选,优先级高于全局配置)
"ConnectionString": ""
}
]
}
}
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
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
# 配置项详解
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| Logging | bool | false | SQL 日志开关 |
| Dialect | SqlDialect | - | 数据库类型 |
| Server | string | - | 服务器地址 |
| Port | int | 0 | 端口号 |
| UserId | string | - | 用户名 |
| Password | string | - | 密码 |
| CreateDatabase | bool | true | 自动创建数据库 |
| InitData | bool | true | 自动初始化数据 |
| Modules | List | - | 模块配置列表 |
# 🔧 核心接口详解
# 1. IDbContext(数据库上下文)
文件位置: Data.Abstractions/IDbContext.cs
public interface IDbContext
{
/// <summary>
/// 数据库配置
/// </summary>
IDbContextOptions Options { get; }
/// <summary>
/// 登录信息
/// </summary>
ILoginInfo LoginInfo { get; }
/// <summary>
/// 数据库是否已存在
/// </summary>
bool DatabaseExists { get; }
/// <summary>
/// 创建新的连接
/// </summary>
IDbConnection NewConnection(IDbTransaction transaction = null);
/// <summary>
/// 创建新的工作单元
/// </summary>
IUnitOfWork NewUnitOfWork();
/// <summary>
/// 获取数据集
/// </summary>
IDbSet<TEntity> Set<TEntity>() where TEntity : IEntity, new();
/// <summary>
/// 创建数据库和表
/// </summary>
void CreateDatabase();
}
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
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
# 2. IDbSet(数据集)
文件位置: Data.Abstractions/IDbSet{T}.cs
# 接口结构
IDbSet (基础接口)
├── Execute 系列 — 执行 SQL 命令
├── ExecuteScalar 系列 — 执行并返回单个值
├── ExecuteReader 系列 — 执行并返回 DataReader
├── QueryFirstOrDefault 系列 — 查询第一条
├── QuerySingleOrDefault 系列 — 查询单条(多条抛异常)
├── QueryMultiple 系列 — 查询多结果集
└── Query 系列 — 查询返回集合
IDbSet<TEntity> (泛型接口,继承 IDbSet)
├── Insert 系列 — 新增
├── BatchInsert 系列 — 批量插入
├── Delete 系列 — 删除
├── SoftDelete 系列 — 软删除
├── Update 系列 — 更新
├── BulkMerge 系列 — 批量合并(Upsert)⭐
├── Get 系列 — 根据主键查询
├── Exists 系列 — 是否存在
├── Find 系列 — 条件查询(返回 IQueryable)
├── 分表操作系列 — 按时间分表
└── Clear 系列 — 清空表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# BulkMerge 系列(批量合并/Upsert)⭐
核心功能:支持"不存在时插入,存在时按条件更新"
// Lambda 版本
IResultModel<BulkMergeResult> BulkMerge(
IEnumerable<TEntity> entities,
IUnitOfWork uow = null,
bool deleteMissing = false,
Expression<Func<TEntity, bool>> whereExpression = null,
Expression<Func<TEntity, object>>[] conditionSelectors = null,
Expression<Func<TEntity, object>>[] includeUpdateSelectors = null,
params Expression<Func<TEntity, object>>[] ignoreUpdateSelectors);
// 字符串版本
IResultModel<BulkMergeResult> BulkMerge(
IEnumerable<TEntity> entities,
IUnitOfWork uow = null,
bool deleteMissing = false,
(string SqlCondition, DynamicParameters Parameters) fixedCondition = default,
string[] matchColumns = null,
string[] updateColumns = null,
string[] ignoreColumns = null);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
| entities | IEnumerable | 数据集合 |
| deleteMissing | bool | 是否删除数据集合中不存在的数据 |
| conditionSelectors | Lambda 数组 | 匹配条件(用于判断是否存在) |
| includeUpdateSelectors | Lambda 数组 | 更新字段(包含模式) |
| ignoreUpdateSelectors | Lambda 数组 | 忽略更新的字段(排除模式) |
返回值:
| 属性 | 说明 |
|---|---|
| InsertedCount | 插入的记录数 |
| UpdatedCount | 更新的记录数 |
| DeletedCount | 删除的记录数 |
# 💡 使用示例
# 1. 实体定义
using DarkM.Lib.Data.Abstractions.Entities;
using DarkM.Lib.Data.Abstractions.Attributes;
/// <summary>
/// 用户实体
/// </summary>
[Table("User")]
public class UserEntity : EntityBase
{
/// <summary>
/// 用户名
/// </summary>
[Column("UserName")]
[Length(50)]
public string UserName { get; set; }
/// <summary>
/// 邮箱
/// </summary>
[Column("Email")]
[Length(100)]
public string Email { get; set; }
/// <summary>
/// 状态
/// </summary>
[Column("Status")]
public Status Status { get; set; } = Status.Enabled;
}
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
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
# 2. 仓储接口
public interface IUserRepository : IRepository<UserEntity>
{
UserEntity GetByUserName(string userName);
Task<bool> ExistsByUserName(string userName);
}
1
2
3
4
5
2
3
4
5
# 3. Service 层使用
public class UserService
{
private readonly IUserRepository _userRepository;
private readonly ICacheHandler _cache;
public UserService(IUserRepository userRepository, ICacheHandler cache)
{
_userRepository = userRepository;
_cache = cache;
}
public async Task<UserEntity> GetByIdAsync(int id)
{
var cacheKey = $"user:{id}";
var user = await _cache.GetAsync<UserEntity>(cacheKey);
if (user != null)
return user;
user = await _userRepository.Get(id);
if (user != null)
await _cache.SetAsync(cacheKey, user, TimeSpan.FromMinutes(30));
return user;
}
public async Task<bool> CreateAsync(CreateUserRequest request)
{
Check.NotNull(request, nameof(request));
Check.NotNull(request.UserName, nameof(request.UserName));
if (await _userRepository.ExistsByUserName(request.UserName))
throw new BusinessException("用户名已存在");
var user = request.ToEntity();
user.CreateTime = DateTime.Now;
return await _userRepository.InsertAsync(user);
}
}
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
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
# 4. 事务操作
using var uow = _dbContext.NewUnitOfWork();
try
{
await _orderRepository.InsertAsync(order, uow);
foreach (var item in items)
{
item.OrderId = order.Id;
await _orderItemRepository.InsertAsync(item, uow);
}
uow.Commit();
}
catch
{
uow.Rollback();
throw;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 5. BulkMerge 使用
// 简单 Upsert(按主键匹配)
var entities = new List<UserEntity>
{
new UserEntity { Id = 1, Name = "张三" },
new UserEntity { Id = 2, Name = "李四" }
};
var result = await _dbSet.BulkMergeAsync(entities);
// 按多条件匹配 + 指定更新字段
var result = await _dbSet.BulkMergeAsync(
entities,
deleteMissing: false,
conditionSelectors: new[] {
e => e.OrderNo, // 按订单号匹配
e => e.ItemId // 按物品 ID 匹配
},
includeUpdateSelectors: new[] {
e => e.Quantity, // 只更新数量
e => e.Price // 只更新价格
}
);
// 同步删除不存在的数据
var result = await _dbSet.BulkMergeAsync(
entities,
deleteMissing: true, // 删除集合中不存在的数据
whereExpression: e => e.Status == 1 // 附加条件
);
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
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
# 6. 分表操作
// 按天分表
var tableName = _dbSet.GetTableNameByDay(DateTime.Now);
var users = _dbSet.Find(tableName).ToList();
// 数据迁移(保留最近 3 个月)
await _dbSet.MoveDataByMonth(
keepLength: 3,
keepType: 2, // 按月保留
byColumn: "CreateTime"
);
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 🔍 常见问题
# Q1: 如何切换数据库类型?
解决方案:
{
"Db": {
"Dialect": 1, // 0=SqlServer, 1=MySql, 2=SQLite, 3=PostgreSQL
"Server": "127.0.0.1",
"Port": 3306,
"UserId": "root",
"Password": "root"
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# Q2: 如何开启 SQL 日志?
解决方案:
{
"Db": {
"Logging": true
}
}
1
2
3
4
5
2
3
4
5
日志将输出到 Serilog 配置的日志文件中。
# Q3: 如何实现软删除?
解决方案:
- 实体继承
EntityBaseWithSoftDelete:
public class UserEntity : EntityBaseWithSoftDelete
{
public string UserName { get; set; }
}
1
2
3
4
2
3
4
- 使用软删除方法:
await _userRepository.SoftDeleteAsync(userId);
1
- 查询时自动过滤已删除数据:
var user = _userRepository.Get(userId); // 不会查到已软删除的数据
1
# Q4: 如何执行原生 SQL?
解决方案:
// 通过 IDbContext 执行
using var connection = _dbContext.NewConnection();
// 查询
var users = connection.Query<UserEntity>(
"SELECT * FROM [User] WHERE Status = @Status",
new { Status = 1 });
// 执行命令
var affected = connection.Execute(
"UPDATE [User] SET Status = 0 WHERE Id = @Id",
new { Id = 1 });
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# Q5: 如何配置分表策略?
分表命名规则:
| 方法 | 格式 | 示例 |
|---|---|---|
| GetTableNameByDay | {表名}_{yyyyMMdd} | Log_20260303 |
| GetTableNameByWeek | {表名}_{yyyyww} | Log_202609 |
| GetTableNameByMonth | {表名}_{yyyyMM} | Log_202603 |
| GetTableNameByQuarter | {表名}_{yyyyq} | Log_20261 |
| GetTableNameByYear | {表名}_{yyyy} | Log_2026 |
使用示例:
var tableName = _dbSet.GetTableNameByMonth(DateTime.Now);
await _dbSet.InsertAsync(entity, tableName: tableName);
1
2
2
# 📚 相关文档
# 🔗 参考链接
- 源代码 (opens new window) -
src/Framework/Data - Dapper 文档 (opens new window)
- NetCoreORM/SqlSugar (opens new window)
最后更新: 2024-09-20