Skip to content

Commit

Permalink
Refactor and continue soft delete
Browse files Browse the repository at this point in the history
  • Loading branch information
si618 committed Mar 17, 2024
1 parent 0fed52b commit 5c74d48
Show file tree
Hide file tree
Showing 18 changed files with 379 additions and 143 deletions.
7 changes: 4 additions & 3 deletions Benchmarks.App/BenchmarkRunner.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace Benchmarks.App;

using System.Collections.ObjectModel;

internal static class BenchmarkRunner
{
public static IEnumerable<Summary> RunAndBuildSummaries()
Expand All @@ -19,7 +21,8 @@ public static IEnumerable<Summary> RunAndBuildSummaries(BenchmarkSettings settin
return RunAndBuildSummaries([type], args);
}

private static IEnumerable<Summary> RunAndBuildSummaries(Type[] benchmarkTypes, string[] args)
// ReSharper disable once ReturnTypeCanBeEnumerable.Local - Performance
private static List<Summary> RunAndBuildSummaries(Type[] benchmarkTypes, string[] args)
{
AnsiConsole.Cursor.Move(CursorDirection.Up, 1);

Expand All @@ -34,8 +37,6 @@ private static IEnumerable<Summary> RunAndBuildSummaries(Type[] benchmarkTypes,

Console.SetOut(Console.Out);

AnsiConsole.Cursor.Move(CursorDirection.Down, 1);

return summaries;
}

Expand Down
40 changes: 24 additions & 16 deletions Benchmarks.Core/Database/BenchmarkDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,38 @@

public class BenchmarkDbContext(DbContextOptions options) : DbContext(options)
{
public DbSet<ClusteredIndex> ClusteredIndexes { get; set; } = null!;
// GuidPrimaryKey Benchmarks
public DbSet<GuidPrimaryKeyWithClusteredIndex> ClusteredIndexes { get; set; } = null!;
public DbSet<GuidPrimaryKey> GuidPrimaryKeys { get; set; } = null!;
public DbSet<HardDeleted> HardDeletes { get; set; } = null!;
public DbSet<NonClusteredIndex> NonClusteredIndexes { get; set; } = null!;
public DbSet<SoftDeleted> SoftDeletes { get; set; } = null!;
public DbSet<GuidPrimaryKeyWithNonClusteredIndex> NonClusteredIndexes { get; set; } = null!;
// SoftDelete Benchmarks
public DbSet<HardDelete> HardDeletes { get; set; } = null!;
public DbSet<SoftDeleteWithFilter> SoftDeleteWithFilters { get; set; } = null!;
public DbSet<SoftDeleteWithoutFilter> SoftDeleteWithoutFilters { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ClusteredIndex>()
.HasKey(p => p.Id)
// GuidPrimaryKey Benchmarks
modelBuilder.Entity<GuidPrimaryKeyWithClusteredIndex>()
.HasKey(e => e.Id)
.IsClustered();

modelBuilder.Entity<GuidPrimaryKey>()
.HasKey(p => p.Id);

modelBuilder.Entity<HardDeleted>()
.HasKey(p => p.Id);

modelBuilder.Entity<NonClusteredIndex>()
.HasKey(p => p.Id)
.HasKey(e => e.Id);
modelBuilder.Entity<GuidPrimaryKeyWithNonClusteredIndex>()
.HasKey(e => e.Id)
.IsClustered(false);

modelBuilder.Entity<SoftDeleted>()
// SoftDelete Benchmarks
modelBuilder.Entity<HardDelete>()
.HasKey(e => e.Id);
modelBuilder.Entity<SoftDeleteWithFilter>()
.HasQueryFilter(e => !e.IsDeleted)
.HasKey(p => p.Id);
modelBuilder.Entity<SoftDeleteWithFilter>()
.HasIndex(e => e.IsDeleted)
.HasFilter("IsDeleted = 0");
modelBuilder.Entity<SoftDeleteWithoutFilter>()
.HasQueryFilter(e => !e.IsDeleted)
.HasKey(p => p.Id);

}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ protected override void Up(MigrationBuilder migrationBuilder)
});

migrationBuilder.CreateTable(
name: "SoftDeletes",
name: "SoftDeleteWithFilters",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
Expand All @@ -83,8 +83,31 @@ protected override void Up(MigrationBuilder migrationBuilder)
},
constraints: table =>
{
table.PrimaryKey("PK_SoftDeletes", x => x.Id);
table.PrimaryKey("PK_SoftDeleteWithFilters", x => x.Id);
});

migrationBuilder.CreateTable(
name: "SoftDeleteWithoutFilters",
columns: table => new
{
Id = table.Column<long>(type: "bigint", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
IsDeleted = table.Column<bool>(type: "boolean", nullable: false),
DeletedAtUtc = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: true),
Text = table.Column<string>(type: "text", nullable: false),
CreatedAtUtc = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
LongInteger = table.Column<long>(type: "bigint", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_SoftDeleteWithoutFilters", x => x.Id);
});

migrationBuilder.CreateIndex(
name: "IX_SoftDeleteWithFilters_IsDeleted",
table: "SoftDeleteWithFilters",
column: "IsDeleted",
filter: "IsDeleted = 0");
}

/// <inheritdoc />
Expand All @@ -103,7 +126,10 @@ protected override void Down(MigrationBuilder migrationBuilder)
name: "NonClusteredIndexes");

migrationBuilder.DropTable(
name: "SoftDeletes");
name: "SoftDeleteWithFilters");

migrationBuilder.DropTable(
name: "SoftDeleteWithoutFilters");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,28 @@ protected override void BuildModel(ModelBuilder modelBuilder)

NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);

modelBuilder.Entity("Benchmarks.Core.Entities.ClusteredIndex", b =>
modelBuilder.Entity("Benchmarks.Core.Entities.GuidPrimaryKey", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");

b.Property<DateTimeOffset>("CreatedAtUtc")
.HasColumnType("timestamp with time zone");

b.Property<long>("LongInteger")
.HasColumnType("bigint");

b.Property<string>("Text")
.IsRequired()
.HasColumnType("text");

b.HasKey("Id");

b.ToTable("GuidPrimaryKeys");
});

modelBuilder.Entity("Benchmarks.Core.Entities.GuidPrimaryKeyWithClusteredIndex", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
Expand All @@ -44,7 +65,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("ClusteredIndexes");
});

modelBuilder.Entity("Benchmarks.Core.Entities.GuidPrimaryKey", b =>
modelBuilder.Entity("Benchmarks.Core.Entities.GuidPrimaryKeyWithNonClusteredIndex", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
Expand All @@ -60,12 +81,13 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.IsRequired()
.HasColumnType("text");

b.HasKey("Id");
b.HasKey("Id")
.HasAnnotation("SqlServer:Clustered", false);

b.ToTable("GuidPrimaryKeys");
b.ToTable("NonClusteredIndexes");
});

modelBuilder.Entity("Benchmarks.Core.Entities.HardDeleted", b =>
modelBuilder.Entity("Benchmarks.Core.Entities.HardDelete", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
Expand All @@ -88,29 +110,39 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.ToTable("HardDeletes");
});

modelBuilder.Entity("Benchmarks.Core.Entities.NonClusteredIndex", b =>
modelBuilder.Entity("Benchmarks.Core.Entities.SoftDeleteWithFilter", b =>
{
b.Property<Guid>("Id")
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid");
.HasColumnType("bigint");

NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));

b.Property<DateTimeOffset>("CreatedAtUtc")
.HasColumnType("timestamp with time zone");

b.Property<DateTimeOffset?>("DeletedAtUtc")
.HasColumnType("timestamp with time zone");

b.Property<bool>("IsDeleted")
.HasColumnType("boolean");

b.Property<long>("LongInteger")
.HasColumnType("bigint");

b.Property<string>("Text")
.IsRequired()
.HasColumnType("text");

b.HasKey("Id")
.HasAnnotation("SqlServer:Clustered", false);
b.HasKey("Id");

b.ToTable("NonClusteredIndexes");
b.HasIndex("IsDeleted")
.HasFilter("IsDeleted = 0");

b.ToTable("SoftDeleteWithFilters");
});

modelBuilder.Entity("Benchmarks.Core.Entities.SoftDeleted", b =>
modelBuilder.Entity("Benchmarks.Core.Entities.SoftDeleteWithoutFilter", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
Expand All @@ -136,7 +168,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)

b.HasKey("Id");

b.ToTable("SoftDeletes");
b.ToTable("SoftDeleteWithoutFilters");
});
#pragma warning restore 612, 618
}
Expand Down
30 changes: 30 additions & 0 deletions Benchmarks.Core/Database/SoftDeleteInterceptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Benchmarks.Core.Database;

public sealed class SoftDeleteInterceptor : SaveChangesInterceptor
{
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData,
InterceptionResult<int> result,
CancellationToken cancellationToken = default)
{
if (eventData.Context is null)
{
return base.SavingChangesAsync(eventData, result, cancellationToken);
}

eventData
.Context
.ChangeTracker
.Entries<ISoftDeletable>()
.Where(e => e.State == EntityState.Deleted)
.ToList()
.ForEach(entity =>
{
entity.State = EntityState.Modified;
entity.Entity.IsDeleted = true;
entity.Entity.DeletedAtUtc = DateTimeOffset.UtcNow;
});

return base.SavingChangesAsync(eventData, result, cancellationToken);
}
}
Loading

0 comments on commit 5c74d48

Please sign in to comment.