Add Composite Activity support

This commit is contained in:
Sipke Schoorstra 2020-11-01 20:03:21 +01:00
parent 8ffc3dd30d
commit 594853d45c
31 changed files with 246 additions and 84 deletions

View file

@ -112,6 +112,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.ContextualWork
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.RunChildWorkflowWorker", "src\samples\Elsa.Samples.RunChildWorkflowWorker\Elsa.Samples.RunChildWorkflowWorker.csproj", "{1E5E1669-AEC9-4DA9-9A3D-3D405101A246}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.CompositeActivitiesConsole", "src\samples\Elsa.Samples.CompositeActivitiesConsole\Elsa.Samples.CompositeActivitiesConsole.csproj", "{CC39E9F8-72D2-4A5B-846B-E2764DFE1C19}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -267,6 +269,10 @@ Global
{1E5E1669-AEC9-4DA9-9A3D-3D405101A246}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1E5E1669-AEC9-4DA9-9A3D-3D405101A246}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1E5E1669-AEC9-4DA9-9A3D-3D405101A246}.Release|Any CPU.Build.0 = Release|Any CPU
{CC39E9F8-72D2-4A5B-846B-E2764DFE1C19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC39E9F8-72D2-4A5B-846B-E2764DFE1C19}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC39E9F8-72D2-4A5B-846B-E2764DFE1C19}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC39E9F8-72D2-4A5B-846B-E2764DFE1C19}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -319,6 +325,7 @@ Global
{565E2F7D-1F5F-45EF-B130-BC09CB3465B1} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
{37753BF9-9D9B-4264-AB5B-D057E467D3C5} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
{1E5E1669-AEC9-4DA9-9A3D-3D405101A246} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
{CC39E9F8-72D2-4A5B-846B-E2764DFE1C19} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}

View file

@ -64,9 +64,9 @@ namespace Elsa.Builders
Func<IActivityBuilder> target,
string outcome = OutcomeNames.Done);
IWorkflowBlueprint Build();
IWorkflowBlueprint Build(IWorkflow workflow);
IWorkflowBlueprint Build(Type workflowType);
IWorkflowBlueprint Build<T>() where T : IWorkflow;
IWorkflowBlueprint Build(string activityIdPrefix = "activity");
IWorkflowBlueprint Build(IWorkflow workflow, string activityIdPrefix = "activity");
IWorkflowBlueprint Build(Type workflowType, string activityIdPrefix = "activity");
IWorkflowBlueprint Build<T>(string activityIdPrefix = "activity") where T : IWorkflow;
}
}

View file

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using Elsa.Services.Models;
namespace Elsa.Models

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading;
@ -43,10 +44,7 @@ namespace Elsa.Services
? provider
: null;
public async ValueTask SetActivityPropertiesAsync(
IActivity activity,
ActivityExecutionContext activityExecutionContext,
CancellationToken cancellationToken = default)
public async ValueTask SetActivityPropertiesAsync(IActivity activity, ActivityExecutionContext activityExecutionContext, CancellationToken cancellationToken = default)
{
var properties = activity.GetType().GetProperties().Where(IsActivityProperty).ToList();
var providers = GetProviders(activity.Id);
@ -64,7 +62,8 @@ namespace Elsa.Services
}
}
private bool IsActivityProperty(PropertyInfo property) =>
property.GetCustomAttribute<ActivityPropertyAttribute>() != null;
private bool IsActivityProperty(PropertyInfo property) => property.GetCustomAttribute<ActivityPropertyAttribute>() != null;
public IEnumerator<KeyValuePair<string, IDictionary<string, IActivityPropertyValueProvider>>> GetEnumerator() => _providers.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

View file

@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Linq;
using Elsa.ActivityResults;
using Elsa.Builders;
using Elsa.Services.Models;
namespace Elsa.Services
{
public abstract class CompositeActivity : Activity
{
public abstract void Build(IWorkflowBuilder workflowBuilder);
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
var startActivities = context.ActivityBlueprint.ChildWorkflow!.GetStartActivities().Select(x => x.Id).ToList();
return Combine(Done(), Schedule(startActivities, null!));
}
}
}

