Browse Source

docs: 添加EFCore批量操作指南

pull/126/head
wangjun 3 years ago
parent
commit
0b24e9d0b5
  1. 22
      docs/content/user-guide/zh/getting-started/quick-start.md
  2. 176
      docs/content/user-guide/zh/infrastructure/batch.md
  3. 47
      docs/mkdocs.yml

22
docs/content/user-guide/zh/getting-started/quick-start.md

@ -31,7 +31,7 @@ docker run -d --name myrabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAU
### 安装 Cli 工具
- [仓库地址](https://github.com/WangJunZzz/Lion.AbpPro.Cli)
- 安装 Cli
```bash
dotnet tool install Lion.AbpPro.Cli -g
@ -39,24 +39,30 @@ dotnet tool install Lion.AbpPro.Cli -g
### 生成项目
**提供了三个模板生成**
- 生成源码版本
```bash
lion.abp new abp-vnext-pro -c 公司名称 -p 项目名称 -v 版本号(默认LastRelease)
lion.abp new -t pro -c 公司名称 -p 项目名称 -v 版本(默认LastRelease) -o 默认当前控制台执行目录
```
- nuget 包形式的网关基础版本
- abp 自带的所有模块,pro 的通知模块,数据字典模块 以及 ocelot 网关。
```bash
lion.abp new -t pro.all -c 公司名称 -p 项目名称 -v 版本(默认LastRelease) -o 默认当前控制台执行目录
```
- nuget 包形式的基础版本,包括 abp 自带的所有模块,已经 pro 的通知模块,数据字典模块 以及 ocelot 网关
- nuget 包形式的基础版本
- abp 自带的所有模块,pro 的通知模块,数据字典模块 无 ocelot 网关
```bash
lion.abp new abp-vnext-pro-basic -c 公司名称 -p 项目名称 -v 版本(默认LastRelease)
lion.abp new -t pro.simplify -c 公司名称 -p 项目名称 -v 版本(默认LastRelease) -o 默认当前控制台执行目录
```
- nuget 包形式的基础版本,包括 abp 自带的所有模块,已经 pro 的通知模块,数据字典模块 无 ocelot 网关
- 模块
```bash
lion.abp new abp-vnext-pro-basic-no-ocelot -c 公司名称 -p 项目名称 -v 版本(默认LastRelease)
lion.abp new -t pro.module -c 公司名称 -p 项目名称 -v 版本(默认LastRelease) -o 默认当前控制台执行目录
```
### 后端

176
docs/content/user-guide/zh/infrastructure/batch.md

@ -0,0 +1,176 @@
# 批量操作
EFCore7.0 之后,提供了批量更新和批量删除,但是不提供批量新增,
[微软 EFCore 批量操作文档](https://learn.microsoft.com/zh-cn/ef/core/saving/execute-insert-update-delete)
## 安装
- 添加以下 NuGet 包到你的项目
- Lion.AbpPro.EntityFrameworkCore.Mysql
- 添加 [DependsOn(typeof(LionAbpProEntityFrameworkCoreMysqlModule))] 到你的项目模块类.
## 原理
- 通过 MySqlBulkCopy 来实现批量新增[官方文档](https://mysqlconnector.net/api/mysqlconnector/mysqlbulkcopytype/)
- 实现 Abp 批量操作接口 IEfCoreBulkOperationProvider
```csharp
public class EfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency
{
/// <summary>
/// 批量新增
/// </summary>
///<remarks>
/// <para>
/// - mysql启用:SET GLOBAL local_infile = true;
/// </para>
/// <para>
/// - 数据库连接字符串需要加上:AllowLoadLocalInfile=true
/// </para>
/// - abp的审计字段需要手动赋值,比如创建人,创建时间,或者使用AuditPropertySetter
/// <para>
/// - 只支持单表,比如有一个Blog表和Post表一对多关系,需要调用两次 InsertManyAsync
/// </para>
/// </remarks>
public virtual async Task InsertManyAsync<TDbContext, TEntity>(IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken)
where TDbContext : IEfCoreDbContext where TEntity : class, IEntity
{
var dbContext = await repository.GetDbContextAsync();
var dbTransaction = dbContext.Database.CurrentTransaction?.GetDbTransaction();
await dbContext.BulkInsertAsync(entities, dbTransaction as MySqlTransaction, cancellationToken);
if (autoSave)
{
await dbContext.SaveChangesAsync(cancellationToken);
}
}
}
```
## 示例
```csharp
namespace Lion.AbpPro.EntityFrameworkCore.Tests.Services;
public class BlogAppService : ApplicationService
{
private readonly IBlogRepository _blogRepository;
private readonly IRepository<Post, Guid> _postRepository;
private readonly IRepository<Comment, Guid> _commentRepository;
private readonly IdentityRoleManager _identityRoleManager;
public BlogAppService(IBlogRepository blogRepository, IdentityRoleManager identityRoleManager, IRepository<Post, Guid> postRepository, IRepository<Comment, Guid> commentRepository)
{
_blogRepository = blogRepository;
_identityRoleManager = identityRoleManager;
_postRepository = postRepository;
_commentRepository = commentRepository;
}
/// <summary>
/// 批量插入10000条数据
/// </summary>
public async Task CreateAsync(int qty = 10000)
{
// mock 数据
var list = GenFu.GenFu.ListOf<Blog>(qty);
var stopwatch = new Stopwatch();
stopwatch.Start();
await _blogRepository.InsertManyAsync(list);
stopwatch.Stop();
Logger.LogInformation($"批量插入{list.Count}条,耗时(单位:毫秒):{stopwatch.ElapsedMilliseconds}");
}
/// <summary>
/// 批量插入10000条数据
/// </summary>
public async Task CreateAllAsync(int qty = 10000)
{
// mock 数据
var blogs = GenFu.GenFu.ListOf<Blog>(qty);
var posts = new List<Post>();
var comments = new List<Comment>();
// blog和post一对多,post和comment一对多
// 有主外键关系,所以循环mock数据
foreach (var blog in blogs)
{
posts.Add(new Post(GuidGenerator.Create(), blog.Id, "name"));
}
foreach (var post in posts)
{
comments.Add(new Comment(GuidGenerator.Create(), 1, post.Id, "content"));
}
var stopwatch = new Stopwatch();
stopwatch.Start();
// 需要执行三次,不会因为ef有定义关系而一次性插入posts和comments
await _blogRepository.InsertManyAsync(blogs);
await _postRepository.InsertManyAsync(posts);
await _commentRepository.InsertManyAsync(comments);
stopwatch.Stop();
Logger.LogInformation($"批量插入blogs:{blogs.Count},posts:{posts.Count},comments:{comments.Count}条,耗时(单位:毫秒):{stopwatch.ElapsedMilliseconds}");
}
/// <summary>
/// 批量插入10000条数据,并且测试事务是否和其它业务逻辑保持一致
/// 测试结果:在一个事务内
/// </summary>
public async Task CreateTransactionAsync(int qty = 10)
{
var list = GenFu.GenFu.ListOf<Blog>(qty);
var stopwatch = new Stopwatch();
stopwatch.Start();
await _blogRepository.InsertManyAsync(list);
stopwatch.Stop();
Logger.LogInformation($"批量插入{list.Count}条,耗时(单位:毫秒):{stopwatch.ElapsedMilliseconds}");
await _identityRoleManager.CreateAsync(new IdentityRole(GuidGenerator.Create(), GuidGenerator.Create().ToString()));
throw new UserFriendlyException("test");
}
/// <summary>
/// 批量更新
/// <see cref="https://learn.microsoft.com/zh-cn/ef/core/saving/execute-insert-update-delete"/>
/// </summary>
public async Task BatchUpdateAsync(int qty = 10000)
{
using (var uow = UnitOfWorkManager.Begin(new AbpUnitOfWorkOptions(true), true))
{
var list = GenFu.GenFu.ListOf<Blog>(qty);
await _blogRepository.InsertManyAsync(list);
await uow.CompleteAsync();
}
var stopwatch = new Stopwatch();
stopwatch.Start();
var dbSet = await _blogRepository.GetDbSetAsync();
await dbSet.ExecuteUpdateAsync(setters => setters
.SetProperty(x => x.IsDeleted, x => true)
.SetProperty(x => x.Name, x => "test"));
stopwatch.Stop();
Logger.LogInformation($"批量更新{qty}条,耗时(单位:毫秒):{stopwatch.ElapsedMilliseconds}");
}
/// <summary>
/// 批量删除
/// <see cref="https://learn.microsoft.com/zh-cn/ef/core/saving/execute-insert-update-delete"/>
/// </summary>
public async Task BatchDeleteAsync(int qty = 10000)
{
using (var uow = UnitOfWorkManager.Begin(new AbpUnitOfWorkOptions(true), true))
{
var list = GenFu.GenFu.ListOf<Blog>(qty);
await _blogRepository.InsertManyAsync(list);
await uow.CompleteAsync();
}
var stopwatch = new Stopwatch();
stopwatch.Start();
var dbSet = await _blogRepository.GetDbSetAsync();
await dbSet.ExecuteDeleteAsync();
stopwatch.Stop();
Logger.LogInformation($"批量删除{qty}条,耗时(单位:毫秒):{stopwatch.ElapsedMilliseconds}");
}
}
```

47
docs/mkdocs.yml

@ -70,28 +70,31 @@ markdown_extensions:
nav:
- Home: index.md
- 文档(中文):
- 入门:
- 快速开始: user-guide/zh/getting-started/quick-start.md
- 介绍: user-guide/zh/getting-started/introduction.md
- 贡献: user-guide/zh/getting-started/contributing.md
- 基础设施:
- 配置: user-guide/zh/infrastructure/config.md
- 前端: user-guide/zh/infrastructure/frontend.md
- FreeSql: user-guide/zh/infrastructure/freesql.md
- 分布式事件: user-guide/zh/infrastructure/cap.md
- 应用模块:
- 基础模块: user-guide/zh/modules/basic.md
- 设置(Setting): user-guide/zh/modules/setting.md
- 数据字典: user-guide/zh/modules/dic.md
- 文件管理: user-guide/zh/modules/file.md
- 实时通信: user-guide/zh/modules/signalr.md
- 扩展:
- 统一返回值格式: user-guide/zh/extension/统一返回值格式.md
- Magicodes.IE: user-guide/zh/extension/MagicodesIE.md
- 部署:
- Docker: user-guide/zh/deploy/docker.md
- Github自动化部署: user-guide/zh/deploy/github.md
- 入门:
- 快速开始: user-guide/zh/getting-started/quick-start.md
- 介绍: user-guide/zh/getting-started/introduction.md
- 贡献: user-guide/zh/getting-started/contributing.md
- 基础设施:
- 配置: user-guide/zh/infrastructure/config.md
- 前端: user-guide/zh/infrastructure/frontend.md
- FreeSql: user-guide/zh/infrastructure/freesql.md
- 分布式事件: user-guide/zh/infrastructure/cap.md
- 批量操作: user-guide/zh/infrastructure/batch.md
- 应用模块:
- 基础模块: user-guide/zh/modules/basic.md
- 设置(Setting): user-guide/zh/modules/setting.md
- 数据字典: user-guide/zh/modules/dic.md
- 文件管理: user-guide/zh/modules/file.md
- 实时通信: user-guide/zh/modules/signalr.md
- 扩展:
- 统一返回值格式: user-guide/zh/extension/统一返回值格式.md
- Magicodes.IE: user-guide/zh/extension/MagicodesIE.md
- 常见问题:
- 编译: user-guide/zh/problem/problem.md
- EFCore: user-guide/zh/problem/ef.md
- 部署:
- Docker: user-guide/zh/deploy/docker.md
- Github自动化部署: user-guide/zh/deploy/github.md
- 常见问题:
- 编译: user-guide/zh/problem/problem.md
- EFCore: user-guide/zh/problem/ef.md

Loading…
Cancel
Save