With the new JSON data type introduced in SQL Server, the database finally provides native support for efficient and type-safe JSON storage and querying.
This change also unlocked new possibilities for the ExecuteUpdate method in EF Core, which can now work directly with JSON columns.
Execute update
ExecuteUpdate was introduced in EF Core to simplify bulk updates at the database level. It offers significantly better performance compared to the traditional approach of loading entities, modifying them, and calling SaveChanges.
await _dbContext.Products
.ExecuteUpdateAsync(set => set
.SetProperty(p =>
p.Price, p => p.Price * 1.02m));
Over time, it gained support for complex types, and now it also works with JSON columns.
NOTE: ExecuteUpdate bypasses the change tracker.
This means you do not need to call SaveChanges. The update is executed directly in the database.
If you want it to be part of the same unit of work as other changes, you need to wrap it in a transaction:
await using var connection = new NpgsqlConnection(ConnectionString);
await connection.OpenAsync();
var transaction = await connection.BeginTransactionAsync();
await _dbContext.Products
.ExecuteUpdateAsync(set => set
.SetProperty(p =>
p.Price, p => p.Price * 1.02m));
await transaction.CommitAsync();
Getting started
If you want to learn more about JSON support in EF Core, check out my previous blog post: JSON Type Support in EF Core 10.
For this example, we will use a Blog entity that contains a complex type stored as JSON:
public class Blog
{
public int Id { get; set; }
public BlogDetails Details { get; set; }
}
public class BlogDetails
{
public int Views { get; set; }
public string[] Tags { get; set; }
public List<Author> Authors { get; set; }
}
public class Author
{
public string Name { get; set; }
public string Email { get; set; }
}
In EF Core, we configure Details as a JSON column:
public class BlogConfiguration : IEntityTypeConfiguration<Blog>
{
public void Configure(EntityTypeBuilder<Blog> builder)
{
builder.ToTable(TableNames.Blogs);
builder.HasKey(b => b.Id);
builder.ComplexProperty(b => b.Details, bd => bd.ToJson());
}
}
Updating Property
Now we can easily update properties inside the JSON document without loading entities into memory:
app.MapPut("blogs/views", async (ApplicationDbContext dbContext) =>
{
await dbContext.Blogs.ExecuteUpdateAsync(s =>
s.SetProperty(b => b.Details.Views, b => b.Details.Views + 1));
return Results.NoContent();
});
Updating a Collection
The same concept applies to collections:
app.MapPut("blogs/tags", async (ApplicationDbContext dbContext) =>
{
await dbContext.Blogs.ExecuteUpdateAsync(s =>
s.SetProperty(b => b.Details.Tags, b => new string[] { "1", "2", "3" }));
return Results.NoContent();
});
app.MapPut("blogs/authors", async (ApplicationDbContext dbContext) =>
{
await dbContext.Blogs.ExecuteUpdateAsync(s =>
s.SetProperty(
b => b.Details.Authors,
b => new List<Author>
{
new WebApi.Entities.Author()
{
Name = "Nikola",
Email = "nikola.knezevic@nikolatech.net"
}
}));
return Results.NoContent();
});
However, working with collections has its limitations, you cannot perform arbitrary operations inside JSON collections.
Even though SQL Server supports JSON and EF Core 10 can update JSON columns in bulk, ExecuteUpdate still has strict translation rules.
It can only generate a single SQL SET expression, which means it cannot iterate over or mutate individual elements of a list.
Because of this, LINQ operations like Select, Add, Remove cannot be translated and will fail when used inside ExecuteUpdate.
Conclusion
The new JSON data type in SQL Server, combined with EF Core’s ExecuteUpdate, brings a powerful and efficient way to perform bulk updates.
You can now update properties inside JSON documents or even replace entire collections with a single database command.
However, it is important to understand the limitations. ExecuteUpdate works with strict SQL translation rules. You can replace them, but you cannot modify individual elements using LINQ operations.
If you want to check out examples I created, you can find the source code here:
I hope you enjoyed it, subscribe and get a notification when a new blog is up!
