diff --git a/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj b/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj
index 542f35b82..b68695e29 100644
--- a/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj
+++ b/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj
@@ -6,6 +6,7 @@
enable
Linux
false
+ latest
diff --git a/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs b/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs
new file mode 100644
index 000000000..9375aa404
--- /dev/null
+++ b/src/bundles/Elsa.Server.Web/Endpoints/DynamicWorkflows/Post/Endpoint.cs
@@ -0,0 +1,38 @@
+using System.Net;
+using Elsa.Abstractions;
+using Elsa.Http;
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Models;
+using Elsa.Workflows.Runtime.Contracts;
+using Elsa.Workflows.Runtime.Options;
+
+namespace Elsa.Server.Web.Endpoints.DynamicWorkflows.Post;
+
+public class Post(IWorkflowRegistry workflowRegistry, IWorkflowRuntime workflowRuntime) : ElsaEndpointWithoutRequest
+{
+ public override void Configure()
+ {
+ Post("/dynamic-workflows");
+ AllowAnonymous();
+ }
+
+ public override async Task HandleAsync(CancellationToken ct)
+ {
+ var workflow = new Workflow
+ {
+ Identity = new WorkflowIdentity("DynamicWorkflow1", 1, "DynamicWorkflow1:v1"),
+ Root = new Sequence
+ {
+ Activities =
+ {
+ new WriteLine("Step 1"),
+ new WriteLine("Step 2"),
+ new WriteLine("Step 3")
+ }
+ }
+ };
+
+ await workflowRegistry.RegisterAsync(workflow, ct);
+ await workflowRuntime.StartWorkflowAsync("DynamicWorkflow1", new StartWorkflowRuntimeOptions());
+ }
+}
\ No newline at end of file
diff --git a/src/bundles/Elsa.Server.Web/Program.cs b/src/bundles/Elsa.Server.Web/Program.cs
index 229c9380f..2bb5cc58c 100644
--- a/src/bundles/Elsa.Server.Web/Program.cs
+++ b/src/bundles/Elsa.Server.Web/Program.cs
@@ -228,6 +228,7 @@ services
elsa.InstallDropIns(options => options.DropInRootDirectory = Path.Combine(Directory.GetCurrentDirectory(), "App_Data", "DropIns"));
elsa.AddSwagger();
+ elsa.AddFastEndpointsAssembly();
});
services.AddHealthChecks();
diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs
index 3e1adc33f..fb428d9b1 100644
--- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs
+++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs
@@ -167,6 +167,7 @@ public class WorkflowManagementFeature : FeatureBase
.AddScoped()
.AddScoped()
.AddScoped()
+ .AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
diff --git a/src/modules/Elsa.Workflows.Management/Materializers/TypedWorkflowMaterializer.cs b/src/modules/Elsa.Workflows.Management/Materializers/TypedWorkflowMaterializer.cs
new file mode 100644
index 000000000..e27be88f9
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Management/Materializers/TypedWorkflowMaterializer.cs
@@ -0,0 +1,32 @@
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Contracts;
+using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Management.Entities;
+using Elsa.Workflows.Management.Mappers;
+using Elsa.Workflows.Models;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Elsa.Workflows.Management.Materializers;
+
+///
+/// A workflow materializer that deserializes workflows created in C# code.
+///
+public class TypedWorkflowMaterializer(WorkflowDefinitionMapper workflowDefinitionMapper) : IWorkflowMaterializer
+{
+ ///
+ /// The name of the materializer.
+ ///
+ public const string MaterializerName = "Typed";
+
+ ///
+ public string Name => MaterializerName;
+
+ ///
+ public ValueTask MaterializeAsync(WorkflowDefinition definition, CancellationToken cancellationToken)
+ {
+ var workflow = ToWorkflow(definition);
+ return new ValueTask(workflow);
+ }
+
+ private Workflow ToWorkflow(WorkflowDefinition definition) => workflowDefinitionMapper.Map(definition);
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs
index e6e5f960e..88e80ce1e 100644
--- a/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs
+++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowDefinitionService.cs
@@ -35,12 +35,12 @@ public class WorkflowDefinitionService : IWorkflowDefinitionService
public async Task MaterializeWorkflowAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default)
{
var materializers = _materializers();
- var provider = materializers.FirstOrDefault(x => x.Name == definition.MaterializerName);
+ var materializer = materializers.FirstOrDefault(x => x.Name == definition.MaterializerName);
- if (provider == null)
+ if (materializer == null)
throw new Exception("Provider not found");
- var workflow = await provider.MaterializeAsync(definition, cancellationToken);
+ var workflow = await materializer.MaterializeAsync(definition, cancellationToken);
var graph = (await _activityVisitor.VisitAsync(workflow, cancellationToken)).Flatten().ToList();
// Assign identities.
diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowDefinitionStorePopulator.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowDefinitionStorePopulator.cs
index b144e9314..62f86a3c7 100644
--- a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowDefinitionStorePopulator.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowDefinitionStorePopulator.cs
@@ -1,4 +1,5 @@
using Elsa.Workflows.Management.Contracts;
+using Elsa.Workflows.Runtime.Models;
namespace Elsa.Workflows.Runtime.Contracts;
@@ -12,4 +13,11 @@ public interface IWorkflowDefinitionStorePopulator
///
/// The cancellation token.
Task PopulateStoreAsync(CancellationToken cancellationToken = default);
+
+ ///
+ /// Adds a workflow definition to the store.
+ ///
+ /// A materialized workflow.
+ /// An optional cancellation token.
+ Task AddAsync(MaterializedWorkflow materializedWorkflow, CancellationToken cancellationToken = default);
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowRegister.cs b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowRegister.cs
new file mode 100644
index 000000000..33519c18b
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Runtime/Contracts/IWorkflowRegister.cs
@@ -0,0 +1,16 @@
+using Elsa.Workflows.Activities;
+
+namespace Elsa.Workflows.Runtime.Contracts;
+
+///
+/// Registers workflows.
+///
+public interface IWorkflowRegistry
+{
+ ///
+ /// Registers a workflow.
+ ///
+ /// The workflow to register.
+ /// An optional cancellation token.
+ Task RegisterAsync(Workflow workflow, CancellationToken cancellationToken = default);
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs
index 75b9f7e6a..41c867f95 100644
--- a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs
@@ -165,6 +165,7 @@ public class WorkflowRuntimeFeature : FeatureBase
.AddScoped()
.AddScoped()
.AddScoped()
+ .AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs
index b7d978729..7cb2ff9ac 100644
--- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs
+++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowDefinitionStorePopulator.cs
@@ -53,12 +53,18 @@ public class DefaultWorkflowDefinitionStorePopulator : IWorkflowDefinitionStoreP
foreach (var result in results)
{
- await AssignIdentities(result.Workflow, cancellationToken);
- await AddOrUpdateAsync(result, cancellationToken);
- await IndexTriggersAsync(result, cancellationToken);
+ await AddAsync(result, cancellationToken);
}
}
}
+
+ ///
+ public async Task AddAsync(MaterializedWorkflow materializedWorkflow, CancellationToken cancellationToken = default)
+ {
+ await AssignIdentities(materializedWorkflow.Workflow, cancellationToken);
+ await AddOrUpdateAsync(materializedWorkflow, cancellationToken);
+ await IndexTriggersAsync(materializedWorkflow, cancellationToken);
+ }
private async Task AssignIdentities(Workflow workflow, CancellationToken cancellationToken)
{
diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowRegistry.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowRegistry.cs
new file mode 100644
index 000000000..775018fe6
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowRegistry.cs
@@ -0,0 +1,17 @@
+using Elsa.Workflows.Activities;
+using Elsa.Workflows.Management.Materializers;
+using Elsa.Workflows.Runtime.Contracts;
+using Elsa.Workflows.Runtime.Models;
+
+namespace Elsa.Workflows.Runtime.Services;
+
+///
+public class DefaultWorkflowRegistry(IWorkflowDefinitionStorePopulator populator) : IWorkflowRegistry
+{
+ ///
+ public async Task RegisterAsync(Workflow workflow, CancellationToken cancellationToken = default)
+ {
+ var materializedWorkflow = new MaterializedWorkflow(workflow, nameof(DefaultWorkflowRegistry), TypedWorkflowMaterializer.MaterializerName);
+ await populator.AddAsync(materializedWorkflow, cancellationToken);
+ }
+}
\ No newline at end of file