ABP vNext 批量操作
ABP框架6.0后提供了 InsertManyAsync UpdateManyAsync DeleteManyAsync 批量操作方法
源码:
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 | public interface IBasicRepository<TEntity> : IReadOnlyBasicRepository<TEntity>, IRepository where TEntity : class , IEntity { Task<TEntity> InsertAsync( TEntity entity, bool autoSave = false , CancellationToken cancellationToken = default (CancellationToken)); Task InsertManyAsync( IEnumerable<TEntity> entities, bool autoSave = false , CancellationToken cancellationToken = default (CancellationToken)); Task<TEntity> UpdateAsync( TEntity entity, bool autoSave = false , CancellationToken cancellationToken = default (CancellationToken)); Task UpdateManyAsync( IEnumerable<TEntity> entities, bool autoSave = false , CancellationToken cancellationToken = default (CancellationToken)); Task DeleteAsync(TEntity entity, bool autoSave = false , CancellationToken cancellationToken = default (CancellationToken)); Task DeleteManyAsync( IEnumerable<TEntity> entities, bool autoSave = false , CancellationToken cancellationToken = default (CancellationToken)); } |
我们的IRepository接口是继承自IBasicRepository接口的
public interface IRepository<TEntity> : IReadOnlyRepository<TEntity>, IReadOnlyBasicRepository<TEntity>, IRepository, IBasicRepository<TEntity> where TEntity : class, IEntity
我们一般在**EntityFrameworkCoreModule 层注入一个默认的仓储接口实现
context.Services.AddAbpDbContext<HolidayDbContext>(options => { /* Remove "includeAllEntities: true" to create * default repositories only for aggregate roots */ options.AddDefaultRepositories(includeAllEntities: true); });
默认实现类
public class EfCoreRepository<TDbContext, TEntity> : RepositoryBase<TEntity>, IEfCoreRepository<TEntity> where TDbContext : IEfCoreDbContext where TEntity : class, IEntity
在此类实现了 InsertManyAsync UpdateManyAsync DeleteManyAsync 方法
源码
public override async Task InsertManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default) { var entityArray = entities.ToArray(); var dbContext = await GetDbContextAsync(); cancellationToken = GetCancellationToken(cancellationToken); foreach (var entity in entityArray) { CheckAndSetId(entity); } if (BulkOperationProvider != null) { await BulkOperationProvider.InsertManyAsync<TDbContext, TEntity>( this, entityArray, autoSave, GetCancellationToken(cancellationToken) ); return; } await dbContext.Set<TEntity>().AddRangeAsync(entityArray, cancellationToken); if (autoSave) { await dbContext.SaveChangesAsync(cancellationToken); } }
public override async Task UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default) { cancellationToken = GetCancellationToken(cancellationToken); if (BulkOperationProvider != null) { await BulkOperationProvider.UpdateManyAsync<TDbContext, TEntity>( this, entities, autoSave, GetCancellationToken(cancellationToken) ); return; } var dbContext = await GetDbContextAsync(); dbContext.Set<TEntity>().UpdateRange(entities); if (autoSave) { await dbContext.SaveChangesAsync(cancellationToken); } }
public override async Task DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default) { cancellationToken = GetCancellationToken(cancellationToken); if (BulkOperationProvider != null) { await BulkOperationProvider.DeleteManyAsync<TDbContext, TEntity>( this, entities, autoSave, cancellationToken ); return; } var dbContext = await GetDbContextAsync(); dbContext.RemoveRange(entities); if (autoSave) { await dbContext.SaveChangesAsync(cancellationToken); } }
public IEfCoreBulkOperationProvider BulkOperationProvider => LazyServiceProvider.LazyGetService<IEfCoreBulkOperationProvider>();
代码中的 BulkOperationProvider 就非常关键了 这个就是官方文档上提供的 自定义批量操作
ABP官方原话
如果你有更好的逻辑或使用外部库实现批量操作, 你可以通过实现 IEfCoreBulkOperationProvider
覆写这个逻辑.
- 你可以使用下面的示例模板
public class MyCustomEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency { public async Task DeleteManyAsync<TDbContext, TEntity>( IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { // Your logic here. } public async Task InsertManyAsync<TDbContext, TEntity>( IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { // Your logic here. } public async Task UpdateManyAsync<TDbContext, TEntity>( IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { // Your logic here. } }
官网文档地址 https://docs.abp.io/zh-Hans/abp/latest/Entity-Framework-Core
在实际项目中,不可避免会遇到数据导入的需求,如果是大量数据导入,就必须引用批量处理的功能;efcore本身不提供批量处理功能,而abp框架虽然提供InsertMany和UpdateMany方法,但本质上仍然是分解成单条insert和update操作,在处理稍大量的数据时,耗时就会明显增加;如果你使用的是SqlServer、PostgreSQL或SQLite,可以使用官方推荐的方法实现批处理,你只需要引用borisdj / EFCore.BulkExtensions并编写IEfCoreBulkOperationProvider的提供程序即可
Install-Package EFCore.BulkExtensions
public class BulkExtensionsEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency { public BulkExtensionsEfCoreBulkOperationProvider() { } public async Task DeleteManyAsync<TDbContext, TEntity>(IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { var dbContext = await repository.GetDbContextAsync(); await dbContext.BulkDeleteAsync(entities.ToList(), cancellationToken: cancellationToken); } public 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(); await dbContext.BulkInsertAsync(entities.ToList(), cancellationToken: cancellationToken); } public async Task UpdateManyAsync<TDbContext, TEntity>(IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { var dbContext = await repository.GetDbContextAsync(); await dbContext.BulkUpdateAsync(entities.ToList(), cancellationToken: cancellationToken); } }
这样常规调用InsertManyAsync或UpdateManyAsync时就可以实现批量操作。但是borisdj / EFCore.BulkExtensions不支持Mysql数据(注意:在6.0以后的版本已经支持Mysql),因此不能使用这种方法,这里我使用的是另一个扩展包yang-er/efcore-ext,跟EFCore.BulkExtensions算是一脉相承,并且添加了对mysql的支持,理论上也可以跟上面一样编写提供程序来实现,但是由于实现原理有点不一样,这样写生成不了Sql语句,还是只能换一个思路来实现了。
public class BulkExtensionsEfCoreBulkOperationProvider : IEfCoreBulkOperationProvider, ITransientDependency { public BulkExtensionsEfCoreBulkOperationProvider() { } public async Task DeleteManyAsync<TDbContext, TEntity>(IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { var dbContext = await repository.GetDbContextAsync(); await dbContext.BulkDeleteAsync(entities.ToList(), cancellationToken: cancellationToken); } public 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(); await dbContext.BulkInsertAsync(entities.ToList(), cancellationToken: cancellationToken); } public async Task UpdateManyAsync<TDbContext, TEntity>(IEfCoreRepository<TEntity> repository, IEnumerable<TEntity> entities, bool autoSave, CancellationToken cancellationToken) where TDbContext : IEfCoreDbContext where TEntity : class, IEntity { var dbContext = await repository.GetDbContextAsync(); await dbContext.BulkUpdateAsync(entities.ToList(), cancellationToken: cancellationToken); } }
如果是使用Mysql : 需要在客户端数据库连接地址添加 AllowLoadLocalInfile=true; 配置
To use MySqlBulkLoader.Local=true, set AllowLoadLocalInfile=true in the connection string
See https://fl.vu/mysql-load-data
开始实例演示如何在abp上使用efcore-ext扩展包:
参考文章 https://blog.csdn.net/duanzilin/article/details/123253444
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
2015-10-21 IIS配置文件的XML格式不正确 applicationHost.config崩溃 恢复解决办法