public async Task Invoke(HttpContext httpContext) { // Ensure all ShellContext are loaded and available. await _shellHost.InitializeAsync(); var shellSettings = _runningShellTable.Match(httpContext); // We only serve the next request if the tenant has been resolved. if (shellSettings != null) { if (shellSettings.State == TenantState.Initializing) { httpContext.Response.Headers.Add(HeaderNames.RetryAfter, "10"); httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; await httpContext.Response.WriteAsync("The requested tenant is currently initializing."); return; } // Makes 'RequestServices' aware of the current 'ShellScope'. httpContext.UseShellScopeServices(); var shellScope = await _shellHost.GetScopeAsync(shellSettings); // Holds the 'ShellContext' for the full request. httpContext.Features.Set(new ShellContextFeature { ShellContext = shellScope.ShellContext, OriginalPath = httpContext.Request.Path, OriginalPathBase = httpContext.Request.PathBase }); await shellScope.UsingAsync(scope => _next.Invoke(httpContext)); } }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { stoppingToken.Register(() => { _logger.LogInformation("'{ServiceName}' is stopping.", nameof(ModularBackgroundService)); }); if (_options.ShellWarmup) { // Ensure all ShellContext are loaded and available. await _shellHost.InitializeAsync(); } while (GetRunningShells().Count() < 1) { try { await Task.Delay(MinIdleTime, stoppingToken); } catch (TaskCanceledException) { break; } } var previousShells = Enumerable.Empty <ShellContext>(); while (!stoppingToken.IsCancellationRequested) { try { var runningShells = GetRunningShells(); await UpdateAsync(previousShells, runningShells, stoppingToken); previousShells = runningShells; var pollingDelay = Task.Delay(PollingTime, stoppingToken); await RunAsync(runningShells, stoppingToken); await WaitAsync(pollingDelay, stoppingToken); } catch (Exception ex) when(!ex.IsFatal()) { _logger.LogError(ex, "Error while executing '{ServiceName}'", nameof(ModularBackgroundService)); } } }
public async Task Invoke(HttpContext httpContext) { // Ensure all ShellContext are loaded and available. await _shellHost.InitializeAsync(); var shellSettings = _runningShellTable.Match(httpContext); // We only serve the next request if the tenant has been resolved. if (shellSettings != null) { if (shellSettings.State == TenantState.Initializing) { httpContext.Response.Headers.Add(HeaderNames.RetryAfter, "10"); httpContext.Response.StatusCode = StatusCodes.Status503ServiceUnavailable; await httpContext.Response.WriteAsync("The requested tenant is currently initializing."); return; } var hasPendingTasks = false; // We need to get a scope and the ShellContext that created it var(scope, shellContext) = await _shellHost.GetScopeAndContextAsync(shellSettings); using (scope) { // Register the shell context as a custom feature httpContext.Features.Set(shellContext); if (!shellContext.IsActivated) { var semaphore = _semaphores.GetOrAdd(shellSettings.Name, (name) => new SemaphoreSlim(1)); await semaphore.WaitAsync(); try { // The tenant gets activated here if (!shellContext.IsActivated) { using (var activatingScope = await _shellHost.GetScopeAsync(shellSettings)) { var tenantEvents = activatingScope.ServiceProvider.GetServices <IModularTenantEvents>(); foreach (var tenantEvent in tenantEvents) { await tenantEvent.ActivatingAsync(); } foreach (var tenantEvent in tenantEvents.Reverse()) { await tenantEvent.ActivatedAsync(); } } shellContext.IsActivated = true; } } finally { semaphore.Release(); _semaphores.TryRemove(shellSettings.Name, out semaphore); } } await _next.Invoke(httpContext); var deferredTaskEngine = scope.ServiceProvider.GetService <IDeferredTaskEngine>(); hasPendingTasks = deferredTaskEngine?.HasPendingTasks ?? false; } // Create a new scope only if there are pending tasks if (hasPendingTasks) { using (var pendingScope = await _shellHost.GetScopeAsync(shellSettings)) { if (pendingScope != null) { var deferredTaskEngine = pendingScope.ServiceProvider.GetService <IDeferredTaskEngine>(); var context = new DeferredTaskContext(pendingScope.ServiceProvider); await deferredTaskEngine.ExecuteTasksAsync(context); } } } } }