135

Lets say I query the database and load a list of items. Then I open one of the items in a detail view form, and instead of re-querying the item out of the database, I create an instance of the item from the datasource in the list.

Is there a way I can update the database record without fetching the record of the individual item?

Here is a sample how I am doing it now:

dataItem itemToUpdate = (from t in dataEntity.items
                                 where t.id == id
                                 select t).FirstOrDefault();

Then after pulling the record I update some values in the item and push the record back:

itemToUpdate.itemstatus = newStatus;
dataEntity.SaveChanges();

I would think there would be a better way to do this, any ideas?

SteveC
  • 15,808
  • 23
  • 102
  • 173
Shane Grant
  • 2,484
  • 4
  • 26
  • 32
  • 2
    It's not a terribly bad way o do things. Do you have concurrent access to that table? – H H Nov 18 '10 at 19:15
  • I would think this is the usage that an ORM like EF is exactly there to serve. To allow operations within the context of the application to be performed on the objects you want to create/modify/delete, without concern for the underlying DB implementation? – Pero P. Nov 18 '10 at 19:24
  • 62
    I think for developers with a background in TSQL trying to accept and embrace ORM's, its a bit inefficient to lookup a record only to update it, and never utilize the fetched data. This concept that a developer does not need to be concerned with the underlying DB implementation is a crock. The more a developer knows about the entire system, the better the solution can be. Options are never a bad thing. – barrypicker Dec 19 '11 at 20:16
  • 2
    The ORM approach is fine for actual objects, but if you also store other things in your database (like large binary blobs) it can be super useful to be able to update them without loading the original contents first. – BrainSlugs83 Mar 22 '17 at 00:06
  • This article as part of [Microsoft's Getting Started](http://msdn.microsoft.com/en-us/data/ee712907) explains entity states and how to do this: [Add/Attach and Entity States](http://msdn.microsoft.com/en-us/data/jj592676) Look at the section 'Attaching an existing but modified entity to the context' Now I'm off to read the rest of these tutorials. – Simon_Weaver May 11 '13 at 06:03

12 Answers12

70

Update: If you're using EF Core 7.0+, see this answer.

You should use the Attach() method.

Attaching and Detaching Objects

Arad Alvand
  • 8,607
  • 10
  • 51
  • 71
CD..
  • 72,281
  • 25
  • 154
  • 163
  • 30
    can you provide an example? – Bart Calixto May 23 '12 at 15:01
  • 18
    context.Products.Attach(product); context.Entry(product).State = EntityState.Modified; – Gabriel Jul 15 '12 at 14:17
  • 11
    @Gabriel Won't this update all properties though? What if I only want to modify a single one? – David Pfeffer Aug 15 '12 at 00:45
  • 29
    Yes this will update all properties. If you want to update a single property you can do this: context.Entry(user).Property(x => x.Property).IsModified = true; (have a look here http://stackoverflow.com/a/5567616/57369) – Gabriel Aug 15 '12 at 03:53
  • 6
    I would just like to add that context.Entry() is only available in .net 4.1, if you are still using 4.0 (like me) then check this out for the alternative: http://stackoverflow.com/questions/7113434/where-is-context-entry which is essentially: context.ObjectStateManager.ChangeObjectState(yourObject, EntityState.Modified); – dyslexicanaboko Aug 30 '12 at 21:15
  • 1
    Here is a really good answer that uses Attach() http://stackoverflow.com/a/17442858/2498426 – Jerther Apr 13 '16 at 14:22
  • 1
    @Jerther that's good, but I also like this one: http://stackoverflow.com/a/15339512/1175496 . I really hope someone can edit this answer here and give an example... – Nate Anderson Oct 08 '16 at 19:08
  • to think that MS didn't provide a simple `update where` – Toolkit Jan 01 '21 at 19:11
  • To save only updated properties, Attach() then update your properties then save – profimedica Feb 26 '21 at 20:34
47

You can also use direct SQL against the database using the context of the datastore. Example:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = 123 ");

For performance reasons, you may want to pass in variables instead of a single hard coded SQL string. This will allow SQL Server to cache the query and reuse with parameters. Example:

dataEntity.ExecuteStoreCommand
   ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });

UPDATE - for EF 6.0

dataEntity.Database.ExecuteSqlCommand
       ("UPDATE items SET itemstatus = 'some status' WHERE id = {0}", new object[] { 123 });
barrypicker
  • 9,740
  • 11
  • 65
  • 79
  • 11
    why would you downgrade this answer without leaving a comment. This suggestion addresses the original authors question spot-on. – barrypicker May 10 '12 at 21:54
  • 23
    `ExecuteStoreCommand` is not really an EF way of doing this, it's just using the `DbConnection` contained inside the `DbContext` to execute a command. It's not database agnostic, let alone persistence agnostic (e.g. this example would crash if the OP switched to XML). – just.another.programmer Feb 07 '13 at 20:15
  • 11
    @just.another.programmer - with great power comes great responsibility. – barrypicker Feb 18 '13 at 00:31
  • 15
    Does it have to be persistence agnostic ? It's not like you gonna change your storage system every other day. – David Oct 15 '13 at 21:12
  • 3
    I needed this answer. It was simple and exactly what I needed to update 2 tables. Funny how all over the place there is a ton of HowTos for MVC that seem to be written in Egyptian Hyrogliphs (sic). The guy didn't ask for anything but this. – JustJohn Nov 15 '14 at 21:44
  • 1
    You might change your storage system several times a day, in fact -- especially if you're doing proper unit testing. (i.e. if your unit tests run against in-memory DBSets, and F5 runs against SQL DB...) -- Writing raw SQL is terrible here, it basically means you can't test this code EXCEPT against a real SQL database. -- You should be doing this the proper way, with Entity Framework's `.IsModified` property. – BrainSlugs83 Mar 22 '17 at 00:21
  • 5
    @BrainSlugs83 - try using EF across link servers that only support OpenQuery - lots of fun. Sometimes you absolutely need raw SQL to get the job done. Not always can you draw the code into isolation for testing. Its not a perfect world out there. – barrypicker Oct 03 '17 at 20:27
  • @DvS - its not a perfect world out there. I am merely pointing out an option. I am not advocating when to use this option. I agree hard coding is not a good practice. Sometimes it is a last resort. In the case of a last resort - at least this option exists and for that - this is a great answer. – barrypicker Nov 15 '18 at 17:45
  • 1
    Oh, you'd be surprised. For some absolutely insane reason, people think EF means you can easily switch the underlying database technology on a whim to same costs. Nevermind all the sprocs and other T-SQL specific features. These ORMs make it easy to build upon different database technologies... but not switch between two different ones without tons and tons of work and irreplaceable feature gaps (e.g. bulk insert mapper for EF that has no MySQL equivalent). – Triynko Jun 07 '19 at 19:09
  • How does this work if your parameter Is a string? do you need to put the `' '` around the `{0}` like so `'{0}'` – Zapnologica May 20 '20 at 05:42
  • `EF ` way of doing it is dumb – Toolkit Jan 01 '21 at 19:12
43

The code:

ExampleEntity exampleEntity = dbcontext.ExampleEntities.Attach(new ExampleEntity { Id = 1 });
exampleEntity.ExampleProperty = "abc";
dbcontext.Entry<ExampleEntity>(exampleEntity).Property(ee => ee.ExampleProperty).IsModified = true;
dbcontext.Configuration.ValidateOnSaveEnabled = false;
dbcontext.SaveChanges();

The result TSQL:

exec sp_executesql N'UPDATE [dbo].[ExampleEntities]
SET [ExampleProperty ] = @0
WHERE ([Id] = @1)
',N'@0 nvarchar(32),@1 bigint',@0='abc',@1=1

Note:

The "IsModified = true" line, is needed because when you create the new ExampleEntity object (only with the Id property populated) all the other properties has their default values (0, null, etc). If you want to update the DB with a "default value", the change will not be detected by entity framework, and then DB will not be updated.

