从 id 列表更新实体框架中的多行

Posted

技术标签:

【中文标题】从 id 列表更新实体框架中的多行【英文标题】:Update Multiple Rows in Entity Framework from a list of ids 【发布时间】:2014-03-02 18:57:27 【问题描述】:

我正在尝试为实体框架创建一个查询,该查询将允许我获取 id 列表并更新与它们关联的字段。

SQL 中的示例:

UPDATE Friends
SET msgSentBy = '1234'
WHERE id IN (1, 2, 3, 4)

如何将以上内容转化为实体框架?

【问题讨论】:

你的数据库平台是什么Oracle mysql .. 我的数据库是 Microsoft SQL 有两个开源项目允许这样做:EntityFramework.Extended 和 Entity Framework Extensions。 对此唯一正确的答案是:你不能。当然,您可以从数据库中提取所有匹配的 Friends 并更新其属性 msgSentBy 并保存更改。但 EF 将为每条记录触发 UPDATE 语句。这与单语句批量更新完全不同。如前所述,寻找提供批量更新的第三方库。 @SamuelLiew 为什么将我的答案移至评论,这是一个有用的答案? 【参考方案1】:

类似下面的东西

var idList=new int[]1, 2, 3, 4;
using (var db=new SomeDatabaseContext())

    var friends= db.Friends.Where(f=>idList.Contains(f.ID)).ToList();
    friends.ForEach(a=>a.msgSentBy='1234');
    db.SaveChanges();

更新:

您可以更新多个字段,如下所示

friends.ForEach(a =>
                      
                         a.property1 = value1;
                         a.property2 = value2;
                      );

【讨论】:

ForEachList 上的一个方法,通常不鼓励使用它,因为它不是一种非常实用的编程方式。只需使用foreach(操作员) 使用此解决方案会为列表中的每个元素生成一个更新查询。有没有办法让 EF 像问题中那样只做一个查询? (UPDATE SomeTable SET SomeField = SomeValue WHERE Id IN (...)) 请注意,从数据库的角度来看,这是一种非常低效的方法。这不仅会发出一个涉及 Friends 表中每一行的大选择语句,而且它会为每条更新的记录发出一个单独的 UPDATE 命令。因此,与其发出一个命令,不如发出许多命令,并从数据库中流出大量数据。 @ShekharPankaj,基本上你想要做的是发出一个 SQL 命令,比如“UPDATE Friends SET msgSentBy = '1234' WHERE ID IN (1, 2, 3, 4)”。我不认为 EF 直接支持这样做。我相信有一些第 3 方解决方案(***.com/questions/12751258/batch-update-delete-ef5),但我没有使用它们。其他选项是使用原始 ADO.NET 而不是 EF。 这不应该是公认的答案,它的效率非常低,并且会导致数据库很快耗尽内存,【参考方案2】:
var idList=new int[]1, 2, 3, 4;
var friendsToUpdate = await Context.Friends.Where(f => 
    idList.Contains(f.Id).ToListAsync();

foreach(var item in previousEReceipts)

  item.msgSentBy = "1234";

您可以使用 foreach 更新每个满足您条件的元素。

下面是一个更通用的例子:

var itemsToUpdate = await Context.friends.Where(f => f.Id == <someCondition>).ToListAsync();

foreach(var item in itemsToUpdate)

   item.property = updatedValue;

Context.SaveChanges()

一般来说,您很可能会使用带有 await 的异步方法进行数据库查询。

【讨论】:

我看不出这如何为现有答案添加任何内容。它基本上只是重复它。 主要区别是使用foreach而不是friends.ForEach 这个答案的问题是它会将所有朋友从数据库中读取到内存中,更改它们然后再次保存它们,而不是在 SQL 中执行单个更新查询到数据库。跨度> foreach 而不是 friends.ForEach 这完全不相关。【参考方案3】:

我创建了一个库来批量删除或更新 EF Core 5 上的往返记录。

示例代码如下:

await ctx.DeleteRangeAsync(b => b.Price > n || b.AuthorName == "zack yang");

await ctx.BatchUpdate()
.Set(b => b.Price, b => b.Price + 3)
.Set(b=>b.AuthorName,b=>b.Title.Substring(3,2)+b.AuthorName.ToUpper())
.Set(b => b.PubTime, b => DateTime.Now)
.Where(b => b.Id > n || b.AuthorName.StartsWith("Zack"))
.ExecuteAsync();

Github 存储库:https://github.com/yangzhongke/Zack.EFCore.Batch 举报:https://www.reddit.com/r/dotnetcore/comments/k1esra/how_to_batch_delete_or_update_in_entity_framework/

【讨论】:

【参考方案4】:

如果您愿意在代码中出现一些原始 SQL,Entity Framework Core 5.0 中引入的IQueryable.ToQueryString 方法可能有助于解决这种情况。此方法将生成可包含在原始 SQL 查询中的 SQL,以对该查询标识的记录执行批量更新。

例如:

using var context = new DbContext();

var ids = new List<int>()  1, 2, 3, 4 ;

var query = context.Friends.Where(_ => ids.Contains(_.id)).Select(_ => _.id);

var sql = $"UPDATE Friends SET msgSentBy = 0 WHERE id IN (query.ToQueryString())";

context.Database.ExecuteSqlRaw(sql, "1234");

这种方法的主要缺点是使用原始 SQL。但是,我不知道有任何合理的方法可以通过当前的 Entity Framework Core 功能来避免这种情况 - 您被这个警告或此处发布的其他答案的警告所困扰,例如:

引入对另一个库的依赖,例如 https://github.com/yangzhongke/Zack.EFCore.Batch。 使用 DbContext.SaveChanges() 一次更新一条记录,而不是进行批量更新。

如果将来(何时)解决以下问题,那么我们可能会在这里得到更好的答案:Bulk (i.e. set-based) CUD operations (without loading data into memory) #795

【讨论】:

以上是关于从 id 列表更新实体框架中的多行的主要内容,如果未能解决你的问题,请参考以下文章

使用 System.Transaction 如何更新实体框架中的多行

实体框架更新多行

如何通过实体框架从 C# 中的同步代码并行运行查询

实体框架模型中的集合未更新

如何根据实体框架中的下拉列表选择项填充表单字段以进行更新?

数据库表的列中的值的下拉列表-MVC实体框架