How to update record using Entity Framework Core?
Entity Framework-6.Net CoreEntity Framework-CoreEntity Framework-6 Problem Overview
What is the best approach to update database table data in Entity Framework Core?
- Retrieve the table row, do the changes and save
- Use keyword Update in DB context and handle exception for item not exist
What are the improved features we can use over EF6?
Entity Framework-6 Solutions
Solution 1 - Entity Framework-6
To update an entity with Entity Framework Core, this is the logical process:
- Create instance for
DbContext
class - Retrieve entity by key
- Make changes on entity's properties
- Save changes
Update()
method in DbContext
:
>Begins tracking the given entity in the Modified state such that it will be updated in the database when SaveChanges()
is called.
Update method doesn't save changes in database; instead, it sets states for entries in DbContext instance.
So, We can invoke Update()
method before to save changes in database.
I'll assume some object definitions to answer your question:
-
Database name is Store
-
Table name is Product
Product class definition:
public class Product
{
public int? ProductID { get; set; }
public string ProductName { get; set; }
public string Description { get; set; }
public decimal? UnitPrice { get; set; }
}
DbContext class definition:
public class StoreDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("Your Connection String");
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>(entity =>
{
// Set key for entity
entity.HasKey(p => p.ProductID);
});
base.OnModelCreating(modelBuilder);
}
}
Logic to update entity:
using (var context = new StoreDbContext())
{
// Retrieve entity by id
// Answer for question #1
var entity = context.Products.FirstOrDefault(item => item.ProductID == id);
// Validate entity is not null
if (entity != null)
{
// Answer for question #2
// Make changes on entity
entity.UnitPrice = 49.99m;
entity.Description = "Collector's edition";
/* If the entry is being tracked, then invoking update API is not needed.
The API only needs to be invoked if the entry was not tracked.
https://www.learnentityframeworkcore.com/dbcontext/modifying-data */
// context.Products.Update(entity);
// Save changes in database
context.SaveChanges();
}
}
Solution 2 - Entity Framework-6
According to Microsoft docs:
> the read-first approach requires an extra database read, and can result in more complex code for handling concurrency conflict
However, you should know that using Update method on DbContext will mark all the fields as modified and will include all of them in the query. If you want to update a subset of fields you should use the Attach method and then mark the desired field as modified manually.
context.Attach(person);
context.Entry(person).Property(p => p.Name).IsModified = true;
context.SaveChanges();
Solution 3 - Entity Framework-6
public async Task<bool> Update(MyObject item)
{
Context.Entry(await Context.MyDbSet.FirstOrDefaultAsync(x => x.Id == item.Id)).CurrentValues.SetValues(item);
return (await Context.SaveChangesAsync()) > 0;
}
Solution 4 - Entity Framework-6
It's super simple
using (var dbContext = new DbContextBuilder().BuildDbContext())
{
dbContext.Update(entity);
await dbContext.SaveChangesAsync();
}
Solution 5 - Entity Framework-6
Microsoft Docs gives us two approaches.
> Recommended HttpPost Edit code: Read and update
This is the same old way we used to do in previous versions of Entity Framework. and this is what Microsoft recommends for us.
Advantages
- Prevents overposting
- EFs automatic change tracking sets the
Modified
flag on the fields that are changed by form input.
> Alternative HttpPost Edit code: Create and attach
an alternative is to attach an entity created by the model binder to the EF context and mark it as modified.
As mentioned in the other answer the read-first approach requires an extra database read, and can result in more complex code for handling concurrency conflicts.
Solution 6 - Entity Framework-6
After going through all the answers I thought i will add two simple options
-
If you already accessed the record using FirstOrDefault() with tracking enabled (without using .AsNoTracking() function as it will disable tracking) and updated some fields then you can simply call context.SaveChanges()
-
In other case either you have entity posted to server using HtppPost or you disabled tracking for some reason then you should call context.Update(entityName) before context.SaveChanges()
1st option will only update the fields you changed but 2nd option will update all the fields in the database even though none of the field values were actually updated :)
Solution 7 - Entity Framework-6
A more generic approach
To simplify this approach an "id" interface is used
public interface IGuidKey
{
Guid Id { get; set; }
}
The helper method
public static void Modify<T>(this DbSet<T> set, Guid id, Action<T> func)
where T : class, IGuidKey, new()
{
var target = new T
{
Id = id
};
var entry = set.Attach(target);
func(target);
foreach (var property in entry.Properties)
{
var original = property.OriginalValue;
var current = property.CurrentValue;
if (ReferenceEquals(original, current))
{
continue;
}
if (original == null)
{
property.IsModified = true;
continue;
}
var propertyIsModified = !original.Equals(current);
property.IsModified = propertyIsModified;
}
}
Usage
dbContext.Operations.Modify(id, x => { x.Title = "aaa"; });