When you load an entity from the database and update some properties, Entity Framework compares the new values against the old values and immediately decides if there was an actual change. When you call context.SaveChanges(), it will create an update statement for each entity where there was an actual change, and the statement will only update the modified property. If no actual changes were made, no updates are made to the database.

// Update an entity with new values which may or may not be different from original values
// Note: dbTransactions contains a local list of entities pulled from the db that are to be updated
// Note: tranImport contains a list of transactions imported from a file
for (int i = 0; i < tranImport.Count; i++) {    
    BankTransaction transaction = dbTransactions.Where(x => x.somevalue == tranImport[i].somevalue).First();
    transaction.Date = tranImport[i].Date;
    transaction.Amount = tranImport[i].Amount;
    transaction.Memo1 = tranImport[i].Memo1;
    transaction.Memo2 = tranImport[i].Memo2;
    transaction.Type = tranImport[i].Type;
}
context.SaveChanges();

Below is the SQL that is executed when two BankTransaction entities have had a value changed. Notice that each update statement ninjas (selectively updates) only the property that has changed:

    exec sp_executesql N'SET NOCOUNT ON;
        UPDATE [Tran] SET [Memo2] = @p0
        WHERE [Id] = @p1;
        SELECT @@ROWCOUNT;',
    N'@p1 int,@p0 nvarchar(4000)',@p1=257,@p0=N'ATM Withdrawal #NA805617'

    exec sp_executesql N'SET NOCOUNT ON;
        UPDATE [Tran] SET [Memo1] = @p0
        WHERE [Id] = @p1;
        SELECT @@ROWCOUNT;',
    N'@p1 int,@p0 nvarchar(4000)',@p1=258,@p0=N'MORTGAGE PAYMENT'

Detecting Changes To Entities

When updating entities, it’s often necessary to know if a value has changed. For example, you might want to apply some updates to a set of records, and if any changed, update a timestamp or add a reason for the change. While it’s probably best practice to implement your own code to compare each property of the existing and updated entities, and only apply the updates when changes are detected, you can also take advantage of the fact that Entity Framework already does that for you by blindly updating all properties and then checking if a change was detected on the entity. Knowing if an entity has been updated can be determined using context.Entry(MyEntity).State.

Here’s an updated example where we update some values, and when a change is detected, we add additional info about the change:

// Update an entity & detect if there was an actual change
// Note: dbTransactions contains a local list of entities pulled from the db that are to be updated
// Note: tranImport contains a list of transactions imported from a file
for (int i = 0; i < tranImport.Count; i++) {    
    BankTransaction transaction = dbTransactions.Where(x => x.somevalue == tranImport[i].somevalue).First();
    transaction.Date = tranImport[i].Date;
    transaction.Amount = tranImport[i].Amount;
    transaction.Memo1 = tranImport[i].Memo1;
    transaction.Memo2 = tranImport[i].Memo2;
    transaction.Type = tranImport[i].Type;
    if (ctx.Entry(transaction).State == EntityState.Modified)
    {
        // EF detected a change, so add more info about the change
        transaction.LastUpdatedTime = DateTime.Now();
        transaction.LastUpdatedReason = "File import #235";
    }
}
context.SaveChanges();

Below is the SQL that is generated when the same entities have had the same properties changed (Memo2 for one, Memo1 for the other) as well as our conditional updates to the columns ‘LastUpdated’ and ‘LastUpdateReason’:

    exec sp_executesql N'SET NOCOUNT ON;
        UPDATE [Tran] SET [LastUpdateReason] = @p0, [LastUpdated] = @p1, [Memo2] = @p2
        WHERE [Id] = @p3;
        SELECT @@ROWCOUNT;',
    N'@p3 int,@p0 nvarchar(64),@p1 datetime,@p2 nvarchar(64)',@p3=257,@p0=N'File import #235',@p1='2020-10-20 17:47:07.517',@p2=N'ATM Withdrawal #NA805617'

    exec sp_executesql N'SET NOCOUNT ON;
        UPDATE [Tran] SET [LastUpdateReason] = @p0, [LastUpdated] = @p1, [Memo1] = @p2
        WHERE [Id] = @p3;
        SELECT @@ROWCOUNT;',
    N'@p3 int,@p0 nvarchar(64),@p1 datetime,@p2 nvarchar(64)',@p3=258,@p0=N'File import #235',@p1='2020-10-20 17:47:07.510',@p2=N'MORTGAGE PAYMENT'