View file

@ -0,0 +1,10 @@
using Elsa.Models;
using Elsa.Services.Models;
namespace Elsa.Services
{
public interface IActivityFactory
{
ActivityInstance Instantiate(IActivityBlueprint activityBlueprint);
}
}

View file

@ -1,10 +1,11 @@
using System.Threading;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Services.Models;
namespace Elsa.Services
{
public interface IActivityPropertyProviders
public interface IActivityPropertyProviders : IEnumerable<KeyValuePair<string, IDictionary<string, IActivityPropertyValueProvider>>>
{
void AddProvider(string activityId, string propertyName, IActivityPropertyValueProvider provider);
IActivityPropertyValueProvider? GetProvider(string activityId, string propertyName);

View file

@ -27,8 +27,8 @@ namespace Elsa.Services.Models
public string Id { get; set; } = default!;
public string? Name { get; set; }
public string Type { get; set; } = default!;
public bool PersistWorkflow { get; set; }
public IWorkflowBlueprint? ChildWorkflow { get; set; }
public Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> CreateActivityAsync { get; set; } = default!;
}

View file

@ -11,5 +11,6 @@ namespace Elsa.Services.Models
public string Type { get; }
public bool PersistWorkflow { get; }
Func<ActivityExecutionContext, CancellationToken, ValueTask<IActivity>> CreateActivityAsync { get; }
IWorkflowBlueprint? ChildWorkflow { get; set; }
}
}

View file