In example:

exampleEntity.ExampleProperty = null;

will not work without the line "IsModified = true", because the property ExampleProperty, is already null when you created the empty ExampleEntity object, you needs to say to EF that this column must be updated, and this is the purpose of this line.

tecla
  • 745
  • 7
  • 9
  • This is perfect. I just tested this and it is exactly what I wanted. I want the changes to go through the EF infrastructure (including using EntityFramework.Triggers project) but wanted to be able to change 1 column with only having the primary key. – MikeJansen Apr 23 '19 at 15:09
38

Now native support for this in EF Core 7 — ExecuteUpdate:

Finally! After a long wait, EF Core 7.0 now has a natively supported way to run UPDATE (and also DELETE) statements while also allowing you to use arbitrary LINQ queries (.Where(u => ...)), without having to first retrieve the relevant entities from the database: The new built-in method called ExecuteUpdate — see "What's new in EF Core 7.0?" and the official docs.

ExecuteUpdate is precisely meant for these kinds of scenarios, it can operate on any IQueryable instance, and lets you update specific columns on any number of rows, while always issuing a single UPDATE statement behind the scenes, making it as efficient as possible.

Usage:

Imagine you wanted to update the Email column of a specific user:

dbContext.Users
    .Where(u => u.Id == someId)
    .ExecuteUpdate(b =>
        b.SetProperty(u => u.Email, "NewEmail@gmail.com")
    );

As you can see, calling ExecuteUpdate requires you to make calls to the SetProperty method, to specify which property to update, and also what new value to assign to it.

EF Core will translate this into the following UPDATE statement:

UPDATE [u]
    SET [u].[Email] = "NewEmail@gmail.com"
FROM [Users] AS [u]
WHERE [u].[Id] = someId

Also, ExecuteDelete for deleting rows:

There's also a counterpart to ExecuteUpdate called ExecuteDelete, which, as the name implies, can be used to delete a single or multiple rows at once without having to first fetch them.

Usage:

// Delete all users that haven't been active in 2022:
dbContext.Users
    .Where(u => u.LastActiveAt.Year < 2022)
    .ExecuteDelete();

Similar to ExecuteUpdate, ExecuteDelete will generate DELETE SQL statements behind the scenes — in this case, the following one:

DELETE FROM [u]
FROM [Users] AS [u]
WHERE DATEPART(year, [u].[LastActiveAt]) < 2022

Other notes:

  • Keep in mind that both ExecuteUpdate and ExecuteDelete are "terminating", meaning that the update/delete operation will take place as soon as you call the method. You're not supposed to call dbContext.SaveChanges() afterwards.
  • If you're curious about the SetProperty method, and you're confused as to why ExectueUpdate doesn't instead receive a member initialization expression (e.g. .ExecuteUpdate(new User { Email = "..." }), then refer to this comment (and the surrounding ones) on the GitHub issue for this feature.
  • Furthermore, if you're curious about the rationale behind the naming, and why the prefix Execute was picked (there were also other candidates), refer to this comment, and the preceding (rather long) conversation.
  • Both methods also have async equivalents, named ExecuteUpdateAsync, and ExecuteDeleteAsync respectively.
