diff --git a/src/modules/Elsa.Persistence.EntityFrameworkCore/Modules/Runtime/Configurations.cs b/src/modules/Elsa.Persistence.EntityFrameworkCore/Modules/Runtime/Configurations.cs index e715bb919..f777a9888 100644 --- a/src/modules/Elsa.Persistence.EntityFrameworkCore/Modules/Runtime/Configurations.cs +++ b/src/modules/Elsa.Persistence.EntityFrameworkCore/Modules/Runtime/Configurations.cs @@ -20,7 +20,6 @@ namespace Elsa.Persistence.EntityFrameworkCore.Modules.Runtime builder.Ignore(x => x.Properties); builder.Ignore(x => x.ActivityOutput); builder.Ignore(x => x.CompletionCallbacks); - builder.Ignore(x => x.PersistentVariables); builder.Ignore(x => x.ActivityExecutionContexts); builder.Property("Data"); builder.Property("CreatedAt"); diff --git a/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs b/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs index d2cd48e8c..c7fe9a696 100644 --- a/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Implementations/WorkflowStateSerializer.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Reflection; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; @@ -9,13 +8,6 @@ namespace Elsa.Workflows.Core.Implementations; public class WorkflowStateSerializer : IWorkflowStateSerializer { - private readonly IServiceProvider _serviceProvider; - - public WorkflowStateSerializer(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - public WorkflowState SerializeState(WorkflowExecutionContext workflowExecutionContext) { var state = new WorkflowState @@ -32,7 +24,6 @@ public class WorkflowStateSerializer : IWorkflowStateSerializer SerializeProperties(state, workflowExecutionContext); SerializeCompletionCallbacks(state, workflowExecutionContext); SerializeActivityExecutionContexts(state, workflowExecutionContext); - SerializePersistentVariables(state, workflowExecutionContext); return state; } @@ -46,17 +37,16 @@ public class WorkflowStateSerializer : IWorkflowStateSerializer DeserializeProperties(state, workflowExecutionContext); DeserializeActivityExecutionContexts(state, workflowExecutionContext); DeserializeCompletionCallbacks(state, workflowExecutionContext); - //DeserializePersistentVariables(state, workflowExecutionContext); } private void SerializeProperties(WorkflowState state, WorkflowExecutionContext workflowExecutionContext) { - state.Properties = workflowExecutionContext.Properties; + state.Properties = new PropertyBag(workflowExecutionContext.Properties); } private void DeserializeProperties(WorkflowState state, WorkflowExecutionContext workflowExecutionContext) { - workflowExecutionContext.Properties = state.Properties; + workflowExecutionContext.Properties = state.Properties.Properties; } private void GetOutput(WorkflowState state, WorkflowExecutionContext workflowExecutionContext) @@ -181,16 +171,6 @@ public class WorkflowStateSerializer : IWorkflowStateSerializer workflowExecutionContext.ActivityExecutionContexts = activityExecutionContexts; } - private void SerializePersistentVariables(WorkflowState state, WorkflowExecutionContext workflowExecutionContext) - { - var workflow = workflowExecutionContext.Workflow; - - state.PersistentVariables = workflow.Variables - .Where(x => x.StorageDriverId != null) - .Select(x => new PersistentVariableState(x.Name, x.StorageDriverId!)) - .ToList(); - } - private Dictionary GetOutputFrom(ActivityNode activityNode) => activityNode.GetType().GetProperties(BindingFlags.Public).Where(x => x.GetCustomAttribute() != null).ToDictionary(x => x.Name, x => x.GetValue(activityNode)!); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Models/PropertyBag.cs b/src/modules/Elsa.Workflows.Core/Models/PropertyBag.cs new file mode 100644 index 000000000..35a872dc8 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Models/PropertyBag.cs @@ -0,0 +1,20 @@ +using System.Text.Json.Serialization; +using Elsa.Workflows.Core.Serialization.Converters; + +namespace Elsa.Workflows.Core.Models; + +[JsonConverter(typeof(PropertyBagConverter))] +public class PropertyBag +{ + [JsonConstructor] + public PropertyBag() : this(new Dictionary()) + { + } + + public PropertyBag(IDictionary properties) + { + Properties = properties; + } + + public IDictionary Properties { get; init; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Converters/PropertyBagConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/PropertyBagConverter.cs new file mode 100644 index 000000000..76f6d616c --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/PropertyBagConverter.cs @@ -0,0 +1,19 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Elsa.Workflows.Core.Models; + +namespace Elsa.Workflows.Core.Serialization.Converters; + +public class PropertyBagConverter : JsonConverter +{ + public override PropertyBag? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var dictionary = JsonSerializer.Deserialize>(ref reader); + return new PropertyBag(dictionary); + } + + public override void Write(Utf8JsonWriter writer, PropertyBag value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value.Properties); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs b/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs index 1b116d565..b0c297be6 100644 --- a/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs +++ b/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs @@ -1,4 +1,6 @@ +using System.Text.Json.Serialization; using Elsa.Workflows.Core.Models; +using Elsa.Workflows.Core.Serialization.Converters; namespace Elsa.Workflows.Core.State; @@ -56,14 +58,10 @@ public class WorkflowState /// A flattened list of objects, representing the various active "call stacks" of the workflow. /// public ICollection ActivityExecutionContexts { get; set; } = new List(); - + /// /// A global property bag that contains properties set by application code and/or activities. /// - public IDictionary Properties { get; set; } = new Dictionary(); - - /// - /// A list of variables that can be persisted. - /// - public ICollection PersistentVariables { get; set; } = new List(); + [JsonConverter(typeof(PropertyBagConverter))] + public PropertyBag Properties { get; set; } = new(); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj b/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj index 47202478b..06f80d19c 100644 --- a/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj +++ b/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj @@ -24,8 +24,4 @@ - - - - diff --git a/src/modules/Elsa.Workflows.Runtime/Middleware/PersistentVariablesMiddleware.cs b/src/modules/Elsa.Workflows.Runtime/Middleware/PersistentVariablesMiddleware.cs index 77835ccfd..563da0a25 100644 --- a/src/modules/Elsa.Workflows.Runtime/Middleware/PersistentVariablesMiddleware.cs +++ b/src/modules/Elsa.Workflows.Runtime/Middleware/PersistentVariablesMiddleware.cs @@ -1,3 +1,5 @@ +using Elsa.Expressions.Helpers; +using Elsa.Workflows.Core; using Elsa.Workflows.Core.Models; using Elsa.Workflows.Core.Pipelines.WorkflowExecution; using Elsa.Workflows.Core.Services; @@ -24,35 +26,41 @@ public class PersistentVariablesMiddleware : WorkflowExecutionMiddleware // Load persistent variables. var dataDriveContext = new DataDriveContext(context, cancellationToken); - var persistentVariables = context.Workflow.Variables - .Where(x => x.StorageDriverId != null) - .Select(x => new PersistentVariableState(x.Name, x.StorageDriverId!)) - .ToList(); + var persistentVariables = context.Workflow.Variables.Where(x => x.StorageDriverId != null).ToList(); - foreach (var variableState in persistentVariables) + foreach (var variable in persistentVariables) { - var drive = _storageDriverManager.GetDriveById(variableState.StorageDriverId); + var drive = _storageDriverManager.GetDriveById(variable.StorageDriverId!); if (drive == null) continue; - var id = $"{context.Id}:{variableState.Name}"; + var id = $"{context.Id}:{variable.Name}"; var value = await drive.ReadAsync(id, dataDriveContext); if (value == null) continue; - var variable = new Variable(variableState.Name, value); + var parsedValue = ParseVariableValue(variable, value); context.MemoryRegister.Declare(variable); + variable.Set(context.MemoryRegister, parsedValue); } // Invoke next middleware. await Next(context); // Persist variables. - - foreach (var variableState in persistentVariables) + foreach (var variable in persistentVariables) { - var drive = _storageDriverManager.GetDriveById(variableState.StorageDriverId); + var drive = _storageDriverManager.GetDriveById(variable.StorageDriverId!); if (drive == null) continue; - if (!context.MemoryRegister.TryGetBlock(variableState.Name, out var block)) continue; + if (!context.MemoryRegister.TryGetBlock(variable.Name, out var block)) continue; if (block.Value == null) continue; - var id = $"{context.Id}:{variableState.Name}"; + var id = $"{context.Id}:{variable.Name}"; await drive.WriteAsync(id, block.Value, dataDriveContext); } } + + private object ParseVariableValue(Variable variable, object value) + { + if (!variable.GetType().GenericTypeArguments.Any()) + return value; + + var type = variable.GetType().GenericTypeArguments.First(); + return value.ConvertTo(type)!; + } } \ No newline at end of file