Finbuckle / Finbuckle.MultiTenant

Finbuckle.MultiTenant is an open-source multitenancy middleware library for .NET. It enables tenant resolution, per-tenant app behavior, and per-tenant data isolation.
https://www.finbuckle.com/multitenant
Apache License 2.0
1.34k stars 266 forks source link

Manual tenant switching without default middleware support in Finbuckle.MultiTenant #900

Open buituansonHE172207 opened 1 week ago

buituansonHE172207 commented 1 week ago

I would like to know if there is a plan or approach for manually changing the current tenant in Finbuckle.MultiTenant without relying on the default middleware. This could be helpful in scenarios where more flexibility is needed, especially in non-HTTP contexts such as background jobs, scheduled tasks, or long-running processes, where tenant context might need to be set or switched programmatically rather than automatically by middleware.

Example use cases include:

Setting the tenant for background jobs managed by job schedulers (e.g., Hangfire or Quartz.NET). Switching tenants within custom workflows or batch processing, where tenant needs to be controlled manually. This feature would help to achieve a smoother integration in various job and background processing scenarios.

AndrewTriesToCode commented 1 week ago

Hi this is possible now by using the TenantResolver class. The middleware simply wraps this class usage for web app scenarios but the other scenarios like you listed can use it directly as well. It is intended to work with DI and have the strategies and stores injected so it can use them. It can work with the generic host just as easily as the web host.

I have improved the documentation on this for the .NET 9 release later this week but I will try to add some samples.

buituansonHE172207 commented 1 week ago

Thank you for your support!

Here is my current approach for setting up the tenant context within a job:

public async Task ExecuteJobForTenant(string? tenantIdentifier, IJobExecutionStrategy jobExecutionStrategy)
{
    using var scope = serviceScopeFactory.CreateScope();
    var serviceProvider = scope.ServiceProvider;
    ArgumentNullException.ThrowIfNull(tenantIdentifier);
    var tenantInfo = await tenantService.GetByIdentifierAsync(tenantIdentifier);
    if (tenantInfo != null)
    {
        var mtcSetter = serviceProvider.GetRequiredService<IMultiTenantContextSetter>();
        var resolver = serviceProvider.GetRequiredService<ITenantResolver>();
        var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
        var httpContext = httpContextAccessor.HttpContext ?? new DefaultHttpContext();

        // Setting the CampusCode header for tenant identification
        httpContext.Request.Headers.Append("CampusCode", tenantInfo.Identifier);
        var mtContext = await resolver.ResolveAsync(httpContext);
        mtcSetter.MultiTenantContext = mtContext;

        // Execute the job with the tenant context
        await jobExecutionStrategy.ExecuteJobForTenantAsync(serviceProvider);
    }
}

Could you please provide feedback on this approach? I’d like to know if this is a recommended way to manually set up the tenant context for non-HTTP contexts like background jobs, and if there are any potential improvements or best practices that could enhance stability and performance.

Thank you!