@ -5,21 +5,23 @@ using Elsa.Models;
using Elsa.Services;
using Elsa.Services.Models;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using NetBox.Extensions;
namespace Elsa.Builders
{
public class WorkflowBuilder : IWorkflowBuilder
{
private readonly IIdGenerator _idGenerator;
private readonly JsonSerializer _serializer;
private readonly IActivityActivator _activityActivator;
private readonly Func<IWorkflowBuilder> _workflowBuilderFactory;
private readonly IList<IActivityBuilder> _activityBuilders;
private readonly IList<IConnectionBuilder> _connectionBuilders;
public WorkflowBuilder(IIdGenerator idGenerator, JsonSerializer serializer, IServiceProvider serviceProvider)
public WorkflowBuilder(IIdGenerator idGenerator, IActivityActivator activityActivator, Func<IWorkflowBuilder> workflowBuilderFactory, IServiceProvider serviceProvider)
{
_idGenerator = idGenerator;
_serializer = serializer;
_activityActivator = activityActivator;
_workflowBuilderFactory = workflowBuilderFactory;
ServiceProvider = serviceProvider;
Id = idGenerator.Generate();
Version = 1;
@ -62,13 +64,15 @@ namespace Elsa.Builders
}
public IWorkflowBuilder WithContextType<T>(WorkflowContextFidelity fidelity) => WithContextType(typeof(T), fidelity);
public IWorkflowBuilder WithContextType(Type type, WorkflowContextFidelity fidelity = WorkflowContextFidelity.Burst) => WithContextOptions(new WorkflowContextOptions
{
ContextType = type,
ContextFidelity = fidelity
});
public IWorkflowBuilder WithContextType(Type type, WorkflowContextFidelity fidelity = WorkflowContextFidelity.Burst) =>
WithContextOptions(
new WorkflowContextOptions
{
ContextType = type,
ContextFidelity = fidelity
});
public IWorkflowBuilder WithContextOptions(WorkflowContextOptions value)
{
ContextOptions = value;
@ -213,34 +217,38 @@ namespace Elsa.Builders
where T : class, IActivity =>
StartWith<T>(branch);
public IWorkflowBlueprint Build(IWorkflow workflow)
public IWorkflowBlueprint Build(IWorkflow workflow, string activityIdPrefix = "activity")
{
WithId(workflow.GetType().Name);
workflow.Build(this);
return Build();
return Build(activityIdPrefix);
}
public IWorkflowBlueprint Build()
public IWorkflowBlueprint Build(string activityIdPrefix = "activity")
{
var definitionId = !string.IsNullOrWhiteSpace(Id) ? Id : _idGenerator.Generate();
var activityBlueprints = new List<IActivityBlueprint>();
var connections = new List<Connection>();
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 = $"activity-{++index}";
var activityBlueprints = _activityBuilders
.Select(BuildActivityBlueprint)
.ToList();
foreach (var activityBuilder in _activityBuilders.Where(x => string.IsNullOrWhiteSpace(x.ActivityId)))
activityBuilder.ActivityId = $"{activityIdPrefix}-{++index}";
activityBlueprints.AddRange(_activityBuilders.Select(BuildActivityBlueprint));
// Build composite activities.
BuildCompositeActivities(_activityBuilders, activityBlueprints, connections, activityPropertyProviders);
var activityBlueprintDictionary = activityBlueprints.ToDictionary(x => x.Id);
var connections = _connectionBuilders
.Select(x => new Connection(activityBlueprintDictionary[x.Source().ActivityId], activityBlueprintDictionary[x.Target().ActivityId], x.Outcome))
.ToList();
connections.AddRange(_connectionBuilders.Select(x => new Connection(activityBlueprintDictionary[x.Source().ActivityId], activityBlueprintDictionary[x.Target().ActivityId], x.Outcome)));
var activityPropertyValueProviders = _activityBuilders
activityPropertyProviders.AddRange(_activityBuilders
.Select(x => (x.ActivityId, x.PropertyValueProviders))
.ToDictionary(x => x.ActivityId!, x => x.PropertyValueProviders!);
.ToDictionary(x => x.ActivityId!, x => x.PropertyValueProviders!));
var workflow = new WorkflowBlueprint(
definitionId,
@ -257,18 +265,42 @@ namespace Elsa.Builders
DeleteCompletedInstances,
activityBlueprints,
connections,
new ActivityPropertyProviders(activityPropertyValueProviders));
new ActivityPropertyProviders(activityPropertyProviders));
return workflow;
}
public IWorkflowBlueprint Build(Type workflowType)
private void BuildCompositeActivities(IEnumerable<IActivityBuilder> activityBuilders, ICollection<IActivityBlueprint> activityBlueprints, ICollection<Connection> connections, IDictionary<string, IDictionary<string, IActivityPropertyValueProvider>> activityPropertyProviders)
{
var workflow = (IWorkflow)ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider, workflowType);
return Build(workflow);
var compositeActivityType = typeof(CompositeActivity);
var compositeActivityBuilders = activityBuilders.Where(x => compositeActivityType.IsAssignableFrom(x.ActivityType)).ToList();
foreach (var activityBuilder in compositeActivityBuilders)
{
var compositeActivity = (CompositeActivity)_activityActivator.ActivateActivity(activityBuilder.ActivityType.Name);
var workflowBuilder = _workflowBuilderFactory();
compositeActivity.Build(workflowBuilder);
var workflow = workflowBuilder.Build($"composite-{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 = activityBlueprints.Single(x => x.Id == activityBuilder.ActivityId);
compositeActivityBlueprint.ChildWorkflow = workflow;
}
}
public IWorkflowBlueprint Build<T>() where T : IWorkflow => Build(typeof(T));
public IWorkflowBlueprint Build(Type workflowType, string activityIdPrefix = "activity")
{
var workflow = (IWorkflow)ActivatorUtilities.GetServiceOrCreateInstance(ServiceProvider, workflowType);
return Build(workflow, activityIdPrefix);
}
public IWorkflowBlueprint Build<T>(string activityIdPrefix = "activity") where T : IWorkflow => Build(typeof(T), activityIdPrefix);
private IActivityBlueprint BuildActivityBlueprint(IActivityBuilder builder, int index) => new ActivityBlueprint(builder.ActivityId, builder.Name, builder.ActivityType.Name, builder.PersistWorkflow, builder.BuildActivityAsync());
}

View file

@ -102,6 +102,7 @@ namespace Microsoft.Extensions.DependencyInjection
.AddSingleton<IWorkflowSchedulerQueue, WorkflowSchedulerQueue>()
.AddScoped<IWorkflowRunner, WorkflowRunner>()
.AddSingleton<IWorkflowFactory, WorkflowFactory>()
.AddSingleton<IActivityFactory, ActivityFactory>()
.AddSingleton<IWorkflowBlueprintMaterializer, WorkflowBlueprintMaterializer>()
.AddScoped<IWorkflowSelector, WorkflowSelector>()
.AddScoped<IWorkflowDefinitionManager, WorkflowDefinitionManager>()

View file

@ -0,0 +1,15 @@
using Elsa.Models;
using Elsa.Services.Models;
using Newtonsoft.Json.Linq;
namespace Elsa.Services
{
public class ActivityFactory : IActivityFactory
{
public ActivityInstance Instantiate(IActivityBlueprint activityBlueprint) => new ActivityInstance(
activityBlueprint.Id,
activityBlueprint.Type,
null,
new JObject());
}
}

View file

@ -3,18 +3,19 @@ using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Services.Models;
using Newtonsoft.Json.Linq;
using NodaTime;
namespace Elsa.Services
{
public class WorkflowFactory : IWorkflowFactory
{
private readonly IActivityFactory _activityFactory;
private readonly IClock _clock;
private readonly IIdGenerator _idGenerator;
public WorkflowFactory(IClock clock, IIdGenerator idGenerator)
public WorkflowFactory(IActivityFactory activityFactory, IClock clock, IIdGenerator idGenerator)
{
_activityFactory = activityFactory;
_clock = clock;
_idGenerator = idGenerator;
}
@ -41,10 +42,6 @@ namespace Elsa.Services
return Task.FromResult(workflowInstance);
}
private ActivityInstance CreateInstance(IActivityBlueprint activityBlueprint) => new ActivityInstance(
activityBlueprint.Id,
activityBlueprint.Type,
null,
new JObject());
private ActivityInstance CreateInstance(IActivityBlueprint activityBlueprint) => _activityFactory.Instantiate(activityBlueprint);
}
}

