diff --git a/docs/content/user-guide/zh/getting-started/quick-start.md b/docs/content/user-guide/zh/getting-started/quick-start.md index 8c6ddc51..d43e49e2 100644 --- a/docs/content/user-guide/zh/getting-started/quick-start.md +++ b/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 默认当前控制台执行目录 ``` ### 后端 diff --git a/docs/content/user-guide/zh/infrastructure/batch.md b/docs/content/user-guide/zh/infrastructure/batch.md new file mode 100644 index 00000000..d02d23f3 --- /dev/null +++ b/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 +{ + /// + /// 批量新增 + /// + /// + /// + /// - mysql启用:SET GLOBAL local_infile = true; + /// + /// + /// - 数据库连接字符串需要加上:AllowLoadLocalInfile=true + /// + /// - abp的审计字段需要手动赋值,比如创建人,创建时间,或者使用AuditPropertySetter + /// + /// - 只支持单表,比如有一个Blog表和Post表一对多关系,需要调用两次 InsertManyAsync + /// + /// + public virtual async Task InsertManyAsync(IEfCoreRepository repository, IEnumerable 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 _postRepository; + private readonly IRepository _commentRepository; + private readonly IdentityRoleManager _identityRoleManager; + + public BlogAppService(IBlogRepository blogRepository, IdentityRoleManager identityRoleManager, IRepository postRepository, IRepository commentRepository) + { + _blogRepository = blogRepository; + _identityRoleManager = identityRoleManager; + _postRepository = postRepository; + _commentRepository = commentRepository; + } + + + /// + /// 批量插入10000条数据 + /// + public async Task CreateAsync(int qty = 10000) + { + // mock 数据 + var list = GenFu.GenFu.ListOf(qty); + var stopwatch = new Stopwatch(); + stopwatch.Start(); + await _blogRepository.InsertManyAsync(list); + stopwatch.Stop(); + Logger.LogInformation($"批量插入{list.Count}条,耗时(单位:毫秒):{stopwatch.ElapsedMilliseconds}"); + } + + /// + /// 批量插入10000条数据 + /// + public async Task CreateAllAsync(int qty = 10000) + { + // mock 数据 + var blogs = GenFu.GenFu.ListOf(qty); + var posts = new List(); + var comments = new List(); + // 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}"); + } + + /// + /// 批量插入10000条数据,并且测试事务是否和其它业务逻辑保持一致 + /// 测试结果:在一个事务内 + /// + public async Task CreateTransactionAsync(int qty = 10) + { + var list = GenFu.GenFu.ListOf(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"); + } + + /// + /// 批量更新 + /// + /// + public async Task BatchUpdateAsync(int qty = 10000) + { + using (var uow = UnitOfWorkManager.Begin(new AbpUnitOfWorkOptions(true), true)) + { + var list = GenFu.GenFu.ListOf(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}"); + } + + /// + /// 批量删除 + /// + /// + public async Task BatchDeleteAsync(int qty = 10000) + { + using (var uow = UnitOfWorkManager.Begin(new AbpUnitOfWorkOptions(true), true)) + { + var list = GenFu.GenFu.ListOf(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}"); + } +} +``` diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index aabdd9d3..bc4ee404 100644 --- a/docs/mkdocs.yml +++ b/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