From bfb1209ab3d9dc2a982a347cbd45ed88063470be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 May 2025 04:13:03 +0000 Subject: [PATCH 1/5] Initial plan for issue From 476152f7471f42a34ba8421b58c06d5278d3abc5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 May 2025 04:18:22 +0000 Subject: [PATCH 2/5] Implement fix for SaveManyAsync to call IEntitySavingHandler when present Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --- .../Elsa.Persistence.EFCore.Common/Store.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs b/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs index 0ac58c34..e755f298 100644 --- a/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs +++ b/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs @@ -173,6 +173,17 @@ public async Task SaveManyAsync( if (entityList.Count == 0) return; + // Check if there are any IEntitySavingHandler instances registered + // If so, fall back to individual SaveAsync calls to ensure handlers are invoked + var entitySavingHandlers = serviceProvider.GetServices().ToList(); + if (entitySavingHandlers.Count > 0) + { + // Fall back to individual saves to ensure IEntitySavingHandler instances are called + var saveTasks = entityList.Select(entity => SaveAsync(entity, keySelector, onSaving, cancellationToken)).ToList(); + await Task.WhenAll(saveTasks); + return; + } + await using var dbContext = await CreateDbContextAsync(cancellationToken); if (onSaving != null) From 6cd3b5d199f574c6a484a46a6043860fcad894b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 May 2025 04:39:07 +0000 Subject: [PATCH 3/5] Modified BulkUpsertAsync to call entity saving handlers directly Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --- .../Extensions/BulkUpsertExtensions.cs | 36 +++++++++++++++++++ .../Elsa.Persistence.EFCore.Common/Store.cs | 11 ------ 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs b/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs index 0238d4ed..050f04ff 100644 --- a/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs +++ b/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs @@ -2,6 +2,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.ChangeTracking; +using Microsoft.Extensions.DependencyInjection; using System.Linq.Expressions; // ReSharper disable once CheckNamespace @@ -55,6 +57,40 @@ public static async Task BulkUpsertAsync( { if (entities.Count == 0) return; + + // Call entity saving handlers if this is an ElsaDbContextBase and handlers are registered + if (dbContext is ElsaDbContextBase elsaDbContext) + { + var serviceProvider = elsaDbContext.GetService(); + var entitySavingHandlers = serviceProvider.GetServices().ToList(); + + if (entitySavingHandlers.Count > 0) + { + // Process each entity through the handlers + foreach (var entity in entities) + { + // Attach entity temporarily to create EntityEntry + var entry = dbContext.Entry(entity); + + // Determine proper EntityState based on whether it exists + var keyPropertyInfo = keySelector.GetMemberAccess().Member as System.Reflection.PropertyInfo; + if (keyPropertyInfo != null) + { + var keyValue = keyPropertyInfo.GetValue(entity) as string; + entry.State = !string.IsNullOrEmpty(keyValue) ? EntityState.Modified : EntityState.Added; + } + + // Call each handler + foreach (var handler in entitySavingHandlers) + { + await handler.HandleAsync(elsaDbContext, entry, cancellationToken); + } + + // Detach the entity to avoid duplicate tracking + entry.State = EntityState.Detached; + } + } + } // Identify the current provider (e.g., "Microsoft.EntityFrameworkCore.SqlServer") var providerName = dbContext.Database.ProviderName?.ToLowerInvariant() ?? string.Empty; diff --git a/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs b/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs index e755f298..0ac58c34 100644 --- a/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs +++ b/src/persistence/Elsa.Persistence.EFCore.Common/Store.cs @@ -173,17 +173,6 @@ public async Task SaveManyAsync( if (entityList.Count == 0) return; - // Check if there are any IEntitySavingHandler instances registered - // If so, fall back to individual SaveAsync calls to ensure handlers are invoked - var entitySavingHandlers = serviceProvider.GetServices().ToList(); - if (entitySavingHandlers.Count > 0) - { - // Fall back to individual saves to ensure IEntitySavingHandler instances are called - var saveTasks = entityList.Select(entity => SaveAsync(entity, keySelector, onSaving, cancellationToken)).ToList(); - await Task.WhenAll(saveTasks); - return; - } - await using var dbContext = await CreateDbContextAsync(cancellationToken); if (onSaving != null) From ee690a524ef542fe095ad31e8f3466492292e9ae Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 May 2025 05:06:36 +0000 Subject: [PATCH 4/5] Optimize BulkUpsertAsync by moving keyPropertyInfo outside loop Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --- .../Extensions/BulkUpsertExtensions.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs b/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs index 050f04ff..1cf169fd 100644 --- a/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs +++ b/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs @@ -66,6 +66,9 @@ public static async Task BulkUpsertAsync( if (entitySavingHandlers.Count > 0) { + // Get key property info once since it's the same for all entities + var keyPropertyInfo = keySelector.GetMemberAccess().Member as System.Reflection.PropertyInfo; + // Process each entity through the handlers foreach (var entity in entities) { @@ -73,7 +76,6 @@ public static async Task BulkUpsertAsync( var entry = dbContext.Entry(entity); // Determine proper EntityState based on whether it exists - var keyPropertyInfo = keySelector.GetMemberAccess().Member as System.Reflection.PropertyInfo; if (keyPropertyInfo != null) { var keyValue = keyPropertyInfo.GetValue(entity) as string; From 380047e63b91902543fc973512d43e9c1622bafa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 28 May 2025 05:16:59 +0000 Subject: [PATCH 5/5] Fix compilation error in BulkUpsertExtensions Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com> --- .../Extensions/BulkUpsertExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs b/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs index 1cf169fd..00ba7052 100644 --- a/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs +++ b/src/persistence/Elsa.Persistence.EFCore.Common/Extensions/BulkUpsertExtensions.cs @@ -67,7 +67,7 @@ public static async Task BulkUpsertAsync( if (entitySavingHandlers.Count > 0) { // Get key property info once since it's the same for all entities - var keyPropertyInfo = keySelector.GetMemberAccess().Member as System.Reflection.PropertyInfo; + var keyPropertyInfo = keySelector.GetMemberAccess() as System.Reflection.PropertyInfo; // Process each entity through the handlers foreach (var entity in entities)