Cleanup and refactoring WorkflowContext APIs

This commit is contained in:
Sipke Schoorstra 2023-04-18 00:00:46 +02:00
parent 970d034037
commit 943df3d274
19 changed files with 230 additions and 100 deletions

View file

@ -43,7 +43,6 @@ services
);
services.AddHealthChecks();
services.AddHttpContextAccessor();
services.AddSingleton<IAuthorizationHandler, LocalHostRequirementHandler>();
services.AddAuthorization(options => options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostRequirement())));
services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()));

View file

@ -50,10 +50,8 @@ services
);
services.Configure<JintOptions>(options => options.AllowClrAccess = true);
services.AddHandlersFrom<Program>();
services.AddHealthChecks();
services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()));
services.AddHttpContextAccessor();
// Configure middleware pipeline.
var app = builder.Build();

View file

@ -4,11 +4,19 @@ using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Common.Features;
/// <summary>
/// Configures the MemoryCache.
/// </summary>
public class MemoryCacheFeature : FeatureBase
{
/// <inheritdoc />
public MemoryCacheFeature(IModule module) : base(module)
{
}
public override void Configure() => Services.AddMemoryCache();
/// <inheritdoc />
public override void Apply()
{
Services.AddMemoryCache();
}
}

View file

@ -12,6 +12,8 @@ namespace Elsa.EntityFrameworkCore.Modules.Management;
/// Configures the <see cref="WorkflowInstancesFeature"/> and <see cref="WorkflowDefinitionsFeature"/> features with an Entity Framework Core persistence provider.
/// </summary>
[DependsOn(typeof(WorkflowManagementFeature))]
[DependsOn(typeof(WorkflowInstancesFeature))]
[DependsOn(typeof(WorkflowDefinitionsFeature))]
[PublicAPI]
public class EFCoreWorkflowManagementPersistenceFeature : PersistenceFeatureBase<ManagementElsaDbContext>
{

View file

@ -1,23 +1,50 @@
using Elsa.WorkflowContexts.Contracts;
using Elsa.Workflows.Core.Models;
using JetBrains.Annotations;
namespace Elsa.WorkflowContexts.Abstractions;
/// <summary>
/// A base class for workflow context providers.
/// </summary>
/// <typeparam name="T">The type of the workflow context value.</typeparam>
[PublicAPI]
public abstract class WorkflowContextProvider<T> : IWorkflowContextProvider
{
async ValueTask<object?> IWorkflowContextProvider.LoadAsync(WorkflowExecutionContext workflowExecutionContext) => await LoadAsync(workflowExecutionContext);
async ValueTask IWorkflowContextProvider.SaveAsync(WorkflowExecutionContext workflowExecutionContext, object? context) => await SaveAsync(workflowExecutionContext, (T?)context);
/// <summary>
/// Load data that will be available to the workflow.
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <returns>The workflow context value.</returns>
protected virtual ValueTask<T?> LoadAsync(WorkflowExecutionContext workflowExecutionContext) => new(Load(workflowExecutionContext));
/// <summary>
/// Load data that will be available to the workflow.
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <returns>The workflow context value.</returns>
protected virtual T? Load(WorkflowExecutionContext workflowExecutionContext) => default;
/// <summary>
/// Save data.
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <param name="context">The workflow context value.</param>
protected virtual ValueTask SaveAsync(WorkflowExecutionContext workflowExecutionContext, T? context)
{
Save(workflowExecutionContext, context);
return ValueTask.CompletedTask;
}
/// <summary>
/// Save data.
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <param name="context">The workflow context value.</param>
protected virtual void Save(WorkflowExecutionContext workflowExecutionContext, T? context)
{
}

View file

@ -0,0 +1,6 @@
namespace Elsa.WorkflowContexts;
internal class Constants
{
public const string WorkflowContextProviderTypesKey = "Elsa:WorkflowContexts";
}

View file

@ -3,18 +3,17 @@
namespace Elsa.WorkflowContexts.Contracts;
/// <summary>
/// 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.
/// </summary>
public interface IWorkflowContextProvider
{
/// <summary>
/// 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.
/// </summary>
ValueTask<object?> LoadAsync(WorkflowExecutionContext workflowExecutionContext);
/// <summary>
/// Implement this method to save an object that was loaded previously
/// Save data.
/// </summary>
ValueTask SaveAsync(WorkflowExecutionContext workflowExecutionContext, object? context);
}

View file

@ -4,43 +4,78 @@ using Elsa.Workflows.Core.Contracts;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Extension methods for <see cref="IActivity"/>.
/// </summary>
public static class ActivityExtensions
{
private const string ActivityWorkflowContextSettingsKey = "ActivityWorkflowContextSettingsKey";
public static IDictionary<WorkflowContext, ActivityWorkflowContextSettings> GetWorkflowContextSettings(this IActivity activity)
/// <summary>
/// Gets the workflow context settings for the specified activity.
/// </summary>
/// <param name="activity">The activity to get the settings for.</param>
/// <returns>The workflow context settings.</returns>
public static IDictionary<Type, ActivityWorkflowContextSettings> GetWorkflowContextSettings(this IActivity activity)
{
return activity.CustomProperties.GetOrAdd(ActivityWorkflowContextSettingsKey, () => new Dictionary<WorkflowContext, ActivityWorkflowContextSettings>())!;
return activity.CustomProperties.GetOrAdd(ActivityWorkflowContextSettingsKey, () => new Dictionary<Type, ActivityWorkflowContextSettings>())!;
}
public static TActivity LoadContext<TActivity>(this TActivity activity, WorkflowContext workflowContext) where TActivity : IActivity
/// <summary>
/// Configures the activity to load the workflow context before executing.
/// </summary>
/// <param name="activity">The activity to configure.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <typeparam name="TActivity">The type of the activity.</typeparam>
/// <returns>The activity.</returns>
public static TActivity LoadContext<TActivity>(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<TActivity>(this TActivity activity, WorkflowContext workflowContext) where TActivity : IActivity
/// <summary>
/// Configures the activity to save the workflow context after executing.
/// </summary>
/// <param name="activity">The activity to configure.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <typeparam name="TActivity">The type of the activity.</typeparam>
/// <returns>The activity.</returns>
public static TActivity SaveContext<TActivity>(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<TActivity>(this TActivity activity, WorkflowContext workflowContext) where TActivity: IActivity
/// <summary>
/// Gets the workflow context settings for the specified activity.
/// </summary>
/// <param name="activity">The activity to get the settings for.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <typeparam name="TActivity">The type of the activity.</typeparam>
/// <returns>The workflow context settings.</returns>
public static ActivityWorkflowContextSettings GetActivityWorkflowContextSettings<TActivity>(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<WorkflowContext, ActivityWorkflowContextSettings> dictionary, WorkflowContext workflowContext)
/// <summary>
/// Gets the workflow context settings for the specified activity.
/// </summary>
/// <param name="dictionary">The dictionary to get the settings from.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <returns>The workflow context settings.</returns>
public static ActivityWorkflowContextSettings GetActivityWorkflowContextSettings(this IDictionary<Type, ActivityWorkflowContextSettings> 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;

View file

@ -0,0 +1,22 @@
using Elsa.WorkflowContexts.Contracts;
using Microsoft.Extensions.DependencyInjection;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Extension methods for <see cref="IServiceCollection"/> to add workflow context providers.
/// </summary>
public static class DependencyInjectionExtensions
{
/// <summary>
/// Adds a workflow context provider.
/// </summary>
/// <param name="services">The service collection.</param>
/// <typeparam name="T">The type of the workflow context provider.</typeparam>
/// <returns>The service collection.</returns>
public static IServiceCollection AddWorkflowContextProvider<T>(this IServiceCollection services) where T : class, IWorkflowContextProvider
{
return services.AddSingleton<IWorkflowContextProvider, T>();
}
}

View file

@ -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;
/// <summary>
/// Adds extension methods to <see cref="IWorkflowBuilder"/>.
/// </summary>
public static class WorkflowExtensions
[PublicAPI]
public static class WorkflowBuilderExtensions
{
/// <summary>
/// Installs the specified workflow context provider type into the specified workflow.
@ -26,7 +29,7 @@ public static class WorkflowExtensions
/// <param name="providerType">The type of the provider to add.</param>
public static IWorkflowBuilder AddWorkflowContextProvider(this IWorkflowBuilder workflow, Type providerType)
{
var providerTypes = workflow.CustomProperties.GetOrAdd("Elsa:WorkflowContexts", () => new List<Type>())!;
var providerTypes = workflow.CustomProperties.GetOrAdd(Constants.WorkflowContextProviderTypesKey, () => new List<Type>())!;
providerTypes.Add(providerType);
return workflow;
}

View file

@ -6,6 +6,9 @@ using Elsa.Workflows.Core.Pipelines.WorkflowExecution;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Extension methods for <see cref="IWorkflowExecutionPipelineBuilder"/>.
/// </summary>
public static class WorkflowExecutionBuilderExtensions
{
/// <summary>

View file

@ -5,31 +5,79 @@ using Elsa.Workflows.Core.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
/// <summary>
/// Adds extension methods to <see cref="WorkflowExecutionContext"/>.
/// </summary>
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<T, TProvider>(this WorkflowExecutionContext workflowExecutionContext) => (T?)workflowExecutionContext.TransientProperties.GetWorkflowContextByProviderType(typeof(TProvider));
public static T? GetWorkflowContext<T, TProvider>(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();
/// <summary>
/// Sets the specified workflow context value.
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <param name="value">The value to set.</param>
public static void SetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext, Type providerType, object value) => workflowExecutionContext.TransientProperties.SetWorkflowContext(providerType, value);
private static void SetWorkflowContext(this IDictionary<object, object> transientProperties, WorkflowContext workflowContext, object value)
/// <summary>
/// Sets the specified workflow context value.
/// </summary>
/// <param name="expressionExecutionContext">The expression execution context.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <param name="value">The value to set.</param>
public static void SetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext, Type providerType, object value) => expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.SetWorkflowContext(providerType, value);
/// <summary>
/// Gets the workflow context value.
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <typeparam name="T">The type of the workflow context value.</typeparam>
/// <typeparam name="TProvider">The type of the workflow context provider.</typeparam>
/// <returns>The workflow context value.</returns>
public static T? GetWorkflowContext<T, TProvider>(this WorkflowExecutionContext workflowExecutionContext) => (T?)workflowExecutionContext.TransientProperties.GetWorkflowContextByProviderType(typeof(TProvider));
/// <summary>
/// Gets the workflow context value.
/// </summary>
/// <param name="expressionExecutionContext">The expression execution context.</param>
/// <typeparam name="T">The type of the workflow context value.</typeparam>
/// <typeparam name="TProvider">The type of the workflow context provider.</typeparam>
/// <returns>The workflow context value.</returns>
public static T GetWorkflowContext<TProvider, T>(this ExpressionExecutionContext expressionExecutionContext) => (T)expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.GetWorkflowContextByProviderType(typeof(TProvider))!;
/// <summary>
/// Gets the workflow context value.
/// </summary>
/// <param name="workflowExecutionContext">The workflow execution context.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <returns>The workflow context value.</returns>
public static object GetWorkflowContext(this WorkflowExecutionContext workflowExecutionContext, Type providerType) => workflowExecutionContext.TransientProperties.GetWorkflowContext(providerType);
/// <summary>
/// Gets the workflow context value.
/// </summary>
/// <param name="expressionExecutionContext">The expression execution context.</param>
/// <param name="providerType">The type of the workflow context provider.</param>
/// <returns>The workflow context value.</returns>
public static object? GetWorkflowContext(this ExpressionExecutionContext expressionExecutionContext, Type providerType) => expressionExecutionContext.GetWorkflowExecutionContext().TransientProperties.GetWorkflowContext(providerType);
private static void SetWorkflowContext(this IDictionary<object, object> transientProperties, Type providerType, object value)
{
var contextDictionary = transientProperties!.GetOrAdd("WorkflowContexts", () => new Dictionary<WorkflowContext, object>())!;
contextDictionary[workflowContext] = value;
var contextDictionary = GetWorkflowContextDictionary(transientProperties);
contextDictionary[providerType] = value;
}
private static object? GetWorkflowContext(this IDictionary<object, object> transientProperties, WorkflowContext workflowContext)
private static object GetWorkflowContext(this IDictionary<object, object> transientProperties, Type providerType)
{
var contextDictionary = transientProperties.GetWorkflowContextDictionary();
return contextDictionary[workflowContext];
return contextDictionary[providerType];
}
private static object? GetWorkflowContextByProviderType(this IDictionary<object, object> transientProperties, Type providerType) =>
transientProperties.FindWorkflowContext(x => x.ProviderType == providerType);
transientProperties.FindWorkflowContext(x => x == providerType);
private static object? FindWorkflowContext(this IDictionary<object, object> transientProperties, Func<WorkflowContext, bool> filter)
private static object? FindWorkflowContext(this IDictionary<object, object> transientProperties, Func<Type, bool> filter)
{
var contextDictionary = transientProperties.GetWorkflowContextDictionary();
@ -41,6 +89,6 @@ public static class WorkflowExecutionContextExtensions
return query.FirstOrDefault();
}
private static IDictionary<WorkflowContext, object> GetWorkflowContextDictionary(this IDictionary<object, object> transientProperties) =>
transientProperties!.GetOrAdd("WorkflowContexts", () => new Dictionary<WorkflowContext, object>())!;
private static IDictionary<Type, object> GetWorkflowContextDictionary(this IDictionary<object, object> transientProperties) =>
transientProperties.GetOrAdd(WorkflowContextsKey, () => new Dictionary<Type, object>());
}

View file

@ -28,44 +28,48 @@ public class WorkflowContextActivityExecutionMiddleware : IActivityExecutionMidd
/// <inheritdoc />
public async ValueTask InvokeAsync(ActivityExecutionContext context)
{
// Check if the workflow contains any workflow contexts.
if (!context.WorkflowExecutionContext.Workflow.CustomProperties!.TryGetValue<ICollection<WorkflowContext>>("Elsa:WorkflowContexts", out var workflowContexts))
// Check if the workflow contains any workflow context providers.
if (!context.WorkflowExecutionContext.Workflow.CustomProperties.TryGetValue<ICollection<Type>>(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);
}
}

View file

@ -23,38 +23,38 @@ public class WorkflowContextWorkflowExecutionMiddleware : WorkflowExecutionMiddl
/// <inheritdoc />
public override async ValueTask InvokeAsync(WorkflowExecutionContext context)
{
// Check if the workflow contains any workflow contexts.
if (!context.Workflow.CustomProperties!.TryGetValue<ICollection<WorkflowContext>>("Elsa:WorkflowContexts", out var workflowContexts))
// Check if the workflow contains any workflow context providers.
if (!context.Workflow.CustomProperties!.TryGetValue<ICollection<Type>>(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);
}
}

View file

@ -1,7 +1,17 @@
namespace Elsa.WorkflowContexts.Models;
/// <summary>
/// Provides activity-specific settings for workflow context providers.
/// </summary>
public class ActivityWorkflowContextSettings
{
/// <summary>
/// Whether to load the context before executing the activity.
/// </summary>
public bool Load { get; set; }
/// <summary>
/// Whether to save the context after executing the activity.
/// </summary>
public bool Save { get; set; }
}

View file

@ -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<T, TProvider> : 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<WorkflowContext, object?>)transientProperties["WorkflowContexts"]!;
return workflowContexts.TryGetValue(this, out var workflowContext) ? (T?)workflowContext : default;
}
}

View file

@ -5,10 +5,10 @@ namespace Elsa.Extensions;
public static class DictionaryExtensions
{
public static bool TryGetValue<T>(this IDictionary<string, object> dictionary, string key, out T? value) => dictionary.TryGetValue<string, T>(key, out value);
public static bool TryGetValue<T>(this IDictionary<object, object> dictionary, string key, out T? value) => dictionary.TryGetValue<object, T>(key, out value);
public static bool TryGetValue<T>(this IDictionary<string, object> dictionary, string key, out T value) => dictionary.TryGetValue<string, T>(key, out value);
public static bool TryGetValue<T>(this IDictionary<object, object> dictionary, string key, out T value) => dictionary.TryGetValue<object, T>(key, out value);
public static bool TryGetValue<TKey, T>(this IDictionary<TKey, object> dictionary, TKey key, out T? value)
public static bool TryGetValue<TKey, T>(this IDictionary<TKey, object> dictionary, TKey key, out T value)
{
if (!dictionary.TryGetValue(key, out var item))
{

View file

@ -59,7 +59,7 @@ public class WorkflowExecutionContext : IExecutionContext
_nodes = nodes.ToList();
_activityExecutionContexts = activityExecutionContexts?.ToList() ?? new List<ActivityExecutionContext>();
Scheduler = scheduler;
Input = input ?? new Dictionary<string, object>();
Input = input != null ? new Dictionary<string, object>(input, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
ExecuteDelegate = executeDelegate;
TriggerActivityId = triggerActivityId;
CancellationToken = cancellationToken;
@ -114,7 +114,7 @@ public class WorkflowExecutionContext : IExecutionContext
/// A map between activity IDs and <see cref="ActivityNode"/>s in the workflow graph.
/// </summary>
public IDictionary<string, ActivityNode> NodeIdLookup { get; }
/// <summary>
/// A map between hashed activity node IDs and <see cref="ActivityNode"/>s in the workflow graph.
/// </summary>
@ -276,7 +276,7 @@ public class WorkflowExecutionContext : IExecutionContext
/// Returns the <see cref="ActivityNode"/> with the specified activity ID from the workflow graph.
/// </summary>
public ActivityNode FindNodeById(string nodeId) => NodeIdLookup[nodeId];
/// <summary>
/// Returns the <see cref="ActivityNode"/> with the specified hash of the activity node ID from the workflow graph.
/// </summary>
@ -298,7 +298,7 @@ public class WorkflowExecutionContext : IExecutionContext
/// Returns the <see cref="IActivity"/> with the specified ID from the workflow graph.
/// </summary>
public IActivity FindActivityByActivityId(string activityId) => FindNodeById(NodeIdLookup.Single(n => n.Key.Contains(activityId)).Value.NodeId).Activity;
/// <summary>
/// Returns the <see cref="IActivity"/> with the specified hash of the activity node ID from the workflow graph.
/// </summary>
@ -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();
}

View file

@ -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<JintOptions>(options => options.AllowClrAccess = true);
services.AddHandlersFrom<Program>();
services.AddHealthChecks();
services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()));
services.AddHttpContextAccessor();
// Configure middleware pipeline.
var app = builder.Build();