如何在一个 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 语句。两个例子是:

  1. Entity Framework Extended Library 允许使用如下语句执行批量更新:

    context.Messages.Update(
         x => x.Read == false && x.SentTo.Id == userID,
         x => new Message { Read = true });

    也可以在 github

  2. 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

肯定还有其他提供类似支持的软件包和库。