如何在一个 SQL 语句中更新多个实体模型?
How do I update multiple Entity models in one SQL statement?
我有以下内容:
List<Message> unreadMessages = this.context.Messages
.Where( x =>
x.AncestorMessage.MessageID == ancestorMessageID &&
x.Read == false &&
x.SentTo.Id == userID ).ToList();
foreach(var unreadMessage in unreadMessages)
{
unreadMessage.Read = true;
}
this.context.SaveChanges();
但必须有一种方法可以做到这一点,而不必执行 2 个 SQL 查询,一个用于选择项目,一个用于更新列表。
我该怎么做?
甚至 SQL 在某种意义上也必须分两步完成,因为 UPDATE
带有 WHERE
子句的查询首先 运行s 相当于SELECT 在幕后,通过 WHERE 子句过滤,然后应用更新。所以真的,我认为你不需要担心改进这个。
另外,LINQ之所以分成两步,正是出于性能考虑。您希望 "select" 尽可能小,即您不希望从数据库中将更多的对象加载到内存中的对象中。只有这样你才能改变对象(在 foreach 中)。
如果你真的想 运行 在 SQL 端使用原生 UPDATE
,你可以使用 System.Data.SqlClient.SqlCommand
来发布更新,而不是让 LINQ 给出您支持然后更新的对象。这会更快,但是你在概念上将一些逻辑从你的 C# 代码对象模型 space 移到数据库模型 space 中(你在数据库中做事,而不是在你的对象中 space),即使 SqlCommand
是从您的代码发出的。
EF 中的当前惯用支持
据我所知,在Entity Framework中"bulk updates"还没有的直接支持(一直在讨论批量操作支持虽然有一段时间,但很可能会在某个时候包含在内)。
(为什么)你要这样做吗?
很明显,这是一个在本机 SQL 中可以在单个语句中实现的操作,并且与您的问题中遵循的方法相比具有一些显着优势。使用单个 SQL 语句,客户端和数据库服务器之间只需要非常少量的 I/O ,语句本身可以完全执行 并优化 数据库服务器。无需传输和遍历可能很大的结果集客户端,只需更新一个或两个字段并以另一种方式发回。
如何
因此,虽然 EF 不直接支持,但仍然可以使用以下两种方法之一来执行此操作。
选项 A。手动编码您的 SQL 更新语句
这是一种非常简单的方法,不需要任何其他 tools/packages 并且也可以异步执行:
var sql = "UPDATE TABLE x SET FIELDA = @fieldA WHERE FIELDB = @fieldb";
var parameters = new SqlParameter[] { ..., ... };
int result = db.Database.ExecuteSqlCommand(sql, parameters);
或
int result = await db.Database.ExecuteSqlCommandAsync(sql, parameters);
明显的缺点是,我们打破了很好的 linqy 范式并且必须手动编码您的 SQL(可能针对多个目标 SQL 方言)。
选项 B。使用 EF extension/utility 包之一
一段时间以来,许多开源 nuget 包都可用,它们提供了对 EF 的特定扩展。其中一些确实提供了一种很好的 "linqy" 方式来向服务器发出单个更新 SQL 语句。两个例子是:
Entity Framework Extended Library 允许使用如下语句执行批量更新:
context.Messages.Update(
x => x.Read == false && x.SentTo.Id == userID,
x => new Message { Read = true });
也可以在 github
EntityFramework.Utilities 允许使用如下语句执行批量更新:
EFBatchOperation
.For(context, context.Messages)
.Where(x => x.Read == false && x.SentTo.Id == userID)
.Update(x => x.Read, x => x.Read = true);
也可以在 github
肯定还有其他提供类似支持的软件包和库。
我有以下内容:
List<Message> unreadMessages = this.context.Messages
.Where( x =>
x.AncestorMessage.MessageID == ancestorMessageID &&
x.Read == false &&
x.SentTo.Id == userID ).ToList();
foreach(var unreadMessage in unreadMessages)
{
unreadMessage.Read = true;
}
this.context.SaveChanges();
但必须有一种方法可以做到这一点,而不必执行 2 个 SQL 查询,一个用于选择项目,一个用于更新列表。
我该怎么做?
甚至 SQL 在某种意义上也必须分两步完成,因为 UPDATE
带有 WHERE
子句的查询首先 运行s 相当于SELECT 在幕后,通过 WHERE 子句过滤,然后应用更新。所以真的,我认为你不需要担心改进这个。
另外,LINQ之所以分成两步,正是出于性能考虑。您希望 "select" 尽可能小,即您不希望从数据库中将更多的对象加载到内存中的对象中。只有这样你才能改变对象(在 foreach 中)。
如果你真的想 运行 在 SQL 端使用原生 UPDATE
,你可以使用 System.Data.SqlClient.SqlCommand
来发布更新,而不是让 LINQ 给出您支持然后更新的对象。这会更快,但是你在概念上将一些逻辑从你的 C# 代码对象模型 space 移到数据库模型 space 中(你在数据库中做事,而不是在你的对象中 space),即使 SqlCommand
是从您的代码发出的。
EF 中的当前惯用支持
据我所知,在Entity Framework中"bulk updates"还没有的直接支持(一直在讨论批量操作支持虽然有一段时间,但很可能会在某个时候包含在内)。
(为什么)你要这样做吗?
很明显,这是一个在本机 SQL 中可以在单个语句中实现的操作,并且与您的问题中遵循的方法相比具有一些显着优势。使用单个 SQL 语句,客户端和数据库服务器之间只需要非常少量的 I/O ,语句本身可以完全执行 并优化 数据库服务器。无需传输和遍历可能很大的结果集客户端,只需更新一个或两个字段并以另一种方式发回。
如何
因此,虽然 EF 不直接支持,但仍然可以使用以下两种方法之一来执行此操作。
选项 A。手动编码您的 SQL 更新语句
这是一种非常简单的方法,不需要任何其他 tools/packages 并且也可以异步执行:
var sql = "UPDATE TABLE x SET FIELDA = @fieldA WHERE FIELDB = @fieldb";
var parameters = new SqlParameter[] { ..., ... };
int result = db.Database.ExecuteSqlCommand(sql, parameters);
或
int result = await db.Database.ExecuteSqlCommandAsync(sql, parameters);
明显的缺点是,我们打破了很好的 linqy 范式并且必须手动编码您的 SQL(可能针对多个目标 SQL 方言)。
选项 B。使用 EF extension/utility 包之一
一段时间以来,许多开源 nuget 包都可用,它们提供了对 EF 的特定扩展。其中一些确实提供了一种很好的 "linqy" 方式来向服务器发出单个更新 SQL 语句。两个例子是:
Entity Framework Extended Library 允许使用如下语句执行批量更新:
context.Messages.Update( x => x.Read == false && x.SentTo.Id == userID, x => new Message { Read = true });
也可以在 github
EntityFramework.Utilities 允许使用如下语句执行批量更新:
EFBatchOperation .For(context, context.Messages) .Where(x => x.Read == false && x.SentTo.Id == userID) .Update(x => x.Read, x => x.Read = true);
也可以在 github
肯定还有其他提供类似支持的软件包和库。