From 7341f1f93ab1f7429dc7369d7249d80c090fd55f Mon Sep 17 00:00:00 2001 From: Raymond den Haan Date: Thu, 8 Aug 2024 16:08:01 +0200 Subject: [PATCH 1/3] Add customizable DB exception handlers Introduced customizable database exception handlers across various modules, enabling specific error handling strategies for different DbContexts. Implemented a generic interface for exception handling and provided specific implementations for PostgreSQL and a No-op handler. Updated the code to utilize these handlers in various persistence features, enhancing error management and modularity. --- .../Contracts/IDbExceptionHandler.cs | 14 +++++++++ .../EntityStore.cs | 9 ++---- .../Elsa.EntityFrameworkCore.Common/Store.cs | 28 +++++++++--------- .../Features.cs | 8 +++++ .../Handlers/DbExceptionHandler.cs | 29 +++++++++++++++++++ .../Handlers/NoopDbExceptionHandler.cs | 23 +++++++++++++++ .../Modules/Alterations/Feature.cs | 11 +++---- .../Modules/Identity/Feature.cs | 12 ++++---- .../Modules/Labels/Feature.cs | 11 ++++--- .../WorkflowDefinitionPersistenceFeature.cs | 12 ++++---- .../WorkflowInstancePersistenceFeature.cs | 12 ++++---- .../WorkflowManagementPersistenceFeature.cs | 12 ++++---- .../WorkflowRuntimePersistenceFeature.cs | 12 ++++---- .../Contexts/WorkflowExecutionContext.cs | 2 +- .../Exceptions/DataProcessingException.cs | 8 +++++ 15 files changed, 149 insertions(+), 54 deletions(-) create mode 100644 src/modules/Elsa.EntityFrameworkCore.Common/Contracts/IDbExceptionHandler.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore.PostgreSql/Handlers/DbExceptionHandler.cs create mode 100644 src/modules/Elsa.EntityFrameworkCore/Handlers/NoopDbExceptionHandler.cs create mode 100644 src/modules/Elsa.Workflows.Core/Exceptions/DataProcessingException.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Common/Contracts/IDbExceptionHandler.cs b/src/modules/Elsa.EntityFrameworkCore.Common/Contracts/IDbExceptionHandler.cs new file mode 100644 index 000000000..78c8707fd --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Common/Contracts/IDbExceptionHandler.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; + +namespace Elsa.EntityFrameworkCore.Common.Contracts; + +/// Defines the contract for an exception handler in a database context. +/// +/// Implementing this interface allows for customized handling of exceptions that occur during database operations. +/// The parameter is used to be able to inject different ExceptionHandlers for each DbContext. +/// +public interface IDbExceptionHandler where TDbContext : DbContext +{ + /// Handles the given exception that occurs during database operations. + public void Handle(DbUpdateException exception); +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore.Common/EntityStore.cs b/src/modules/Elsa.EntityFrameworkCore.Common/EntityStore.cs index d97a47a06..1097da421 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Common/EntityStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Common/EntityStore.cs @@ -1,4 +1,5 @@ using Elsa.Common.Entities; +using Elsa.EntityFrameworkCore.Common.Contracts; using Microsoft.EntityFrameworkCore; namespace Elsa.EntityFrameworkCore.Common; @@ -8,13 +9,9 @@ namespace Elsa.EntityFrameworkCore.Common; /// /// The type of the database context. /// The type of the entity. -public class EntityStore : Store where TDbContext : DbContext where TEntity : Entity, new() +public class EntityStore(IDbContextFactory dbContextFactory, IDbExceptionHandler exceptionHandler) + : Store(dbContextFactory, exceptionHandler) where TDbContext : DbContext where TEntity : Entity, new() { - /// - public EntityStore(IDbContextFactory dbContextFactory) : base(dbContextFactory) - { - } - /// /// Saves the entity. /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Common/Store.cs b/src/modules/Elsa.EntityFrameworkCore.Common/Store.cs index 622bb773d..c1fbdf658 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Common/Store.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Common/Store.cs @@ -1,6 +1,7 @@ using System.Linq.Expressions; using Elsa.Common.Entities; using Elsa.Common.Models; +using Elsa.EntityFrameworkCore.Common.Contracts; using Elsa.EntityFrameworkCore.Extensions; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; @@ -14,28 +15,18 @@ namespace Elsa.EntityFrameworkCore.Common; /// The type of the database context. /// The type of the entity. [PublicAPI] -public class Store where TDbContext : DbContext where TEntity : class, new() +public class Store(IDbContextFactory dbContextFactory, IDbExceptionHandler exceptionHandler) where TDbContext : DbContext where TEntity : class, new() { // ReSharper disable once StaticMemberInGenericType // Justification: This is a static member that is used to ensure that only one thread can access the database for TEntity at a time. private static readonly SemaphoreSlim Semaphore = new(1, 1); - private readonly IDbContextFactory _dbContextFactory; - - /// - /// Initializes a new instance of the class. - /// - public Store(IDbContextFactory dbContextFactory) - { - _dbContextFactory = dbContextFactory; - } - /// /// Creates a new instance of the database context. /// /// The cancellation token. /// The database context. - public async Task CreateDbContextAsync(CancellationToken cancellationToken = default) => await _dbContextFactory.CreateDbContextAsync(cancellationToken); + public async Task CreateDbContextAsync(CancellationToken cancellationToken = default) => await dbContextFactory.CreateDbContextAsync(cancellationToken); /// /// Adds the specified entity. @@ -132,6 +123,10 @@ public class Store where TDbContext : DbContext where TEnti set.Entry(entity).State = exists ? EntityState.Modified : EntityState.Added; await dbContext.SaveChangesAsync(cancellationToken); } + catch (DbUpdateException ex) + { + exceptionHandler.Handle(ex); + } finally { Semaphore.Release(); @@ -168,7 +163,14 @@ public class Store where TDbContext : DbContext where TEnti await Task.WhenAll(savingTasks); } - await dbContext.BulkUpsertAsync(entityList, keySelector, cancellationToken); + try + { + await dbContext.BulkUpsertAsync(entityList, keySelector, cancellationToken); + } + catch (DbUpdateException ex) + { + exceptionHandler.Handle(ex); + } } /// diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Features.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Features.cs index 3bfee3284..064912eb0 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Features.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Features.cs @@ -5,6 +5,7 @@ using Elsa.EntityFrameworkCore.Modules.Identity; using Elsa.EntityFrameworkCore.Modules.Labels; using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.EntityFrameworkCore.PostgreSql.Handlers; // ReSharper disable once CheckNamespace namespace Elsa.EntityFrameworkCore.Extensions; @@ -22,6 +23,7 @@ public static class PostgreSqlProvidersExtensions public static EFCoreIdentityPersistenceFeature UsePostgreSql(this EFCoreIdentityPersistenceFeature feature, string connectionString, ElsaDbContextOptions? options = default) { feature.DbContextOptionsBuilder = (_, db) => db.UseElsaPostgreSql(Assembly, connectionString, options); + feature.DbExceptionHandler = _ => new DbExceptionHandler(); return feature; } @@ -31,6 +33,7 @@ public static class PostgreSqlProvidersExtensions public static EFCoreAlterationsPersistenceFeature UsePostgreSql(this EFCoreAlterationsPersistenceFeature feature, string connectionString, ElsaDbContextOptions? options = default) { feature.DbContextOptionsBuilder = (_, db) => db.UseElsaPostgreSql(Assembly, connectionString, options); + feature.DbExceptionHandler = _ => new DbExceptionHandler(); return feature; } @@ -40,6 +43,7 @@ public static class PostgreSqlProvidersExtensions public static EFCoreLabelPersistenceFeature UsePostgreSql(this EFCoreLabelPersistenceFeature feature, string connectionString, ElsaDbContextOptions? options = default) { feature.DbContextOptionsBuilder = (_, db) => db.UseElsaPostgreSql(Assembly, connectionString, options); + feature.DbExceptionHandler = _ => new DbExceptionHandler(); return feature; } @@ -49,6 +53,7 @@ public static class PostgreSqlProvidersExtensions public static EFCoreWorkflowDefinitionPersistenceFeature UsePostgreSql(this EFCoreWorkflowDefinitionPersistenceFeature feature, string connectionString, ElsaDbContextOptions? options = default) { feature.DbContextOptionsBuilder = (_, db) => db.UseElsaPostgreSql(Assembly, connectionString, options); + feature.DbExceptionHandler = _ => new DbExceptionHandler(); return feature; } @@ -58,6 +63,7 @@ public static class PostgreSqlProvidersExtensions public static EFCoreWorkflowInstancePersistenceFeature UsePostgreSql(this EFCoreWorkflowInstancePersistenceFeature feature, string connectionString, ElsaDbContextOptions? options = default) { feature.DbContextOptionsBuilder = (_, db) => db.UseElsaPostgreSql(Assembly, connectionString, options); + feature.DbExceptionHandler = _ => new DbExceptionHandler(); return feature; } @@ -67,6 +73,7 @@ public static class PostgreSqlProvidersExtensions public static WorkflowManagementPersistenceFeature UsePostgreSql(this WorkflowManagementPersistenceFeature feature, string connectionString, ElsaDbContextOptions? options = default) { feature.DbContextOptionsBuilder = (_, db) => db.UseElsaPostgreSql(Assembly, connectionString, options); + feature.DbExceptionHandler = _ => new DbExceptionHandler(); return feature; } @@ -76,6 +83,7 @@ public static class PostgreSqlProvidersExtensions public static EFCoreWorkflowRuntimePersistenceFeature UsePostgreSql(this EFCoreWorkflowRuntimePersistenceFeature feature, string connectionString, ElsaDbContextOptions? options = default) { feature.DbContextOptionsBuilder = (_, db) => db.UseElsaPostgreSql(Assembly, connectionString, options); + feature.DbExceptionHandler = _ => new DbExceptionHandler(); return feature; } } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Handlers/DbExceptionHandler.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Handlers/DbExceptionHandler.cs new file mode 100644 index 000000000..f23150beb --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Handlers/DbExceptionHandler.cs @@ -0,0 +1,29 @@ +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Modules.Alterations; +using Elsa.EntityFrameworkCore.Modules.Identity; +using Elsa.EntityFrameworkCore.Modules.Labels; +using Elsa.EntityFrameworkCore.Modules.Management; +using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.Workflows.Exceptions; +using Microsoft.EntityFrameworkCore; +using Npgsql; + +namespace Elsa.EntityFrameworkCore.PostgreSql.Handlers; + +/// +/// Handles database exceptions encountered when using a postgreSQL database. +/// +public class DbExceptionHandler : IDbExceptionHandler, + IDbExceptionHandler, + IDbExceptionHandler, + IDbExceptionHandler, + IDbExceptionHandler +{ + /// Handles database exceptions encountered when using a postgreSQL database. + public void Handle(DbUpdateException exception) + { + var ex = exception.InnerException as PostgresException; + + throw new DataProcessingException(ex?.SqlState == "23505", "Unable to save data", exception); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Handlers/NoopDbExceptionHandler.cs b/src/modules/Elsa.EntityFrameworkCore/Handlers/NoopDbExceptionHandler.cs new file mode 100644 index 000000000..7fb04ecb7 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Handlers/NoopDbExceptionHandler.cs @@ -0,0 +1,23 @@ +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Modules.Alterations; +using Elsa.EntityFrameworkCore.Modules.Identity; +using Elsa.EntityFrameworkCore.Modules.Labels; +using Elsa.EntityFrameworkCore.Modules.Management; +using Elsa.EntityFrameworkCore.Modules.Runtime; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.EntityFrameworkCore.Handlers; + +/// A No-Op database exception handler. +public class NoopDbExceptionHandler : IDbExceptionHandler, + IDbExceptionHandler, + IDbExceptionHandler, + IDbExceptionHandler, + IDbExceptionHandler +{ + /// Handles the given exception that occurs during database operations. + public void Handle(DbUpdateException exception) + { + throw exception; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Alterations/Feature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Alterations/Feature.cs index 5a66c8fb4..d2d18bcd7 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Alterations/Feature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Alterations/Feature.cs @@ -1,6 +1,8 @@ using Elsa.Alterations.Core.Entities; using Elsa.Alterations.Features; using Elsa.EntityFrameworkCore.Common; +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Handlers; using Elsa.Features.Attributes; using Elsa.Features.Services; using Microsoft.Extensions.DependencyInjection; @@ -11,12 +13,10 @@ namespace Elsa.EntityFrameworkCore.Modules.Alterations; /// Configures the default workflow runtime to use EF Core persistence providers. /// [DependsOn(typeof(AlterationsFeature))] -public class EFCoreAlterationsPersistenceFeature : PersistenceFeatureBase +public class EFCoreAlterationsPersistenceFeature(IModule module) : PersistenceFeatureBase(module) { - /// - public EFCoreAlterationsPersistenceFeature(IModule module) : base(module) - { - } + /// Delegate for determining the exception handler. + public Func> DbExceptionHandler { get; set; } = _ => new NoopDbExceptionHandler(); /// public override void Configure() @@ -32,6 +32,7 @@ public class EFCoreAlterationsPersistenceFeature : PersistenceFeatureBase(); AddEntityStore(); diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/Feature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/Feature.cs index a31477233..c370c89d8 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/Feature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/Feature.cs @@ -1,4 +1,6 @@ using Elsa.EntityFrameworkCore.Common; +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Handlers; using Elsa.Features.Attributes; using Elsa.Features.Services; using Elsa.Identity.Entities; @@ -13,12 +15,10 @@ namespace Elsa.EntityFrameworkCore.Modules.Identity; /// [DependsOn(typeof(IdentityFeature))] [PublicAPI] -public class EFCoreIdentityPersistenceFeature : PersistenceFeatureBase +public class EFCoreIdentityPersistenceFeature(IModule module) : PersistenceFeatureBase(module) { - /// - public EFCoreIdentityPersistenceFeature(IModule module) : base(module) - { - } + /// Delegate for determining the exception handler. + public Func> DbExceptionHandler { get; set; } = _ => new NoopDbExceptionHandler(); /// public override void Configure() @@ -36,6 +36,8 @@ public class EFCoreIdentityPersistenceFeature : PersistenceFeatureBase(); AddEntityStore(); AddEntityStore(); diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Labels/Feature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Labels/Feature.cs index 7dbe21ee1..462d55b3a 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Labels/Feature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Labels/Feature.cs @@ -1,4 +1,6 @@ using Elsa.EntityFrameworkCore.Common; +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Handlers; using Elsa.Extensions; using Elsa.Features.Attributes; using Elsa.Features.Services; @@ -9,11 +11,10 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.EntityFrameworkCore.Modules.Labels; [DependsOn(typeof(LabelsFeature))] -public class EFCoreLabelPersistenceFeature : PersistenceFeatureBase +public class EFCoreLabelPersistenceFeature(IModule module) : PersistenceFeatureBase(module) { - public EFCoreLabelPersistenceFeature(IModule module) : base(module) - { - } + /// Delegate for determining the exception handler. + public Func> DbExceptionHandler { get; set; } = _ => new NoopDbExceptionHandler(); public override void Configure() { @@ -28,6 +29,8 @@ public class EFCoreLabelPersistenceFeature : PersistenceFeatureBase(); AddEntityStore(); } diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionPersistenceFeature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionPersistenceFeature.cs index 812525368..6c4a28c54 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionPersistenceFeature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowDefinitionPersistenceFeature.cs @@ -1,4 +1,6 @@ using Elsa.EntityFrameworkCore.Common; +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Handlers; using Elsa.Features.Attributes; using Elsa.Features.Services; using Elsa.Workflows.Management.Entities; @@ -11,12 +13,10 @@ namespace Elsa.EntityFrameworkCore.Modules.Management; /// Configures the feature with an Entity Framework Core persistence provider. /// [DependsOn(typeof(WorkflowManagementFeature))] -public class EFCoreWorkflowDefinitionPersistenceFeature : PersistenceFeatureBase +public class EFCoreWorkflowDefinitionPersistenceFeature(IModule module) : PersistenceFeatureBase(module) { - /// - public EFCoreWorkflowDefinitionPersistenceFeature(IModule module) : base(module) - { - } + /// Delegate for determining the exception handler. + public Func> DbExceptionHandler { get; set; } = _ => new NoopDbExceptionHandler(); /// public override void Configure() @@ -32,6 +32,8 @@ public class EFCoreWorkflowDefinitionPersistenceFeature : PersistenceFeatureBase { base.Apply(); + Services.AddScoped(DbExceptionHandler); + AddEntityStore(); } } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstancePersistenceFeature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstancePersistenceFeature.cs index d1188a726..aebdfc924 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstancePersistenceFeature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowInstancePersistenceFeature.cs @@ -1,4 +1,6 @@ using Elsa.EntityFrameworkCore.Common; +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Handlers; using Elsa.Features.Attributes; using Elsa.Features.Services; using Elsa.Workflows.Management.Entities; @@ -11,12 +13,10 @@ namespace Elsa.EntityFrameworkCore.Modules.Management; /// Configures the feature with an Entity Framework Core persistence provider. /// [DependsOn(typeof(WorkflowManagementFeature))] -public class EFCoreWorkflowInstancePersistenceFeature : PersistenceFeatureBase +public class EFCoreWorkflowInstancePersistenceFeature(IModule module) : PersistenceFeatureBase(module) { - /// - public EFCoreWorkflowInstancePersistenceFeature(IModule module) : base(module) - { - } + /// Delegate for determining the exception handler. + public Func> DbExceptionHandler { get; set; } = _ => new NoopDbExceptionHandler(); /// public override void Configure() @@ -29,6 +29,8 @@ public class EFCoreWorkflowInstancePersistenceFeature : PersistenceFeatureBase(); } } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowManagementPersistenceFeature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowManagementPersistenceFeature.cs index 3f702d065..f73185bb7 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowManagementPersistenceFeature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/WorkflowManagementPersistenceFeature.cs @@ -1,4 +1,6 @@ using Elsa.EntityFrameworkCore.Common; +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Handlers; using Elsa.Features.Attributes; using Elsa.Features.Services; using Elsa.Workflows.Management.Entities; @@ -15,12 +17,10 @@ namespace Elsa.EntityFrameworkCore.Modules.Management; [DependsOn(typeof(WorkflowInstancesFeature))] [DependsOn(typeof(WorkflowDefinitionsFeature))] [PublicAPI] -public class WorkflowManagementPersistenceFeature : PersistenceFeatureBase +public class WorkflowManagementPersistenceFeature(IModule module) : PersistenceFeatureBase(module) { - /// - public WorkflowManagementPersistenceFeature(IModule module) : base(module) - { - } + /// Delegate for determining the exception handler. + public Func> DbExceptionHandler { get; set; } = _ => new NoopDbExceptionHandler(); /// public override void Configure() @@ -34,6 +34,8 @@ public class WorkflowManagementPersistenceFeature : PersistenceFeatureBase(); AddEntityStore(); } diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs index 3cf255f7e..caf87fb14 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowRuntimePersistenceFeature.cs @@ -1,4 +1,6 @@ using Elsa.EntityFrameworkCore.Common; +using Elsa.EntityFrameworkCore.Common.Contracts; +using Elsa.EntityFrameworkCore.Handlers; using Elsa.Features.Attributes; using Elsa.Features.Services; using Elsa.KeyValues.Entities; @@ -13,12 +15,10 @@ namespace Elsa.EntityFrameworkCore.Modules.Runtime; /// Configures the default workflow runtime to use EF Core persistence providers. /// [DependsOn(typeof(WorkflowRuntimeFeature))] -public class EFCoreWorkflowRuntimePersistenceFeature : PersistenceFeatureBase +public class EFCoreWorkflowRuntimePersistenceFeature(IModule module) : PersistenceFeatureBase(module) { - /// - public EFCoreWorkflowRuntimePersistenceFeature(IModule module) : base(module) - { - } + /// Delegate for determining the exception handler. + public Func> DbExceptionHandler { get; set; } = _ => new NoopDbExceptionHandler(); /// public override void Configure() @@ -42,6 +42,8 @@ public class EFCoreWorkflowRuntimePersistenceFeature : PersistenceFeatureBase(); AddStore(); AddEntityStore(); diff --git a/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs b/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs index be3520236..93765f25c 100644 --- a/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs +++ b/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs @@ -165,7 +165,7 @@ public partial class WorkflowExecutionContext : IExecutionContext Action? statusUpdatedCallback = null, CancellationTokens cancellationTokens = default) { - // Setup a workflow execution context. + // Set up a workflow execution context. var workflowExecutionContext = new WorkflowExecutionContext( serviceProvider, workflowGraph, diff --git a/src/modules/Elsa.Workflows.Core/Exceptions/DataProcessingException.cs b/src/modules/Elsa.Workflows.Core/Exceptions/DataProcessingException.cs new file mode 100644 index 000000000..2af7ab1e2 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Exceptions/DataProcessingException.cs @@ -0,0 +1,8 @@ +namespace Elsa.Workflows.Exceptions; + +/// An exception that occurs during data processing. +public class DataProcessingException(bool isUkViolation, string message, Exception exception) : Exception(message, exception) +{ + /// Gets a value indicating whether the exception is a Unique Key violation. + public bool IsUkViolation { get; } = isUkViolation; +} From b6acb18dc639b16592d5234b46fc452151d7cf40 Mon Sep 17 00:00:00 2001 From: rosca-sabina <43028000+rosca-sabina@users.noreply.github.com> Date: Fri, 9 Aug 2024 19:14:09 +0300 Subject: [PATCH 2/3] Changed RegisterClassMap to TryRegisterClassMap on SerializedKeyValuePair classmap registration (#5877) --- src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs index 9689ff6e9..2b8014974 100644 --- a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs @@ -66,7 +66,7 @@ public class MongoDbFeature : FeatureBase .SetSerializer(new StringSerializer(BsonType.String)); }); - BsonClassMap.RegisterClassMap(map => + BsonClassMap.TryRegisterClassMap(map => { map.AutoMap(); map.SetIgnoreExtraElements(true); // Needed for missing ID property From ef2dfd39578f06fb1e76448d59c00b816c013e6c Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 9 Aug 2024 18:20:49 +0200 Subject: [PATCH 3/3] Refactor journal data access and improve byte[] handling (#5878) Switched to dictionary index access for "ResumeAt" in Delay.cs to ensure more efficient updating. Added handling for byte[] in ObjectConverter.cs to correctly deserialize from base64 strings. --- src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs | 8 +++++++- src/modules/Elsa.Scheduling/Activities/Delay.cs | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs b/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs index 571397bee..a66d344e6 100644 --- a/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs +++ b/src/modules/Elsa.Expressions/Helpers/ObjectConverter.cs @@ -112,7 +112,13 @@ public static class ObjectConverter if (underlyingSourceType == typeof(string) && !underlyingTargetType.IsPrimitive && underlyingTargetType != typeof(object)) { var stringValue = (string)value; - + + if (underlyingTargetType == typeof(byte[])) + { + // Byte arrays are serialized to base64, so in this case, we convert the string back to the requested target type of byte[]. + return Convert.FromBase64String(stringValue); + } + try { var firstChar = stringValue.TrimStart().FirstOrDefault(); diff --git a/src/modules/Elsa.Scheduling/Activities/Delay.cs b/src/modules/Elsa.Scheduling/Activities/Delay.cs index 1c56d11ee..63981e03d 100644 --- a/src/modules/Elsa.Scheduling/Activities/Delay.cs +++ b/src/modules/Elsa.Scheduling/Activities/Delay.cs @@ -78,7 +78,7 @@ public class Delay : Activity, IActivityPropertyDefaultValueProvider var resumeAt = clock.UtcNow.Add(timeSpan); var payload = new DelayPayload(resumeAt); - context.JournalData.Add("ResumeAt", resumeAt); + context.JournalData["ResumeAt"] = resumeAt; context.CreateBookmark(payload); }