diff --git a/Samples.sln b/Samples.sln
index 6854d488d..fc4741eeb 100644
--- a/Samples.sln
+++ b/Samples.sln
@@ -162,6 +162,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.WhileLoopWorke
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.CustomAttributesChildWorker", "src\samples\worker\Elsa.Samples.CustomAttributesChildWorker\Elsa.Samples.CustomAttributesChildWorker.csproj", "{AEA00027-F280-43A4-AC60-E2AF3014CF34}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.CustomActivityTypeProviders", "src\samples\aspnet\Elsa.Samples.CustomActivityTypeProviders\Elsa.Samples.CustomActivityTypeProviders.csproj", "{E0090EF3-C4A7-4262-9300-1D395BE25DD0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -393,6 +395,10 @@ Global
{AEA00027-F280-43A4-AC60-E2AF3014CF34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AEA00027-F280-43A4-AC60-E2AF3014CF34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AEA00027-F280-43A4-AC60-E2AF3014CF34}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E0090EF3-C4A7-4262-9300-1D395BE25DD0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E0090EF3-C4A7-4262-9300-1D395BE25DD0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E0090EF3-C4A7-4262-9300-1D395BE25DD0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E0090EF3-C4A7-4262-9300-1D395BE25DD0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -470,6 +476,7 @@ Global
{FD2D1BD0-1229-4DCF-BE70-6BFD396DD489} = {E42743A0-FBDD-4150-9D53-6000496D9B87}
{EA3832C4-8079-4E84-AF8C-12744E4EFD44} = {E42743A0-FBDD-4150-9D53-6000496D9B87}
{AEA00027-F280-43A4-AC60-E2AF3014CF34} = {E42743A0-FBDD-4150-9D53-6000496D9B87}
+ {E0090EF3-C4A7-4262-9300-1D395BE25DD0} = {22E75696-6FE9-436A-9097-EE21C603F818}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}
diff --git a/Samples.sln.DotSettings b/Samples.sln.DotSettings
index 08cde52e6..0c28b1630 100644
--- a/Samples.sln.DotSettings
+++ b/Samples.sln.DotSettings
@@ -9,6 +9,8 @@
True
True
True
+ True
+ True
True
True
True
diff --git a/src/core/Elsa.Abstractions/ActivityProviders/ActivityType.cs b/src/core/Elsa.Abstractions/ActivityProviders/ActivityType.cs
index 3283b06aa..3761df5af 100644
--- a/src/core/Elsa.Abstractions/ActivityProviders/ActivityType.cs
+++ b/src/core/Elsa.Abstractions/ActivityProviders/ActivityType.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Threading.Tasks;
using Elsa.ActivityResults;
using Elsa.Services.Models;
@@ -23,6 +24,11 @@ namespace Elsa.ActivityProviders
///
public string? Description { get; set; }
+ ///
+ /// Anything you want to store with this activity type.
+ ///
+ public IDictionary Annotations { get; set; } = new Dictionary();
+
///
/// Returns a value of whether the specified activity can execute.
///
diff --git a/src/core/Elsa.Abstractions/Extensions/ActivityActivatorExtensions.cs b/src/core/Elsa.Abstractions/Extensions/ActivityActivatorExtensions.cs
new file mode 100644
index 000000000..bbb86e3c1
--- /dev/null
+++ b/src/core/Elsa.Abstractions/Extensions/ActivityActivatorExtensions.cs
@@ -0,0 +1,11 @@
+using System.Threading.Tasks;
+using Elsa.Services;
+using Elsa.Services.Models;
+
+namespace Elsa
+{
+ public static class ActivityActivatorExtensions
+ {
+ public static async Task ActivateActivityAsync(this IActivityActivator activator, ActivityExecutionContext context) where T : IActivity => (T) await activator.ActivateActivityAsync(context, typeof(T));
+ }
+}
\ No newline at end of file
diff --git a/src/core/Elsa.Abstractions/Services/IActivityActivator.cs b/src/core/Elsa.Abstractions/Services/IActivityActivator.cs
index 03c122700..d4d384600 100644
--- a/src/core/Elsa.Abstractions/Services/IActivityActivator.cs
+++ b/src/core/Elsa.Abstractions/Services/IActivityActivator.cs
@@ -1,21 +1,11 @@
-// using System;
-// using System.Collections.Generic;
-// using System.Threading;
-// using System.Threading.Tasks;
-// using Elsa.Models;
-// using Elsa.Services.Models;
-// using Microsoft.Extensions.DependencyInjection;
-//
-// namespace Elsa.Services
-// {
-// public interface IActivityActivator
-// {
-// Task ActivateActivity(IServiceScope serviceScope, string activityTypeName, Action? setup = default, CancellationToken cancellationToken = default);
-// IActivity ActivateActivity(IServiceScope serviceScope, IActivityBlueprint activityBlueprint);
-// T ActivateActivity(IServiceScope serviceScope, Action? configure = default) where T : class, IActivity;
-// IActivity ActivateActivity(IServiceScope serviceScope, Type type);
-// IActivity ActivateActivity(IServiceScope serviceScope, ActivityDefinition activityDefinition);
-// IEnumerable GetActivityTypes();
-// Type? GetActivityType(string activityTypeName);
-// }
-// }
\ No newline at end of file
+using System;
+using System.Threading.Tasks;
+using Elsa.Services.Models;
+
+namespace Elsa.Services
+{
+ public interface IActivityActivator
+ {
+ Task ActivateActivityAsync(ActivityExecutionContext context, Type type);
+ }
+}
\ No newline at end of file
diff --git a/src/core/Elsa.Abstractions/Services/IWorkflowInterruptor.cs b/src/core/Elsa.Abstractions/Services/IWorkflowInterruptor.cs
new file mode 100644
index 000000000..a5db5aa1d
--- /dev/null
+++ b/src/core/Elsa.Abstractions/Services/IWorkflowInterruptor.cs
@@ -0,0 +1,18 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Elsa.Models;
+using Elsa.Services.Models;
+
+namespace Elsa.Services
+{
+ ///
+ /// Causes a suspended workflow's trigger to be interrupted
+ ///
+ public interface IWorkflowInterruptor
+ {
+ Task InterruptActivityAsync(WorkflowInstance workflowInstance, string activityId, object? input = default, CancellationToken cancellationToken = default);
+ Task InterruptActivityAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityId, object? input = default, CancellationToken cancellationToken = default);
+ Task InterruptActivityTypeAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityType, object? input = default, CancellationToken cancellationToken = default);
+ Task InterruptActivityTypeAsync(WorkflowInstance workflowInstance, string activityType, object? input = default, CancellationToken cancellationToken = default);
+ }
+}
\ No newline at end of file
diff --git a/src/core/Elsa.Core/ActivityTypeProviders/TypeBasedActivityProvider.cs b/src/core/Elsa.Core/ActivityTypeProviders/TypeBasedActivityProvider.cs
index 987c33736..8ce39be0f 100644
--- a/src/core/Elsa.Core/ActivityTypeProviders/TypeBasedActivityProvider.cs
+++ b/src/core/Elsa.Core/ActivityTypeProviders/TypeBasedActivityProvider.cs
@@ -15,11 +15,13 @@ namespace Elsa.ActivityTypeProviders
public class TypeBasedActivityProvider : IActivityTypeProvider
{
private readonly IActivityDescriber _activityDescriber;
+ private readonly IActivityActivator _activityActivator;
private readonly Lazy> _lazyActivityTypeLookup;
- public TypeBasedActivityProvider(IServiceProvider serviceProvider, IActivityDescriber activityDescriber)
+ public TypeBasedActivityProvider(IServiceProvider serviceProvider, IActivityDescriber activityDescriber, IActivityActivator activityActivator)
{
_activityDescriber = activityDescriber;
+ _activityActivator = activityActivator;
_lazyActivityTypeLookup = new Lazy>(
() =>
@@ -63,13 +65,6 @@ namespace Elsa.ActivityTypeProviders
private IEnumerable GetActivityTypes() => ActivityTypeLookup.Values.ToList();
- private static async Task ActivateActivity(ActivityExecutionContext context, Type type)
- {
- var activity = (IActivity) ActivatorUtilities.GetServiceOrCreateInstance(context.ServiceScope.ServiceProvider, type);
- activity.Data = context.ActivityInstance.Data;
- activity.Id = context.ActivityInstance.Id;
- await context.WorkflowExecutionContext.WorkflowBlueprint.ActivityPropertyProviders.SetActivityPropertiesAsync(activity, context, context.CancellationToken);
- return activity;
- }
+ private Task ActivateActivity(ActivityExecutionContext context, Type type) => _activityActivator.ActivateActivityAsync(context, type);
}
}
\ No newline at end of file
diff --git a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs
index ee344e4af..d1c4d1cf2 100644
--- a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs
+++ b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs
@@ -14,6 +14,7 @@ using Elsa.Converters;
using Elsa.Data.Extensions;
using Elsa.Expressions;
using Elsa.Extensions;
+using Elsa.HostedServices;
using Elsa.Indexes;
using Elsa.Mapping;
using Elsa.Metadata;
@@ -25,6 +26,7 @@ using Elsa.Triggers;
using Elsa.WorkflowProviders;
using MediatR;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Hosting;
using NodaTime;
using Rebus.ServiceProvider;
@@ -54,27 +56,27 @@ namespace Microsoft.Extensions.DependencyInjection
return services;
}
- public static IServiceCollection AddActivity(this IServiceCollection services)
- where T : class, IActivity
- {
- return services
+ public static IServiceCollection AddActivity(this IServiceCollection services) where T : class, IActivity =>
+ services
.AddTransient()
.AddTransient(sp => sp.GetRequiredService());
- }
public static IServiceCollection AddWorkflow(this IServiceCollection services) where T : class, IWorkflow
{
- return services
- .AddSingleton()
- .AddSingleton(sp => sp.GetRequiredService());
+ services.TryAddSingleton();
+ services.TryAddSingleton(sp => sp.GetRequiredService());
+ return services;
}
- public static IServiceCollection AddWorkflow(this IServiceCollection services, IWorkflow workflow)
- {
- return services
+ public static IServiceCollection AddWorkflow(this IServiceCollection services, IWorkflow workflow) =>
+ services
.AddSingleton(workflow.GetType(), workflow)
.AddTransient(sp => workflow);
- }
+
+ public static IServiceCollection StartWorkflow(this IServiceCollection services) where T : class, IWorkflow =>
+ services
+ .AddWorkflow()
+ .AddHostedService>();
private static IServiceCollection AddMediatR(this ElsaOptions options) => options.Services.AddMediatR(mediatr => mediatr.AsScoped(), typeof(IActivity));
@@ -96,7 +98,9 @@ namespace Microsoft.Extensions.DependencyInjection
.AddScoped()
.AddScoped()
.AddSingleton()
+ .AddSingleton()
.AddScoped()
+ .AddScoped()
.AddSingleton()
.AddSingleton()
.AddSingleton()
@@ -110,7 +114,6 @@ namespace Microsoft.Extensions.DependencyInjection
.AddIndexProvider()
.AddIndexProvider()
.AddStartupRunner()
- //.AddSingleton()
.AddSingleton()
.AddSingleton()
.AddWorkflowProvider()
diff --git a/src/core/Elsa.Core/Extensions/WorkflowDefinitionManagerExtensions.cs b/src/core/Elsa.Core/Extensions/WorkflowDefinitionManagerExtensions.cs
index 54ce96ecd..8b17af40d 100644
--- a/src/core/Elsa.Core/Extensions/WorkflowDefinitionManagerExtensions.cs
+++ b/src/core/Elsa.Core/Extensions/WorkflowDefinitionManagerExtensions.cs
@@ -36,5 +36,13 @@ namespace Elsa.Extensions
manager
.Query(x => x.WorkflowStatus == workflowStatus)
.ListAsync();
+
+ public static Task> ListByBlockingActivityTypeAsync(
+ this IWorkflowInstanceManager manager,
+ string activityType,
+ CancellationToken cancellationToken = default) =>
+ manager
+ .Query(x => x.ActivityType == activityType)
+ .ListAsync();
}
}
\ No newline at end of file
diff --git a/src/core/Elsa.Core/HostedServices/StartWorkflow.cs b/src/core/Elsa.Core/HostedServices/StartWorkflow.cs
new file mode 100644
index 000000000..651b15bda
--- /dev/null
+++ b/src/core/Elsa.Core/HostedServices/StartWorkflow.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Elsa.Builders;
+using Elsa.Services;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace Elsa.HostedServices
+{
+ ///
+ /// A hosted service that starts a workflow of the specified type.
+ ///
+ public class StartWorkflow : IHostedService where T : IWorkflow
+ {
+ private readonly IServiceProvider _serviceProvider;
+ public StartWorkflow(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
+
+ public async Task StartAsync(CancellationToken cancellationToken)
+ {
+ using var scope = _serviceProvider.CreateScope();
+ var workflowRunner = scope.ServiceProvider.GetRequiredService();
+ await workflowRunner.RunWorkflowAsync(cancellationToken: cancellationToken);
+ }
+
+ public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
+ }
+}
\ No newline at end of file
diff --git a/src/core/Elsa.Core/Indexes/WorkflowInstanceIndex.cs b/src/core/Elsa.Core/Indexes/WorkflowInstanceIndex.cs
index 6705bc4d6..0a227fff4 100644
--- a/src/core/Elsa.Core/Indexes/WorkflowInstanceIndex.cs
+++ b/src/core/Elsa.Core/Indexes/WorkflowInstanceIndex.cs
@@ -46,17 +46,23 @@ namespace Elsa.Indexes
context.For()
.Map(
- workflowInstance => workflowInstance.BlockingActivities
- .Select(
- activity => new WorkflowInstanceBlockingActivitiesIndex
- {
- ActivityId = activity.ActivityId,
- ActivityType = activity.ActivityType,
- CorrelationId = workflowInstance.CorrelationId,
- TenantId = workflowInstance.TenantId,
- WorkflowStatus = workflowInstance.Status,
- CreatedAt = workflowInstance.CreatedAt.ToDateTimeOffset()
- }));
+ workflowInstance =>
+ {
+ if (workflowInstance.Status != WorkflowStatus.Suspended || !workflowInstance.BlockingActivities.Any())
+ return default;
+
+ return workflowInstance.BlockingActivities
+ .Select(
+ activity => new WorkflowInstanceBlockingActivitiesIndex
+ {
+ ActivityId = activity.ActivityId,
+ ActivityType = activity.ActivityType,
+ CorrelationId = workflowInstance.CorrelationId,
+ TenantId = workflowInstance.TenantId,
+ WorkflowStatus = workflowInstance.Status,
+ CreatedAt = workflowInstance.CreatedAt.ToDateTimeOffset()
+ });
+ });
}
}
}
\ No newline at end of file
diff --git a/src/core/Elsa.Core/Services/ActivityActivator.cs b/src/core/Elsa.Core/Services/ActivityActivator.cs
index affb924cc..6a4554d3e 100644
--- a/src/core/Elsa.Core/Services/ActivityActivator.cs
+++ b/src/core/Elsa.Core/Services/ActivityActivator.cs
@@ -1,71 +1,19 @@
-// using System;
-// using System.Collections.Generic;
-// using System.Linq;
-// using System.Threading;
-// using System.Threading.Tasks;
-// using Elsa.ActivityProviders;
-// using Elsa.Models;
-// using Elsa.Services.Models;
-// using Microsoft.Extensions.DependencyInjection;
-//
-// namespace Elsa.Services
-// {
-// public class ActivityActivator : IActivityActivator
-// {
-// private readonly IActivityTypeService _activityTypeService;
-//
-// public ActivityActivator(IActivityTypeService activityTypeService)
-// {
-// _activityTypeService = activityTypeService;
-// }
-//
-// public async Task ActivateActivity(IServiceScope serviceScope, string activityTypeName, Action? setup, CancellationToken cancellationToken)
-// {
-// var activityType = await _activityTypeService.GetActivityTypeAsync(activityTypeName, cancellationToken);
-// var activity = activityType.
-//
-// setup?.Invoke(activity);
-// return activity;
-// }
-//
-// public T ActivateActivity(IServiceScope serviceScope, Action? setup = null) where T : class, IActivity
-// {
-// var activity = ActivatorUtilities.GetServiceOrCreateInstance(serviceScope.ServiceProvider);
-// setup?.Invoke(activity);
-// return activity;
-// }
-//
-// public IActivity ActivateActivity(IServiceScope serviceScope, Type type) => (IActivity)ActivatorUtilities.GetServiceOrCreateInstance(serviceScope.ServiceProvider, type);
-//
-// public IActivity ActivateActivity(IServiceScope serviceScope, IActivityBlueprint activityBlueprint)
-// {
-// return ActivateActivity(
-// serviceScope,
-// activityBlueprint.Type,
-// activity =>
-// {
-// activity.Id = activityBlueprint.Id;
-// activity.Name = activityBlueprint.Name;
-// activity.PersistWorkflow = activityBlueprint.PersistWorkflow;
-// activity.SaveWorkflowContext = activityBlueprint.SaveWorkflowContext;
-// activity.LoadWorkflowContext = activityBlueprint.LoadWorkflowContext;
-// });
-// }
-//
-// public IActivity ActivateActivity(IServiceScope serviceScope, ActivityDefinition activityDefinition)
-// {
-// var activity = ActivateActivity(serviceScope, activityDefinition.Type);
-// activity.Description = activityDefinition.Description;
-// activity.Id = activityDefinition.ActivityId;
-// activity.Name = activityDefinition.Name;
-// activity.DisplayName = activityDefinition.DisplayName;
-// activity.PersistWorkflow = activityDefinition.PersistWorkflow;
-// activity.LoadWorkflowContext = activityDefinition.LoadWorkflowContext;
-// activity.SaveWorkflowContext = activityDefinition.SaveWorkflowContext;
-// return activity;
-// }
-//
-// public IEnumerable GetActivityTypes() => ActivityTypeLookup.Values.ToList();
-// public Type? GetActivityType(string activityTypeName) => ActivityTypeLookup.ContainsKey(activityTypeName) ? ActivityTypeLookup[activityTypeName] : default;
-// }
-// }
\ No newline at end of file
+using System;
+using System.Threading.Tasks;
+using Elsa.Services.Models;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Elsa.Services
+{
+ public class ActivityActivator : IActivityActivator
+ {
+ public async Task ActivateActivityAsync(ActivityExecutionContext context, Type type)
+ {
+ var activity = (IActivity) ActivatorUtilities.GetServiceOrCreateInstance(context.ServiceScope.ServiceProvider, type);
+ activity.Data = context.ActivityInstance.Data;
+ activity.Id = context.ActivityInstance.Id;
+ await context.WorkflowExecutionContext.WorkflowBlueprint.ActivityPropertyProviders.SetActivityPropertiesAsync(activity, context, context.CancellationToken);
+ return activity;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/core/Elsa.Core/Services/WorkflowInterruptor.cs b/src/core/Elsa.Core/Services/WorkflowInterruptor.cs
new file mode 100644
index 000000000..a051f390c
--- /dev/null
+++ b/src/core/Elsa.Core/Services/WorkflowInterruptor.cs
@@ -0,0 +1,59 @@
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Elsa.Exceptions;
+using Elsa.Models;
+using Elsa.Services.Models;
+
+namespace Elsa.Services
+{
+ public class WorkflowInterruptor : IWorkflowInterruptor
+ {
+ private readonly IWorkflowRunner _workflowRunner;
+ private readonly IWorkflowRegistry _workflowRegistry;
+
+ public WorkflowInterruptor(IWorkflowRunner workflowRunner, IWorkflowRegistry workflowRegistry)
+ {
+ _workflowRunner = workflowRunner;
+ _workflowRegistry = workflowRegistry;
+ }
+
+ public async Task InterruptActivityAsync(WorkflowInstance workflowInstance, string activityId, object? input, CancellationToken cancellationToken)
+ {
+ var workflowBlueprint = await GetWorkflowBlueprintAsync(workflowInstance, cancellationToken);
+ return await InterruptActivityAsync(workflowBlueprint!, workflowInstance, activityId, input, cancellationToken);
+ }
+
+ public async Task InterruptActivityAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityId, object? input, CancellationToken cancellationToken)
+ {
+ if (workflowInstance.Status != WorkflowStatus.Suspended)
+ throw new WorkflowException("Cannot interrupt workflows that are not in the Suspended state.");
+
+ var blockingActivity = workflowInstance.BlockingActivities.SingleOrDefault(x => x.ActivityId == activityId);
+
+ if (blockingActivity == null)
+ throw new WorkflowException($"No blocking activity with ID {activityId} found.");
+
+ return await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, input, cancellationToken);
+ }
+
+ public async Task InterruptActivityTypeAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityType, object? input, CancellationToken cancellationToken)
+ {
+ var blockingActivities = workflowInstance.BlockingActivities.Where(x => x.ActivityType == activityType).ToList();
+
+ foreach (var blockingActivity in blockingActivities)
+ workflowInstance = await InterruptActivityAsync(workflowBlueprint, workflowInstance, blockingActivity.ActivityId, input, cancellationToken);
+
+ return workflowInstance;
+ }
+
+ public async Task InterruptActivityTypeAsync(WorkflowInstance workflowInstance, string activityType, object? input, CancellationToken cancellationToken)
+ {
+ var workflowBlueprint = await GetWorkflowBlueprintAsync(workflowInstance, cancellationToken);
+ return await InterruptActivityTypeAsync(workflowBlueprint!, workflowInstance, activityType, input, cancellationToken);
+ }
+
+ private async Task GetWorkflowBlueprintAsync(WorkflowInstance workflowInstance, CancellationToken cancellationToken) =>
+ await _workflowRegistry.GetWorkflowAsync(workflowInstance.WorkflowDefinitionId, workflowInstance.TenantId, VersionOptions.SpecificVersion(workflowInstance.Version), cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Activities/Sleep.cs b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Activities/Sleep.cs
new file mode 100644
index 000000000..0b11ddd6c
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Activities/Sleep.cs
@@ -0,0 +1,14 @@
+using Elsa.Activities.Timers;
+using Elsa.Activities.Timers.Services;
+using Elsa.Services;
+using NodaTime;
+
+namespace Elsa.Samples.CustomActivityTypeProviders.Activities
+{
+ public class Sleep : TimerEvent
+ {
+ public Sleep(IWorkflowInstanceManager workflowInstanceManager, IWorkflowScheduler workflowScheduler, IClock clock) : base(workflowInstanceManager, workflowScheduler, clock)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Elsa.Samples.CustomActivityTypeProviders.csproj b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Elsa.Samples.CustomActivityTypeProviders.csproj
new file mode 100644
index 000000000..5e9d9a675
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Elsa.Samples.CustomActivityTypeProviders.csproj
@@ -0,0 +1,12 @@
+
+
+
+ net5.0
+ latest
+
+
+
+
+
+
+
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Endpoints/WakeUp.cs b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Endpoints/WakeUp.cs
new file mode 100644
index 000000000..6b4223d45
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Endpoints/WakeUp.cs
@@ -0,0 +1,37 @@
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Elsa.Extensions;
+using Elsa.Samples.CustomActivityTypeProviders.Activities;
+using Elsa.Services;
+using Microsoft.AspNetCore.Mvc;
+
+namespace Elsa.Samples.CustomActivityTypeProviders.Endpoints
+{
+ [ApiController]
+ [Route("wakeup")]
+ public class WakeUp : Controller
+ {
+ private readonly IWorkflowInterruptor _workflowInterruptor;
+ private readonly IWorkflowInstanceManager _workflowInstanceManager;
+
+ public WakeUp(IWorkflowInterruptor workflowInterruptor, IWorkflowInstanceManager workflowInstanceManager)
+ {
+ _workflowInterruptor = workflowInterruptor;
+ _workflowInstanceManager = workflowInstanceManager;
+ }
+
+ [HttpGet]
+ public async Task Handle(CancellationToken cancellationToken)
+ {
+ // Get all workflows blocked on the "Sleep" activity.
+ var suspendedWorkflows = (await _workflowInstanceManager.ListByBlockingActivityTypeAsync(nameof(Sleep), cancellationToken)).ToList();
+
+ // Interrupt each workflow by triggering the "Sleep" activity.
+ foreach (var workflowInstance in suspendedWorkflows)
+ await _workflowInterruptor.InterruptActivityTypeAsync(workflowInstance, nameof(Sleep), cancellationToken: cancellationToken);
+
+ return Ok($"Interrupted {suspendedWorkflows.Count} workflows.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Program.cs b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Program.cs
new file mode 100644
index 000000000..6a51a0b7b
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Program.cs
@@ -0,0 +1,17 @@
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Hosting;
+
+namespace Elsa.Samples.CustomActivityTypeProviders
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup());
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Properties/launchSettings.json b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Properties/launchSettings.json
new file mode 100644
index 000000000..065f277c1
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Properties/launchSettings.json
@@ -0,0 +1,13 @@
+{
+ "profiles": {
+ "Elsa.Samples.CustomActivityTypeProviders": {
+ "commandName": "Project",
+ "dotnetRunMessages": "true",
+ "launchBrowser": false,
+ "applicationUrl": "https://localhost:6171",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Startup.cs b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Startup.cs
new file mode 100644
index 000000000..05397a808
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Startup.cs
@@ -0,0 +1,33 @@
+using Elsa.Samples.CustomActivityTypeProviders.Activities;
+using Elsa.Samples.CustomActivityTypeProviders.Workflows;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Elsa.Samples.CustomActivityTypeProviders
+{
+ public class Startup
+ {
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllers();
+
+ services
+ .AddElsa()
+ .AddConsoleActivities()
+ .AddHttpActivities()
+ .AddTimerActivities()
+ .AddActivity()
+ .StartWorkflow();
+ }
+
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
+ {
+ app.UseDeveloperExceptionPage();
+ app.UseHttpActivities();
+ app.UseRouting();
+ app.UseEndpoints(endpoints => endpoints.MapControllers());
+ app.UseWelcomePage();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Workflows/InterruptableWorkflow.cs b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Workflows/InterruptableWorkflow.cs
new file mode 100644
index 000000000..dcf20342b
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/Workflows/InterruptableWorkflow.cs
@@ -0,0 +1,19 @@
+using Elsa.Activities.Console;
+using Elsa.Builders;
+using Elsa.Samples.CustomActivityTypeProviders.Activities;
+using NodaTime;
+
+namespace Elsa.Samples.CustomActivityTypeProviders.Workflows
+{
+ public class InterruptableWorkflow : IWorkflow
+ {
+ public void Build(IWorkflowBuilder workflow)
+ {
+ workflow
+ .WriteLine("This workflow will sleep for 5 minutes before it continues.")
+ .WriteLine("Can't wait that long? Send me a message at https://localhost:6171/wakeup.")
+ .Then(sleep => sleep.Set(x => x.Timeout, Duration.FromMinutes(5)))
+ .WriteLine("Done.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/appsettings.json b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/appsettings.json
new file mode 100644
index 000000000..d9d9a9bff
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/appsettings.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft": "Warning",
+ "Microsoft.Hosting.Lifetime": "Information"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/interrupt.http b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/interrupt.http
new file mode 100644
index 000000000..1e6ebc03d
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.CustomActivityTypeProviders/interrupt.http
@@ -0,0 +1 @@
+GET https://localhost:6171/wakeup
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.HelloWorldHttp/Startup.cs b/src/samples/aspnet/Elsa.Samples.HelloWorldHttp/Startup.cs
index 38dd3705c..b4c080842 100644
--- a/src/samples/aspnet/Elsa.Samples.HelloWorldHttp/Startup.cs
+++ b/src/samples/aspnet/Elsa.Samples.HelloWorldHttp/Startup.cs
@@ -9,7 +9,7 @@ namespace Elsa.Samples.HelloWorldHttp
public void ConfigureServices(IServiceCollection services)
{
services
- .AddElsa(option => option.UsePersistence(db => db.UseSqLite("Data Source=elsa.db;Cache=Shared")))
+ .AddElsa()
.AddHttpActivities()
.AddWorkflow();
}