View file

@ -23,7 +23,7 @@ namespace Elsa.Services
private readonly IWorkflowRegistry _workflowRegistry;
private readonly IWorkflowFactory _workflowFactory;
private readonly IWorkflowBuilder _workflowBuilder;
private readonly Func<IWorkflowBuilder> _workflowBuilderFactory;
private readonly IWorkflowContextManager _workflowContextManager;
private readonly IMediator _mediator;
private readonly IServiceProvider _serviceProvider;
@ -32,7 +32,7 @@ namespace Elsa.Services
public WorkflowRunner(
IWorkflowRegistry workflowRegistry,
IWorkflowFactory workflowFactory,
IWorkflowBuilder workflowBuilder,
Func<IWorkflowBuilder> workflowBuilderFactory,
IWorkflowContextManager workflowContextManager,
IMediator mediator,
IServiceProvider serviceProvider,
@ -40,7 +40,7 @@ namespace Elsa.Services
{
_workflowRegistry = workflowRegistry;
_workflowFactory = workflowFactory;
_workflowBuilder = workflowBuilder;
_workflowBuilderFactory = workflowBuilderFactory;
_workflowContextManager = workflowContextManager;
_mediator = mediator;
_serviceProvider = serviceProvider;
@ -71,7 +71,7 @@ namespace Elsa.Services
string? contextId = default,
CancellationToken cancellationToken = default)
where T : IWorkflow =>
await RunWorkflowAsync(_workflowBuilder.Build<T>(), activityId, input, correlationId, contextId, cancellationToken);
await RunWorkflowAsync(_workflowBuilderFactory().Build<T>(), activityId, input, correlationId, contextId, cancellationToken);
public async ValueTask<WorkflowInstance> RunWorkflowAsync<T>(
WorkflowInstance workflowInstance,
@ -79,7 +79,7 @@ namespace Elsa.Services
object? input = default,
CancellationToken cancellationToken = default)
where T : IWorkflow =>
await RunWorkflowAsync(_workflowBuilder.Build<T>(), workflowInstance, activityId, input, cancellationToken);
await RunWorkflowAsync(_workflowBuilderFactory().Build<T>(), workflowInstance, activityId, input, cancellationToken);
public async ValueTask<WorkflowInstance> RunWorkflowAsync(
IWorkflow workflow,
@ -89,7 +89,7 @@ namespace Elsa.Services
string? contextId = default,
CancellationToken cancellationToken = default)
{
var workflowBlueprint = _workflowBuilder.Build(workflow);
var workflowBlueprint = _workflowBuilderFactory().Build(workflow);
return await RunWorkflowAsync(workflowBlueprint, activityId, input, correlationId, contextId, cancellationToken);
}
@ -100,7 +100,7 @@ namespace Elsa.Services
object? input = default,
CancellationToken cancellationToken = default)
{
var workflowBlueprint = _workflowBuilder.Build(workflow);
var workflowBlueprint = _workflowBuilderFactory().Build(workflow);
return await RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, input, cancellationToken);
}

