135

I am trying to create a query for Entity Framework that will allow me to take a list of ids and update a field associated with them.

Example in SQL:

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

How do I convert the above into Entity Framework?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
allencoded
  • 7,015
  • 17
  • 72
  • 126
  • What is your database platform Oracle mysql .. – z atef Feb 06 '14 at 02:03
  • My database is Microsoft SQL – allencoded Feb 06 '14 at 02:04
  • There are two open source projects allowing this: [EntityFramework.Extended](https://entityframework-plus.net/?z=ef-extended) and E[ntity Framework Extensions](http://efe.codeplex.com/). – Peter Kerr Jul 23 '15 at 14:54
  • 1
    The only correct answer to this is: you can't. Sure, you can pull all matching `Friend`s from the database and update their property `msgSentBy` and save changes. But EF will fire `UPDATE` statements for each individual record. That's not at all the same as a one-statement bulk update. As said, look for a third-party library that offers bulk update. – Gert Arnold Nov 03 '20 at 13:41
  • @SamuelLiew why move my answer to a comment, it is an answer and a useful one at that? – Peter Kerr May 19 '21 at 10:57
  • [An answer](https://stackoverflow.com/questions/21592596/update-multiple-rows-in-entity-framework-from-a-list-of-ids/21592733#21592733) is the subject of [a meta question](https://meta.stackoverflow.com/questions/424146/could-be-an-irrelevant-modification-of-answer-be-flagged-as-a-plagiarized-answer). – Peter Mortensen May 21 '23 at 17:59

6 Answers6

212

Something like below:

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();
}

You can update multiple fields as below:

friends.ForEach(a =>
                      {
                         a.property1 = value1;
                         a.property2 = value2;
                      });
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Damith
  • 62,401
  • 13
  • 102
  • 153
  • can i update more than one field in the foreach above you did just sent by which is exactly what i asked for. just curious if you could do more? Also doesn't look like SubmitChanges quite works anymore. I am using the latest entity framework. Maybe SaveChanges()? – allencoded Feb 06 '14 at 02:24
  • 15
    `ForEach` is a method on `List`, and it's generally discouraged to use because it's not a very functional-way of programming. Just use `foreach` *(the operator)*. – BlueRaja - Danny Pflughoeft Apr 04 '15 at 03:44
  • 72
    Using this solution generates one update query for each element in the list. Is there a way to get EF to do just one query like in the question? (`UPDATE SomeTable SET SomeField = SomeValue WHERE Id IN (...)`) – RamNow Oct 03 '15 at 23:40
  • 34
    Be aware that this is a pretty inefficient way to go about this from a database perspective. Not only does this issue a big select statement involving every row from the Friends table, but it issues a separate UPDATE command for every record that is updated. So rather than issuing one command you are issues potentially many many commands as well as streaming a bunch of data out of your database. – d512 Feb 06 '16 at 23:00
  • @user1334007 any solution to avoid this? – Shekhar Pankaj Sep 27 '16 at 11:10
  • 2
    @ShekharPankaj, basically what you want to do is issue a SQL command like "UPDATE Friends SET msgSentBy = '1234' WHERE ID IN (1, 2, 3, 4)". I don't think that EF has direct support for doing that. I believe there are some 3rd party solutions to this (http://stackoverflow.com/questions/12751258/batch-update-delete-ef5) but I haven't used them. The other options is to use raw ADO.NET instead of EF. – d512 Sep 27 '16 at 17:30
  • This is also getting the data first from the DB. Is there anything else that we can use without getting the data first? – Mansur Apr 09 '20 at 09:26
  • 14
    This should NOT be the accepted answer, it's pretty inefficient and will cause the db to run out of memory pretty fast, – barnacle.m Dec 15 '20 at 02:27
  • Entity Framework 7 solves this. https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#executeupdate-and-executedelete-bulk-updates – Santiago Semhan Jan 12 '23 at 18:39
  • You cant still do in Entity Framework 7 WHERE ID IN (1, 2, 3, 4) What they did is you can update a range like WHERE ID >1 – dawid debinski Feb 23 '23 at 14:00
10

The best way to do a massive update with Entity Framework 7 is like this:

var idList = new int[]{1, 2, 3, 4};
context.Friends
       .Where(f => idList.Contains(f.ID))
       .ExecuteUpdate(f => f.SetProperty(x => x.Name, x => $"Updated {x.Name}"));

The advantage of this is that it doesn't retrieve all the records. It just sends an update query.

Reference: ExecuteUpdate and ExecuteDelete (Bulk updates)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Santiago Semhan
  • 193
  • 2
  • 9
5

The IQueryable.ToQueryString method introduced in Entity Framework Core 5.0 may help with this scenario, if you are willing to have some raw SQL appearing in your code. This method will generate SQL that can be included in a raw SQL query to perform a bulk update of records identified by that query.

For example:

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");

The major drawback of this approach is the use of raw SQL. However I don't know of any reasonable way to avoid that with current Entity Framework Core capabilities - you're stuck with this caveat, or the caveats of other answers posted here such as:

If (when) the following issue is addressed in the future then we are likely to get a better answer here: Bulk (i.e. set-based) CUD operations (without loading data into memory) #795

chrisg
  • 1,117
  • 8
  • 8
4
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";
}

You can use foreach to update each element that meets your condition.

Here is an example in a more generic way:

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

foreach(var item in itemsToUpdate)
{
   item.property = updatedValue;
}
Context.SaveChanges()

In general you will most probably use async methods with await for db queries.

Raphael Pinel
  • 2,352
  • 24
  • 26
3

I have created a library to batch delete or update records with a round trip on EF Core 5.

Sample code is as follows:

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 repository: Zack.EFCore.Batch Report: How to batch delete or update in Entity Framework Core 5

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
zack yang
  • 99
  • 1
  • 1
-1

For those who are searching for a bulk update (like me):

Based on Microsoft EF documents, in the Performance section (Efficient Update), if you want to do bulk update, you better use executing raw SQL to improve the performance.

context.Database.ExecuteSqlRaw("UPDATE [Employees] SET [Salary] = [Salary] + 1000");

Efficient Updating

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ahmad Tadi
  • 44
  • 7
  • @Twenty: It doesn't look like a new question. It looks like an answer. – Peter Mortensen Jul 25 '23 at 03:34
  • @PeterMortensen You are totally correct. I guess I commented it because it doesn't quite solve the issue of the OP, as you are not updating the rows by ids. Anyhow, my comment was wrong, thanks :) – Twenty Jul 25 '23 at 11:32