首页 > 其他分享 >EFCore中ExecuteUpdate 和 ExecuteDelete

EFCore中ExecuteUpdate 和 ExecuteDelete

时间:2024-03-30 17:12:37浏览次数:17  
标签:Rating EFCore ExecuteUpdate Blogs 更改 context ExecuteDelete

ExecuteUpdate 和 ExecuteDelete
项目
2023/05/11
4 个参与者

反馈
本文内容
ExecuteDelete
ExecuteUpdate
Change tracking
事务
显示另外 3 个
备注

EF Core 7.0 中已引入此功能。

ExecuteUpdate 和 ExecuteDelete 是一种将数据保存到数据库的方法,无需使用 EF 的传统更改跟踪和 SaveChanges() 方法。 有关这两种方法的介绍性比较,请参阅有关保存数据的概述页。

ExecuteDelete
假设需要删除评分低于特定阈值的所有博客。 传统 SaveChanges() 方法要求执行以下操作:

c#

复制
foreach (var blog in context.Blogs.Where(b => b.Rating < 3))
{
context.Blogs.Remove(blog);
}

context.SaveChanges();
这是执行此任务的低效方法:我们在数据库中查询与筛选器匹配的所有博客,然后查询、具体化和跟踪所有这些实例;匹配实体的数量可能很大。 然后,我们告诉 EF 的更改跟踪器,需要删除每个博客,并通过调用 SaveChanges() 来应用这些更改,这会为每个博客生成 DELETE 语句。

下面是通过 ExecuteDelete API 执行的相同任务:

c#

复制
context.Blogs.Where(b => b.Rating < 3).ExecuteDelete();
这会使用熟悉的 LINQ 运算符来确定哪些博客应受到影响(就像我们在查询它们一样),然后告诉 EF 针对数据库执行 SQL DELETE:

SQL

复制
DELETE FROM [b]
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
除了更简单、更短外,还会在数据库中非常高效地执行,无需从数据库加载任何数据或涉及 EF 的更改跟踪器。 请注意,可以使用任意 LINQ 运算符来选择要删除的博客 - 这些博客将转换为 SQL 以在数据库中执行,就像查询这些博客一样。

ExecuteUpdate
如果我们想要更改属性以指示应隐藏这些博客,而不是将其删除,该怎么办? ExecuteUpdate 提供了一种类似的方法来表达 SQL UPDATE 语句:

c#

复制
context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdate(setters => setters.SetProperty(b => b.IsVisible, false));
与 ExecuteDelete 一样,我们首先使用 LINQ 来确定应受到影响的博客;但对于 ExecuteUpdate,我们还需要表达要应用于匹配博客的更改。 这是通过在 ExecuteUpdate 调用中调用 SetProperty,并为其提供两个参数来完成的:要更改的属性 (IsVisible),以及它应具有的新值 (false)。 这会导致执行以下 SQL:

SQL

复制
UPDATE [b]
SET [b].[IsVisible] = CAST(0 AS bit)
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
更新多个属性
ExecuteUpdate 允许在单个调用中更新多个属性。 例如,若要将 IsVisible 设置为 false 并将 Rating 设置为零,只需将其他 SetProperty 调用链接在一起:

c#

复制
context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdate(setters => setters
.SetProperty(b => b.IsVisible, false)
.SetProperty(b => b.Rating, 0));
这会执行以下 SQL:

SQL

复制
UPDATE [b]
SET [b].[Rating] = 0,
[b].[IsVisible] = CAST(0 AS bit)
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
引用现有属性值
上述示例将属性更新为新的常量值。 ExecuteUpdate 还允许在计算新值时引用现有属性值;例如,若要将所有匹配博客的评分提高一分,请使用以下内容:

c#

复制
context.Blogs
.Where(b => b.Rating < 3)
.ExecuteUpdate(setters => setters.SetProperty(b => b.Rating, b => b.Rating + 1));
请注意,SetProperty 的第二个参数现在是 lambda 函数,而不是之前的常量。 其 b 参数表示正在更新的博客;因此,在该 lambda 中,b.Rating 包含发生任何更改前的评分。 这会执行以下 SQL:

SQL

复制
UPDATE [b]
SET [b].[Rating] = [b].[Rating] + 1
FROM [Blogs] AS [b]
WHERE [b].[Rating] < 3
导航和相关实体
ExecuteUpdate 当前不支持在 SetProperty lambda 中引用导航。 例如,假设我们要更新所有博客的评分,以便每个博客的新评分是其所有帖子评分的平均值。 我们可能会尝试使用 ExecuteUpdate,如下所示:

c#

复制
context.Blogs.ExecuteUpdate(
setters => setters.SetProperty(b => b.Rating, b => b.Posts.Average(p => p.Rating)));
但是,EF 确实允许执行此操作,方法是先使用 Select 计算平均评分并将其投影为匿名类型,然后针对该类型使用 ExecuteUpdate:

c#

复制
context.Blogs
.Select(b => new { Blog = b, NewRating = b.Posts.Average(p => p.Rating) })
.ExecuteUpdate(setters => setters.SetProperty(b => b.Blog.Rating, b => b.NewRating));
这会执行以下 SQL:

SQL

复制
UPDATE [b]
SET [b].[Rating] = CAST((
SELECT AVG(CAST([p].[Rating] AS float))
FROM [Post] AS [p]
WHERE [b].[Id] = [p].[BlogId]) AS int)
FROM [Blogs] AS [b]
Change tracking
熟悉 SaveChanges 的用户习惯于执行多个更改,然后调用 SaveChanges 以将所有这些更改应用于数据库;这可以通过 EF 的更改跟踪器实现,该跟踪器会累积或跟踪这些更改。

ExecuteUpdate 和 ExecuteDelete 的工作方式大不相同:它们在调用时立即生效。 这意味着,虽然单个 ExecuteUpdate 或 ExecuteDelete 操作可能会影响许多行,但无法累积多个此类操作并同时应用它们,例如在调用 SaveChanges 时。 事实上,这些函数完全不知道 EF 的更改跟踪器,并且没有任何交互。 这可产生多个重要的结果。

考虑下列代码:

c#

复制
// 1. Query the blog with the name `SomeBlog`. Since EF queries are tracking by default, the Blog is now tracked by EF's change tracker.
var blog = context.Blogs.Single(b => b.Name == "SomeBlog");

// 2. Increase the rating of all blogs in the database by one. This executes immediately.
context.Blogs.ExecuteUpdate(setters => setters.SetProperty(b => b.Rating, b => b.Rating + 1));

// 3. Increase the rating of `SomeBlog` by two. This modifies the .NET `Rating` property and is not yet persisted to the database.
blog.Rating += 2;

// 4. Persist tracked changes to the database.
context.SaveChanges();
非常重要的一点是,当调用 ExecuteUpdate 且在数据库中更新所有博客时,EF 的更改跟踪器不会更新,并且跟踪的 .NET 实例仍具有其原始评分值(从查询时开始)。 假设博客的评分最初为 5;执行第 3 行后,数据库中的评分现在为 6(由于 ExecuteUpdate),而跟踪的 .NET 实例中的评分为 7。 调用 SaveChanges 时,EF 检测到新值 7 与原始值 5 不同,并保留该更改。 由 ExecuteUpdate 执行的更改将被覆盖且不考虑在内。

因此,通常最好避免通过 ExecuteUpdate/ExecuteDelete 混合跟踪的 SaveChanges 修改和未跟踪的修改。

事务
继续上述内容,请务必了解 ExecuteUpdate 和 ExecuteDelete 不会隐式启动调用它们的事务。 考虑下列代码:

c#

复制
context.Blogs.ExecuteUpdate(/* some update */);
context.Blogs.ExecuteUpdate(/* another update */);

var blog = context.Blogs.Single(b => b.Name == "SomeBlog");
blog.Rating += 2;
context.SaveChanges();
每次 ExecuteUpdate 调用都会将单个 SQL UPDATE 发送到数据库。 由于未创建事务,因此,如果任何类型的失败阻止第二个 ExecuteUpdate 成功完成,则第一个操作的影响仍会保存到数据库中。 事实上,上述四个操作(ExecuteUpdate 的两次调用、一个查询和 SaveChanges)各自在其自己的事务中执行。 若要在单个事务中包装多个操作,请使用 DatabaseFacade 显式启动事务:

c#

复制
using (var transaction = context.Database.BeginTransaction())
{
context.Blogs.ExecuteUpdate(/* some update */);
context.Blogs.ExecuteUpdate(/* another update */);

...
}
有关事务处理的详细信息,请参阅使用事务。

并发控制和受影响的行
SaveChanges 提供自动并发控制,使用并发令牌确保在加载行到保存对该行的更改的一段时间内不会更改行。 由于 ExecuteUpdate 和 ExecuteDelete 不与更改跟踪器交互,因此它们无法自动应用并发控制。

但是,这两种方法都返回受操作影响的行数;这对于自行实现并发控制特别有用:

c#

复制
// (load the ID and concurrency token for a Blog in the database)

var numUpdated = context.Blogs
.Where(b => b.Id == id && b.ConcurrencyToken == concurrencyToken)
.ExecuteUpdate(/* ... */);
if (numUpdated == 0)
{
throw new Exception("Update failed!");
}
在此代码中,我们使用 LINQ Where 运算符将更新应用于特定博客,并且仅当其并发令牌具有特定值(例如,从数据库查询博客时看到的值)时应用。 我们检查 ExecuteUpdate 实际更新了多少行;如果结果为零,则不更新任何行,并发令牌可能会因并发更新而更改。

