ABP vNext 批量操作
ABP框架6.0后提供了 InsertManyAsync UpdateManyAsync DeleteManyAsync 批量操作方法
源码:
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