View file

@ -155,8 +155,7 @@ namespace Elsa.Triggers
{
var providers = _triggerProviders.ToList();
var descriptors = new List<TriggerDescriptor>();
var loadWorkflowContext = new LoadWorkflowContext(workflowBlueprint, workflowInstance);
var workflowContext = await _workflowContextManager.LoadContext(loadWorkflowContext, cancellationToken);
var workflowContext = workflowBlueprint.ContextOptions != null ? await _workflowContextManager.LoadContext(new LoadWorkflowContext(workflowBlueprint, workflowInstance), cancellationToken) : default;
var workflowExecutionContext = new WorkflowExecutionContext(_serviceProvider, workflowBlueprint, workflowInstance, default, workflowContext);
foreach (var blockingActivity in blockingActivities)

View file

@ -1,16 +1,16 @@
using Elsa.DistributedLock;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Azure.Storage.RetryPolicies;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Elsa.DistributedLock;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
using Microsoft.Azure.Storage.RetryPolicies;
using Microsoft.Extensions.Logging;
namespace Elsa.DistributedLocking.AzureBlob
namespace Elsa
{
// See also:
// The lock duration can be 15 to 60 seconds, or can be infinite, Reference: https://docs.microsoft.com/en-us/rest/api/storageservices/lease-blob

View file

@ -1,8 +1,8 @@
using System;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace Elsa.DistributedLocking.AzureBlob
namespace Elsa
{
public static class ElsaOptionsExtensions
{

View file

@ -1,6 +1,6 @@
using Microsoft.Azure.Storage.Blob;
namespace Elsa.DistributedLocking.AzureBlob
namespace Elsa
{
internal class LockedBlob
{

View file

@ -1,13 +1,13 @@
using System;
using System.Collections.Generic;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using RedLockNet;
using RedLockNet.SERedis;
using RedLockNet.SERedis.Configuration;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
namespace Elsa.DistributedLocking.Redis
namespace Elsa
{
public static class ElsaOptionsExtensions
{

View file

@ -1,12 +1,12 @@
using Elsa.DistributedLock;
using Microsoft.Extensions.Logging;
using RedLockNet;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.DistributedLock;
using Microsoft.Extensions.Logging;
using RedLockNet;
namespace Elsa.DistributedLocking.Redis
namespace Elsa
{
public class RedisLockProvider : IDistributedLockProvider
{

View file

@ -1,7 +1,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace Elsa.DistributedLocking.SqlServer
namespace Elsa
{
public static class ElsaOptionsExtensions
{

View file

@ -7,7 +7,7 @@ using System.Threading.Tasks;
using Elsa.DistributedLock;
using Microsoft.Extensions.Logging;
namespace Elsa.DistributedLocking.SqlServer
namespace Elsa
{
// CREDITS:
// Implementation taken & adapted from Workflow Core: https://github.com/danielgerlag/workflow-core/blob/master/src/providers/WorkflowCore.LockProviders.SqlServer/SqlLockProvider.cs

View file

@ -0,0 +1,17 @@
using Elsa.Activities.Console;
using Elsa.Builders;
using Elsa.Services;
namespace Elsa.Samples.CompositeActivitiesConsole.Activities
{
public class CountDownActivity : CompositeActivity
{
public override void Build(IWorkflowBuilder workflowBuilder)
{
workflowBuilder
.WriteLine("3!")
.WriteLine("2!")
.WriteLine("1!");
}
}
}

View file

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.9" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\core\Elsa\Elsa.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,34 @@
using System.Data;
using System.Threading.Tasks;
using Elsa.Samples.CompositeActivitiesConsole.Activities;
using Elsa.Samples.CompositeActivitiesConsole.Workflows;
using Elsa.Services;
using Microsoft.Extensions.DependencyInjection;
using YesSql.Provider.Sqlite;
namespace Elsa.Samples.CompositeActivitiesConsole
{
class Program
{
static async Task Main(string[] args)
{
// Create a service container with Elsa services.
var services = new ServiceCollection()
.AddElsa(option => option.UsePersistence(db => db.UseSqLite("Data Source=elsa.db;Cache=Shared", IsolationLevel.ReadUncommitted)))
.AddConsoleActivities()
.AddActivity<CountDownActivity>()
.AddWorkflow<CompositionWorkflow>()
.BuildServiceProvider();
// Run startup actions (not needed when registering Elsa with a Host).
var startupRunner = services.GetRequiredService<IStartupRunner>();
await startupRunner.StartupAsync();
// Get a workflow host.
var workflowRunner = services.GetService<IWorkflowRunner>();
// Execute the workflow.
await workflowRunner.RunWorkflowAsync<CompositionWorkflow>();
}
}
}

View file

@ -0,0 +1,17 @@
using Elsa.Activities.Console;
using Elsa.Builders;
using Elsa.Samples.CompositeActivitiesConsole.Activities;
namespace Elsa.Samples.CompositeActivitiesConsole.Workflows
{
/// <summary>
/// A basic workflow with just one WriteLine activity.
/// </summary>
public class CompositionWorkflow : IWorkflow
{
public void Build(IWorkflowBuilder workflow) => workflow
.WriteLine("Welcome to the Composite Activities demo workflow!")
.Then<CountDownActivity>()
.WriteLine("Done!");
}
}

View file

@ -2,7 +2,6 @@ using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NodaTime;
using System.Threading.Tasks;
using Elsa.DistributedLocking.Redis;
using YesSql.Provider.Sqlite;
namespace Elsa.Samples.DistributedLock

View file

@ -5,10 +5,10 @@ using System.Threading.Tasks;
using AutoFixture;
using Elsa.Activities.Console;
using Elsa.ComponentTests.Helpers;
using Elsa.Core.IntegrationTests.Helpers;
using Elsa.Models;
using Elsa.Server.Api.Endpoints.WorkflowDefinitions;
using Elsa.Testing.Shared.AutoFixture;
using Elsa.Testing.Shared.Helpers;
using Xunit;
namespace Elsa.ComponentTests.Endpoints.WorkflowDefinitions

View file

@ -1,7 +1,7 @@
using System;
using System.IO;
namespace Elsa.Core.IntegrationTests.Helpers
namespace Elsa.Testing.Shared.Helpers
{
public class TemporaryFolder : IDisposable
{

View file

@ -1,7 +1,6 @@
using System;
using System.Threading.Tasks;
using Elsa.Builders;
using Elsa.Core.IntegrationTests.Helpers;
using Elsa.Services;
using Elsa.Triggers;
using Microsoft.Extensions.DependencyInjection;

View file

@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using Xunit.Abstractions;
namespace Elsa.Core.IntegrationTests.Helpers
namespace Elsa.Testing.Shared.Helpers
{
public class XunitConsoleForwarder : TextWriter
{