From 09d768791a2e63b981db693a66bb36145cf93936 Mon Sep 17 00:00:00 2001 From: RalfvandenBurg Date: Mon, 9 Feb 2026 11:03:49 +0100 Subject: [PATCH] Assign Tenant.AgnosticTenantId in populator (#7252) * Assign Tenant.AgnosticTenantId in populator * Corrected comment * Fix entities persisted with NULL TenantId instead of default tenant Use normalized TenantId property (which returns empty string for default tenant) instead of Tenant?.Id (which returns null when no tenant is set). When Tenant?.Id returned null: TenantAwareDbContextFactory set dbContext.TenantId to null, ApplyTenantId skipped assigning TenantId, entities were saved with NULL TenantId, and query filters using SQL equality against NULL never matched these entities. This affected all EF Core stores including BookmarkStore and any store using bulk upsert operations. Co-authored-by: Cursor * Update src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * PR comments * Set Tenant.AgnosticTenantId when no tenantId is specified --------- Co-authored-by: Ralf Co-authored-by: Cursor Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- src/modules/Elsa.Persistence.EFCore.Common/Store.cs | 2 +- .../TenantAwareDbContextFactory.cs | 2 +- .../Providers/ClrWorkflowsProvider.cs | 5 +++-- .../Services/DefaultWorkflowDefinitionStorePopulator.cs | 8 ++++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/modules/Elsa.Persistence.EFCore.Common/Store.cs b/src/modules/Elsa.Persistence.EFCore.Common/Store.cs index 680e2ca7a..c8ddbdd43 100644 --- a/src/modules/Elsa.Persistence.EFCore.Common/Store.cs +++ b/src/modules/Elsa.Persistence.EFCore.Common/Store.cs @@ -182,7 +182,7 @@ public class Store(IDbContextFactory dbContextF } // When doing a custom SQL query (Bulk Upsert), none of the installed query filters will be applied. Hence, we are assigning the current tenant ID explicitly. - var tenantId = serviceProvider.GetRequiredService().Tenant?.Id; + var tenantId = serviceProvider.GetRequiredService().TenantId; foreach (var entity in entityList) { if (entity is Entity entityWithTenant) diff --git a/src/modules/Elsa.Persistence.EFCore.Common/TenantAwareDbContextFactory.cs b/src/modules/Elsa.Persistence.EFCore.Common/TenantAwareDbContextFactory.cs index 8128553ce..bcf4fd227 100644 --- a/src/modules/Elsa.Persistence.EFCore.Common/TenantAwareDbContextFactory.cs +++ b/src/modules/Elsa.Persistence.EFCore.Common/TenantAwareDbContextFactory.cs @@ -32,6 +32,6 @@ public class TenantAwareDbContextFactory( private void SetTenantId(TDbContext context) { if (context is ElsaDbContextBase elsaContext) - elsaContext.TenantId = tenantAccessor.Tenant?.Id; + elsaContext.TenantId = tenantAccessor.TenantId; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Providers/ClrWorkflowsProvider.cs b/src/modules/Elsa.Workflows.Runtime/Providers/ClrWorkflowsProvider.cs index debe17fb2..94aa271a2 100644 --- a/src/modules/Elsa.Workflows.Runtime/Providers/ClrWorkflowsProvider.cs +++ b/src/modules/Elsa.Workflows.Runtime/Providers/ClrWorkflowsProvider.cs @@ -1,3 +1,4 @@ +using Elsa.Common.Multitenancy; using Elsa.Workflows.Management.Materializers; using Elsa.Workflows.Runtime.Features; using Elsa.Workflows.Runtime.Options; @@ -10,7 +11,7 @@ namespace Elsa.Workflows.Runtime.Providers; /// Provides workflows to the system that are registered with /// [UsedImplicitly] -public class ClrWorkflowsProvider( +public class ( IOptions options, IWorkflowBuilderFactory workflowBuilderFactory, IServiceProvider serviceProvider) : IWorkflowsProvider @@ -41,7 +42,7 @@ public class ClrWorkflowsProvider( { Id = id, DefinitionId = definitionId, - TenantId = workflow.Identity.TenantId + TenantId = workflow.Identity.TenantId ?? Tenant.AgnosticTenantId }; var materializerContext = new ClrWorkflowMaterializerContext(workflowBuilder.GetType()); diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs index 7650fea41..b49f63785 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs @@ -60,7 +60,7 @@ public class DefaultWorkflowDefinitionStorePopulator : IWorkflowDefinitionStoreP { var providers = _workflowDefinitionProviders(); var workflowDefinitions = new List(); - var currentTenantId = (_tenantAccessor.Tenant?.Id).NormalizeTenantId(); + var currentTenantId = _tenantAccessor.TenantId; foreach (var provider in providers) { @@ -69,7 +69,7 @@ public class DefaultWorkflowDefinitionStorePopulator : IWorkflowDefinitionStoreP foreach (var result in results) { // Normalize tenant IDs for comparison (null becomes empty string) - var definitionTenantId = result.Workflow.Identity.TenantId.NormalizeTenantId(); + var definitionTenantId = result.Workflow.Identity.TenantId ?? _tenantAccessor.TenantId; // Only import workflows belonging to the current tenant or tenant-agnostic workflows (TenantId = "*"). if (definitionTenantId != currentTenantId && definitionTenantId != Tenant.AgnosticTenantId) @@ -191,8 +191,8 @@ public class DefaultWorkflowDefinitionStorePopulator : IWorkflowDefinitionStoreP await UpdateIsPublished(); // Determine the tenant ID for the workflow definition - // If the workflow has no tenant ID, use the current tenant (normalized to handle null -> "") - var workflowTenantId = workflow.Identity.TenantId ?? (_tenantAccessor.Tenant?.Id).NormalizeTenantId(); + // If the workflow has no tenant ID, use the current tenant (normalized to handle null -> "*") + var workflowTenantId = workflow.Identity.TenantId ?? _tenantAccessor.TenantId; var workflowDefinition = existingDefinitionVersion ?? new WorkflowDefinition {