fullstackhero / dotnet-starter-kit

Production Grade Cloud-Ready .NET 8 Starter Kit (Web API + Blazor Client) with Multitenancy Support, and Clean/Modular Architecture that saves roughly 200+ Development Hours! All Batteries Included.
https://fullstackhero.net/dotnet-webapi-boilerplate/
MIT License
5.19k stars 1.56k forks source link

Multi Tenant - ApplicationDbContext on BackgroundJob, Worker or isolated process: Example HangFire #165

Closed frankyjquintero closed 2 years ago

frankyjquintero commented 2 years ago

The use of the 'contextdb' within an isolated process presents conflicts since the whole definition depends on the httpContext for some internal processes such as audit or multiple tenant (ITenantService tenantService, ICurrentUser currentUserService)

https://github.com/fullstackhero/dotnet-webapi-boilerplate/blob/ef53f36f419a2817c57381719cf77aaafc133f15/src/Infrastructure/Persistence/ApplicationDbContext.cs#L21

The dependency of the httpContext is generated in these processes

https://github.com/fullstackhero/dotnet-webapi-boilerplate/blob/ef53f36f419a2817c57381719cf77aaafc133f15/src/Infrastructure/Services/General/TenantService.cs#L43

https://github.com/fullstackhero/dotnet-webapi-boilerplate/blob/bc433a04013c1b3580f937d7331b4f6fd779b582/src/Infrastructure/Identity/Services/CurrentUser.cs#L14

This step is a great limitation to be able to create isolated or background processes for secondary tasks or Jobs with Hangfire that require an injection of 'ApplicationDbContext' or any of the existing repositories.

I consider that the process of defining tenantId and CurrentUserId should occur in the flow from the application so the services, dbcontext and other dependencies are isolated from the httpcontext layer

PS: in the case of HangFire, an extra solution must be created since achieving tenure from this point requires additional steps such as those mentioned here:

https://stackoverflow.com/questions/57394712/hangfire-multi-tenant-asp-net-core-resolving-the-correct-tenant

It is to clarify that I do not mean to create a database for each hangfire, but to use the dbcontext within it for background tasks which for obvious reasons cannot carry the HTTP context.

https://docs.hangfire.io/en/latest/background-methods/passing-dependencies.html

Example Basic:

BrandGeneratorTask.cs

`public class BrandGeneratorTask {

    private readonly IRepositoryAsync _repository;
    public BrandGeneratorTask(IRepositoryAsync repository)
    {
        _repository = repository;
    }

    [Queue("notdefault")]
    [DisplayName("Generate Random Brand example job on Queue notDefault")]
    public void Execute()
    {
        foreach (int item in Enumerable.Range(0, 100))
        {
            await _repository.CreateAsync<Brand>(new Brand(name: $"Brand Random - {Guid.NewGuid()}", "Funny description"));
        }
        await _repository.SaveChangesAsync();
    }
}`

Call example(representation of the generic call process from the jobservice):

BackgroundJob.Enqueue<BrandGeneratorTask>(x => x.Execute());

It is finally that the process of sending arguments to Hangfire should not be of type ref or out

https://docs.hangfire.io/en/latest/background-methods/passing-arguments.html

https://www.hangfire.io/blog/2014/05/10/are-your-methods-ready-to-run-in-background.html

frankyjquintero commented 2 years ago

141

frankyjquintero commented 2 years ago

166 Hangfire: Multi tenant resolving the correct(ApplicationDbContext and Services) for tenant on BackgroundJob

@mohitook Based on your main need to solve the dbcontext for each tenant through dependency injection, I have created this solution that I would like you to test