55 lines
2.4 KiB
C#
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('=');
|
|
} |