EFCore批量操作,你真的清楚吗
背景
EntityFramework Core有許多新的特性,其中一個重要特性便是批量操作。批量操作意味著不需要為每次Insert/Update/Delete操作發送單獨的命令,而是在一次SQL請求中發送批量組合指令。
EFCore批量操作實踐
批處理是期待已久的功能,社區多次提出要求。現在EFCore支持開箱即用確實很棒,可以提高應用程序的性能和速度。
1
對比實踐
以常見的批量插入為例,使用SQL Server Profiler觀察產生并執行的SQL語句。
using?(var?c=?new?SampleDBContext())
{
????c.Categories.Add(new?Category()?{?CategoryID?=?1,?CategoryName?=?"Clothing"?});
????c.Categories.Add(new?Category()?{?CategoryID?=?2,?CategoryName?=?"Footwear"?});
????c.Categories.Add(new?Category()?{?CategoryID?=?3,?CategoryName?=?"Accessories"?});
????c.SaveChanges();
}
當執行SaveChanges(), 從SQL Profiler追溯到的SQL:
exec?sp_executesql?N'SET?NOCOUNT?ON;INSERT?INTO?[Categories]?([CategoryID],?[CategoryName])VALUES?(@p0,?@p1),(@p2,?@p3),(@p4,?@p5);',N'@p0?int,@p1?nvarchar(4000),@p2?int,@p3?nvarchar(4000),@p4?int,@p5?nvarchar(4000)',
@p0=1,@p1=N'Clothing',@p2=2,@p3=N'Footwear',@p4=3,@p5=N'Accessories'
如你所見,批量插入沒有產生3個獨立的語句,而是被組合為一個傳參存儲過程腳本(用列值作為參數);如果使用EF6執行相同的代碼,則在SQL Server Profiler中將看到3個獨立的插入語句 。下面是EFCore、EF6批量插入的對比截圖:
① 就性能和速度而言,EFCore批量插入更具優勢
② 若數據庫是針對云部署,EF6運行這些查詢,還將產生額外的流量成本
經過驗證:EFCore批量更新、批量刪除功能,EFCore均發出了使用sp_executesql存儲過程+批量參數構建的SQL腳本。
2
深入分析
起關鍵作用的存儲過程sp_executesql:可以多次執行的語句或批處理 (可帶參)
-?Syntax?for?SQL?Server,?Azure?SQL?Database,?Azure?SQL?Data?Warehouse,?Parallel?Data?Warehouse????
sp_executesql?[?@stmt?=?]?statement??
[???
??{?,?[?@params?=?]?N'@parameter_name?data_type?[?OUT?|?OUTPUT?][?,...n?]'?}???
?????{?,?[?@param1?=?]?'value1'?[?,...n?]?}??
]
注意官方限制:
The amount of data that can be passed by using this method is limited by the number of parameters allowed. SQL Server procedures can have, at most, 2100 parameters. Server-side logic is required to assemble these individual values into a table variable or a temporary table for processing.??? ? ?// SQL存儲過程最多可使用2100個參數
3
豁然開朗
SqlServer sp_executesql存儲過程最多支持2100個批量操作形成的列值參數,所以遇到很大數量的批量操作,EFCore SqlProvider會幫我們將批量操作分塊傳輸,這也是我們在實際大批量使用時看到分塊發送的原因。
EFCore開放了【配置關系型數據庫批量操作大小】:
protected?override?void?OnConfiguring(DbContextOptionsBuilder?optionbuilder){
????string?sConnString?=?@"Server=localhost;Database=EFSampleDB;Trusted_Connection=true;";
????optionbuilder.UseSqlServer(sConnString?,?b?=>?b.MaxBatchSize(1));?
???//?批量操作的SQL語句數量,也可設定為1禁用批量插入
}
總結
① EFCore 相比EF6,已經支持批量操作,能有效提高應用程序的性能
② EFCore的批量操作能力,由對應的DataBaseProvider支撐(Provider實現過程跟背后的存儲載體密切相關);關注SQL存儲過程sp_executesql,官方明文顯示批量操作的列值參數最多2100個,這個關鍵因素決定了在大批量操作的時候 依舊會被分塊傳輸。
③ 另外一個批量操作的方法,這里也點一下:構造Rawsql 【EFCore也支持Rawsql】
sqlite不支持存儲過程,為批量插入提高性能,可采用此方案:
var?insertStr?=?new?StringBuilder();
insertStr.AppendLine("insert?into?ProfileUsageCounters?(profileid,datetime,quota,usage,natureusage)?values");
var?txt?=?insertStr.AppendLine(string.Join(',',?usgaeEntities.ToList().Select(x?=>
{
???????return?$"({x.ProfileId},{x.DateTime},{x.Quota},{x.Usage},{x.NatureUsage})";
}).ToArray()));
await?_context.Database.ExecuteSqlCommandAsync(txt.ToString());
+?https://github.com/aspnet/EntityFrameworkCore/issues/6604
+?https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/table-valued-parameters?redirectedfrom=MSDN
總結
以上是生活随笔為你收集整理的EFCore批量操作,你真的清楚吗的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用ASP.NET Core 3.x 构
- 下一篇: asp.net core 自定义 Pol