9.4 仓储模式

9.4.1 什么是仓储#

在领域层和数据映射层的中介,使用类似集合的接口来存取领域对象,实际上,仓储被用于领域对象在数据库上的操作(实体 Entity 和值对象 Value types)。一般来说,我们针对不同的实体(或聚合根 Aggregate Root)会创建相对应的仓储。

简单来说,仓储就是数据存取操作的载体,但不限定于数据库。

9.4.1 内置仓储#

Furion 框架内置了一个数据库操作的仓储,方便大家拓展和集成:

9.4.1.1 非泛型超级仓储#

  • IRepository:默认非泛型仓储接口,支持切换到任何仓储
  • EFCoreRepository:默认非泛型仓储实现

9.4.1.2 泛型实体仓储#

  • IRepository<TEntity>:默认数据库实体仓储接口
  • EFCoreRepository<TEntity>:默认数据库实体仓储实现

9.4.1.3 泛型多数据库实体仓储#

  • IRepository<TEntity, TDbContextLocator>:任意数据库的实体仓储接口
  • EFCoreRepository<TEntity, TDbContextLocator>:任意数据库的实体仓储实现

9.4.1.4 Sql 操作仓储#

  • ISqlRepository:默认数据库 Sql 操作仓储接口
  • SqlRepository:默认数据库 Sql 操作仓储实现

9.4.1.5 多数据库 Sql 操作仓储#

  • ISqlRepository<TDbContextLocator>:任意数据库的 Sql 操作仓储接口
  • SqlRepository<TDbContextLocator>:任意数据库的 Sql 操作仓储实现

9.4.1.6 只读实体仓储(支持多库)#

  • IReadableRepository<TEntity>:默认数据库只读实体仓储接口
  • IReadableRepository<TEntity, TDbContextLocator>:多数据库只读实体仓储实现

9.4.1.7 只写实体仓储(支持多库)#

  • IWritableRepository<TEntity>:默认数据库只写实体仓储接口
  • IWritableRepository<TEntity, TDbContextLocator>:多数据库只写实体仓储实现

9.4.1.8 只允许新增实体仓储(支持多库)#

  • IInsertableRepository<TEntity>:默认数据库只允许新增的实体仓储接口
  • IInsertableRepository<TEntity, TDbContextLocator>:多数据库只允许新增的实体仓储实现

9.4.1.9 只允许更新实体仓储(支持多库)#

  • IUpdateableRepository<TEntity>:默认数据库只允许更新的实体仓储接口
  • IUpdateableRepository<TEntity, TDbContextLocator>:多数据库只允许更新的实体仓储实现

9.4.1.10 只允许删除实体仓储(支持多库)#

  • IDeletableRepository<TEntity>:默认数据库只允许删除的实体仓储接口
  • IDeletableRepository<TEntity, TDbContextLocator>:多数据库只允许删除的实体仓储实现

9.4.1.11 只允许拓展操作实体仓储(支持多库)#

  • IOperableRepository<TEntity>:默认数据库只允许拓展操作实体仓储接口
  • IOperableRepository<TEntity, TDbContextLocator>:多数据库只允许拓展操作实体仓储实现

9.4.1.12 只允许 Sql 查询仓储(支持多库)#

  • ISqlReaderRepository:默认数据库只允许 Sql 查询仓储接口
  • ISqlReaderRepository<TDbContextLocator>:多数据库只允许 Sql 查询仓储实现

9.4.1.13 只允许 Sql 非查询仓储(支持多库)#

  • ISqlExecutableRepository:默认数据库只允许 Sql 非查询仓储接口
  • ISqlExecutableRepository<TDbContextLocator>:多数据库只允许 Sql 非查询仓储实现

9.4.1.14 读写分离仓储#

  • IMSRepository:最多支持 一主 7 从 仓储

9.4.1.15 定位器仓储#

  • IDbRepository<TDbContextLocator>:初始化特定数据库仓储

9.4.2 仓储使用#

Furion 提供了非常多的方式创建仓储,目的是为了让大家可以在不同的场景中使用。

9.4.2.1 构造函数注入#

private readonly IRepository<Person> _personRepository;
public FurService(IRepository<Person> personRepository)
{
_personRepository = personRepository;
}

9.4.2.2 方法参数注入#

public async Task<List<PersonDto>> GetAll([FromServices] IRepository<Person> repository, string keyword)
{
var persons = await repository.AsQueryable().ToListAsync();
return persons.Adapt<List<PersonDto>>();
}

9.4.2.3 Db.GetRepository 获取#

// 非泛型仓储
var repository = Db.GetRepository();
// 泛型仓储
var repository = Db.GetRepository<Person>();
// Sql 仓储
var sqlRepository = Db.GetSqlRepository();
特别说明

不管采用哪种方式,Furion 都保证了仓储一次请求唯一性。同时 Db.GetRepository<TEntity>() 方式支持任何静态类中使用。

9.4.3 仓储高级用法#

9.4.3.1 动态切换实体仓储#

var userRepository = personRepository.Change<User>();

9.4.3.2 动态切换仓储类型#

比如,读写分离/主从库仓储:

// 只读仓储
var readRepository = personRepository.Constraint<IReadableRepository<User>>();
// 只写仓储
var writeRepository = personRepository.Constraint<IWritableRepository<User>>();
小知识

.Constraint 支持切换任何仓储类型。

9.4.3.3 获取 Sql 操作仓储#

var sqlRepository = repository.Sql();

9.4.4 多数据库操作#

Furion 通过 DbContextLocator 数据库上下文定位器实现多种数据库操作,可以随意切换数据库

9.4.4.1 动态切换多个数据库#

动态切换数据库#

// 切换到 MSSQL 操作 Person表
var mssqlRepository = repository.Change<Person, MsSqlDbContextLocator>();
// 切换到 MySql 操作 Person表
var mysqlRepository = repository.Change<Person, MySqlDbContextLocator>();
// 切换到 Sqlite 操作 Person表
var sqliteRepository = repository.Change<Person, SqliteDbContextLocator>();
// 其他更多数据库一样的操作

另外任何仓储或实体配置都支持多个数据库同时操作#

仓储方式

IRepository<Person, MsSqlDbContextLocator> mssqlRepository
ISqlRepository<MsSqlDbContextLocator> mssqlRepository;

动态 sql 方式

"select * from person".Change<MsSqlDbContextLocator>().SqlQuery();

实体配置方式

public class User:Entity<MsSqlDbContextLocator, MySqlDbContextLocator>
{
}

Sql 代理方式

[SqlFunction("funcName", DbContextLocator = typeof(MySqlDbContextLocator))]
int GetAge(int id);

Linq 中方式

[QueryableFunction("funcName","dbo", DbContextLocator = typeof(MySqlDbContextLocator))]
string GetName()=> throw Oops.Oh("不支持该数据库操作");

9.4.5 反馈与建议#

与我们交流

给 Furion 提 Issue