# DbRepository
**Repository Path**: Sean-Lu/DbRepository
## Basic Information
- **Project Name**: DbRepository
- **Description**: ORM框架,支持数据库:MySQL、MariaDB、TiDB、OceanBase、SQL Server、Oracle、SQLite、DuckDB、MS Access、Firebird、PostgreSql、OpenGauss、HighgoDB、IvorySQL、QuestDB、DB2、Informix、ClickHouse、达梦、人大金仓、神通、虚谷
- **Primary Language**: C#
- **License**: MIT
- **Default Branch**: dev
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2022-12-06
- **Last Updated**: 2025-08-23
## Categories & Tags
**Categories**: Uncategorized
**Tags**: ORM, CRUD, ddd, repository, dapper
## README
## 🌈 简介
> `ORM`框架,支持数据库:[`MySQL`](https://www.mysql.com/)、[`MariaDB`](https://mariadb.org/)、[`TiDB`](https://pingcap.com/)、[`OceanBase`](https://open.oceanbase.com/)、[`SQL Server`](https://www.microsoft.com/)、[`Oracle`](https://www.oracle.com/)、[`SQLite`](https://www.sqlite.org/)、[`DuckDB`](https://duckdb.org/)、[`MS Access`](https://www.microsoft.com/)、[`Firebird`](https://www.firebirdsql.org/)、[`PostgreSql`](https://www.postgresql.org/)、[`OpenGauss`](https://opengauss.org/)、[`HighgoDB(瀚高)`](https://www.highgo.com/)、[`IvorySQL`](https://ivorysql.org/)、[`QuestDB`](https://questdb.io/)、[`DB2`](https://www.ibm.com/)、[`Informix`](https://www.ibm.com/)、[`ClickHouse`](https://clickhouse.com/)、[`Dameng(达梦)`](https://www.dameng.com/)、[`KingbaseES(人大金仓)`](https://www.kingbase.com.cn/)、[`ShenTong(神通)`](http://www.shentongdata.com/)、[`Xugu(虚谷)`](http://www.xugucn.com/)
- 支持:`DbFirst`、`CodeFirst`
- 支持主从库分离(主库:增\删\改,从库:查)
- 支持分表(自定义表名规则)
- 支持`Expression`表达式树解析:`whereExpression`、`fieldExpression`
- 常用类:
| Class | Namespace | Description |
| -------------------------------------------------------------- | ------------------------------- | ------------------------------ |
| `DbFactory` | `Sean.Core.DbRepository` | 数据库工厂 |
| `SqlFactory` | `Sean.Core.DbRepository` | `SQL`创建工厂(CRUD) |
| **`BaseRepository`
`BaseRepository`** | `Sean.Core.DbRepository` | 基于`DbFactory`实现 |
| **`DapperBaseRepository`
`DapperBaseRepository`** | `Sean.Core.DbRepository.Dapper` | 基于`DbFactory`+`Dapper`实现 |
## ⭐ 开源
- GitHub: https://github.com/Sean-Lu/DbRepository
- Gitee: https://gitee.com/Sean-Lu/DbRepository
## 💖 Nuget Packages
| Package | NuGet Stable | NuGet Pre-release | Downloads |
| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Sean.Core.DbRepository](https://www.nuget.org/packages/Sean.Core.DbRepository/) | [](https://www.nuget.org/packages/Sean.Core.DbRepository/) | [](https://www.nuget.org/packages/Sean.Core.DbRepository/) | [](https://www.nuget.org/packages/Sean.Core.DbRepository/) |
| [Sean.Core.DbRepository.Dapper](https://www.nuget.org/packages/Sean.Core.DbRepository.Dapper/) | [](https://www.nuget.org/packages/Sean.Core.DbRepository.Dapper/) | [](https://www.nuget.org/packages/Sean.Core.DbRepository.Dapper/) | [](https://www.nuget.org/packages/Sean.Core.DbRepository.Dapper/) |
## 🍉 数据库
> CRUD Test: `TestRepository.cs`
>
> DbFirst: `CodeGeneratorFactory`
>
> CodeFirst: `SqlGeneratorFactory`
| Database | CRUD Test | DbFirst | CodeFirst | Description |
| ------------ | --------- | ------- | --------- |------------ |
| `MySQL` | ✅ | ✅ | ✅ | |
| `MariaDB` | ✅ | ✅ | ✅ | |
| `TiDB` | ✅ | ✅ | ✅ | |
| `OceanBase` | ✅ | ✅ | ✅ | |
| `SQL Server` | ✅ | ✅ | ✅ | |
| `Oracle` | ✅ | ✅ | ✅ | |
| `SQLite` | ✅ | ✅ | ✅ | |
| `DuckDB` | ✅ | ✅ | ✅ | |
| `MS Access` | ✅ | ✅ | ✅ | |
| `Firebird` | ✅ | ✅ | ✅ | |
| `PostgreSql` | ✅ | ✅ | ✅ | |
| `OpenGauss` | ✅ | ✅ | ✅ | |
| `HighgoDB` | ✅ | ✅ | ✅ | 瀚高数据库 |
| `IvorySQL` | ✅ | ✅ | ✅ | |
| `QuestDB` | ✅ | | ✅ | |
| `DB2` | ✅ | | ✅ | |
| `Informix` | ✅ | | ✅ | |
| `ClickHouse` | ✅ | | ✅ | |
| `Dameng` | ✅ | ✅ | ✅ | 达梦 |
| `KingbaseES` | ✅ | | ✅ | 人大金仓 |
| `ShenTong` | ✅ | | ✅ | 神通数据库 |
| `Xugu` | ✅ | | ✅ | 虚谷数据库 |
## 💯 性能测试
> `Dapper`的`Execute`方法执行插入批量实体数据的本质是一条一条的插入,当数据量非常大时会很慢,可以分批把多条实体数据拼成一条脚本一次性执行(`BulkInsert`)。
- 以下测试结果来自单元测试:**`PerformanceComparisonTest.CompareBulkInsertTimeConsumed`**
- 测试数据库:MySQL 8.0.27
- 测试表:Test
- 测试时间:2023-02-07 15:00:00
| Operation | 50 Entities | 200 Entities | 1000 Entities | 2000 Entities | 5000 Entities |
| ---------------- | ----------- | ------------ | -------------- | -------------- | -------------- |
| `Dapper.Execute` | 318 ms | 1401 ms | 5875 ms | 11991 ms | 29968 ms |
| `BulkInsert` | 15 ms | 27 ms | 84 ms | 176 ms | 471 ms |
## 👉 使用示例
### 数据库连接字符串配置
> `.NET Framework`: `App.config`、`Web.config`
- 配置示例:
```xml
```
> `.NET Core`: `appsettings.json`
- 配置示例:可以通过设置`ProviderName`或`DatabaseType`的值来指定数据库类型
```json
{
"ConnectionStrings": {
// 主库:如果配置了多个数据库,数据库名称后缀是以1开始的数字。
"master": "DataSource=127.0.0.1;Database=test;uid=root;pwd=12345!a;ProviderName=MySql.Data.MySqlClient",
// 从库:如果配置了多个数据库,数据库名称后缀是以1开始的数字。
"secondary1": "DataSource=127.0.0.1;Database=test;uid=root;pwd=12345!a;ProviderName=MySql.Data.MySqlClient",
"secondary2": "DataSource=127.0.0.1;Database=test;uid=root;pwd=12345!a;ProviderName=MySql.Data.MySqlClient",
"test_SqlServer": "server=127.0.0.1;database=test;uid=sa;pwd=123456!a;DatabaseType=SqlServer",
"test_Oracle": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XXX)));User ID=XXX;Password=XXX;Persist Security Info=True;DatabaseType=Oracle",
"test_SQLite": "data source=.\\test.db;pooling=True;busytimeout=30000;journal mode=Wal;DatabaseType=SQLite"
}
}
```
### 数据库提供者工厂配置
> 支持2种方式来配置数据库和数据库提供者工厂之间的映射关系:
- 方式1:通过代码实现
- 方式2:通过配置文件实现
#### 方式1:代码
- 代码示例1:
```csharp
DatabaseType.MySql.SetDbProviderMap(new DbProviderMap("MySql.Data.MySqlClient", MySqlClientFactory.Instance));// MySql
DatabaseType.SqlServer.SetDbProviderMap(new DbProviderMap("System.Data.SqlClient", SqlClientFactory.Instance));// Microsoft SQL Server
DatabaseType.Oracle.SetDbProviderMap(new DbProviderMap("Oracle.ManagedDataAccess.Client", OracleClientFactory.Instance));// Oracle
DatabaseType.SQLite.SetDbProviderMap(new DbProviderMap("System.Data.SQLite", SQLiteFactory.Instance));// SQLite
```
- 代码示例2:
```csharp
DatabaseType.MySql.SetDbProviderMap(new DbProviderMap("MySql.Data.MySqlClient", "MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data"));// MySql
DatabaseType.SqlServer.SetDbProviderMap(new DbProviderMap("System.Data.SqlClient", "System.Data.SqlClient.SqlClientFactory,System.Data.SqlClient"));// Microsoft SQL Server
DatabaseType.Oracle.SetDbProviderMap(new DbProviderMap("Oracle.ManagedDataAccess.Client", "Oracle.ManagedDataAccess.Client.OracleClientFactory,Oracle.ManagedDataAccess"));// Oracle
DatabaseType.SQLite.SetDbProviderMap(new DbProviderMap("System.Data.SQLite", "System.Data.SQLite.SQLiteFactory,System.Data.SQLite"));// SQLite
```
- 代码示例3:
```csharp
// 如果直接使用数据库提供者工厂,也可以不配置数据库和数据库提供者工厂之间的映射关系。代码示例:
var db = new DbFactory("Database connection string...", MySqlClientFactory.Instance);// MySql
```
#### 方式2:配置文件
> 配置文件路径可以通过`DbContextConfiguration.Options.DbProviderFactoryConfigurationPath`设置
- 配置文件示例:
```xml
```
### 实体类特性
> `TableEntity`
| Attribute | Use | Description |
| ------------------------------------------------------------------------------ | -------- | -------------------------------------------------------------- |
| `Sean.Core.DbRepository.CodeFirstAttribute` | Class | 标记为CodeFirst的类 |
| **`System.ComponentModel.DataAnnotations.Schema.TableAttribute`** | Class | 设置表名 |
| `Sean.Core.DbRepository.SequenceAttribute` | Property | 设置序列号名称
(生成自增Id) |
| **`System.ComponentModel.DataAnnotations.KeyAttribute`** | Property | 标记为主键字段 |
| **`System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedAttribute`** | Property | 设置数据库生成字段值的方式
(通常和`KeyAttribute`一起使用) |
| **`System.ComponentModel.DataAnnotations.Schema.ColumnAttribute`** | Property | 设置字段名 |
| `System.ComponentModel.DescriptionAttribute` | Property | 设置字段描述 |
| `Sean.Core.DbRepository.NumericAttribute` | Property | 设置数值字段的位数和精度 |
| `System.ComponentModel.DataAnnotations.MaxLengthAttribute` | Property | 设置字段的最大长度 |
| `System.ComponentModel.DataAnnotations.RequiredAttribute` | Property | 设置字段不允许为空 |
| `System.ComponentModel.DefaultValueAttribute` | Property | 设置字段默认值 |
| **`System.ComponentModel.DataAnnotations.Schema.NotMappedAttribute`** | Property | 标记为为忽略字段 |
| ~~`System.ComponentModel.DataAnnotations.Schema.ForeignKeyAttribute`~~ | Property | 标记为外键字段(***暂不支持***) |
### 增删改查
> CRUD: `IBaseRepository`
```csharp
// 新增数据:
_testRepository.Add(entity);
// 批量新增数据:
_testRepository.Add(entities);
// 新增或更新数据:
_testRepository.AddOrUpdate(entity);
// 批量新增或更新数据:
_testRepository.AddOrUpdate(entities);
// 删除数据:过滤条件默认为实体的主键字段
_testRepository.Delete(entity);
// 删除数据:自定义过滤条件
_testRepository.Delete(entity => entity.UserId == 10001 && entity.Status != 0);
// 删除全部数据:
_testRepository.Delete(entity => true);
// 删除全部数据:
_testRepository.DeleteAll();
// 更新数据:更新全部字段,过滤条件默认为实体的主键字段
_testRepository.Update(entity);
// 更新数据:更新部分字段,过滤条件默认为实体的主键字段
_testRepository.Update(entity, fieldExpression: entity => new { entity.Status, entity.UpdateTime });
// 更新数据:更新全部字段,自定义过滤条件
_testRepository.Update(entity, whereExpression: entity => entity.UserId == 10001 && entity.Status != 0);
// 更新数据:更新部分字段,自定义过滤条件
_testRepository.Update(entity, fieldExpression: entity => new { entity.Status, entity.UpdateTime }, whereExpression: entity => entity.UserId == 10001 && entity.Status != 0);
// 批量更新数据:更新全部字段,过滤条件默认为实体的主键字段
_testRepository.Update(entities);
// 批量更新数据:更新部分字段,过滤条件默认为实体的主键字段
_testRepository.Update(entities, fieldExpression: entity => new { entity.Status, entity.UpdateTime });
// 数值字段递增:
_testRepository.Increment(10.0M, fieldExpression: entity => entity.AccountBalance, whereExpression: entity => entity.Id == 10001);
// 数值字段递减:
_testRepository.Decrement(10.0M, fieldExpression: entity => entity.AccountBalance, whereExpression: entity => entity.Id == 10001);
// 查询数据:分页 + 排序
int pageNumber = 1;// 当前页号(最小值为1)
int pageSize = 10;// 页大小
OrderByCondition orderBy = OrderByConditionBuilder.Build(OrderByType.Asc, entity => entity.CreateTime);
orderBy.Next = OrderByConditionBuilder.Build(OrderByType.Asc, entity => entity.Id);
List queryResult = _testRepository.Query(entity => entity.UserId == 10001, orderBy, pageNumber, pageSize)?.ToList();
// 查询单个数据:
TestEntity getResult = _testRepository.Get(entity => entity.Id == 2);
// 统计数量:
int countResult = _testRepository.Count(entity => entity.UserId == 10001);
// 数据是否存在:
bool exists = _testRepository.Exists(entity => entity.UserId == 10001);
// 更多使用示例在单元测试中:Sean.Core.DbRepository.Test.TableRepositoryTest
```
> 表达式树:`Expression> whereExpression`
```csharp
// 常量
entity => entity.UserId == 10001L
// 变量
entity => entity.UserId == _model.UserId
// bool
entity => entity.IsVip
// bool
entity => !entity.IsVip
// &&
entity => entity.UserId == _model.UserId && entity.AccountBalance < accountBalance
// ||
entity => entity.UserId == _model.UserId || entity.AccountBalance >= accountBalance
// StartsWith
entity => entity.UserId == _model.UserId && entity.Remark.StartsWith("测试")
// 更多使用示例在单元测试中:Sean.Core.DbRepository.Test.WhereExpressionTest
```
> 表达式树:`Expression> fieldExpression`
```csharp
// 单个字段:
entity => entity.Status
// 多个字段(匿名类型):
entity => new { entity.Status, entity.UpdateTime }
// 更多使用示例在单元测试中:Sean.Core.DbRepository.Test.FieldExpressionTest
```
## ❓ 常见问题
> `OleDb`和`ODBC`的区别?
1. `OleDb`是Microsoft开发的一种数据库连接技术,它是面向对象的,可以连接多种类型的数据库,包括`Access`、`Excel`、`SQL Server`等等。OleDb使用COM接口连接数据库,因此只能在Windows平台上使用。
2. `ODBC`是一种通用的数据库连接技术,它可以连接多种类型的数据库,包括`Access`、`Excel`、`SQL Server`等等。ODBC使用标准的API连接数据库,因此可以在多个平台上使用,包括Windows、Linux、Unix等等。
> 怎么解决`SQLite`数据库并发读写导致的锁库问题(database is locked)?
```
问:为什么SQLite并发读写容易出现锁库问题?
答:SQLite使用文件级锁定来管理对数据库的并发访问。这意味着在同一时间,只有一个写操作可以进行,而多个读操作可以并发进行。
SQLite数据库的WAL模式是一种日志机制,它可以减少写入操作时的锁冲突,从而提高并发性能。有以下2种方式启用WAL模式:
```
方式1:在连接字符串中添加:journal mode=Wal
```csharp
using System.Data.SQLite;
// 数据库连接字符串
var connStringBuilder = new SQLiteConnectionStringBuilder
{
DataSource = @".\test.db",
Pooling = true,
BusyTimeout = 30000,
JournalMode = SQLiteJournalModeEnum.Wal
};
var connString = connStringBuilder.ConnectionString;// data source=.\test.db;pooling=True;busytimeout=30000;journal mode=Wal
```
方式2:通过执行SQL命令来设置WAL模式:
```sql
-- 查看 SQLite 数据库日志模式:
PRAGMA journal_mode;
-- 设置 SQLite 数据库日志模式:
PRAGMA journal_mode = 'wal';
-- 查看 WAL 文件中未被同步的页面数:
PRAGMA wal_checkpoint;
```
- 需要注意的是,如果在多个线程或进程中对同一个数据库文件进行写入,且没有适当的同步机制,那么即使使用WAL模式,也可能会出现锁库的情况。使用WAL模式,读和写可以完全地并发执行,不会互相阻塞(但是写之间仍然不能并发)。为了解决并发写入问题,需要增加如下配置(同步锁机制):
```csharp
DbContextConfiguration.Configure(options =>
{
#if UseSqlite
options.SynchronousWriteOptions.Enable = true;// 启用同步写入模式:解决多线程并发写入导致的锁库问题
options.SynchronousWriteOptions.LockTimeout = 30000;// 同步写入锁等待超时时间(单位:毫秒),默认值:10000
options.SynchronousWriteOptions.OnLockTakenFailed = lockTimeout =>
{
Console.WriteLine($"######获取同步写入锁失败({lockTimeout}ms)");
return true;// 返回true:继续执行(仍然可能会发生锁库问题)。返回false:不再继续执行,直接返回默认值
};
#endif
});
```