Meysam Hadeli 6da82e39ce .
2024-09-16 01:58:31 +03:30

129 lines
4.1 KiB
C#

using BuildingBlocks.EFCore;
using Microsoft.EntityFrameworkCore;
namespace BuildingBlocks.PersistMessageProcessor.Data;
using Configurations;
using Core.Model;
using Microsoft.Extensions.Logging;
using Exception = System.Exception;
using IsolationLevel = System.Data.IsolationLevel;
public class PersistMessageDbContext : DbContext, IPersistMessageDbContext
{
private readonly ILogger<PersistMessageDbContext>? _logger;
public PersistMessageDbContext(DbContextOptions<PersistMessageDbContext> options,
ILogger<PersistMessageDbContext>? logger = null)
: base(options)
{
_logger = logger;
}
public DbSet<PersistMessage> PersistMessages => Set<PersistMessage>();
protected override void OnModelCreating(ModelBuilder builder)
{
builder.ApplyConfiguration(new PersistMessageConfiguration());
base.OnModelCreating(builder);
builder.ToSnakeCaseTables();
}
//ref: https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency#execution-strategies-and-transactions
public Task ExecuteTransactionalAsync(CancellationToken cancellationToken = default)
{
var strategy = Database.CreateExecutionStrategy();
return strategy.ExecuteAsync(async () =>
{
await using var transaction =
await Database.BeginTransactionAsync(IsolationLevel.ReadCommitted, cancellationToken);
try
{
await SaveChangesAsync(cancellationToken);
await transaction.CommitAsync(cancellationToken);
}
catch
{
await transaction.RollbackAsync(cancellationToken);
throw;
}
});
}
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
OnBeforeSaving();
try
{
return await base.SaveChangesAsync(cancellationToken);
}
//ref: https://learn.microsoft.com/en-us/ef/core/saving/concurrency?tabs=data-annotations#resolving-concurrency-conflicts
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in ex.Entries)
{
var databaseValues = await entry.GetDatabaseValuesAsync(cancellationToken);
if (databaseValues == null)
{
_logger.LogError("The record no longer exists in the database, The record has been deleted by another user.");
throw;
}
// Refresh the original values to bypass next concurrency check
entry.OriginalValues.SetValues(databaseValues);
}
return await base.SaveChangesAsync(cancellationToken);
}
}
public void CreatePersistMessageTable()
{
if (Database.GetPendingMigrations().Any())
{
throw new InvalidOperationException("Cannot create table if there are pending migrations.");
}
string createTableSql = @"
create table if not exists persist_message (
id uuid not null,
data_type text,
data text,
created timestamp with time zone not null,
retry_count integer not null,
message_status text not null default 'InProgress'::text,
delivery_type text not null default 'Outbox'::text,
version bigint not null,
constraint pk_persist_message primary key (id)
)";
Database.ExecuteSqlRaw(createTableSql);
}
private void OnBeforeSaving()
{
try
{
foreach (var entry in ChangeTracker.Entries<IVersion>())
{
switch (entry.State)
{
case EntityState.Modified:
entry.Entity.Version++;
break;
case EntityState.Deleted:
entry.Entity.Version++;
break;
}
}
}
catch (Exception ex)
{
throw new Exception("try for find IVersion", ex);
}
}
}