背景

EntityFramework Core有许多新的特性,其中一个重要特性是 批量操作。

批量操作意味着不需要为每次Insert/Update/Delete操作发送单独的命令,而是在一次SQL请求中发送批量组合指令。

EFCore批量操作实践

批处理是期待已久的功能,社区多次提出要求。现在EFCore支持开箱即用确实很棒,可以提高应用程序的性能和速度。

P1 对比实践

下面以常见的批量插入为例,使用SQL Server Profiler 观察实际产生并执行的SQL语句。

另一种观察EFCore生成sql的方法:

添加Nlog支持,关注Microsoft.EntityFrameworkCore.Database.Command 日志

定义插入模型Category, 插入4个实体,这里为什么强调4,请留意下文。

public class Category

{

public int Id { get; set; }

public int CategoryID { get; set; }

public string CategoryName { get; set; }

}

/*EFCore 查看模型属性,有Id使用id作为主键,

没有Id,搜索public "{TableName}Id"作为主键,默认为int形主键设置标记列自增;

*/

info: Microsoft.EntityFrameworkCore.Database.Command[20100]

Executing DbCommand [Parameters=[], CommandType='Text', CommandTimeout='30']

CREATE TABLE [Categories] (

[Id] int NOT NULL IDENTITY,

[CategoryID] int NOT NULL,

[CategoryName] nvarchar(max) NULL,

CONSTRAINT [PK_Categories] PRIMARY KEY ([Id])

);

using (var db = new BloggingContext())

{

db.Categories.Add(new Category() { CategoryID = 1, CategoryName = "Clothing" });

db.Categories.Add(new Category() { CategoryID = 2, CategoryName = "Footwear" });

db.Categories.Add(new Category() { CategoryID = 3, CategoryName = "Accessories" });

db.Categories.Add(new Category() { CategoryID = 4, CategoryName = "Accessories" });

db.SaveChanges();

}

当执行SaveChanges(), 日志显示:

info: Microsoft.EntityFrameworkCore.Database.Command[20100]

Executing DbCommand [Parameters=[@p0='1', @p1='Clothing' (Size = 4000), @p2='2', @p3='Footwear' (Size = 4000), @p4='3', @p5='Accessories' (Size = 4000), @p6='4', @p7='Accessories' (Size = 4000)], CommandType='Text', CommandTimeout='30']

SET NOCOUNT ON;

DECLARE @inserted0 TABLE ([Id] int, [_Position] [int]);

MERGE [Categories] USING (

VALUES (@p0, @p1, 0),

(@p2, @p3, 1),

(@p4, @p5, 2),

(@p6, @p7, 3)) AS i ([CategoryID], [CategoryName], _Position) ON 1=0

WHEN NOT MATCHED THEN

INSERT ([CategoryID], [CategoryName])

VALUES (i.[CategoryID], i.[CategoryName])

OUTPUT INSERTED.[Id], i._Position

INTO @inserted0;

SELECT [t].[Id] FROM [Categories] t

INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id])

ORDER BY [i].[_Position];

从SQL Profiler追溯到的SQL:

exec sp_executesql N'SET NOCOUNT ON;

DECLARE @inserted0 TABLE ([Id] int, [_Position] [int]);

MERGE [Categories] USING (

VALUES (@p0, @p1, 0),

(@p2, @p3, 1),

(@p4, @p5, 2),

(@p6, @p7, 3)) AS i ([CategoryID], [CategoryName], _Position) ON 1=0

WHEN NOT MATCHED THEN

INSERT ([CategoryID], [CategoryName])

VALUES (i.[CategoryID], i.[CategoryName])

OUTPUT INSERTED.[Id], i._Position

INTO @inserted0;

SELECT [t].[Id] FROM [Categories] t

INNER JOIN @inserted0 i ON ([t].[Id] = [i].[Id])

ORDER BY [i].[_Position];

',N'@p0 int,@p1 nvarchar(4000),@p2 int,@p3 nvarchar(4000),@p4 int,@p5 nvarchar(4000),@p6 int,@p7 nvarchar(4000)',@p0=1,@p1=N'Clothing',@p2=2,@p3=N'Footwear',@p4=3,@p5=N'Accessories',@p6=4,@p7=N'Accessories'

如你所见,批量插入没有产生4个独立的语句,而是被组合为一个传参存储过程脚本(用列值作为参数);

如果使用EF6执行相同的代码,则在SQL Server Profiler中将看到4个独立的插入语句 。

① 就性能和速度而言,EFCore批量插入更具优势。

② 若数据库是针对云部署,EF6运行这些查询,还将产生额外的流量成本。

经过验证:EFCore批量更新、批量删除功能,EFCore均发出了使用sp_executesql存储过程+批量参数构建的SQL脚本。

P2  sp_executesql ?

起关键作用的 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个参数

P3 豁然开朗

SqlServer  sp_executesql存储过程最多支持2100个批量操作形成的列值参数,所以遇到很大数量的批量操作,EFCore SqlProvider会帮我们将批量操作分块传输,

实际上EFCore 对于少于4个的批量命令,不会使用sp_executesql 存储过程,我这边自己根据官方验证确实如此:

