first commit
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Fitness_core_BE.Infra.Tenancy
|
||||
{
|
||||
// A matched Central/Tenant context pair bound to one specific, already-known schema --
|
||||
// built for callers that have no HttpContext to resolve a tenant from (background jobs),
|
||||
// so TenantConnectionResolver's request-based resolution can't apply. Dispose both
|
||||
// contexts together via `await using`.
|
||||
public sealed class TenantSchemaContext : IAsyncDisposable
|
||||
{
|
||||
public Guid ClientId { get; }
|
||||
public string SchemaName { get; }
|
||||
public CentralDbContext Central { get; }
|
||||
public TenantDbContext Tenant { get; }
|
||||
|
||||
public TenantSchemaContext(Guid clientId, string schemaName, CentralDbContext central, TenantDbContext tenant)
|
||||
{
|
||||
ClientId = clientId;
|
||||
SchemaName = schemaName;
|
||||
Central = central;
|
||||
Tenant = tenant;
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
await Tenant.DisposeAsync();
|
||||
await Central.DisposeAsync();
|
||||
}
|
||||
}
|
||||
|
||||
// Single club, single database -- every background job that used to loop over "every
|
||||
// provisioned tenant schema" now just runs once against the one Client row / one DB. Kept
|
||||
// as a class (rather than deleting it and rewriting every caller) so OverdueLockJob,
|
||||
// SubscriptionRenewalJob and AdminBackgroundJobsController keep working unchanged.
|
||||
public class TenantSchemaIterator
|
||||
{
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public TenantSchemaIterator(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
// Every client row (there's exactly one for Vijaya) -- callers open their own
|
||||
// TenantSchemaContext per entry.
|
||||
public async IAsyncEnumerable<(Guid ClientId, string SchemaName)> GetProvisionedRegistrationsAsync(
|
||||
[System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken ct = default)
|
||||
{
|
||||
List<Guid> clientIds;
|
||||
await using (var central = CreateCentralDbContext())
|
||||
{
|
||||
clientIds = await central.Client.AsNoTracking().Select(c => c.ClientId).ToListAsync(ct);
|
||||
}
|
||||
|
||||
foreach (var clientId in clientIds)
|
||||
yield return (clientId, "default");
|
||||
}
|
||||
|
||||
// One client's schema, when the caller already knows the ClientId (e.g. OverdueLockJob's
|
||||
// existing per-client loop from the central Client table). Null if the client doesn't exist.
|
||||
public async Task<TenantSchemaContext?> OpenSchemaForClientAsync(Guid clientId, CancellationToken ct = default)
|
||||
{
|
||||
bool exists;
|
||||
await using (var central = CreateCentralDbContext())
|
||||
{
|
||||
exists = await central.Client.AsNoTracking().AnyAsync(c => c.ClientId == clientId, ct);
|
||||
}
|
||||
|
||||
return exists ? OpenSchema(clientId, "default") : null;
|
||||
}
|
||||
|
||||
// Opens a matched context pair for an already-known ClientId. Building the contexts
|
||||
// doesn't itself open a connection -- EF Core connects lazily on first query.
|
||||
public TenantSchemaContext OpenSchema(Guid clientId, string schemaName)
|
||||
{
|
||||
var central = CreateCentralDbContext();
|
||||
var tenant = CreateTenantDbContext();
|
||||
return new TenantSchemaContext(clientId, schemaName, central, tenant);
|
||||
}
|
||||
|
||||
private CentralDbContext CreateCentralDbContext()
|
||||
{
|
||||
var connectionString = _configuration.GetConnectionString("DefaultConnection")
|
||||
?? throw new InvalidOperationException("DefaultConnection is not configured.");
|
||||
var optionsBuilder = new DbContextOptionsBuilder<CentralDbContext>();
|
||||
optionsBuilder.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 0)));
|
||||
return new CentralDbContext(optionsBuilder.Options);
|
||||
}
|
||||
|
||||
private TenantDbContext CreateTenantDbContext()
|
||||
{
|
||||
var connectionString = _configuration.GetConnectionString("DefaultConnection")
|
||||
?? throw new InvalidOperationException("DefaultConnection is not configured.");
|
||||
var optionsBuilder = new DbContextOptionsBuilder<TenantDbContext>();
|
||||
optionsBuilder.UseMySql(connectionString, new MySqlServerVersion(new Version(8, 0, 0)));
|
||||
return new TenantDbContext(optionsBuilder.Options);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user