120

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
  • 14,716
  • 23
  • 93
  • 165
Shane Grant
  • 2,324
  • 4
  • 23
  • 32
  • 2
    It's not a terribly bad way o do things. Do you have concurrent access to that table? – Henk Holterman 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
  • 52
    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

9 Answers9

70

You should use the Attach() method.

Attaching and Detaching Objects

CD..
  • 68,981
  • 24
  • 147
  • 156
  • 26
    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
  • 10
    @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... – The Red Pea 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
44

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,050
  • 8
  • 59
  • 75
  • 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
  • 22
    `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
35

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
  • 625
  • 6
  • 8
  • 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
11

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,120
  • 7
  • 35
  • 61
4

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
  • 10,491
  • 5
  • 12
  • 29
1

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
  • 4,010
  • 5
  • 30
  • 50
  • 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 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 Sep 02 '21 at 20:50
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
-2

this has worked for me in EF core 3.1

await _unitOfWork.Context.Database.ExecuteSqlRawAsync("UPDATE Student SET Age = 22 Where StudentId = 123"); 
sean717
  • 10,491
  • 19
  • 62
  • 82
-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,115
  • 4
  • 46
  • 64