Arad Alvand
  • 8,607
  • 10
  • 51
  • 71
  • 1
    Awesome! Thanks for sharing this, somehow I haven't seen it so far. – VPetrovic May 17 '23 at 21:41
  • @VPetrovic Happy to help! It's understandable, the feature hasn't been properly documented yet ([hopefully will](https://github.com/dotnet/EntityFramework.Docs/issues/4333)) which is why many people aren't aware of it yet, but it is fully supported in EF Core 7.0+. – Arad Alvand May 17 '23 at 23:29
  • What if the new value of my property is dependent on its original value? Something like incrementing an int column. I can't do `b.SetProperty(u => u.Counter, u.Counter + 1)` – swinn Jul 24 '23 at 14:38
  • 2
    @swinn You can do that. Pass an expression as the second argument to `SetProperty`; like so: `b.SetProperty(u => u.Counter, u => u.Counter + 1)`. This sort of usage is demonstrated in the official article I linked to — see [the first example in this section](https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-7.0/whatsnew#basic-executeupdate-examples). – Arad Alvand Jul 25 '23 at 02:16
13

If the DataItem has fields EF will pre-validate (like non-nullable fields), we'll have to disable that validation for this context:

DataItem itemToUpdate = new DataItem { Id = id, Itemstatus = newStatus };
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.Configuration.ValidateOnSaveEnabled = false;
dataEntity.SaveChanges();
//dataEntity.Configuration.ValidateOnSaveEnabled = true;

Otherwise we can try satisfy the pre-validation and still only update the single column:

DataItem itemToUpdate = new DataItem
{
    Id = id,
    Itemstatus = newStatus,
    NonNullableColumn = "this value is disregarded - the db original will remain"
};
dataEntity.Entry(itemToUpdate).Property(x => x.Itemstatus).IsModified = true;
dataEntity.SaveChanges();

Assuming dataEntity is a System.Data.Entity.DbContext

You can verify the query generated by adding this to the DbContext:

/*dataEntity.*/Database.Log = m => System.Diagnostics.Debug.Write(m);
Aske B.
  • 6,419
  • 8
  • 35
  • 62
5

I recommend using Entity Framework Plus

Updating using Entity Framework Core can be very slow if you need to update hundreds or thousands of entities with the same expression. Entities are first loaded in the context before being updated which is very bad for the performance and then, they are updated one by one which makes the update operation even worse.

EF+ Batch Update updates multiple rows using an expression in a single database roundtrip and without loading entities in the context.

// using Z.EntityFramework.Plus; // Don't forget to include this.

// UPDATE all users inactive for 2 years
var date = DateTime.Now.AddYears(-2);
ctx.Users.Where(x => x.LastLoginDate < date)
         .Update(x => new User() { IsSoftDeleted = 1 });
OMR
  • 11,736
  • 5
  • 20
  • 35
3

Simple and elegant extension method:

I've written an extension method for DbContext that does exactly what the OP asked for.

In addition to that, it only requires you to provide a member initialization expression (e.g. new User { ... }), and it then figures out on its own what properties you've changed, so you won't have to specify them by hand:

public static void UpdateEntity<TEntity>(
    this DbContext context,
    int id,
    Expression<Func<TEntity>> updateExpression
) where TEntity : BaseEntity, new()
{
    if (updateExpression.Body is not MemberInitExpression memberInitExpr)
        throw new ArgumentException("The update expression should be a member initialization.");

    TEntity entityToUpdate = updateExpression.Compile().Invoke();
    entityToUpdate.Id = id;
    context.Attach(entityToUpdate);

    var updatedPropNames = memberInitExpr.Bindings.Select(b => b.Member.Name);
    foreach (string propName in updatedPropNames)
        context.Entry(entityToUpdate).Property(propName).IsModified = true;
}

You also need a BaseEntity class or interface that has your primary key in it, like:

public abstract class BaseEntity
{
    public int Id { get; set; }
}

Usage:

Here's how you'd use the method:

dbContext.UpdateEntity(1234 /* <- this is the ID */, () => new User
{
    Name = "New Name",
    Email = "TheNewEmail@gmail.con",
});
dbContext.SaveChanges();

Nice and simple! :D

And here's the resulting SQL that gets generated by Entity Framework:

UPDATE [Users]
SET [Name] = @p0, [Email] = @p1
WHERE [Id] = @p2;

Limitation:

This method only allows you to update a single row using its primary key. So, it doesn't work with .Where(...), IQueryable<...>, and so on. If you don't have the PK, or you want to bulk-update, then this wouldn't be your best option. In general, if you have more complex update operations, then I'd recommend you use Entity Framework Plus, or similar libraries.

Arad Alvand
  • 8,607
  • 10
  • 51
  • 71
  • Looks good and could be useful, although there are some limitations. It requires entities to inherit from `BaseEntity` to have `Id` as key. And it may violate validation, which is always a caveat with stubs. – Gert Arnold Sep 02 '21 at 20:20
  • @GertArnold Hi :) Regarding the `BaseEntity` inheritance, that was not a problem for me as all my entity classes already inherited from a base class like that. But if somebody doesn't want to have a base class at all, I think they can either use reflection in the `UpdateEntity` method to set the property that corresponds to the PK (if there's such a property across all their entities, of course), or the `UpdateEntity` method could have an additional parameter with which you could specify which property on the object is the PK; like `dbContext.UpdateEntity(u => u.Id, 1234, new User { ... })`. – Arad Alvand Sep 02 '21 at 20:48
  • @GertArnold And regarding the validation thing, so far as I know it's not a problem in EF Core. Take the example in my answer, for instance. `User.Name` is a required property/column, but if you don't assign a value to it when calling the `UpdateEntity` method (which obviously means that it's going to have its default value `null`), everything will work as expected, and EF will not throw a validation error, So, unless I've misunderstood what you're referring to, that's not a problem. – Arad Alvand Sep 02 '21 at 20:50
3

ef core 7 :

public async Task<int> Update(UpdateLevelVm vm)
{
    return await _db.Levels.Where(l => l.Id == vm.LevelId)
        .ExecuteUpdateAsync(u => u
         .SetProperty(l => l.GradeId, vm.GradeId)
         .SetProperty(l => l.Title, vm.Title)
        );
}
M Komaei
  • 7,006
  • 2
  • 28
  • 34
0

It works somewhat different in EF Core:

There may be a faster way to do this in EF Core, but the following ensures an UPDATE without having to do a SELECT (tested with EF Core 2 and JET on the .NET Framework 4.6.2):

Ensure your model does not have IsRequired properties

Then use the following template (in VB.NET):

    Using dbContext = new MyContext()
        Dim bewegung = dbContext.MyTable.Attach(New MyTable())
        bewegung.Entity.myKey = someKey
        bewegung.Entity.myOtherField = "1"

        dbContext.Entry(bewegung.Entity).State = EntityState.Modified
        dbContext.Update(bewegung.Entity)

        Dim BewegungenDescription = (From tp In dbContext.Model.GetEntityTypes() Where tp.ClrType.Name = "MyTable" Select tp).First()
        For Each p In (From prop In BewegungenDescription.GetProperties() Select prop)
            Dim pp = dbContext.Entry(bewegung.Entity).Property(p.Name)
            pp.IsModified = False
        Next
        dbContext.Entry(bewegung.Entity).Property(Function(row) row.myOtherField).IsModified = True
        dbContext.SaveChanges()
    End Using
Wolfgang Grinfeld
  • 870
  • 10
  • 11
0

With the new version of EF Core (starting from 7.x), there is a new and more efficient way to update rows without the need of writing any SQL query directly in code. This can be achieved by making use of the newly added ExecuteUpdate() extension method.

You can use it as follows:

dbContext.Entities.ExecuteUpdateAsync(x => x.SetProperty(c => c.PropertyName, c => "NewPropertyValue"));

You can read more about it in here: https://learn.microsoft.com/en-us/ef/core/performance/efficient-updating?tabs=ef7#use-executeupdate-and-executedelete-when-relevant

Genc Hani
  • 85
  • 1
  • 11
-3

this has worked for me in EF core 3.1

await _unitOfWork.Context.Database.ExecuteSqlRawAsync("UPDATE Student SET Age = 22 Where StudentId = 123"); 
sean717
  • 11,759
  • 20
  • 66
  • 90
-5

Generally speaking, if you used Entity Framework to query all the items, and you saved the entity object, you can update the individual items in the entity object and call SaveChanges() when you are finished. For example:

var items = dataEntity.Include("items").items;
// For each one you want to change:
items.First(item => item.id == theIdYouWant).itemstatus = newStatus;
// After all changes:
dataEntity.SaveChanges();

The retrieval of the one item you want should not generate a new query.

Andrew
  • 14,325
  • 4
  • 43
  • 64