using System; using System.Linq; using System.Threading.Tasks; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using Bogus; using Bogus.DataSets; using LinqToDB; using LinqToDB.Configuration; using LinqToDB.Data; using LinqToDB.Mapping; using Microsoft.EntityFrameworkCore; namespace BulkInsertBench { [MemoryDiagnoser] public class BulkInsertBench { private readonly Faker _faker = new Faker() .RuleFor(u => u.Id, _ => 0) .RuleFor(u => u.Gender, f => f.PickRandom()) .RuleFor(u => u.FirstName, (f, u) => f.Name.FirstName(u.Gender)) .RuleFor(u => u.LastName, (f, u) => f.Name.LastName(u.Gender)) .RuleFor(u => u.Avatar, f => f.Internet.Avatar()) .RuleFor(u => u.UserName, (f, u) => f.Internet.UserName(u.FirstName, u.LastName)) .RuleFor(u => u.Email, (f, u) => f.Internet.Email(u.FirstName, u.LastName)) .RuleFor(u => u.DateOfBirth, f => f.Date.Past(100, DateTime.Parse("1/1/2010"))); private User[] _users; [Params(10_000, 100_000)] public int N; [IterationSetup] public void IterationSetup() { _users = Enumerable.Range(0, N).Select(_ => _faker.Generate()).ToArray(); } [IterationCleanup] public void IterationCleanup() { using var db = new LinqToDb(); db.Users.Delete(); } private async Task EfAdd(EfContext context) { foreach (var user in _users) await context.AddAsync(user); await context.SaveChangesAsync(); } [Benchmark] public async Task EfAdd() { await using var context = new EfContext(); await EfAdd(context); } [Benchmark] public async Task EfAddNoTracking() { await using var context = new EfContext {ChangeTracker = {AutoDetectChangesEnabled = false}}; await EfAdd(context); } private async Task EfAddRange(EfContext context) { await context.Users.AddRangeAsync(_users); await context.SaveChangesAsync(); } [Benchmark] public async Task EfAddRange() { await using var context = new EfContext(); await EfAddRange(context); } [Benchmark] public async Task EfAddRangeNoTracking() { await using var context = new EfContext {ChangeTracker = {AutoDetectChangesEnabled = false}}; await EfAddRange(context); } [Benchmark] public async Task EfExtensionsBulkInsert() { await using var db = new EfContext(); await db.BulkInsertAsync(_users); } [Benchmark] public async Task LinqToDbBulkCopy() { await using var db = new LinqToDb(); await db.BulkCopyAsync(_users); } } class Program { static void Main() { BenchmarkRunner.Run(); } } class EfContext : DbContext { public DbSet Users { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) { options.UseSqlServer(@"Data Source=(LocalDb)\MSSQLLocalDB;Database=BulkInsertBenchDb;"); } } public class LinqToDb : DataConnection { public LinqToDb() : base(new LinqToDbConnectionOptionsBuilder() .UseSqlServer(@"Data Source=(LocalDb)\MSSQLLocalDB;Database=BulkInsertBenchDb;").Build()) { } public ITable Users => GetTable(); } [Table(Name = "Users")] public class User { [PrimaryKey] public int Id { get; set; } [Column] public string FirstName { get; set; } [Column] public string LastName { get; set; } [Column] public string UserName { get; set; } [Column] public string Email { get; set; } [Column] public string Avatar { get; set; } [Column] public Name.Gender Gender { get; set; } [Column] public DateTime DateOfBirth { get; set; } } }