Assign Tenant.AgnosticTenantId in populator#7252
Conversation
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 <cursoragent@cursor.com>
|
Fix for #7253 |
|
@copilot please review these changes and add/update unit & component tests. |
|
@greptile |
Greptile OverviewGreptile SummaryThis PR fixes a critical bug where entities were being persisted with NULL Changes Made
Issues FoundThe changes in
According to ADR-0009, workflows without explicit tenant IDs should be assigned to the current tenant (empty string for default), not made tenant-agnostic (asterisk). Making workflows tenant-agnostic requires explicit developer intent by setting Recommended FixUse Confidence Score: 2/5
|
| Filename | Overview |
|---|---|
| src/modules/Elsa.Persistence.EFCore.Common/Store.cs | Changed from Tenant?.Id to TenantId property to ensure default tenant uses empty string instead of null |
| src/modules/Elsa.Persistence.EFCore.Common/TenantAwareDbContextFactory.cs | Changed from Tenant?.Id to TenantId property to ensure dbContext gets normalized tenant ID |
| src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs | Changed null handling to use AgnosticTenantId instead of NormalizeTenantId, but creates inconsistency with currentTenantId comparison |
Sequence Diagram
sequenceDiagram
participant WP as WorkflowProvider
participant Pop as DefaultWorkflowDefinitionStorePopulator
participant TA as ITenantAccessor
participant Store as WorkflowDefinitionStore
participant DB as Database (EF Core)
participant Factory as TenantAwareDbContextFactory
Note over Pop: PopulateStoreAsync called
Pop->>TA: Get current tenant
TA-->>Pop: Tenant (may be null for default)
Note over Pop: OLD: Tenant?.Id.NormalizeTenantId()<br/>NEW: TenantId property
Pop->>WP: GetWorkflowsAsync()
WP-->>Pop: Workflows (some with TenantId=null)
Note over Pop: For each workflow:
Pop->>Pop: Normalize workflow.Identity.TenantId
Note over Pop: ISSUE: null → "*" (AgnosticTenantId)<br/>SHOULD BE: null → "" (via TenantId)
Pop->>Store: AddOrUpdateAsync(workflow)
Store->>Factory: CreateDbContextAsync()
Factory->>TA: Get TenantId property
TA-->>Factory: Normalized tenant ID ("")
Note over Factory: OLD: Tenant?.Id (null)<br/>NEW: TenantId property ("")
Factory-->>Store: DbContext with TenantId=""
Store->>DB: SaveChangesAsync()
Note over DB: Entities saved with<br/>TenantId="" (default tenant)<br/>instead of NULL
src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs
Outdated
Show resolved
Hide resolved
src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs
Outdated
Show resolved
Hide resolved
Additional Comments (1)
The This is consistent with the changes in |
…initionStorePopulator.cs Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
sfmskywalker
left a comment
There was a problem hiding this comment.
Looks great. Thanks @RalfvandenBurg !
| { | ||
| // Normalize tenant IDs for comparison (null becomes empty string) | ||
| var definitionTenantId = result.Workflow.Identity.TenantId.NormalizeTenantId(); | ||
| var definitionTenantId = result.Workflow.Identity.TenantId ?? _tenantAccessor.TenantId; |
There was a problem hiding this comment.
Heads up: this change seems incorrect. What will happen now is that if a workflow without a tenant ID (e.g. a code-first workflow) will receive the ID of the current tenant, which is problematic given that this populator is executed for all tenants in the system. This means that the last tenant being iterated will be the owner of the code-first workflows.
for that reason, I will revert this part - but I also don't want to break your stuff, so if you let me know why this change was necessary in your scenarios, please let me know and we can find a better fix.
|
@sfmskywalker Thanks for the review (and also for correcting the missing class name mistake)
|
|
@RalfvandenBurg Thanks for the context, it makes sense 👍🏻 |
When having Workflow in code. After updating to 3.6.0-rc3 WorkflowGraphs cannot be located any more, because the TenantId is set to the DefaultTenantId.