|
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<User> _faker = new Faker<User>() |
|
.RuleFor(u => u.Id, _ => 0) |
|
.RuleFor(u => u.Gender, f => f.PickRandom<Name.Gender>()) |
|
.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<BulkInsertBench>(); |
|
} |
|
} |
|
|
|
class EfContext : DbContext |
|
{ |
|
public DbSet<User> 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<User> Users => GetTable<User>(); |
|
} |
|
|
|
[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; } |
|
} |
|
} |