112 lines
3.4 KiB
C#
112 lines
3.4 KiB
C#
using System.Data;
|
|
using System.Text.Json;
|
|
using AMREZ.EOP.Abstractions.Applications.Tenancy;
|
|
using AMREZ.EOP.Abstractions.Infrastructures.Common;
|
|
using AMREZ.EOP.Abstractions.Storage;
|
|
using AMREZ.EOP.Domain.Entities.Authentications;
|
|
using AMREZ.EOP.Domain.Shared._Users;
|
|
using AMREZ.EOP.Infrastructures.Data;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.EntityFrameworkCore.Storage;
|
|
using StackExchange.Redis;
|
|
|
|
namespace AMREZ.EOP.Infrastructures.UnitOfWork;
|
|
|
|
public sealed class EFUnitOfWork : IUnitOfWork, IAsyncDisposable
|
|
{
|
|
private readonly IDbScope _scope;
|
|
private readonly ITenantDbContextFactory _factory;
|
|
private readonly IConnectionMultiplexer? _redis;
|
|
|
|
private IDbContextTransaction? _tx;
|
|
private ITenantContext? _tenant;
|
|
|
|
public string Backend => "ef";
|
|
|
|
public EFUnitOfWork(
|
|
IDbScope scope,
|
|
ITenantDbContextFactory factory,
|
|
IConnectionMultiplexer? redis = null)
|
|
{
|
|
_scope = scope;
|
|
_factory = factory;
|
|
_redis = redis;
|
|
}
|
|
|
|
public async Task BeginAsync(ITenantContext tenant, IsolationLevel isolation = IsolationLevel.ReadCommitted, CancellationToken ct = default)
|
|
{
|
|
if (_tx is not null) return;
|
|
_tenant = tenant ?? throw new ArgumentNullException(nameof(tenant));
|
|
|
|
_scope.EnsureForTenant(tenant);
|
|
var db = _scope.Get<AppDbContext>();
|
|
_tx = await db.Database.BeginTransactionAsync(isolation, ct);
|
|
}
|
|
|
|
public async Task CommitAsync(CancellationToken ct = default)
|
|
{
|
|
if (_tx is null) return;
|
|
|
|
var db = _scope.Get<AppDbContext>();
|
|
|
|
var changedUsers = db.ChangeTracker.Entries<User>()
|
|
.Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted)
|
|
.Select(e => e.Entity)
|
|
.ToList();
|
|
|
|
var changedIdentities = db.ChangeTracker.Entries<UserIdentity>()
|
|
.Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted)
|
|
.Select(e => e.Entity)
|
|
.ToList();
|
|
|
|
await db.SaveChangesAsync(ct);
|
|
await _tx.CommitAsync(ct);
|
|
|
|
if (_redis is not null && _tenant is not null && (changedUsers.Count > 0 || changedIdentities.Count > 0))
|
|
{
|
|
var r = _redis.GetDatabase();
|
|
var tenantId = _tenant.Id;
|
|
var tasks = new List<Task>();
|
|
|
|
foreach (var u in changedUsers)
|
|
{
|
|
var keyId = $"eop:{tenantId}:user:id:{u.Id:N}";
|
|
tasks.Add(r.KeyDeleteAsync(keyId));
|
|
var payload = JsonSerializer.Serialize(u);
|
|
var ttl = TimeSpan.FromMinutes(5);
|
|
tasks.Add(r.StringSetAsync(keyId, payload, ttl));
|
|
}
|
|
|
|
foreach (var i in changedIdentities.Where(i => i.Type == IdentityType.Email))
|
|
{
|
|
var k = $"eop:{tenantId}:user:identity:email:{i.Identifier}";
|
|
tasks.Add(r.KeyDeleteAsync(k));
|
|
}
|
|
|
|
try { await Task.WhenAll(tasks); } catch { }
|
|
}
|
|
|
|
await DisposeAsync();
|
|
}
|
|
|
|
public async Task RollbackAsync(CancellationToken ct = default)
|
|
{
|
|
try
|
|
{
|
|
if (_tx is not null)
|
|
await _tx.RollbackAsync(ct);
|
|
}
|
|
finally
|
|
{
|
|
await DisposeAsync();
|
|
}
|
|
}
|
|
|
|
public ValueTask DisposeAsync()
|
|
{
|
|
try { _tx?.Dispose(); } catch { }
|
|
_tx = null;
|
|
_tenant = null;
|
|
return ValueTask.CompletedTask;
|
|
}
|
|
} |