Skip to content

Instantly share code, notes, and snippets.

@davepcallan
Last active February 8, 2024 19:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davepcallan/2cbcca38fde59c856746d7b8b5dcb78e to your computer and use it in GitHub Desktop.
Save davepcallan/2cbcca38fde59c856746d7b8b5dcb78e to your computer and use it in GitHub Desktop.
Entity Framework BulkUpdate v SaveChange v Manual SQL benchmarks
using Microsoft.EntityFrameworkCore;
public class MyContext : DbContext
{
private int _batchSize;
public MyContext(int batchSize = 42)
{
_batchSize = batchSize;
}
public DbSet<Blog> Blogs { get; set; }
public DbSet<RssBlog> RssBlogs { get; set; }
public DbSet<PrivateBlog> PrivateBlogs { get; set; }
public DbSet<PrivateBlogWithMFA> PrivateBlogsWithMFA { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EF7BulkUpdate;Trusted_Connection=True", opt => opt.MaxBatchSize(_batchSize));
//.EnableSensitiveDataLogging()
// .LogTo(
// s => Console.WriteLine(s), LogLevel.Information);
}
public class Blog
{
public int BlogId { get; set; }
public string Url { get; set; }
public bool IsActive { get; set; }
public DateTime LastUpdated { get; set; }
}
public class RssBlog : Blog
{
public string RssUrl { get; set; }
}
public class PrivateBlog : Blog
{
public string Password { get; set; }
}
public class PrivateBlogWithMFA : PrivateBlog
{
public bool mfaOn { get; set; }
}
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Columns;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Reports;
using Microsoft.EntityFrameworkCore;
using Microsoft.Data.SqlClient;
namespace BenchmarkDotNet.Samples
{
[Config(typeof(Config))]
[SimpleJob(RuntimeMoniker.Net70)]
[MemoryDiagnoser]
[HideColumns(Column.AllocRatio, Column.RatioSD, Column.Median, Column.Gen0, Column.Gen1)]
public class EF7BulkUpdateBenchmark
{
private string SQLConnectionString
= "Data Source=(localdb)\\mssqllocaldb;Database=EF7BulkUpdate;Integrated Security=sspi;";
[Params(42, 500, 1000)]
public int records;
[GlobalSetup]
public void GlobalSetup()
{
using var context = new MyContext();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var random = new Random();
var date = DateTime.UtcNow;
for (int i = 0; i < records; i++)
{
context.Add(new
Blog
{
Url = "https://blogurl/ID/" + random.Next(1, 10000).ToString(),
LastUpdated = date
});
}
context.SaveChanges();
}
[Benchmark]
public void ADONET_PlainSQL_BulkUpdate()
{
using (SqlConnection connection = new SqlConnection(SQLConnectionString))
{
connection.Open();
using (SqlCommand command =
new SqlCommand("UPDATE [Blogs] SET [LastUpdated] = @date; --ADONETUpdate", connection))
{
command.Parameters.AddWithValue("@date", DateTime.UtcNow);
command.ExecuteNonQuery();
}
}
}
[Benchmark]
public void EF_PlainSQL_BulkUpdate()
{
using var context = new MyContext();
context.Database
.ExecuteSql($"UPDATE [Blogs] SET [LastUpdated] = {DateTime.UtcNow}; --PlainSQLUpdate");
}
[Benchmark(Baseline = true)]
public void EF_ExecuteUpdate_BulkUpdate()
{
using var context = new MyContext();
context.Blogs.ExecuteUpdate(
s => s.SetProperty(
blog => blog.LastUpdated, DateTime.UtcNow));
}
[Benchmark]
public void EF_SaveChanges_NoBulkUpdate()
{
using var context = new MyContext();
var date = DateTime.UtcNow;
var blogs = context.Blogs.ToList();
foreach (var blog in blogs)
{
blog.LastUpdated = date;
}
context.SaveChanges();
}
private class Config : ManualConfig
{
public Config()
{
SummaryStyle =
SummaryStyle.Default.WithRatioStyle(RatioStyle.Percentage);
}
}
}
}
using BenchmarkDotNet.Running;
namespace EF7Benchmarks
{
internal class Program
{
static void Main(string[] args)
{
BenchmarkSwitcher.
FromAssembly(typeof(Program).Assembly).Run();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment