mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-29 01:04:56 +08:00
Merge pull request #107 from meysamhadeli/fix/fix-optimistic-concurrency
fix: Fix optimistic concurrency issue
This commit is contained in:
commit
c307eb253d
@ -35,6 +35,7 @@
|
|||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
|
||||||
<PackageReference Include="Mongo2Go" Version="3.1.3" />
|
<PackageReference Include="Mongo2Go" Version="3.1.3" />
|
||||||
|
<PackageReference Include="Npgsql" Version="7.0.1" />
|
||||||
<PackageReference Include="NSubstitute" Version="4.4.0" />
|
<PackageReference Include="NSubstitute" Version="4.4.0" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.0.0-rc9.7" />
|
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.0.0-rc9.7" />
|
||||||
<PackageReference Include="Polly" Version="7.2.3" />
|
<PackageReference Include="Polly" Version="7.2.3" />
|
||||||
|
|||||||
@ -25,7 +25,7 @@ namespace BuildingBlocks.Core.Model
|
|||||||
return dequeuedEvents;
|
return dequeuedEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long Version { get; set; } = -1;
|
public long Version { get; set; }
|
||||||
|
|
||||||
public TId Id { get; set; }
|
public TId Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,13 +7,16 @@ using Microsoft.EntityFrameworkCore.Storage;
|
|||||||
|
|
||||||
namespace BuildingBlocks.EFCore;
|
namespace BuildingBlocks.EFCore;
|
||||||
|
|
||||||
|
using System.Data;
|
||||||
|
|
||||||
public abstract class AppDbContextBase : DbContext, IDbContext
|
public abstract class AppDbContextBase : DbContext, IDbContext
|
||||||
{
|
{
|
||||||
private readonly ICurrentUserProvider _currentUserProvider;
|
private readonly ICurrentUserProvider _currentUserProvider;
|
||||||
|
|
||||||
private IDbContextTransaction _currentTransaction;
|
private IDbContextTransaction _currentTransaction;
|
||||||
|
|
||||||
protected AppDbContextBase(DbContextOptions options, ICurrentUserProvider currentUserProvider = null) : base(options)
|
protected AppDbContextBase(DbContextOptions options, ICurrentUserProvider currentUserProvider = null) :
|
||||||
|
base(options)
|
||||||
{
|
{
|
||||||
_currentUserProvider = currentUserProvider;
|
_currentUserProvider = currentUserProvider;
|
||||||
}
|
}
|
||||||
@ -30,7 +33,7 @@ public abstract class AppDbContextBase : DbContext, IDbContext
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_currentTransaction ??= await Database.BeginTransactionAsync(cancellationToken);
|
_currentTransaction ??= await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CommitTransactionAsync(CancellationToken cancellationToken = default)
|
public async Task CommitTransactionAsync(CancellationToken cancellationToken = default)
|
||||||
@ -68,8 +71,17 @@ public abstract class AppDbContextBase : DbContext, IDbContext
|
|||||||
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
OnBeforeSaving();
|
OnBeforeSaving();
|
||||||
|
try
|
||||||
|
{
|
||||||
return base.SaveChangesAsync(cancellationToken);
|
return base.SaveChangesAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
catch (DbUpdateConcurrencyException ex)
|
||||||
|
{
|
||||||
|
var data = ex.Entries.Single();
|
||||||
|
data.OriginalValues.SetValues(data.GetDatabaseValues() ?? throw new InvalidOperationException());
|
||||||
|
return base.SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public IReadOnlyList<IDomainEvent> GetDomainEvents()
|
public IReadOnlyList<IDomainEvent> GetDomainEvents()
|
||||||
{
|
{
|
||||||
@ -104,7 +116,6 @@ public abstract class AppDbContextBase : DbContext, IDbContext
|
|||||||
case EntityState.Added:
|
case EntityState.Added:
|
||||||
entry.Entity.CreatedBy = userId;
|
entry.Entity.CreatedBy = userId;
|
||||||
entry.Entity.CreatedAt = DateTime.Now;
|
entry.Entity.CreatedAt = DateTime.Now;
|
||||||
entry.Entity.Version++;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EntityState.Modified:
|
case EntityState.Modified:
|
||||||
|
|||||||
@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace BuildingBlocks.PersistMessageProcessor.Data.Migrations
|
namespace BuildingBlocks.PersistMessageProcessor.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PersistMessageDbContext))]
|
[DbContext(typeof(PersistMessageDbContext))]
|
||||||
[Migration("20230113183839_initial")]
|
[Migration("20230120222214_initial")]
|
||||||
partial class initial
|
partial class initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -1,5 +1,4 @@
|
|||||||
using BuildingBlocks.Core.Model;
|
using BuildingBlocks.Core.Model;
|
||||||
using BuildingBlocks.IdsGenerator;
|
|
||||||
using Flight.Aircrafts.Features.CreateAircraft.Events.Domain.V1;
|
using Flight.Aircrafts.Features.CreateAircraft.Events.Domain.V1;
|
||||||
|
|
||||||
namespace Flight.Aircrafts.Models;
|
namespace Flight.Aircrafts.Models;
|
||||||
|
|||||||
@ -8,8 +8,13 @@ public class AircraftConfiguration : IEntityTypeConfiguration<Aircraft>
|
|||||||
{
|
{
|
||||||
public void Configure(EntityTypeBuilder<Aircraft> builder)
|
public void Configure(EntityTypeBuilder<Aircraft> builder)
|
||||||
{
|
{
|
||||||
|
|
||||||
builder.ToTable(nameof(Aircraft));
|
builder.ToTable(nameof(Aircraft));
|
||||||
builder.HasKey(r => r.Id);
|
builder.HasKey(r => r.Id);
|
||||||
builder.Property(r => r.Id).ValueGeneratedNever();
|
builder.Property(r => r.Id).ValueGeneratedNever();
|
||||||
|
|
||||||
|
|
||||||
|
// // ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=fluent-api
|
||||||
|
builder.Property(r => r.Version).IsConcurrencyToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,9 +8,14 @@ public class AirportConfiguration: IEntityTypeConfiguration<Airport>
|
|||||||
{
|
{
|
||||||
public void Configure(EntityTypeBuilder<Airport> builder)
|
public void Configure(EntityTypeBuilder<Airport> builder)
|
||||||
{
|
{
|
||||||
|
|
||||||
builder.ToTable(nameof(Airport));
|
builder.ToTable(nameof(Airport));
|
||||||
|
|
||||||
builder.HasKey(r => r.Id);
|
builder.HasKey(r => r.Id);
|
||||||
builder.Property(r => r.Id).ValueGeneratedNever();
|
builder.Property(r => r.Id).ValueGeneratedNever();
|
||||||
|
|
||||||
|
|
||||||
|
// // ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=fluent-api
|
||||||
|
builder.Property(r => r.Version).IsConcurrencyToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,10 @@ public class FlightConfiguration : IEntityTypeConfiguration<Flights.Models.Fligh
|
|||||||
builder.HasKey(r => r.Id);
|
builder.HasKey(r => r.Id);
|
||||||
builder.Property(r => r.Id).ValueGeneratedNever();
|
builder.Property(r => r.Id).ValueGeneratedNever();
|
||||||
|
|
||||||
|
|
||||||
|
// // ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=fluent-api
|
||||||
|
builder.Property(r => r.Version).IsConcurrencyToken();
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.HasOne<Aircraft>()
|
.HasOne<Aircraft>()
|
||||||
.WithMany()
|
.WithMany()
|
||||||
|
|||||||
@ -16,6 +16,9 @@ public class SeatConfiguration : IEntityTypeConfiguration<Seat>
|
|||||||
builder.HasKey(r => r.Id);
|
builder.HasKey(r => r.Id);
|
||||||
builder.Property(r => r.Id).ValueGeneratedNever();
|
builder.Property(r => r.Id).ValueGeneratedNever();
|
||||||
|
|
||||||
|
// // ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=fluent-api
|
||||||
|
builder.Property(r => r.Version).IsConcurrencyToken();
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.HasOne<Flights.Models.Flight>()
|
.HasOne<Flights.Models.Flight>()
|
||||||
.WithMany()
|
.WithMany()
|
||||||
|
|||||||
@ -7,7 +7,6 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
|
|
||||||
namespace Flight.Data;
|
namespace Flight.Data;
|
||||||
|
|
||||||
|
|
||||||
public sealed class FlightDbContext : AppDbContextBase
|
public sealed class FlightDbContext : AppDbContextBase
|
||||||
{
|
{
|
||||||
public FlightDbContext(DbContextOptions<FlightDbContext> options, ICurrentUserProvider currentUserProvider) : base(
|
public FlightDbContext(DbContextOptions<FlightDbContext> options, ICurrentUserProvider currentUserProvider) : base(
|
||||||
|
|||||||
@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
namespace Flight.Data.Migrations
|
namespace Flight.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(FlightDbContext))]
|
[DbContext(typeof(FlightDbContext))]
|
||||||
[Migration("20230113183335_Init")]
|
[Migration("20230120222458_Init")]
|
||||||
partial class Init
|
partial class Init
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -64,6 +64,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -112,6 +113,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -191,6 +193,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("status");
|
.HasColumnName("status");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -255,6 +258,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("type");
|
.HasColumnName("type");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -61,6 +61,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -109,6 +110,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("name");
|
.HasColumnName("name");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -188,6 +190,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("status");
|
.HasColumnName("status");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -252,6 +255,7 @@ namespace Flight.Data.Migrations
|
|||||||
.HasColumnName("type");
|
.HasColumnName("type");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
using BuildingBlocks.EFCore;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
||||||
|
|
||||||
@ -12,5 +11,8 @@ public class PassengerConfiguration: IEntityTypeConfiguration<Passengers.Models.
|
|||||||
|
|
||||||
builder.HasKey(r => r.Id);
|
builder.HasKey(r => r.Id);
|
||||||
builder.Property(r => r.Id).ValueGeneratedNever();
|
builder.Property(r => r.Id).ValueGeneratedNever();
|
||||||
|
|
||||||
|
// // ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=fluent-api
|
||||||
|
builder.Property(r => r.Version).IsConcurrencyToken();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ using Passenger.Data;
|
|||||||
namespace Passenger.Data.Migrations
|
namespace Passenger.Data.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PassengerDbContext))]
|
[DbContext(typeof(PassengerDbContext))]
|
||||||
[Migration("20230113183717_initial")]
|
[Migration("20230120222631_initial")]
|
||||||
partial class initial
|
partial class initial
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -68,6 +68,7 @@ namespace Passenger.Data.Migrations
|
|||||||
.HasColumnName("passport_number");
|
.HasColumnName("passport_number");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
@ -65,6 +65,7 @@ namespace Passenger.Data.Migrations
|
|||||||
.HasColumnName("passport_number");
|
.HasColumnName("passport_number");
|
||||||
|
|
||||||
b.Property<long>("Version")
|
b.Property<long>("Version")
|
||||||
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("bigint")
|
.HasColumnType("bigint")
|
||||||
.HasColumnName("version");
|
.HasColumnName("version");
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user