static void

EF Code First - Updating changed values only

Published Saturday 17 March 2012

When you are a detached entity returns from the UI, you normally save the update like this:
public void UpdateMovie(Movie movie)
{
    using (var context = new DomainContext())
    {
        //attach it
        context.Movies.Attach(movie);
        //mark it as modified
        context.Entry(movie).State = EntityState.Modified;
        //save - but saves all properties...
        context.SaveChanges();
    }
}
The update property saves all the properties - but what if we only wanted to save certain properties? Like this:
update [dbo].[Movies]
set [BoxOffice] = @0
where ([Id] = @1)

The safest way is to do it manually (and you avoid mass assignment vulnerabilities). You have to reload the entity from the database.
public void UpdateMovieBoxOffice(Movie movie)
{
    using (var context = new DomainContext())
    {
        //get database version
        var databaseMovie = context.Movies.Find(movie.Id);
        //manually copy the values
        databaseMovie.BoxOffice = movie.BoxOffice;
        //save
        context.SaveChanges();
    }
}
The UI may be able to track changes (IPropertyNotifyChanged or similar) and give the data service a list of the changed properties. If so, we can use the context.Entry to specify the modified properties. The SQL UPDATE statement will update only those properties.
public void UpdateMovieProperties(Movie movie, IList<string> propertyNames)
{
    using (var context = new DomainContext())
    {
        //attach it
        context.Movies.Attach(movie);
        //use the context entry
        DbEntityEntry<Movie> entry = context.Entry(movie);
        foreach (var propertyName in propertyNames)
        {
            //modify the specific property states only
            entry.Property(propertyName).IsModified = true;
        }
        //save
        context.SaveChanges();
    }
}

The other way is to detect the changes by comparing them to the database. This is similar to the second method, but we use entry.GetDatabaseValues() to get the database values and then compare them. As only the changed properties are marked as modified, the UPDATE statement uses only those properties.
public void UpdateMovieChangedProperties(Movie movie)
{
    using (var context = new DomainContext())
    {
        //attach it
        context.Movies.Attach(movie);
        //use the context entry
        DbEntityEntry<Movie> entry = context.Entry(movie);
        //do a database call to get the state
        var databaseValues = entry.GetDatabaseValues();
        foreach (var propertyName in databaseValues.PropertyNames)
        {
            //modify the specific property states only
            entry.Property(propertyName).IsModified = true;
        }
        //save
        context.SaveChanges();
    }
}
We don't take account of Complex Properties here (the DbPropertyEntries can be nested).


Previously: EF Code First - references vs validation (22 Feb 2012)