EFCore.BulkExtensions 簡介
EntityFrameworkCore擴展:批量操作(插入,更新,刪除,讀取,更新,同步)和批處理(刪除,更新)。
庫是輕量級的,並且非常高效,具有所有最常用的CRUD操作。
在Microsoft推薦的EFcore擴展 Top 20。
當前版本使用的是EF Core 3.1,目前支持Microsoft SQL Server(2008+)和SQLite。
它針對NetStandard 2.0,因此可以用於針對NetCore(2.0+)或NetFramework(4.6.1+)的項目。
3.1.0和3.0.0之間的版本使用的是EF Core 3.0,並且以NetStandard 2.1為目標,因此只能在NetCore(3.0+)上使用。
3.0之前的版本(最后2.6.4)針對NetStandard 2.0,可以與NetCore(2.2)或NetFramework(4.6.1+)一起使用。
EFCore / v.Nuget:EFCore2.1 / v2.4.1 EFCore2.0 / v2.0.8,對於EF Core 1.x,請使用1.1.0(以NetStandard 1.4為目標)
請使用SqlBulkCopy進行插入,更新/刪除合並具有原始Sql的 MERGE BulkInsert 。
對於SQLite,沒有BulkCopy,而是庫將普通SQL與UPSERT結合使用。
批量測試不能具有UseInMemoryDb,因為InMemoryProvider不支持特定於關系的方法。
在新版nuget上,用軟件包管理器控制台命令安裝:
Install-Package EFCore.BulkExtensions
用法
用法非常簡單明了。
批量擴展是在DbContext類上進行的,可以像這樣使用(支持常規和異步方法):
context.BulkInsert(entitiesList); context.BulkInsertAsync(entitiesList); context.BulkUpdate(entitiesList); context.BulkUpdateAsync(entitiesList); context.BulkDelete(entitiesList); context.BulkDeleteAsync(entitiesList); context.BulkInsertOrUpdate(entitiesList); context.BulkInsertOrUpdateAsync(entitiesList); //Upsert context.BulkInsertOrUpdateOrDelete(entitiesList); context.BulkInsertOrUpdateOrDeleteAsync(entitiesList); //Sync context.BulkRead(entitiesList); context.BulkReadAsync(entitiesList); context.Truncate(); context.TruncateAsync();
批處理擴展是在IQueryable DbSet上進行的,可以在以下代碼段中使用。
它們以純sql的形式完成,並且不檢查某些對象是否已預先裝入內存並被跟蹤。(updateColumns是可選參數,其中的PropertyNames在我們需要更新為其默認值時明確添加)
// Delete context.Items.Where(a => a.ItemId > 500).BatchDelete(); context.Items.Where(a => a.ItemId > 500).BatchDeleteAsync(); // Update (using Expression arg.) supports Increment/Decrement context.Items.Where(a => a.ItemId <= 500).BatchUpdate(a => new Item { Quantity = a.Quantity + 100 }); // can be as value '+100' or as variable '+incrementStep' (int incrementStep = 100;) // Update (via simple object) context.Items.Where(a => a.ItemId <= 500).BatchUpdate(new Item { Description = "Updated" }); context.Items.Where(a => a.ItemId <= 500).BatchUpdateAsync(new Item { Description = "Updated" }); // Update (via simple object) - requires additional Argument for setting to Property default value var updateColumns = new List { nameof(Item.Quantity) }; // Update 'Quantity' to default value('0'-zero) var q = context.Items.Where(a => a.ItemId <= 500); int affected = q.BatchUpdate(new Item { Description = "Updated" }, updateColumns);//result assigned to variable
批量操作
直接使用這些操作時,每個操作都是獨立的事務,並且會自動提交。
如果我們需要在單個過程中執行多個操作,則應使用顯式事務,例如:
using (var transaction = context.Database.BeginTransaction()) { context.BulkInsert(entitiesList); context.BulkInsert(subEntitiesList); transaction.Commit(); }
在abpvnext實際項目中,有EFCore中應用,效率非常高,用前端vue導入調用后端此方式的接口進行導入100W+級數據量(Excel格式),在一分鍾內(有相關檢驗操作,並非簡單插入操作)導入成功!
private readonly IRepository<BranchRole> _branchRoleRepository; public UserAppService( IRepository<BranchRole> branchRoleRepository) { _branchRoleRepository = branchRoleRepository; } //方法中核心代碼如下 await _branchRoleRepository.GetDbContext().BulkInsertAsync<BranchRole>(entityList);
1. 說明
通過Nuget安裝程序集【EFCore.BulkExtensions】,該程序集目前版本【3.1.0】,支持CRUD操作,支持的數據庫僅有:SQLServer和SQLite,它是免費開源的。
GitHub地址:https://github.com/borisdj/EFCore.BulkExtensions
2. 用法說明
(1).Bulk相關(一條操作一個事務,均是傳入實體)
A.增加:BulkInsert
B.修改:BulkUpdate,需要傳入完整實體,不傳的字段就會被更新為空
C.增加或修改:BulkInsertOrUpdate (主鍵存在執行update,不存在執行insert)
D.刪除:BulkDelete 和 Truncate(刪除整張表)
//1. 增加 List<T_UserInfor> ulist1 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), userName = "ypf", userSex = "男", userAge = 111, addTime = DateTime.Now }; ulist1.Add(userInfor); } dbContext.BulkInsert(ulist1); //2. 修改 List<T_UserInfor> ulist2 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { //此處不寫的字段就會被更新成null了 T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), userName = "ypf1", }; ulist2.Add(userInfor); } dbContext.BulkUpdate(ulist2); //3. 刪除 List<T_UserInfor> ulist3 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { //此處不寫的字段就會被更新成null了 T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), }; ulist3.Add(userInfor); } dbContext.BulkDelete(ulist3);
(2).Batch相關
A.條件刪除:BatchDelete
B.條件更新:BatchUpdate (可以基於原有數據)
PS:以上方法均支持Async異步方法。
//4.條件刪除 int count1 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchDelete(); //5. 條件更新 //5.1 基於原有數據改 int count2 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(a => new T_UserInfor() { userAge = a.userAge + 1 }); //5.2 直接改成新數據 int count3 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(new T_UserInfor() { userSex = "女" });
(3).事務
和正常用法一樣, using(var transaction = dbContext.Database.BeginTransaction())包裹,有using的情況下, catch中不用寫rollback,走完using如果報錯會自動回滾。
這種寫法僅支持SQLServer,Sqlite中寫法不一樣,注意以下方式在純的EFCore中應用是可以的,但是不適用於abpvnext,原因接下來會做說明。
using (var transaction = dbContext.Database.BeginTransaction()) { try { List<T_UserInfor> ulist1 = new List<T_UserInfor>(); for (int i = 0; i < 100; i++) { T_UserInfor userInfor = new T_UserInfor() { id = i.ToString(), userName = "ypf", userSex = "男", userAge = 111, addTime = DateTime.Now }; ulist1.Add(userInfor); } dbContext.BulkInsert(ulist1); int count2 = dbContext.T_UserInfor.Where(u => u.id.StartsWith("2")).BatchUpdate(a => new T_UserInfor() { id = a.id + "fsdfsdfsdfsfdsadfsadfsdfsfsafsfsdfsdfsdfsdf" }); //統一提交 transaction.Commit(); } catch (Exception ex) { //using包裹不需要手寫rollback Console.WriteLine(ex.Message); } }
說明:在abpvnext中顯示加入以上數據庫事務(例如:var transaction = dbContext.Database.BeginTransaction()),會報錯,大致的意思是事務沖突,主要是abpvnext中的工作單元導致,即使禁止UOW也報錯,原因未知待研究,目前在項目中的應用就是不顯示加入context.Database.BeginTransaction事務。
using (var transaction = context.Database.BeginTransaction()) { context.BulkInsert(entities1List); context.BulkInsert(entities2List); transaction.Commit(); }
(4).相關配置
可以配置的參數有:PreserveInsertOrder, SetOutputIdentity, BatchSize, NotifyAfter, BulkCopyTimeout, EnableStreaming, UseTempDB, TrackingEntities,UseOnlyDataTable, WithHoldlock, CalculateStats, StatsInfo, PropertiesToInclude, PropertiesToExclude, UpdateByProperties, SqlBulkCopyOptions . 根據自己的情況選擇使用吧
配置文件:實例化BulkConfig,在實際應用中遇到過插入100W+級數據,設置了配置文件比如BatchSize = 10000,發現有時還會出現數據庫連接超時的問題(相關web.config也做了超時配置),按照其原理是每插入1W連接一次數據,應該不會超時才對,但是就是報超時,最后的解決辦法就是去掉了相關配置,好使了。如果以上文章對您有一點作用,請幫點個推薦,謝謝。
var bulkConfig = new BulkConfig { SetOutputIdentity = true, BatchSize = 4000 };
context.BulkInsert(entList, bulkConfig);
context.BulkInsertOrUpdate(entList, new BulkConfig { SetOutputIdentity = true }); context.BulkInsertOrUpdate(entList, b => b.SetOutputIdentity = true); // example of BulkConfig set with Action arg.