Add refresh/persist workflow context options to activity
This commit is contained in:
parent
50b0921e98
commit
1bb2953efa
|
|
@ -16,12 +16,16 @@ namespace Elsa.Builders
|
|||
public string? Description { get; set; }
|
||||
IDictionary<string, IActivityPropertyValueProvider>? PropertyValueProviders { get; }
|
||||
bool PersistWorkflow { get; set; }
|
||||
bool LoadWorkflowContext { get; set; }
|
||||
bool SaveWorkflowContext { get; set; }
|
||||
IActivityBuilder Add<T>(Action<ISetupActivity<T>>? setup = default) where T : class, IActivity;
|
||||
IOutcomeBuilder When(string outcome);
|
||||
IActivityBuilder Then(IActivityBuilder targetActivity);
|
||||
IConnectionBuilder Then(string activityName);
|
||||
IActivityBuilder WithId(string? id);
|
||||
IActivityBuilder WithName(string? name);
|
||||
IActivityBuilder WithLoadWorkflowContext(bool value = true);
|
||||
IActivityBuilder WithSaveWorkflowContext(bool value = true);
|
||||
Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> BuildActivityAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,8 @@ namespace Elsa.Models
|
|||
public int? Left { get; set; }
|
||||
public int? Top { get; set; }
|
||||
public bool PersistWorkflow { get; set; }
|
||||
public bool LoadWorkflowContext { get; set; }
|
||||
public bool SaveWorkflowContext { get; set; }
|
||||
public ActivityDefinitionProperties Properties { get; set; } = new ActivityDefinitionProperties();
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,8 @@ namespace Elsa.Services
|
|||
public string? DisplayName { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool PersistWorkflow { get; set; }
|
||||
public bool LoadWorkflowContext { get; set; }
|
||||
public bool SaveWorkflowContext { get; set; }
|
||||
public JObject Data { get; set; } = new JObject();
|
||||
public ValueTask<bool> CanExecuteAsync(ActivityExecutionContext context, CancellationToken cancellationToken) => OnCanExecuteAsync(context, cancellationToken);
|
||||
public ValueTask<IActivityExecutionResult> ExecuteAsync(ActivityExecutionContext context, CancellationToken cancellationToken) => OnExecuteAsync(context, cancellationToken);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,16 @@ namespace Elsa.Services
|
|||
/// </summary>
|
||||
bool PersistWorkflow { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether the workflow context (if any) will be refreshed automatically before executing this activity.
|
||||
/// </summary>
|
||||
bool LoadWorkflowContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether the workflow context (if any) will be persisted automatically after executing this activity.
|
||||
/// </summary>
|
||||
bool SaveWorkflowContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A data store for the activity to store information that needs to be persisted as part of the workflow instance.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -15,12 +15,16 @@ namespace Elsa.Services.Models
|
|||
string? name,
|
||||
string type,
|
||||
bool persistWorkflow,
|
||||
bool loadWorkflowContext,
|
||||
bool saveWorkflowContext,
|
||||
Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> createActivity)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Type = type;
|
||||
PersistWorkflow = persistWorkflow;
|
||||
LoadWorkflowContext = loadWorkflowContext;
|
||||
SaveWorkflowContext = saveWorkflowContext;
|
||||
CreateActivityAsync = createActivity;
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +32,8 @@ namespace Elsa.Services.Models
|
|||
public string? Name { get; set; }
|
||||
public string Type { get; set; } = default!;
|
||||
public bool PersistWorkflow { get; set; }
|
||||
public bool LoadWorkflowContext { get; set; }
|
||||
public bool SaveWorkflowContext { get; set; }
|
||||
|
||||
public Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> CreateActivityAsync { get; set; } = default!;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ namespace Elsa.Services.Models
|
|||
string? name,
|
||||
string type,
|
||||
bool persistWorkflow,
|
||||
Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> createActivity) : base(id, name, type, persistWorkflow, createActivity)
|
||||
bool loadWorkflowContext,
|
||||
bool saveWorkflowContext,
|
||||
Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> createActivity) : base(id, name, type, persistWorkflow, loadWorkflowContext, saveWorkflowContext, createActivity)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ namespace Elsa.Services.Models
|
|||
string? Name { get; }
|
||||
public string Type { get; }
|
||||
public bool PersistWorkflow { get; }
|
||||
bool LoadWorkflowContext { get; set; }
|
||||
bool SaveWorkflowContext { get; set; }
|
||||
Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> CreateActivityAsync { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ namespace Elsa.Services.Models
|
|||
bool deleteCompletedInstances,
|
||||
IEnumerable<IActivityBlueprint> activities,
|
||||
IEnumerable<IConnection> connections,
|
||||
IActivityPropertyProviders activityPropertyValueProviders) : base(id, name, id, true, null!)
|
||||
IActivityPropertyProviders activityPropertyValueProviders) : base(id, name, id, true, false, false, null!)
|
||||
{
|
||||
Id = id;
|
||||
Version = version;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ namespace Elsa.Builders
|
|||
public string? Name { get; set; }
|
||||
public string? Description { get; set; }
|
||||
public bool PersistWorkflow { get; set; }
|
||||
public bool LoadWorkflowContext { get; set; }
|
||||
public bool SaveWorkflowContext { get; set; }
|
||||
public IDictionary<string, IActivityPropertyValueProvider>? PropertyValueProviders { get; protected set; }
|
||||
|
||||
public IActivityBuilder Add<T>(
|
||||
|
|
@ -71,6 +73,18 @@ namespace Elsa.Builders
|
|||
Name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IActivityBuilder WithLoadWorkflowContext(bool value)
|
||||
{
|
||||
LoadWorkflowContext = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public IActivityBuilder WithSaveWorkflowContext(bool value)
|
||||
{
|
||||
SaveWorkflowContext = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> BuildActivityAsync() =>
|
||||
async (context, cancellationToken) =>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ namespace Elsa.Builders
|
|||
private readonly Func<ICompositeActivityBuilder> _workflowBuilderFactory;
|
||||
|
||||
public CompositeActivityBuilder(
|
||||
IIdGenerator idGenerator,
|
||||
IIdGenerator idGenerator,
|
||||
IActivityActivator activityActivator,
|
||||
IServiceProvider serviceProvider)
|
||||
{
|
||||
|
|
@ -23,7 +23,7 @@ namespace Elsa.Builders
|
|||
ActivityId = idGenerator.Generate();
|
||||
ActivityBuilders = new List<IActivityBuilder>();
|
||||
ConnectionBuilders = new List<IConnectionBuilder>();
|
||||
|
||||
|
||||
_workflowBuilderFactory = () =>
|
||||
{
|
||||
var builder = serviceProvider.GetRequiredService<ICompositeActivityBuilder>();
|
||||
|
|
@ -101,7 +101,7 @@ namespace Elsa.Builders
|
|||
ActivityBuilders.Add(activityBuilder);
|
||||
return activityBuilder;
|
||||
}
|
||||
|
||||
|
||||
public override IActivityBuilder Then<T>(Action<ISetupActivity<T>>? setup = null, Action<IActivityBuilder>? branch = null) => StartWith(setup, branch);
|
||||
|
||||
public override IActivityBuilder Then(IActivityBuilder targetActivity) => Add(targetActivity);
|
||||
|
|
@ -124,30 +124,30 @@ namespace Elsa.Builders
|
|||
ConnectionBuilders.Add(connectionBuilder);
|
||||
return connectionBuilder;
|
||||
}
|
||||
|
||||
|
||||
public ICompositeActivityBlueprint Build(string activityIdPrefix = "activity")
|
||||
{
|
||||
var activityBuilders = ActivityBuilders.ToList();
|
||||
var activityBlueprints = new List<IActivityBlueprint>();
|
||||
var connections = new List<IConnection>();
|
||||
var activityPropertyProviders = new Dictionary<string, IDictionary<string, IActivityPropertyValueProvider>>();
|
||||
|
||||
|
||||
// Assign automatic ids to activity builders
|
||||
var index = 0;
|
||||
|
||||
|
||||
foreach (var activityBuilder in activityBuilders.Where(x => string.IsNullOrWhiteSpace(x.ActivityId)))
|
||||
activityBuilder.ActivityId = $"{activityIdPrefix}-{++index}";
|
||||
|
||||
|
||||
activityBlueprints.AddRange(activityBuilders.Select(BuildActivityBlueprint));
|
||||
|
||||
|
||||
// Build composite activities.
|
||||
var compositeActivityBuilders = activityBuilders.Where(x => typeof(CompositeActivity).IsAssignableFrom(x.ActivityType));
|
||||
BuildCompositeActivities(compositeActivityBuilders, activityBlueprints, connections, activityPropertyProviders);
|
||||
|
||||
|
||||
var activityBlueprintDictionary = activityBlueprints.ToDictionary(x => x.Id);
|
||||
|
||||
|
||||
connections.AddRange(ConnectionBuilders.Select(x => new Connection(activityBlueprintDictionary[x.Source().ActivityId], activityBlueprintDictionary[x.Target().ActivityId], x.Outcome)));
|
||||
|
||||
|
||||
activityPropertyProviders.AddRange(
|
||||
activityBuilders
|
||||
.Select(x => (x.ActivityId, x.PropertyValueProviders))
|
||||
|
|
@ -161,7 +161,7 @@ namespace Elsa.Builders
|
|||
ActivityPropertyProviders = new ActivityPropertyProviders(activityPropertyProviders)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void BuildCompositeActivities(
|
||||
IEnumerable<IActivityBuilder> compositeActivityBuilders,
|
||||
ICollection<IActivityBlueprint> activityBlueprints,
|
||||
|
|
@ -172,29 +172,29 @@ namespace Elsa.Builders
|
|||
{
|
||||
var compositeActivity = (CompositeActivity)_activityActivator.ActivateActivity(activityBuilder.ActivityType.Name);
|
||||
var workflowBuilder = _workflowBuilderFactory();
|
||||
|
||||
|
||||
compositeActivity.Build(workflowBuilder);
|
||||
|
||||
|
||||
var workflow = workflowBuilder.Build($"{activityBuilder.ActivityId}:activity");
|
||||
var activityDictionary = workflow.Activities.ToDictionary(x => x.Id);
|
||||
|
||||
|
||||
activityBlueprints.AddRange(workflow.Activities);
|
||||
connections.AddRange(workflow.Connections.Select(x => new Connection(activityDictionary[x.Source.Activity.Id], activityDictionary[x.Target.Activity.Id], x.Source.Outcome)));
|
||||
activityPropertyProviders.AddRange(workflow.ActivityPropertyProviders);
|
||||
|
||||
|
||||
var compositeActivityBlueprint = (ICompositeActivityBlueprint)activityBlueprints.Single(x => x.Id == activityBuilder.ActivityId);
|
||||
compositeActivityBlueprint.Activities = workflow.Activities;
|
||||
compositeActivityBlueprint.Connections = workflow.Connections;
|
||||
compositeActivityBlueprint.ActivityPropertyProviders = workflow.ActivityPropertyProviders;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private IActivityBlueprint BuildActivityBlueprint(IActivityBuilder builder, int index)
|
||||
{
|
||||
var isComposite = typeof(CompositeActivity).IsAssignableFrom(builder.ActivityType);
|
||||
return isComposite
|
||||
? new CompositeActivityBlueprint(builder.ActivityId, builder.Name, builder.ActivityType.Name, builder.PersistWorkflow, builder.BuildActivityAsync())
|
||||
: new ActivityBlueprint(builder.ActivityId, builder.Name, builder.ActivityType.Name, builder.PersistWorkflow, builder.BuildActivityAsync());
|
||||
? new CompositeActivityBlueprint(builder.ActivityId, builder.Name, builder.ActivityType.Name, builder.PersistWorkflow, builder.LoadWorkflowContext, builder.SaveWorkflowContext, builder.BuildActivityAsync())
|
||||
: new ActivityBlueprint(builder.ActivityId, builder.Name, builder.ActivityType.Name, builder.PersistWorkflow, builder.LoadWorkflowContext, builder.SaveWorkflowContext, builder.BuildActivityAsync());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,6 +48,13 @@ namespace Elsa.Services
|
|||
setup?.Invoke(activity);
|
||||
return activity;
|
||||
}
|
||||
|
||||
public T ActivateActivity<T>(Action<T>? setup = null) where T : class, IActivity
|
||||
{
|
||||
var activity = ActivatorUtilities.GetServiceOrCreateInstance<T>(_serviceProvider);
|
||||
setup?.Invoke(activity);
|
||||
return activity;
|
||||
}
|
||||
|
||||
public IActivity ActivateActivity(IActivityBlueprint activityBlueprint)
|
||||
{
|
||||
|
|
@ -58,16 +65,11 @@ namespace Elsa.Services
|
|||
activity.Id = activityBlueprint.Id;
|
||||
activity.Name = activityBlueprint.Name;
|
||||
activity.PersistWorkflow = activityBlueprint.PersistWorkflow;
|
||||
activity.SaveWorkflowContext = activityBlueprint.SaveWorkflowContext;
|
||||
activity.LoadWorkflowContext = activityBlueprint.LoadWorkflowContext;
|
||||
});
|
||||
}
|
||||
|
||||
public T ActivateActivity<T>(Action<T>? setup = null) where T : class, IActivity
|
||||
{
|
||||
var activity = ActivatorUtilities.GetServiceOrCreateInstance<T>(_serviceProvider);
|
||||
setup?.Invoke(activity);
|
||||
return activity;
|
||||
}
|
||||
|
||||
public IActivity ActivateActivity(ActivityDefinition activityDefinition)
|
||||
{
|
||||
var activity = ActivateActivity(activityDefinition.Type);
|
||||
|
|
@ -76,6 +78,8 @@ namespace Elsa.Services
|
|||
activity.Name = activityDefinition.Name;
|
||||
activity.DisplayName = activityDefinition.DisplayName;
|
||||
activity.PersistWorkflow = activityDefinition.PersistWorkflow;
|
||||
activity.LoadWorkflowContext = activityDefinition.LoadWorkflowContext;
|
||||
activity.SaveWorkflowContext = activityDefinition.SaveWorkflowContext;
|
||||
return activity;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,13 +98,14 @@ namespace Elsa.Services
|
|||
|
||||
private static async ValueTask<IActivity> CreateActivityAsync(ActivityDefinition activityDefinition, ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var activity = context.ActivateActivity();
|
||||
var activity = await context.ActivateActivityAsync(cancellationToken);
|
||||
activity.Description = activityDefinition.Description;
|
||||
activity.Id = activityDefinition.ActivityId;
|
||||
activity.Name = activityDefinition.Name;
|
||||
activity.DisplayName = activityDefinition.DisplayName;
|
||||
activity.PersistWorkflow = activityDefinition.PersistWorkflow;
|
||||
await context.SetActivityPropertiesAsync(activity, cancellationToken);
|
||||
activity.LoadWorkflowContext = activityDefinition.LoadWorkflowContext;
|
||||
activity.SaveWorkflowContext = activityDefinition.SaveWorkflowContext;
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ namespace Elsa.Services
|
|||
object? input = default,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowContext = await LoadWorkflowContextAsync(workflowBlueprint, workflowInstance, WorkflowContextFidelity.Burst, cancellationToken);
|
||||
var workflowContext = await LoadWorkflowContextAsync(workflowBlueprint, workflowInstance, WorkflowContextFidelity.Burst, false, cancellationToken);
|
||||
var workflowExecutionContext = CreateWorkflowExecutionContext(workflowBlueprint, workflowInstance, input, workflowContext, _serviceProvider);
|
||||
var activity = activityId != null ? workflowBlueprint.GetActivity(activityId) : default;
|
||||
|
||||
|
|
@ -147,7 +147,7 @@ namespace Elsa.Services
|
|||
break;
|
||||
}
|
||||
|
||||
workflowInstance.ContextId = await SaveWorkflowContextAsync(workflowExecutionContext, WorkflowContextFidelity.Burst, cancellationToken);
|
||||
workflowInstance.ContextId = await SaveWorkflowContextAsync(workflowExecutionContext, WorkflowContextFidelity.Burst, false, cancellationToken);
|
||||
await _mediator.Publish(new WorkflowExecuted(workflowExecutionContext), cancellationToken);
|
||||
|
||||
var statusEvent = workflowExecutionContext.Status switch
|
||||
|
|
@ -165,20 +165,20 @@ namespace Elsa.Services
|
|||
return workflowExecutionContext.WorkflowInstance;
|
||||
}
|
||||
|
||||
private async ValueTask<object?> LoadWorkflowContextAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, WorkflowContextFidelity fidelity, CancellationToken cancellationToken)
|
||||
private async ValueTask<object?> LoadWorkflowContextAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, WorkflowContextFidelity fidelity, bool always, CancellationToken cancellationToken)
|
||||
{
|
||||
if (workflowInstance.ContextId == null || workflowBlueprint.ContextOptions == null || workflowBlueprint.ContextOptions.ContextFidelity != fidelity)
|
||||
if (!always && (workflowInstance.ContextId == null || workflowBlueprint.ContextOptions == null || workflowBlueprint.ContextOptions.ContextFidelity != fidelity))
|
||||
return null;
|
||||
|
||||
var context = new LoadWorkflowContext(workflowBlueprint, workflowInstance);
|
||||
return await _workflowContextManager.LoadContext(context, cancellationToken);
|
||||
}
|
||||
|
||||
private async ValueTask<string?> SaveWorkflowContextAsync(WorkflowExecutionContext workflowExecutionContext, WorkflowContextFidelity fidelity, CancellationToken cancellationToken)
|
||||
private async ValueTask<string?> SaveWorkflowContextAsync(WorkflowExecutionContext workflowExecutionContext, WorkflowContextFidelity fidelity, bool always, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowContext = workflowExecutionContext.WorkflowContext;
|
||||
|
||||
if (workflowContext == null || workflowExecutionContext.WorkflowBlueprint.ContextOptions?.ContextFidelity != fidelity)
|
||||
if (!always && (workflowContext == null || workflowExecutionContext.WorkflowBlueprint.ContextOptions?.ContextFidelity != fidelity))
|
||||
return workflowExecutionContext.WorkflowInstance.ContextId;
|
||||
|
||||
var context = new SaveWorkflowContext(workflowExecutionContext);
|
||||
|
|
@ -233,19 +233,20 @@ namespace Elsa.Services
|
|||
|
||||
while (workflowExecutionContext.HasScheduledActivities)
|
||||
{
|
||||
if(workflowBlueprint.ContextOptions?.ContextFidelity == WorkflowContextFidelity.Activity)
|
||||
workflowExecutionContext.WorkflowContext = await LoadWorkflowContextAsync(workflowBlueprint, workflowInstance, WorkflowContextFidelity.Activity, cancellationToken);
|
||||
|
||||
var scheduledActivity = workflowExecutionContext.PopScheduledActivity();
|
||||
var currentActivityId = scheduledActivity.ActivityId;
|
||||
var activityBlueprint = workflowBlueprint.GetActivity(currentActivityId)!;
|
||||
|
||||
if(workflowBlueprint.ContextOptions?.ContextFidelity == WorkflowContextFidelity.Activity || activityBlueprint.LoadWorkflowContext)
|
||||
workflowExecutionContext.WorkflowContext = await LoadWorkflowContextAsync(workflowBlueprint, workflowInstance, WorkflowContextFidelity.Activity, activityBlueprint.LoadWorkflowContext, cancellationToken);
|
||||
|
||||
var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, serviceProvider, activityBlueprint, scheduledActivity.Input);
|
||||
var activity = await activityBlueprint.CreateActivityAsync(activityExecutionContext, cancellationToken);
|
||||
var result = await activityOperation(activityExecutionContext, activity, cancellationToken);
|
||||
await _mediator.Publish(new ActivityExecuting(activityExecutionContext), cancellationToken);
|
||||
await result.ExecuteAsync(activityExecutionContext, cancellationToken);
|
||||
await _mediator.Publish(new ActivityExecuted(activityExecutionContext), cancellationToken);
|
||||
workflowExecutionContext.WorkflowInstance.ContextId = await SaveWorkflowContextAsync(workflowExecutionContext, WorkflowContextFidelity.Activity, cancellationToken);
|
||||
workflowExecutionContext.WorkflowInstance.ContextId = await SaveWorkflowContextAsync(workflowExecutionContext, WorkflowContextFidelity.Activity, activityBlueprint.SaveWorkflowContext, cancellationToken);
|
||||
|
||||
activityOperation = Execute;
|
||||
workflowExecutionContext.CompletePass();
|
||||
|
|
|
|||
Loading…
Reference in a new issue