From 943df3d274cd2fed9e251c63169973542fe69cff Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Tue, 18 Apr 2023 00:00:46 +0200 Subject: [PATCH] Cleanup and refactoring WorkflowContext APIs --- src/bundles/Elsa.AllInOne.Web/Program.cs | 1 - .../Elsa.WorkflowServer.Web/Program.cs | 2 - .../Features/MemoryCacheFeature.cs | 10 ++- ...oreWorkflowManagementPersistenceFeature.cs | 2 + .../Abstractions/WorkflowContextProvider.cs | 27 +++++++ .../Elsa.WorkflowContexts/Constants.cs | 6 ++ .../Contracts/IWorkflowContextProvider.cs | 7 +- .../Extensions/ActivityExtensions.cs | 57 +++++++++++--- .../DependencyInjectionExtensions.cs | 22 ++++++ .../Extensions/WorkflowBuilderExtensions.cs | 7 +- .../WorkflowExecutionBuilderExtensions.cs | 3 + .../WorkflowExecutionContextExtensions.cs | 78 +++++++++++++++---- ...kflowContextActivityExecutionMiddleware.cs | 28 ++++--- ...kflowContextWorkflowExecutionMiddleware.cs | 20 ++--- .../Models/ActivityWorkflowContextSettings.cs | 10 +++ .../Models/WorkflowContext.cs | 30 ------- .../Extensions/DictionaryExtensions.cs | 6 +- .../Models/WorkflowExecutionContext.cs | 10 +-- .../Elsa.Samples.ProtoActorRuntime/Program.cs | 4 - 19 files changed, 230 insertions(+), 100 deletions(-) create mode 100644 src/modules/Elsa.WorkflowContexts/Constants.cs create mode 100644 src/modules/Elsa.WorkflowContexts/Extensions/DependencyInjectionExtensions.cs delete mode 100644 src/modules/Elsa.WorkflowContexts/Models/WorkflowContext.cs diff --git a/src/bundles/Elsa.AllInOne.Web/Program.cs b/src/bundles/Elsa.AllInOne.Web/Program.cs index aa420f2bc..99c1bb470 100644 --- a/src/bundles/Elsa.AllInOne.Web/Program.cs +++ b/src/bundles/Elsa.AllInOne.Web/Program.cs @@ -43,7 +43,6 @@ services ); services.AddHealthChecks(); -services.AddHttpContextAccessor(); services.AddSingleton(); services.AddAuthorization(options => options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostRequirement()))); services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin())); diff --git a/src/bundles/Elsa.WorkflowServer.Web/Program.cs b/src/bundles/Elsa.WorkflowServer.Web/Program.cs index 12dd27e8e..d71d2e5e8 100644 --- a/src/bundles/Elsa.WorkflowServer.Web/Program.cs +++ b/src/bundles/Elsa.WorkflowServer.Web/Program.cs @@ -50,10 +50,8 @@ services ); services.Configure(options => options.AllowClrAccess = true); -services.AddHandlersFrom(); services.AddHealthChecks(); services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin())); -services.AddHttpContextAccessor(); // Configure middleware pipeline. var app = builder.Build(); diff --git a/src/modules/Elsa.Common/Features/MemoryCacheFeature.cs b/src/modules/Elsa.Common/Features/MemoryCacheFeature.cs index ca0bfb4bc..e5313d98f 100644 --- a/src/modules/Elsa.Common/Features/MemoryCacheFeature.cs +++ b/src/modules/Elsa.Common/Features/MemoryCacheFeature.cs @@ -4,11 +4,19 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Common.Features; +/// +/// Configures the MemoryCache. +/// public class MemoryCacheFeature : FeatureBase { + /// public MemoryCacheFeature(IModule module) : base(module) { } - public override void Configure() => Services.AddMemoryCache(); + /// + public override void Apply() + { + Services.AddMemoryCache(); + } } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/EFCoreWorkflowManagementPersistenceFeature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/EFCoreWorkflowManagementPersistenceFeature.cs index e58618bc1..382d26eea 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Management/EFCoreWorkflowManagementPersistenceFeature.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Management/EFCoreWorkflowManagementPersistenceFeature.cs @@ -12,6 +12,8 @@ namespace Elsa.EntityFrameworkCore.Modules.Management; /// Configures the and features with an Entity Framework Core persistence provider. /// [DependsOn(typeof(WorkflowManagementFeature))] +[DependsOn(typeof(WorkflowInstancesFeature))] +[DependsOn(typeof(WorkflowDefinitionsFeature))] [PublicAPI] public class EFCoreWorkflowManagementPersistenceFeature : PersistenceFeatureBase { diff --git a/src/modules/Elsa.WorkflowContexts/Abstractions/WorkflowContextProvider.cs b/src/modules/Elsa.WorkflowContexts/Abstractions/WorkflowContextProvider.cs index adf338d6e..ef46079e4 100644 --- a/src/modules/Elsa.WorkflowContexts/Abstractions/WorkflowContextProvider.cs +++ b/src/modules/Elsa.WorkflowContexts/Abstractions/WorkflowContextProvider.cs @@ -1,23 +1,50 @@ using Elsa.WorkflowContexts.Contracts; using Elsa.Workflows.Core.Models; +using JetBrains.Annotations; namespace Elsa.WorkflowContexts.Abstractions; +/// +/// A base class for workflow context providers. +/// +/// The type of the workflow context value. +[PublicAPI] public abstract class WorkflowContextProvider : IWorkflowContextProvider { async ValueTask IWorkflowContextProvider.LoadAsync(WorkflowExecutionContext workflowExecutionContext) => await LoadAsync(workflowExecutionContext); async ValueTask IWorkflowContextProvider.SaveAsync(WorkflowExecutionContext workflowExecutionContext, object? context) => await SaveAsync(workflowExecutionContext, (T?)context); + /// + /// Load data that will be available to the workflow. + /// + /// The workflow execution context. + /// The workflow context value. protected virtual ValueTask LoadAsync(WorkflowExecutionContext workflowExecutionContext) => new(Load(workflowExecutionContext)); + + /// + /// Load data that will be available to the workflow. + /// + /// The workflow execution context. + /// The workflow context value. protected virtual T? Load(WorkflowExecutionContext workflowExecutionContext) => default; + /// + /// Save data. + /// + /// The workflow execution context. + /// The workflow context value. protected virtual ValueTask SaveAsync(WorkflowExecutionContext workflowExecutionContext, T? context) { Save(workflowExecutionContext, context); return ValueTask.CompletedTask; } + /// + /// Save data. + /// + /// The workflow execution context. + /// The workflow context value. protected virtual void Save(WorkflowExecutionContext workflowExecutionContext, T? context) { } diff --git a/src/modules/Elsa.WorkflowContexts/Constants.cs b/src/modules/Elsa.WorkflowContexts/Constants.cs new file mode 100644 index 000000000..8c9b9c2f1 --- /dev/null +++ b/src/modules/Elsa.WorkflowContexts/Constants.cs @@ -0,0 +1,6 @@ +namespace Elsa.WorkflowContexts; + +internal class Constants +{ + public const string WorkflowContextProviderTypesKey = "Elsa:WorkflowContexts"; +} \ No newline at end of file diff --git a/src/modules/Elsa.WorkflowContexts/Contracts/IWorkflowContextProvider.cs b/src/modules/Elsa.WorkflowContexts/Contracts/IWorkflowContextProvider.cs index 7d45234ae..fdf70fa22 100644 --- a/src/modules/Elsa.WorkflowContexts/Contracts/IWorkflowContextProvider.cs +++ b/src/modules/Elsa.WorkflowContexts/Contracts/IWorkflowContextProvider.cs @@ -3,18 +3,17 @@ namespace Elsa.WorkflowContexts.Contracts; /// -/// Implement this interface to implement a workflow context provider that loads application-specific objects into the workflow. -/// These providers can then be configured on a given workflow. +/// Represents a workflow context provider that loads application-specific data and makes it available to the workflow. /// public interface IWorkflowContextProvider { /// - /// Implement this method to load an object into memory that is accessible throughout the lifetime of the workflow's current execution. + /// Load data that will be available to the workflow. /// ValueTask LoadAsync(WorkflowExecutionContext workflowExecutionContext); /// - /// Implement this method to save an object that was loaded previously + /// Save data. /// ValueTask SaveAsync(WorkflowExecutionContext workflowExecutionContext, object? context); } \ No newline at end of file diff --git a/src/modules/Elsa.WorkflowContexts/Extensions/ActivityExtensions.cs b/src/modules/Elsa.WorkflowContexts/Extensions/ActivityExtensions.cs index 0eda45a07..94964032a 100644 --- a/src/modules/Elsa.WorkflowContexts/Extensions/ActivityExtensions.cs +++ b/src/modules/Elsa.WorkflowContexts/Extensions/ActivityExtensions.cs @@ -4,43 +4,78 @@ using Elsa.Workflows.Core.Contracts; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; +/// +/// Extension methods for . +/// public static class ActivityExtensions { private const string ActivityWorkflowContextSettingsKey = "ActivityWorkflowContextSettingsKey"; - public static IDictionary GetWorkflowContextSettings(this IActivity activity) + /// + /// Gets the workflow context settings for the specified activity. + /// + /// The activity to get the settings for. + /// The workflow context settings. + public static IDictionary GetWorkflowContextSettings(this IActivity activity) { - return activity.CustomProperties.GetOrAdd(ActivityWorkflowContextSettingsKey, () => new Dictionary())!; + return activity.CustomProperties.GetOrAdd(ActivityWorkflowContextSettingsKey, () => new Dictionary())!; } - public static TActivity LoadContext(this TActivity activity, WorkflowContext workflowContext) where TActivity : IActivity + /// + /// Configures the activity to load the workflow context before executing. + /// + /// The activity to configure. + /// The type of the workflow context provider. + /// The type of the activity. + /// The activity. + public static TActivity LoadContext(this TActivity activity, Type providerType) where TActivity : IActivity { - var settings = activity.GetActivityWorkflowContextSettings(workflowContext); + var settings = activity.GetActivityWorkflowContextSettings(providerType); settings.Load = true; return activity; } - public static TActivity SaveContext(this TActivity activity, WorkflowContext workflowContext) where TActivity : IActivity + /// + /// Configures the activity to save the workflow context after executing. + /// + /// The activity to configure. + /// The type of the workflow context provider. + /// The type of the activity. + /// The activity. + public static TActivity SaveContext(this TActivity activity, Type providerType) where TActivity : IActivity { - var settings = activity.GetActivityWorkflowContextSettings(workflowContext); + var settings = activity.GetActivityWorkflowContextSettings(providerType); settings.Save = true; return activity; } - public static ActivityWorkflowContextSettings GetActivityWorkflowContextSettings(this TActivity activity, WorkflowContext workflowContext) where TActivity: IActivity + /// + /// Gets the workflow context settings for the specified activity. + /// + /// The activity to get the settings for. + /// The type of the workflow context provider. + /// The type of the activity. + /// The workflow context settings. + public static ActivityWorkflowContextSettings GetActivityWorkflowContextSettings(this TActivity activity, Type providerType) where TActivity: IActivity { var dictionary = activity.GetWorkflowContextSettings()!; - return dictionary.GetActivityWorkflowContextSettings(workflowContext); + return dictionary.GetActivityWorkflowContextSettings(providerType); } - public static ActivityWorkflowContextSettings GetActivityWorkflowContextSettings(this IDictionary dictionary, WorkflowContext workflowContext) + /// + /// Gets the workflow context settings for the specified activity. + /// + /// The dictionary to get the settings from. + /// The type of the workflow context provider. + /// The workflow context settings. + public static ActivityWorkflowContextSettings GetActivityWorkflowContextSettings(this IDictionary dictionary, Type providerType) { - var settings = dictionary.ContainsKey(workflowContext) ? dictionary[workflowContext] : default; + var settings = dictionary.ContainsKey(providerType) ? dictionary[providerType] : default; if(settings == null) { settings = new ActivityWorkflowContextSettings(); - dictionary[workflowContext] = settings; + dictionary[providerType] = settings; } return settings; diff --git a/src/modules/Elsa.WorkflowContexts/Extensions/DependencyInjectionExtensions.cs b/src/modules/Elsa.WorkflowContexts/Extensions/DependencyInjectionExtensions.cs new file mode 100644 index 000000000..fcfedfd95 --- /dev/null +++ b/src/modules/Elsa.WorkflowContexts/Extensions/DependencyInjectionExtensions.cs @@ -0,0 +1,22 @@ +using Elsa.WorkflowContexts.Contracts; +using Microsoft.Extensions.DependencyInjection; + +// ReSharper disable once CheckNamespace +namespace Elsa.Extensions; + +/// +/// Extension methods for to add workflow context providers. +/// +public static class DependencyInjectionExtensions +{ + /// + /// Adds a workflow context provider. + /// + /// The service collection. + /// The type of the workflow context provider. + /// The service collection. + public static IServiceCollection AddWorkflowContextProvider(this IServiceCollection services) where T : class, IWorkflowContextProvider + { + return services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowBuilderExtensions.cs b/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowBuilderExtensions.cs index 7d36b623c..d0ba7c983 100644 --- a/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowBuilderExtensions.cs +++ b/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowBuilderExtensions.cs @@ -1,5 +1,7 @@ +using Elsa.WorkflowContexts; using Elsa.WorkflowContexts.Contracts; using Elsa.Workflows.Core.Contracts; +using JetBrains.Annotations; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; @@ -7,7 +9,8 @@ namespace Elsa.Extensions; /// /// Adds extension methods to . /// -public static class WorkflowExtensions +[PublicAPI] +public static class WorkflowBuilderExtensions { /// /// Installs the specified workflow context provider type into the specified workflow. @@ -26,7 +29,7 @@ public static class WorkflowExtensions /// The type of the provider to add. public static IWorkflowBuilder AddWorkflowContextProvider(this IWorkflowBuilder workflow, Type providerType) { - var providerTypes = workflow.CustomProperties.GetOrAdd("Elsa:WorkflowContexts", () => new List())!; + var providerTypes = workflow.CustomProperties.GetOrAdd(Constants.WorkflowContextProviderTypesKey, () => new List())!; providerTypes.Add(providerType); return workflow; } diff --git a/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionBuilderExtensions.cs b/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionBuilderExtensions.cs index 7e5899b63..b7f51de71 100644 --- a/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionBuilderExtensions.cs +++ b/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionBuilderExtensions.cs @@ -6,6 +6,9 @@ using Elsa.Workflows.Core.Pipelines.WorkflowExecution; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; +/// +/// Extension methods for . +/// public static class WorkflowExecutionBuilderExtensions { /// diff --git a/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionContextExtensions.cs b/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionContextExtensions.cs index 95ea55a0f..b2368c955 100644 --- a/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionContextExtensions.cs +++ b/src/modules/Elsa.WorkflowContexts/Extensions/WorkflowExecutionContextExtensions.cs @@ -5,31 +5,79 @@ using Elsa.Workflows.Core.Models; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; +/// +/// Adds extension methods to . +/// public static class WorkflowExecutionContextExtensions { - public static void SetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext, WorkflowContext workflowContext, object value) => workflowExecutionContext.TransientProperties.SetWorkflowContext(workflowContext, value); - public static void SetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext, WorkflowContext workflowContext, object value) => expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.SetWorkflowContext(workflowContext, value); - public static T? GetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext) => (T?)workflowExecutionContext.TransientProperties.GetWorkflowContextByProviderType(typeof(TProvider)); - public static T? GetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext) => (T?)expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.GetWorkflowContextByProviderType(typeof(TProvider)); - public static object? GetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext, WorkflowContext workflowContext) => workflowExecutionContext.TransientProperties.GetWorkflowContext(workflowContext); - public static object? GetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext, WorkflowContext workflowContext) => expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.GetWorkflowContext(workflowContext); + private static readonly object WorkflowContextsKey = new(); + + /// + /// Sets the specified workflow context value. + /// + /// The workflow execution context. + /// The type of the workflow context provider. + /// The value to set. + public static void SetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext, Type providerType, object value) => workflowExecutionContext.TransientProperties.SetWorkflowContext(providerType, value); - private static void SetWorkflowContext(this IDictionary transientProperties, WorkflowContext workflowContext, object value) + /// + /// Sets the specified workflow context value. + /// + /// The expression execution context. + /// The type of the workflow context provider. + /// The value to set. + public static void SetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext, Type providerType, object value) => expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.SetWorkflowContext(providerType, value); + + /// + /// Gets the workflow context value. + /// + /// The workflow execution context. + /// The type of the workflow context value. + /// The type of the workflow context provider. + /// The workflow context value. + public static T? GetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext) => (T?)workflowExecutionContext.TransientProperties.GetWorkflowContextByProviderType(typeof(TProvider)); + + /// + /// Gets the workflow context value. + /// + /// The expression execution context. + /// The type of the workflow context value. + /// The type of the workflow context provider. + /// The workflow context value. + public static T GetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext) => (T)expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.GetWorkflowContextByProviderType(typeof(TProvider))!; + + /// + /// Gets the workflow context value. + /// + /// The workflow execution context. + /// The type of the workflow context provider. + /// The workflow context value. + public static object GetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext, Type providerType) => workflowExecutionContext.TransientProperties.GetWorkflowContext(providerType); + + /// + /// Gets the workflow context value. + /// + /// The expression execution context. + /// The type of the workflow context provider. + /// The workflow context value. + public static object? GetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext, Type providerType) => expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.GetWorkflowContext(providerType); + + private static void SetWorkflowContext(this IDictionary transientProperties, Type providerType, object value) { - var contextDictionary = transientProperties!.GetOrAdd("WorkflowContexts", () => new Dictionary())!; - contextDictionary[workflowContext] = value; + var contextDictionary = GetWorkflowContextDictionary(transientProperties); + contextDictionary[providerType] = value; } - private static object? GetWorkflowContext(this IDictionary transientProperties, WorkflowContext workflowContext) + private static object GetWorkflowContext(this IDictionary transientProperties, Type providerType) { var contextDictionary = transientProperties.GetWorkflowContextDictionary(); - return contextDictionary[workflowContext]; + return contextDictionary[providerType]; } private static object? GetWorkflowContextByProviderType(this IDictionary transientProperties, Type providerType) => - transientProperties.FindWorkflowContext(x => x.ProviderType == providerType); + transientProperties.FindWorkflowContext(x => x == providerType); - private static object? FindWorkflowContext(this IDictionary transientProperties, Func filter) + private static object? FindWorkflowContext(this IDictionary transientProperties, Func filter) { var contextDictionary = transientProperties.GetWorkflowContextDictionary(); @@ -41,6 +89,6 @@ public static class WorkflowExecutionContextExtensions return query.FirstOrDefault(); } - private static IDictionary GetWorkflowContextDictionary(this IDictionary transientProperties) => - transientProperties!.GetOrAdd("WorkflowContexts", () => new Dictionary())!; + private static IDictionary GetWorkflowContextDictionary(this IDictionary transientProperties) => + transientProperties.GetOrAdd(WorkflowContextsKey, () => new Dictionary()); } \ No newline at end of file diff --git a/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextActivityExecutionMiddleware.cs b/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextActivityExecutionMiddleware.cs index 3c8ee7c57..8b463b780 100644 --- a/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextActivityExecutionMiddleware.cs +++ b/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextActivityExecutionMiddleware.cs @@ -28,44 +28,48 @@ public class WorkflowContextActivityExecutionMiddleware : IActivityExecutionMidd /// public async ValueTask InvokeAsync(ActivityExecutionContext context) { - // Check if the workflow contains any workflow contexts. - if (!context.WorkflowExecutionContext.Workflow.CustomProperties!.TryGetValue>("Elsa:WorkflowContexts", out var workflowContexts)) + // Check if the workflow contains any workflow context providers. + if (!context.WorkflowExecutionContext.Workflow.CustomProperties.TryGetValue>(Constants.WorkflowContextProviderTypesKey, out var providerTypes)) { await _next(context); return; } - // For each workflow context, invoke its provider. + // Invoke each workflow context provider. using (var scope = _serviceScopeFactory.CreateScope()) { - foreach (var workflowContext in workflowContexts!) + foreach (var providerType in providerTypes) { - var load = context.Activity.GetActivityWorkflowContextSettings(workflowContext).Load; + // Is the activity configured to load the context? + var load = context.Activity.GetActivityWorkflowContextSettings(providerType).Load; if (!load) continue; - var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, workflowContext.ProviderType); + // Load the context. + var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, providerType); var value = await provider.LoadAsync(context.WorkflowExecutionContext); // Store the loaded value into the workflow execution context. - context.WorkflowExecutionContext.SetWorkflowContext(workflowContext, value!); + context.WorkflowExecutionContext.SetWorkflowContext(providerType, value!); } } // Invoke the next middleware. await _next(context); - // For each workflow context, invoke its provider to update the context. + // Invoke each workflow context provider to persists the context. using (var scope = _serviceScopeFactory.CreateScope()) { - foreach (var workflowContext in workflowContexts!) + foreach (var providerType in providerTypes) { - var save = context.Activity.GetActivityWorkflowContextSettings(workflowContext).Load; + // Is the activity configured to save the context? + var save = context.Activity.GetActivityWorkflowContextSettings(providerType).Load; if (!save) continue; // Get the loaded value from the workflow execution context. - var value = context.WorkflowExecutionContext.GetWorkflowContext(workflowContext); + var value = context.WorkflowExecutionContext.GetWorkflowContext(providerType); - var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, workflowContext.ProviderType); + // Save the context. + var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, providerType); await provider.SaveAsync(context.WorkflowExecutionContext, value); } } diff --git a/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextWorkflowExecutionMiddleware.cs b/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextWorkflowExecutionMiddleware.cs index 85a79ac53..05c6c6b03 100644 --- a/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextWorkflowExecutionMiddleware.cs +++ b/src/modules/Elsa.WorkflowContexts/Middleware/WorkflowContextWorkflowExecutionMiddleware.cs @@ -23,38 +23,38 @@ public class WorkflowContextWorkflowExecutionMiddleware : WorkflowExecutionMiddl /// public override async ValueTask InvokeAsync(WorkflowExecutionContext context) { - // Check if the workflow contains any workflow contexts. - if (!context.Workflow.CustomProperties!.TryGetValue>("Elsa:WorkflowContexts", out var workflowContexts)) + // Check if the workflow contains any workflow context providers. + if (!context.Workflow.CustomProperties!.TryGetValue>(Constants.WorkflowContextProviderTypesKey, out var providerTypes)) { await Next(context); return; } - // For each workflow context, invoke its provider. + // Invoke each workflow context provider. using (var scope = _serviceScopeFactory.CreateScope()) { - foreach (var workflowContext in workflowContexts!) + foreach (var providerType in providerTypes) { - var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, workflowContext.ProviderType); + var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, providerType); var value = await provider.LoadAsync(context); // Store the loaded value into the workflow execution context. - context.SetWorkflowContext(workflowContext, value!); + context.SetWorkflowContext(providerType, value!); } } // Invoke the next middleware. await Next(context); - // For each workflow context, invoke its provider to update the context. + // Invoke each workflow context provider to persists the context. using (var scope = _serviceScopeFactory.CreateScope()) { - foreach (var workflowContext in workflowContexts!) + foreach (var providerType in providerTypes) { // Get the loaded value from the workflow execution context. - var value = context.GetWorkflowContext(workflowContext); + var value = context.GetWorkflowContext(providerType); - var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, workflowContext.ProviderType); + var provider = (IWorkflowContextProvider)ActivatorUtilities.GetServiceOrCreateInstance(scope.ServiceProvider, providerType); await provider.SaveAsync(context, value); } } diff --git a/src/modules/Elsa.WorkflowContexts/Models/ActivityWorkflowContextSettings.cs b/src/modules/Elsa.WorkflowContexts/Models/ActivityWorkflowContextSettings.cs index e1a09cbcc..2faeb07b2 100644 --- a/src/modules/Elsa.WorkflowContexts/Models/ActivityWorkflowContextSettings.cs +++ b/src/modules/Elsa.WorkflowContexts/Models/ActivityWorkflowContextSettings.cs @@ -1,7 +1,17 @@ namespace Elsa.WorkflowContexts.Models; +/// +/// Provides activity-specific settings for workflow context providers. +/// public class ActivityWorkflowContextSettings { + /// + /// Whether to load the context before executing the activity. + /// public bool Load { get; set; } + + /// + /// Whether to save the context after executing the activity. + /// public bool Save { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.WorkflowContexts/Models/WorkflowContext.cs b/src/modules/Elsa.WorkflowContexts/Models/WorkflowContext.cs deleted file mode 100644 index 0dae46c73..000000000 --- a/src/modules/Elsa.WorkflowContexts/Models/WorkflowContext.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Elsa.Expressions.Models; -using Elsa.Extensions; -using Elsa.WorkflowContexts.Contracts; - -namespace Elsa.WorkflowContexts.Models; - -public class WorkflowContext -{ - public WorkflowContext(Type providerType) - { - ProviderType = providerType; - } - - public Type ProviderType { get; } -} - -public class WorkflowContext : WorkflowContext where TProvider:IWorkflowContextProvider -{ - public WorkflowContext() : base(typeof(TProvider)) - { - } - - public T? Get(ExpressionExecutionContext context) - { - var workflowExecutionContext = context.GetWorkflowExecutionContext(); - var transientProperties = workflowExecutionContext.TransientProperties; - var workflowContexts = (IDictionary)transientProperties["WorkflowContexts"]!; - return workflowContexts.TryGetValue(this, out var workflowContext) ? (T?)workflowContext : default; - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs index 5b0cd0eea..55f1cac5b 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs @@ -5,10 +5,10 @@ namespace Elsa.Extensions; public static class DictionaryExtensions { - public static bool TryGetValue(this IDictionary dictionary, string key, out T? value) => dictionary.TryGetValue(key, out value); - public static bool TryGetValue(this IDictionary dictionary, string key, out T? value) => dictionary.TryGetValue(key, out value); + public static bool TryGetValue(this IDictionary dictionary, string key, out T value) => dictionary.TryGetValue(key, out value); + public static bool TryGetValue(this IDictionary dictionary, string key, out T value) => dictionary.TryGetValue(key, out value); - public static bool TryGetValue(this IDictionary dictionary, TKey key, out T? value) + public static bool TryGetValue(this IDictionary dictionary, TKey key, out T value) { if (!dictionary.TryGetValue(key, out var item)) { diff --git a/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs b/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs index 4657ecc93..98fe72ce6 100644 --- a/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs +++ b/src/modules/Elsa.Workflows.Core/Models/WorkflowExecutionContext.cs @@ -59,7 +59,7 @@ public class WorkflowExecutionContext : IExecutionContext _nodes = nodes.ToList(); _activityExecutionContexts = activityExecutionContexts?.ToList() ?? new List(); Scheduler = scheduler; - Input = input ?? new Dictionary(); + Input = input != null ? new Dictionary(input, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase); ExecuteDelegate = executeDelegate; TriggerActivityId = triggerActivityId; CancellationToken = cancellationToken; @@ -114,7 +114,7 @@ public class WorkflowExecutionContext : IExecutionContext /// A map between activity IDs and s in the workflow graph. /// public IDictionary NodeIdLookup { get; } - + /// /// A map between hashed activity node IDs and s in the workflow graph. /// @@ -276,7 +276,7 @@ public class WorkflowExecutionContext : IExecutionContext /// Returns the with the specified activity ID from the workflow graph. /// public ActivityNode FindNodeById(string nodeId) => NodeIdLookup[nodeId]; - + /// /// Returns the with the specified hash of the activity node ID from the workflow graph. /// @@ -298,7 +298,7 @@ public class WorkflowExecutionContext : IExecutionContext /// Returns the with the specified ID from the workflow graph. /// public IActivity FindActivityByActivityId(string activityId) => FindNodeById(NodeIdLookup.Single(n => n.Key.Contains(activityId)).Value.NodeId).Activity; - + /// /// Returns the with the specified hash of the activity node ID from the workflow graph. /// @@ -393,7 +393,7 @@ public class WorkflowExecutionContext : IExecutionContext { var activityExecutionContext = ActivityExecutionContexts.FirstOrDefault(x => x.Activity == activity); - if (activityExecutionContext != null) + if (activityExecutionContext != null) await activityExecutionContext.CancelActivityAsync(); } diff --git a/src/samples/aspnet/Elsa.Samples.ProtoActorRuntime/Program.cs b/src/samples/aspnet/Elsa.Samples.ProtoActorRuntime/Program.cs index 15a48ec25..5a7a185fe 100644 --- a/src/samples/aspnet/Elsa.Samples.ProtoActorRuntime/Program.cs +++ b/src/samples/aspnet/Elsa.Samples.ProtoActorRuntime/Program.cs @@ -3,7 +3,6 @@ using Elsa.EntityFrameworkCore.Modules.Labels; using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; using Elsa.Extensions; -using Elsa.JavaScript.Options; using Microsoft.Data.Sqlite; using Proto.Persistence.Sqlite; @@ -57,11 +56,8 @@ services .UseHttp() ); -services.Configure(options => options.AllowClrAccess = true); -services.AddHandlersFrom(); services.AddHealthChecks(); services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin())); -services.AddHttpContextAccessor(); // Configure middleware pipeline. var app = builder.Build();