估摸着EFCore使用sp_executesql 也是有点耗资源的,对于小批量(小于4条的批量操作)依旧是产生单条sql。

// 同时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实现过程跟背后的存储载体密切相关)

–  对于小批量操作(当前EFCore默认MinBatchSize为4》),EFCore不会启用sp_executesql

- 大批量操作会使用存储过程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());

原文出处:博客园【Dotnet Plus】

本文观点不代表Dotnet9立场,转载请联系原作者。

efcore 批量_EFCore批量操作内幕相关推荐

  1. efcore 批量_EFcore使用EFCore.BulkExtensions 批量增加、删除、修改

    EFCore.BulkExtensions 简介 EntityFrameworkCore扩展:批量操作(插入,更新,删除,读取,更新,同步)和批处理(删除,更新). 库是轻量级的,并且非常高效,具有所 ...

  2. efcore 批量_EF批量插入(转)

    原作者地址http://blog.csdn.net/zlts000/article/details/46385773 之前做项目的时候,做出来的系统的性能不太好,在框架中使用了EntityFramew ...

  3. efcore 批量_【EF Core】Entity Framework Core 批处理语句

    在Entity Framework Core (EF Core)有许多新的功能,最令人期待的功能之一就是批处理语句.那么批处理语句是什么呢?批处理语句意味着它不会为每个插入/更新/删除语句发送单独的请 ...

  4. php批量删除 批量操作,关于thinkPHP实现批量删除的方法

    这篇文章主要介绍了thinkPHP批量删除的实现方法,结合实例形式分析了thinkPHP实现批量删除数据的数据库及模板操作相关技巧,需要的朋友可以参考下 本文实例讲述了thinkPHP批量删除的实现方 ...

  5. EFCore批量操作,你真的清楚吗

    背景 EntityFramework Core有许多新的特性,其中一个重要特性便是批量操作.批量操作意味着不需要为每次Insert/Update/Delete操作发送单独的命令,而是在一次SQL请求中 ...

  6. 4亿数据批量操作插入,为什么不用Mybatis,而是选择原生JDBC?(文中有各类批量操作效率对比和总结)

    手打不易,如果转摘,请注明出处! 注明原文:https://zhangxiaofan.blog.csdn.net/article/details/121351546 目录 背景 数据库:Oracle( ...

  7. Mybatis批量更新转

    Mybatis批量更新 批量操作就不进行赘述了.减少服务器与数据库之间的交互.网上有很多关于批量插入还有批量删除的帖子.但是批量更新却没有详细的解决方案. 实现目标 这里主要讲的是1张table中.根 ...

  8. java批量添加注解到所有业务接口

    背景: 已经完成或者持续更新的一个项目,已经有上千个业务接口,需要在这上千个业务接口上添加一个我自定义的注解,不可能一个一个添加呀!!! 所以应该批量吧:批量操作使用缓存流 package com.d ...

  9. 最新elasticsearch7(二、批量插入存在即更新java)

    文章目录 前言 唯一键 批量插入 批量插入或更新 结尾 前言 本篇开发环境基于上篇,客户端client使用rest风格的高等级(high level)API,这节我们来讲下ES的批量插入或更新操作. ...

最新文章

  1. mysql 书籍推荐 简书_Mysql复习必备----50条经典Sql语句
  2. 组合恒等式3 母函数与形式幂级数的运算
  3. IE这回在css flex中扳回一局?
  4. 程序员修神之路--高并发下如何缩短响应时间
  5. BestCoder22 1003.NPY and shot 解题报告
  6. Ubuntu 挂载新磁盘
  7. 步步为营-87-imageAreaSelect插件使用(图片剪切)
  8. 解决想从证书导出p12文件但是该证书中没有密码无法导出P12文件的问题
  9. SiteSucker Pro for Mac 4.3 强大的扒站神器
  10. 布客·ApacheCN 翻译/校对/笔记整理活动进度公告 2020.1
  11. 安装deb软件包时出现Unknown media type in type **/** 的解决办法
  12. 【ISO9126】软件质量模型的介绍(软件质量管理的六大特征和二十七个子特征)
  13. AAAI 2020 | 清华大学:借助BabelNet构建多语言义原知识库
  14. ubuntu格式化硬盘
  15. 历史双色球数据分析---python
  16. 有些话很轻、很淡、很疼!+ 有些话,经典的让人想流泪
  17. 十个设计师必备网站,解决你设计的所有难题
  18. BMap添加海量点数据,BMap.Point携带数据
  19. Android解析lrc里的歌词
  20. 为什么诉讼融资会火?

热门文章

  1. 线性映射和线性变换的区别
  2. 服务器控件在客户端触发alert对话框后,根据情况进行回发服务器操作
  3. C# 或Asp.Net 将excel表格导入数据库
  4. Invoke and BeginInvoke BeginInvoke和EndInvoke方法 (转)3
  5. DWZ中navTab使用解析
  6. mysql查询全年星期_数据库查询显示一年中所有的周一到周五的数据
  7. 题外:分类篇(音乐风格分类)基于BP神经网络
  8. 图像基础知识 —— Opencv图像处理
  9. leetcode —— 1025. 除数博弈
  10. 深度学习篇-Keras(初级)