Refactor expression descriptors and remove unnecessary async calls

This commit refactors how expression descriptors are created and retrieved across Elsa project. The asynchronous methods have been simplified to synchronous ones removing unnecessary `ValueTask` returns. Additionally, `IExpressionDescriptorRegistryPopulator` has been removed resulting in less complexity and better performance.
This commit is contained in:
Sipke Schoorstra 2023-12-10 21:43:24 +01:00
parent 3d68207547
commit 6ff6cc6826
12 changed files with 94 additions and 119 deletions

View file

@ -92,7 +92,7 @@ Open Program.cs and replace its contents with the following code:
```csharp
using Elsa.Extensions;
using Elsa.Workflows.Core.Activities;
using Elsa.Workflows.Core.Services;
using Elsa.Workflows.Core.Contracts;
using Microsoft.Extensions.DependencyInjection;
// Set up the service container.

View file

@ -10,18 +10,14 @@ internal class CSharpExpressionDescriptorProvider : IExpressionDescriptorProvide
{
private const string TypeName = "CSharp";
public ValueTask<IEnumerable<ExpressionDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
public IEnumerable<ExpressionDescriptor> GetDescriptors()
{
var descriptor = CreateCSharpDescriptor();
return ValueTask.FromResult<IEnumerable<ExpressionDescriptor>>(new[] { descriptor });
yield return new()
{
Type = TypeName,
DisplayName = "C#",
Properties = new { MonacoLanguage = "csharp" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<CSharpExpressionHandler>
};
}
private static ExpressionDescriptor CreateCSharpDescriptor() => new()
{
Type = TypeName,
DisplayName = "C#",
Properties = new { MonacoLanguage = "csharp" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<CSharpExpressionHandler>
};
}

View file

@ -10,5 +10,5 @@ public interface IExpressionDescriptorProvider
/// <summary>
/// Gets the descriptors for the expression syntaxes supported by this provider.
/// </summary>
ValueTask<IEnumerable<ExpressionDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default);
IEnumerable<ExpressionDescriptor> GetDescriptors();
}

View file

@ -1,13 +1,13 @@
namespace Elsa.Expressions.Contracts;
/// <summary>
/// Populates the expression descriptor registry.
/// </summary>
public interface IExpressionDescriptorRegistryPopulator
{
/// <summary>
/// Populates the expression descriptor registry.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
ValueTask PopulateRegistryAsync(CancellationToken cancellationToken = default);
}
// namespace Elsa.Expressions.Contracts;
//
// /// <summary>
// /// Populates the expression descriptor registry.
// /// </summary>
// public interface IExpressionDescriptorRegistryPopulator
// {
// /// <summary>
// /// Populates the expression descriptor registry.
// /// </summary>
// /// <param name="cancellationToken">The cancellation token.</param>
// ValueTask PopulateRegistryAsync(CancellationToken cancellationToken = default);
// }

View file

@ -2,7 +2,6 @@ using Elsa.Expressions.Contracts;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.JavaScript.Expressions;
using Elsa.Workflows.Core.Contracts;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.JavaScript.Providers;
@ -11,18 +10,14 @@ internal class JavaScriptExpressionDescriptorProvider : IExpressionDescriptorPro
{
private const string TypeName = "JavaScript";
public ValueTask<IEnumerable<ExpressionDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
public IEnumerable<ExpressionDescriptor> GetDescriptors()
{
var javaScript = CreateJavaScriptDescriptor();
return ValueTask.FromResult<IEnumerable<ExpressionDescriptor>>(new[] { javaScript });
yield return new()
{
Type = TypeName,
DisplayName = "JavaScript",
Properties = new { MonacoLanguage = "javascript" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<JavaScriptExpressionHandler>
};
}
private static ExpressionDescriptor CreateJavaScriptDescriptor() => new()
{
Type = TypeName,
DisplayName = "JavaScript",
Properties = new { MonacoLanguage = "javascript" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<JavaScriptExpressionHandler>
};
}

View file

@ -2,7 +2,6 @@ using Elsa.Expressions.Contracts;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.Liquid.Expressions;
using Elsa.Workflows.Core.Contracts;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Liquid.Providers;
@ -16,26 +15,16 @@ public class LiquidExpressionDescriptorProvider : IExpressionDescriptorProvider
/// Gets the name of the expression type.
/// </summary>
public const string TypeName = "Liquid";
private readonly IIdentityGenerator _identityGenerator;
/// <summary>
/// Initializes a new instance of the <see cref="LiquidExpressionDescriptorProvider"/> class.
/// </summary>
public LiquidExpressionDescriptorProvider(IIdentityGenerator identityGenerator) => _identityGenerator = identityGenerator;
/// <inheritdoc />
public ValueTask<IEnumerable<ExpressionDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
public IEnumerable<ExpressionDescriptor> GetDescriptors()
{
var liquidDescriptor = CreateLiquidDescriptor();
return ValueTask.FromResult<IEnumerable<ExpressionDescriptor>>(new[] { liquidDescriptor });
yield return new()
{
Type = TypeName,
DisplayName = "Liquid",
Properties = new { MonacoLanguage = "liquid" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<LiquidExpressionHandler>
};
}
private ExpressionDescriptor CreateLiquidDescriptor() => new()
{
Type = TypeName,
DisplayName = "Liquid",
Properties = new { MonacoLanguage = "liquid" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<LiquidExpressionHandler>
};
}

View file

@ -9,19 +9,15 @@ namespace Elsa.Python.Providers;
internal class PythonExpressionDescriptorProvider : IExpressionDescriptorProvider
{
private const string TypeName = "Python";
public ValueTask<IEnumerable<ExpressionDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
{
var javaScript = CreatePythonDescriptor();
return ValueTask.FromResult<IEnumerable<ExpressionDescriptor>>(new[] { javaScript });
public IEnumerable<ExpressionDescriptor> GetDescriptors()
{
yield return new()
{
Type = TypeName,
DisplayName = "Python",
Properties = new { MonacoLanguage = "python" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<PythonExpressionHandler>
};
}
private ExpressionDescriptor CreatePythonDescriptor() => new()
{
Type = TypeName,
DisplayName = "Python",
Properties = new { MonacoLanguage = "python" }.ToDictionary(),
HandlerFactory = ActivatorUtilities.GetServiceOrCreateInstance<PythonExpressionHandler>
};
}

View file

@ -166,7 +166,6 @@ public class WorkflowManagementFeature : FeatureBase
.AddSingleton<IActivityRegistryPopulator, ActivityRegistryPopulator>()
.AddSingleton<IExpressionDescriptorRegistry, ExpressionDescriptorRegistry>()
.AddSingleton<IExpressionDescriptorProvider, DefaultExpressionDescriptorProvider>()
.AddSingleton<IExpressionDescriptorRegistryPopulator, ExpressionDescriptorRegistryPopulator>()
.AddSingleton<ISerializationOptionsConfigurator, SerializationOptionsConfigurator>()
.AddSingleton<IWorkflowMaterializer, ClrWorkflowMaterializer>()
.AddSingleton<IWorkflowMaterializer, JsonWorkflowMaterializer>()

View file

@ -12,15 +12,13 @@ namespace Elsa.Workflows.Management.Providers;
public class DefaultExpressionDescriptorProvider : IExpressionDescriptorProvider
{
/// <inheritdoc />
public ValueTask<IEnumerable<ExpressionDescriptor>> GetDescriptorsAsync(CancellationToken cancellationToken = default)
public IEnumerable<ExpressionDescriptor> GetDescriptors()
{
var literal = CreateLiteralDescriptor();
var @object = CreateObjectDescriptor();
var json = CreateJsonDescriptor();
var @delegate = CreateDelegateDescriptor();
var variable = CreateVariableDescriptor();
return ValueTask.FromResult<IEnumerable<ExpressionDescriptor>>(new[] { literal, @object, json, @delegate, variable });
yield return CreateLiteralDescriptor();
yield return CreateObjectDescriptor();
yield return CreateJsonDescriptor();
yield return CreateDelegateDescriptor();
yield return CreateVariableDescriptor();
}
private ExpressionDescriptor CreateLiteralDescriptor() => CreateDescriptor<LiteralExpressionHandler>("Literal", "Literal", isBrowsable: false);

View file

@ -7,6 +7,16 @@ namespace Elsa.Workflows.Management.Services;
public class ExpressionDescriptorRegistry : IExpressionDescriptorRegistry
{
private readonly IDictionary<string, ExpressionDescriptor> _expressionSyntaxDescriptors = new Dictionary<string, ExpressionDescriptor>();
/// Represents a registry of expression descriptors.
public ExpressionDescriptorRegistry(IEnumerable<IExpressionDescriptorProvider> providers)
{
foreach (var provider in providers)
{
var descriptors = provider.GetDescriptors();
AddRange(descriptors);
}
}
/// <inheritdoc />
public void Add(ExpressionDescriptor descriptor) => _expressionSyntaxDescriptors[descriptor.Type] = descriptor;

View file

@ -1,29 +1,29 @@
using Elsa.Expressions.Contracts;
namespace Elsa.Workflows.Management.Services;
/// <inheritdoc />
public class ExpressionDescriptorRegistryPopulator : IExpressionDescriptorRegistryPopulator
{
private readonly IEnumerable<IExpressionDescriptorProvider> _providers;
private readonly IExpressionDescriptorRegistry _registry;
/// <summary>
/// Initializes a new instance of the <see cref="ExpressionDescriptorRegistryPopulator"/> class.
/// </summary>
public ExpressionDescriptorRegistryPopulator(IEnumerable<IExpressionDescriptorProvider> providers, IExpressionDescriptorRegistry registry)
{
_providers = providers;
_registry = registry;
}
/// <inheritdoc />
public async ValueTask PopulateRegistryAsync(CancellationToken cancellationToken)
{
foreach (var provider in _providers)
{
var descriptors = await provider.GetDescriptorsAsync(cancellationToken);
_registry.AddRange(descriptors);
}
}
}
// using Elsa.Expressions.Contracts;
//
// namespace Elsa.Workflows.Management.Services;
//
// /// <inheritdoc />
// public class ExpressionDescriptorRegistryPopulator : IExpressionDescriptorRegistryPopulator
// {
// private readonly IEnumerable<IExpressionDescriptorProvider> _providers;
// private readonly IExpressionDescriptorRegistry _registry;
//
// /// <summary>
// /// Initializes a new instance of the <see cref="ExpressionDescriptorRegistryPopulator"/> class.
// /// </summary>
// public ExpressionDescriptorRegistryPopulator(IEnumerable<IExpressionDescriptorProvider> providers, IExpressionDescriptorRegistry registry)
// {
// _providers = providers;
// _registry = registry;
// }
//
// /// <inheritdoc />
// public async ValueTask PopulateRegistryAsync(CancellationToken cancellationToken)
// {
// foreach (var provider in _providers)
// {
// var descriptors = await provider.GetDescriptorsAsync(cancellationToken);
// _registry.AddRange(descriptors);
// }
// }
// }

View file

@ -10,27 +10,19 @@ public class DefaultRegistriesPopulator : IRegistriesPopulator
{
private readonly IWorkflowDefinitionStorePopulator _workflowDefinitionStorePopulator;
private readonly IActivityRegistryPopulator _activityRegistryPopulator;
private readonly IExpressionDescriptorRegistryPopulator _expressionDescriptorRegistryPopulator;
/// <summary>
/// Initializes a new instance of the <see cref="PopulateRegistriesHostedService"/> class.
/// </summary>
public DefaultRegistriesPopulator(
IWorkflowDefinitionStorePopulator workflowDefinitionStorePopulator,
IActivityRegistryPopulator activityRegistryPopulator,
IExpressionDescriptorRegistryPopulator expressionDescriptorRegistryPopulator)
public DefaultRegistriesPopulator(IWorkflowDefinitionStorePopulator workflowDefinitionStorePopulator, IActivityRegistryPopulator activityRegistryPopulator)
{
_workflowDefinitionStorePopulator = workflowDefinitionStorePopulator;
_activityRegistryPopulator = activityRegistryPopulator;
_expressionDescriptorRegistryPopulator = expressionDescriptorRegistryPopulator;
}
/// <inheritdoc />
public async Task PopulateAsync(CancellationToken cancellationToken = default)
{
// Stage 0: Populate the expression syntax registry.
await _expressionDescriptorRegistryPopulator.PopulateRegistryAsync(cancellationToken);
// Stage 1: Populate the activity registry.
// Because workflow definitions can be used as activities, we need to make sure that the activity registry is populated before we populate the workflow definition store.
await _activityRegistryPopulator.PopulateRegistryAsync(cancellationToken);