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.