Improved support for named workflow instances

Introduced the ability to set and persist custom names for workflow instances. Updated relevant classes, services, and tests to ensure proper handling of the `Name` property.
This commit is contained in:
Sipke Schoorstra 2025-03-02 11:17:18 +01:00
parent b2d3fd9fda
commit 046b88f5d2
No known key found for this signature in database
GPG key ID: 5C10502B28A4268F
19 changed files with 141 additions and 57 deletions

View file

@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using Elsa.Extensions;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Models;
using JetBrains.Annotations;
@ -12,18 +13,13 @@ namespace Elsa.Workflows.Activities;
[PublicAPI]
public class SetName : CodeActivity
{
/// <summary>
/// The property key name used to store the workflow instance name.
/// </summary>
public const string WorkflowInstanceNameKey = "WorkflowInstanceName";
/// <inheritdoc />
public SetName([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line)
public SetName([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line)
{
}
/// <inheritdoc />
public SetName(Input<string> value, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line)
public SetName(Input<string> value, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line)
{
Value = value;
}
@ -36,7 +32,7 @@ public class SetName : CodeActivity
/// <inheritdoc />
protected override void Execute(ActivityExecutionContext context)
{
var value = context.Get(Value);
context.WorkflowExecutionContext.SetProperty(WorkflowInstanceNameKey, value!);
var value = Value.GetOrDefault(context);
context.WorkflowExecutionContext.Name = value;
}
}

View file

@ -71,8 +71,8 @@ public partial class WorkflowExecutionContext : IExecutionContext
_activityExecutionContexts = new List<ActivityExecutionContext>();
Scheduler = serviceProvider.GetRequiredService<IActivitySchedulerFactory>().CreateScheduler();
IdentityGenerator = serviceProvider.GetRequiredService<IIdentityGenerator>();
Input = input != null ? new Dictionary<string, object>(input, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
Properties = properties != null ? new Dictionary<string, object>(properties, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
Input = input != null ? new(input, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
Properties = properties != null ? new(properties, StringComparer.OrdinalIgnoreCase) : new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
ExecuteDelegate = executeDelegate;
TriggerActivityId = triggerActivityId;
CreatedAt = createdAt;
@ -193,7 +193,7 @@ public partial class WorkflowExecutionContext : IExecutionContext
MemoryRegister = workflowGraph.Workflow.CreateRegister()
};
workflowExecutionContext.ExpressionExecutionContext = new ExpressionExecutionContext(serviceProvider, workflowExecutionContext.MemoryRegister, cancellationToken: cancellationToken);
workflowExecutionContext.ExpressionExecutionContext = new(serviceProvider, workflowExecutionContext.MemoryRegister, cancellationToken: cancellationToken);
await workflowExecutionContext.SetWorkflowGraphAsync(workflowGraph);
return workflowExecutionContext;
@ -253,6 +253,9 @@ public partial class WorkflowExecutionContext : IExecutionContext
/// An application-specific identifier associated with the execution context.
public string? CorrelationId { get; set; }
/// Gets or sets the name of the workflow instance.
public string? Name { get; set; }
/// The ID of the workflow instance that triggered this instance.
public string? ParentWorkflowInstanceId { get; set; }

View file

@ -128,7 +128,7 @@ public class JsonWorkflowStateSerializer : ConfigurableSerializer, IWorkflowStat
public override JsonSerializerOptions GetOptions()
{
var options = base.GetOptions();
return new JsonSerializerOptions(options)
return new(options)
{
ReferenceHandler = new CrossScopedReferenceHandler()
};

View file

@ -25,7 +25,7 @@ public class WorkflowRunner(
: IWorkflowRunner
{
/// <inheritdoc />
public async Task<RunWorkflowResult> RunAsync(IActivity activity, RunWorkflowOptions? options = default, CancellationToken cancellationToken = default)
public async Task<RunWorkflowResult> RunAsync(IActivity activity, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default)
{
var workflow = Workflow.FromActivity(activity);
var workflowGraph = await workflowGraphBuilder.BuildAsync(workflow, cancellationToken);
@ -33,7 +33,7 @@ public class WorkflowRunner(
}
/// <inheritdoc />
public async Task<RunWorkflowResult> RunAsync(IWorkflow workflow, RunWorkflowOptions? options = default, CancellationToken cancellationToken = default)
public async Task<RunWorkflowResult> RunAsync(IWorkflow workflow, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default)
{
var builder = workflowBuilderFactory.CreateBuilder();
var workflowDefinition = await builder.BuildWorkflowAsync(workflow, cancellationToken);
@ -41,14 +41,14 @@ public class WorkflowRunner(
}
/// <inheritdoc />
public async Task<RunWorkflowResult<TResult>> RunAsync<TResult>(WorkflowBase<TResult> workflow, RunWorkflowOptions? options = default, CancellationToken cancellationToken = default)
public async Task<RunWorkflowResult<TResult>> RunAsync<TResult>(WorkflowBase<TResult> workflow, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default)
{
var result = await RunAsync((IWorkflow)workflow, options, cancellationToken);
return new(result.WorkflowState, result.Workflow, (TResult)result.Result!);
}
/// <inheritdoc />
public async Task<RunWorkflowResult> RunAsync<T>(RunWorkflowOptions? options = default, CancellationToken cancellationToken = default) where T : IWorkflow, new()
public async Task<RunWorkflowResult> RunAsync<T>(RunWorkflowOptions? options = null, CancellationToken cancellationToken = default) where T : IWorkflow, new()
{
var builder = workflowBuilderFactory.CreateBuilder();
var workflowDefinition = await builder.BuildWorkflowAsync<T>(cancellationToken);
@ -56,7 +56,7 @@ public class WorkflowRunner(
}
/// <inheritdoc />
public async Task<TResult> RunAsync<T, TResult>(RunWorkflowOptions? options = default, CancellationToken cancellationToken = default) where T : WorkflowBase<TResult>, new()
public async Task<TResult> RunAsync<T, TResult>(RunWorkflowOptions? options = null, CancellationToken cancellationToken = default) where T : WorkflowBase<TResult>, new()
{
var builder = workflowBuilderFactory.CreateBuilder();
var workflow = await builder.BuildWorkflowAsync<T>(cancellationToken);
@ -65,7 +65,7 @@ public class WorkflowRunner(
}
/// <inheritdoc />
public async Task<RunWorkflowResult> RunAsync(WorkflowGraph workflowGraph, RunWorkflowOptions? options = default, CancellationToken cancellationToken = default)
public async Task<RunWorkflowResult> RunAsync(WorkflowGraph workflowGraph, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default)
{
// Set up a workflow execution context.
var instanceId = options?.WorkflowInstanceId ?? identityGenerator.GenerateId();
@ -82,7 +82,7 @@ public class WorkflowRunner(
parentWorkflowInstanceId,
input,
properties,
default,
null,
triggerActivityId,
cancellationToken);
@ -93,21 +93,21 @@ public class WorkflowRunner(
}
/// <inheritdoc />
public async Task<RunWorkflowResult> RunAsync(Workflow workflow, RunWorkflowOptions? options = default, CancellationToken cancellationToken = default)
public async Task<RunWorkflowResult> RunAsync(Workflow workflow, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default)
{
var workflowGraph = await workflowGraphBuilder.BuildAsync(workflow, cancellationToken);
return await RunAsync(workflowGraph, options, cancellationToken);
}
/// <inheritdoc />
public async Task<RunWorkflowResult> RunAsync(Workflow workflow, WorkflowState workflowState, RunWorkflowOptions? options = default, CancellationToken cancellationToken = default)
public async Task<RunWorkflowResult> RunAsync(Workflow workflow, WorkflowState workflowState, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default)
{
var workflowGraph = await workflowGraphBuilder.BuildAsync(workflow, cancellationToken);
return await RunAsync(workflowGraph, workflowState, options, cancellationToken);
}
/// <inheritdoc />
public async Task<RunWorkflowResult> RunAsync(WorkflowGraph workflowGraph, WorkflowState workflowState, RunWorkflowOptions? options = default, CancellationToken cancellationToken = default)
public async Task<RunWorkflowResult> RunAsync(WorkflowGraph workflowGraph, WorkflowState workflowState, RunWorkflowOptions? options = null, CancellationToken cancellationToken = default)
{
// Create a workflow execution context.
var input = options?.Input;
@ -123,7 +123,7 @@ public class WorkflowRunner(
parentWorkflowInstanceId,
input,
properties,
default,
null,
triggerActivityId,
cancellationToken);

View file

@ -18,6 +18,7 @@ public class WorkflowStateExtractor : IWorkflowStateExtractor
DefinitionVersionId = workflowExecutionContext.Workflow.Identity.Id,
DefinitionVersion = workflowExecutionContext.Workflow.Identity.Version,
CorrelationId = workflowExecutionContext.CorrelationId,
Name = workflowExecutionContext.Name,
ParentWorkflowInstanceId = workflowExecutionContext.ParentWorkflowInstanceId,
Status = workflowExecutionContext.Status,
SubStatus = workflowExecutionContext.SubStatus,
@ -45,6 +46,7 @@ public class WorkflowStateExtractor : IWorkflowStateExtractor
{
workflowExecutionContext.Id = state.Id;
workflowExecutionContext.CorrelationId = state.CorrelationId;
workflowExecutionContext.Name = state.Name;
workflowExecutionContext.ParentWorkflowInstanceId = state.ParentWorkflowInstanceId;
workflowExecutionContext.SubStatus = state.SubStatus;
workflowExecutionContext.Bookmarks = state.Bookmarks;

View file

@ -11,17 +11,17 @@ public class WorkflowState
/// <summary>
/// Gets or sets the ID.
/// </summary>
public string Id { get; set; } = default!;
public string Id { get; set; } = null!;
/// <summary>
/// The workflow definition ID.
/// </summary>
public string DefinitionId { get; set; } = default!;
public string DefinitionId { get; set; } = null!;
/// <summary>
/// The workflow definition version ID.
/// </summary>
public string DefinitionVersionId { get; set; } = default!;
public string DefinitionVersionId { get; set; } = null!;
/// <summary>
/// The workflow definition version.
@ -37,6 +37,11 @@ public class WorkflowState
/// The correlation ID of the workflow, if any.
/// </summary>
public string? CorrelationId { get; set; }
/// <summary>
/// Gets or sets the name of the workflow instance.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The status of the workflow.

View file

@ -1,5 +1,3 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.State;
@ -16,14 +14,14 @@ public class WorkflowStateMapper
public WorkflowInstance? Map(WorkflowState? source)
{
if (source == null)
return default;
return null;
var workflowInstance = new WorkflowInstance();
Apply(source, workflowInstance);
return workflowInstance;
}
/// <summary>
/// Maps a workflow state to a workflow instance.
/// </summary>
@ -38,14 +36,12 @@ public class WorkflowStateMapper
target.Status = source.Status;
target.SubStatus = source.SubStatus;
target.CorrelationId = source.CorrelationId;
target.Name = source.Name;
target.IncidentCount = source.Incidents.Count;
target.IsSystem = source.IsSystem;
target.UpdatedAt = source.UpdatedAt;
target.FinishedAt = source.FinishedAt;
target.WorkflowState = source;
if (source.Properties.TryGetValue<string>(SetName.WorkflowInstanceNameKey, out var name))
target.Name = name;
}
/// <summary>
@ -54,7 +50,7 @@ public class WorkflowStateMapper
public WorkflowState? Map(WorkflowInstance? source)
{
if (source == null)
return default;
return null;
var workflowState = source.WorkflowState;
workflowState.Id = source.Id;
@ -66,13 +62,11 @@ public class WorkflowStateMapper
workflowState.Status = source.Status;
workflowState.SubStatus = source.SubStatus;
workflowState.CorrelationId = source.CorrelationId;
workflowState.Name = source.Name;
workflowState.UpdatedAt = source.UpdatedAt;
workflowState.FinishedAt = source.FinishedAt;
workflowState.IsSystem = source.IsSystem;
if (source.Name != null)
workflowState.Properties[SetName.WorkflowInstanceNameKey] = source.Name;
return workflowState;
}
}

View file

@ -13,6 +13,11 @@ public class WorkflowInstanceOptions
/// </summary>
public string? CorrelationId { get; set; }
/// <summary>
/// The name of the workflow instance.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The input to the workflow instance, if any.
/// </summary>

View file

@ -14,13 +14,14 @@ public class WorkflowInstanceFactory(IIdentityGenerator identityGenerator, ISyst
public WorkflowState CreateWorkflowState(Workflow workflow, WorkflowInstanceOptions? options = null)
{
var now = systemClock.UtcNow;
return new WorkflowState
return new()
{
Id = string.IsNullOrEmpty(options?.WorkflowInstanceId) ? identityGenerator.GenerateId() : options.WorkflowInstanceId,
DefinitionId = workflow.Identity.DefinitionId,
DefinitionVersionId = workflow.Identity.Id,
DefinitionVersion = workflow.Identity.Version,
CorrelationId = options?.CorrelationId,
Name = options?.Name,
Input = options?.Input ?? new Dictionary<string, object>(),
Properties = options?.Properties ?? new Dictionary<string, object>(),
Status = WorkflowStatus.Running,
@ -36,7 +37,7 @@ public class WorkflowInstanceFactory(IIdentityGenerator identityGenerator, ISyst
public WorkflowInstance CreateWorkflowInstance(Workflow workflow, WorkflowInstanceOptions? options = null)
{
var workflowState = CreateWorkflowState(workflow, options);
return new WorkflowInstance
return new()
{
Id = workflowState.Id,
ParentWorkflowInstanceId = workflowState.ParentWorkflowInstanceId,
@ -45,6 +46,7 @@ public class WorkflowInstanceFactory(IIdentityGenerator identityGenerator, ISyst
DefinitionVersionId = workflowState.DefinitionVersionId,
Version = workflowState.DefinitionVersion,
CorrelationId = workflowState.CorrelationId,
Name = workflowState.Name,
Status = workflowState.Status,
SubStatus = workflowState.SubStatus,
IncidentCount = workflowState.Incidents.Count,

View file

@ -23,6 +23,7 @@ public class CreateAndRunWorkflowInstanceRequestMapper(WorkflowDefinitionHandleM
WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle),
WorkflowInstanceId = workflowInstanceId.EmptyIfNull(),
CorrelationId = source.CorrelationId.EmptyIfNull(),
Name = source.Name.EmptyIfNull(),
ParentId = source.ParentId.EmptyIfNull(),
Input = source.Input?.SerializeInput() ?? new ProtoInput(),
Properties = source.Properties?.SerializeProperties() ?? new ProtoProperties(),
@ -41,8 +42,9 @@ public class CreateAndRunWorkflowInstanceRequestMapper(WorkflowDefinitionHandleM
return new()
{
WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle),
CorrelationId = source.CorrelationId,
ParentId = source.ParentId,
CorrelationId = source.CorrelationId.NullIfEmpty(),
Name = source.Name.NullIfEmpty(),
ParentId = source.ParentId.NullIfEmpty(),
Input = source.Input?.DeserializeInput(),
Properties = source.Properties?.DeserializeProperties(),
ActivityHandle = activityHandleMapper.Map(source.ActivityHandle),

View file

@ -24,6 +24,7 @@ public class CreateWorkflowInstanceRequestMapper(WorkflowDefinitionHandleMapper
WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle),
WorkflowInstanceId = workflowInstanceId,
CorrelationId = source.CorrelationId.EmptyIfNull(),
Name = source.Name.EmptyIfNull(),
ParentId = source.ParentId.EmptyIfNull(),
Input = source.Input?.SerializeInput() ?? new ProtoInput(),
Properties = source.Properties?.SerializeProperties() ?? new ProtoProperties()
@ -40,8 +41,9 @@ public class CreateWorkflowInstanceRequestMapper(WorkflowDefinitionHandleMapper
return new()
{
WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle),
CorrelationId = source.CorrelationId,
ParentId = source.ParentId,
CorrelationId = source.CorrelationId.NullIfEmpty(),
Name = source.Name.NullIfEmpty(),
ParentId = source.ParentId.NullIfEmpty(),
Input = source.Input?.DeserializeInput(),
Properties = source.Properties?.DeserializeProperties()
};

View file

@ -45,9 +45,10 @@ message CreateWorkflowInstanceRequest{
WorkflowDefinitionHandle WorkflowDefinitionHandle = 1;
string WorkflowInstanceId = 2;
optional string CorrelationId = 3;
optional string ParentId = 4;
optional Input input = 5;
optional Properties properties = 6;
optional string Name = 4;
optional string ParentId = 5;
optional Input input = 6;
optional Properties properties = 7;
}
message CreateWorkflowInstanceResponse{
@ -71,11 +72,12 @@ message CreateAndRunWorkflowInstanceRequest{
WorkflowDefinitionHandle WorkflowDefinitionHandle = 1;
string WorkflowInstanceId = 2;
optional string CorrelationId = 3;
optional string ParentId = 4;
optional Input input = 5;
optional Properties properties = 6;
optional ActivityHandle ActivityHandle = 7;
optional string TriggerActivityId = 8;
optional string Name = 4;
optional string ParentId = 5;
optional Input input = 6;
optional Properties properties = 7;
optional ActivityHandle ActivityHandle = 8;
optional string TriggerActivityId = 9;
}
message ExportWorkflowStateResponse {

View file

@ -12,12 +12,17 @@ public class CreateAndRunWorkflowInstanceRequest
/// <summary>
/// The ID of the workflow definition version to create an instance of.
/// </summary>
public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; set; } = default!;
public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; set; } = null!;
/// <summary>
/// The correlation ID of the workflow, if any.
/// </summary>
public string? CorrelationId { get; set; }
/// <summary>
/// The name of the workflow instance to be created.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The input to the workflow instance, if any.

View file

@ -12,13 +12,18 @@ public class CreateWorkflowInstanceRequest
/// <summary>
/// The ID of the workflow definition version to create an instance of.
/// </summary>
public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; set; } = default!;
public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; set; } = null!;
/// <summary>
/// The correlation ID of the workflow, if any.
/// </summary>
public string? CorrelationId { get; set; }
/// <summary>
/// The name of the workflow instance to be created.
/// </summary>
public string? Name { get; set; }
/// <summary>
/// The input to the workflow instance, if any.
/// </summary>

View file

@ -35,6 +35,7 @@ public class LocalWorkflowClient(
{
WorkflowInstanceId = WorkflowInstanceId,
CorrelationId = request.CorrelationId,
Name = request.Name,
ParentWorkflowInstanceId = request.ParentId,
Input = request.Input,
Properties = request.Properties
@ -58,6 +59,7 @@ public class LocalWorkflowClient(
{
Properties = request.Properties,
CorrelationId = request.CorrelationId,
Name = request.Name,
Input = request.Input,
WorkflowDefinitionHandle = request.WorkflowDefinitionHandle,
ParentId = request.ParentId
@ -148,6 +150,7 @@ public class LocalWorkflowClient(
{
WorkflowInstanceId = WorkflowInstanceId,
CorrelationId = request.CorrelationId,
Name = request.Name,
ParentWorkflowInstanceId = request.ParentId,
Input = request.Input,
Properties = request.Properties

View file

@ -15,13 +15,13 @@ public class SetNameTests
_serviceProvider = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build();
}
[Fact(DisplayName = "WriteLine prints the expected line to the console.")]
[Fact(DisplayName = "SetName sets the workflow instance name.")]
public async Task Test1()
{
const string expectedName = "Foo";
var setName = new SetName(new Input<string>(expectedName));
var result = await _serviceProvider.RunActivityAsync(setName);
var actualName = result.WorkflowState.Properties[SetName.WorkflowInstanceNameKey];
var actualName = result.WorkflowState.Name;
Assert.Equal(expectedName, actualName);
}
}

View file

@ -59,7 +59,7 @@ public class DefaultRuntimeTests
const string workflowDefinitionId = nameof(SimpleSuspendedWorkflow);
var workflowClient = await _workflowRuntime.CreateClientAsync();
await workflowClient.CreateInstanceAsync(new CreateWorkflowInstanceRequest
await workflowClient.CreateInstanceAsync(new()
{
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(workflowDefinitionId, VersionOptions.Published)
});

View file

@ -0,0 +1,46 @@
using Elsa.Common.Models;
using Elsa.Testing.Shared;
using Elsa.Workflows.IntegrationTests.Scenarios.WorkflowInstanceName.Workflows;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Messages;
using Microsoft.Extensions.DependencyInjection;
using Xunit.Abstractions;
namespace Elsa.Workflows.IntegrationTests.Scenarios.WorkflowInstanceName;
public class WorkflowInstanceNameTests
{
private readonly IServiceProvider _services;
private readonly CapturingTextWriter _capturingTextWriter = new();
private readonly IWorkflowRuntime _workflowRuntime;
public WorkflowInstanceNameTests(ITestOutputHelper testOutputHelper)
{
_services = new TestApplicationBuilder(testOutputHelper)
.WithCapturingTextWriter(_capturingTextWriter)
.AddWorkflow<NamedWorkflow>()
.Build();
_workflowRuntime = _services.GetRequiredService<IWorkflowRuntime>();
}
[Fact(DisplayName = "Setting a workflow instance name keeps the workflow instance name when the workflow is executed")]
public async Task SuspendedCancelTest()
{
await _services.PopulateRegistriesAsync();
const string workflowDefinitionId = nameof(NamedWorkflow);
var desiredName = Guid.NewGuid().ToString();
var workflowClient = await _workflowRuntime.CreateClientAsync();
await workflowClient.CreateInstanceAsync(new()
{
Name = desiredName,
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(workflowDefinitionId, VersionOptions.Published)
});
await workflowClient.RunInstanceAsync(RunWorkflowInstanceRequest.Empty);
var workflowState = await workflowClient.ExportStateAsync();
Assert.Equal([desiredName], _capturingTextWriter.Lines);
Assert.Equal(desiredName, workflowState.Name);
}
}

View file

@ -0,0 +1,12 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
namespace Elsa.Workflows.IntegrationTests.Scenarios.WorkflowInstanceName.Workflows;
public class NamedWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
builder.Root = new WriteLine(x => x.GetWorkflowExecutionContext().Name);
}
}