Files
amrez-nova-eop-services-api/AMREZ.EOP.Infrastructures/Tenancy/TenantDbContextFactory.cs
Thanakarn Klangkasame 92e614674c Init Git
2025-09-30 11:01:02 +07:00

55 lines
2.4 KiB
C#

using AMREZ.EOP.Abstractions.Applications.Tenancy;
using AMREZ.EOP.Domain.Shared.Tenancy;
using AMREZ.EOP.Infrastructures.Data;
using AMREZ.EOP.Infrastructures.Options;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
namespace AMREZ.EOP.Infrastructures.Tenancy;
public sealed class TenantDbContextFactory : ITenantDbContextFactory
{
private readonly IOptions<AuthOptions> _opts;
private readonly SearchPathInterceptor _searchPath;
private readonly TenantRlsInterceptor _rls;
public TenantDbContextFactory(IOptions<AuthOptions> opts, SearchPathInterceptor searchPath, TenantRlsInterceptor rls)
{ _opts = opts; _searchPath = searchPath; _rls = rls; }
public TContext Create<TContext>(ITenantContext t) where TContext : class
{
if (typeof(TContext) != typeof(AppDbContext))
throw new NotSupportedException($"Unsupported context: {typeof(TContext).Name}");
var builder = new DbContextOptionsBuilder<AppDbContext>();
// ✅ การ์ด: ถ้า connection string ของ tenant ไม่ใช่รูปแบบ key=value ให้ fallback ไป DefaultConnection (platform)
var raw = (t.ConnectionString ?? string.Empty).Trim();
string conn = IsLikelyValidConnectionString(raw)
? raw
: (_opts.Value.DefaultConnection ?? string.Empty).Trim();
if (!IsLikelyValidConnectionString(conn))
throw new InvalidOperationException(
$"Invalid connection string for tenant '{t.TenantKey}'. Provide a valid per-tenant or DefaultConnection.");
builder.UseNpgsql(conn);
// RLS per-tenant (ถ้า platform ควรกำหนดให้ Interceptor ทำงานในโหมด platform/disable ตามที่คุณนิยาม)
_rls.Configure(t.Id);
builder.AddInterceptors(_rls);
// ใช้ schema ต่อ tenant เมื่อเปิดโหมดนี้
if (_opts.Value.UseSchemaPerTenant)
{
var schema = string.IsNullOrWhiteSpace(t.Schema) ? t.Id : t.Schema!.Trim();
_searchPath.Configure(schema, enabled: true); // CREATE IF NOT EXISTS + SET LOCAL search_path
builder.AddInterceptors(_searchPath);
}
return (new AppDbContext(builder.Options) as TContext)!;
}
private static bool IsLikelyValidConnectionString(string s)
=> !string.IsNullOrWhiteSpace(s) && s.Contains('=');
}