限制
目前仅支持更新和删除;必须通过 DbSet<TEntity>.Add 和 SaveChanges() 完成插入。
虽然 SQL UPDATE 和 DELETE 语句允许检索受影响行的原始列值,但 ExecuteUpdate 和 ExecuteDelete 目前不支持此操作。
这些方法的多次调用无法进行批处理。 每次调用都会对数据库执行自己的往返。
数据库通常只允许使用 UPDATE 或 DELETE 修改单个表。
这些方法目前仅适用于关系数据库提供程序。
其他资源

标签:Rating,EFCore,ExecuteUpdate,Blogs,更改,context,ExecuteDelete
From: https://www.cnblogs.com/zy8899/p/18105747

相关文章

  • EFCore
    《1》数据更新方法//方法1批量更新数据库数据,直接使用SQL语句ctx.Database.ExecuteSql($"UPDATE[T_Books]SET[Price]=[Price]+2");//方法2EFCore仍会为每个本书发送UPDATE语句,并且数据库必须单独执行每个语句......
  • EFcore 连接mysql的一些坑。
    好久不耍mysql了,EFcore连接的时候遇到了点问题,特此记录下来,防止以后又忘了。 第一个问题相当的奇葩,居然告诉我缺.NETcore2.0,,,无语,估计是依赖项里面引用了2.0,于是乎老老实实去官网下载安装。地址如下:https://dotnet.microsoft.com/zh-cn/download/dotnet/thank-you/sdk-2.0......
  • 开源.NET8.0小项目伪微服务框架(分布式、EFCore、Redis、RabbitMQ、Mysql等)
    1、前言为什么说是伪微服务框架,常见微服务框架可能还包括服务容错、服务间的通信、服务追踪和监控、服务注册和发现等等,而我这里为了在使用中的更简单,将很多东西进行了简化或者省略了。年前到现在在开发一个新的小项目,刚好项目最初的很多功能是比较通用的,所以就想着将这些功能......
  • 开源.NET8.0小项目伪微服务框架(分布式、EFCore、Redis、RabbitMQ、Mysql等)
    1、前言为什么说是伪微服务框架,常见微服务框架可能还包括服务容错、服务间的通信、服务追踪和监控、服务注册和发现等等,而我这里为了在使用中的更简单,将很多东西进行了简化或者省略了。年前到现在在开发一个新的小项目,刚好项目最初的很多功能是比较通用的,所以就想着将这些功能抽......
  • .NET6 + EF Core + MySQL 创建实体和数据库、EFCore 数据迁移、属性导航
    一、创建asp.netcoreweb(MVC)项目二、导包Microsoft.EntityFrameworkCore.DesignMicrosoft.EntifyFrameworkCore.ToolsPomelo.EntityFrameworkCore.MySql三、创建实例这里创建了两个实例namespacedemo.Models{publicclassSupplier{[DatabaseGe......
  • 学习总结基于VUE+ASP.NET Core mvc+EFCore+Axios.js+ehcart.js开发一个web应用
    Vue是一个用于构建用户界面(基于数据渲染出用户看到的页面)的渐进式(循序渐进)框架。分为(声明式渲染,基于js包、组建系统、客户端路由、大规模状态管理和构建工具)Vue的使用方法分为:1.Vue核心包开发:局部模块改造;2.Vue核心包+Vue插件工程化开发:整站开发1.开始之前准备下述包 在prog......
  • EFCore 经验
    注意:EFCore在执行Remove-Migration取消最后一次迁移生成的文件时,先不要更改Config配置文件,否则可能无法执行取消操作当主表和明细表是主从关系时,默认生成时为级联删除,不需要在配置文件中声明publicclassProductionPlanDetailConfig:IEntityTypeConfiguration<Production......
  • dotnet 多数据库 sqlite efcore model和entity区别 一对多 多对一 多对多
    efcore-multi-db/MultiDb.slnMicrosoftVisualStudioSolutionFile,FormatVersion12.00#VisualStudio15VisualStudioVersion=15.0.27130.2024MinimumVisualStudioVersion=10.0.40219.1Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}")="......
  • dotnet efcore 多数据库 使用
    efcore的使用依赖包efcore-multi-db/MultiDb.Two/MultiDb.Two.csproj<ProjectSdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFramework>net7.0</TargetFramework></PropertyGroup><ItemGroup><Packa......
  • dotnet efcore sqlite entity
    dotnet使用efcore读写sqlite数据库要使用EFCore读写SQLite数据库,您需要安装以下NuGet包:Microsoft.EntityFrameworkCore.SqliteMicrosoft.EntityFrameworkCore.Tools以下是一个示例代码,它演示了如何使用EFCore读写SQLite数据库:usingSystem;usingMicrosoft.EntityFramew......