Optimize workflow selector
This commit is contained in:
parent
4cd3d08d44
commit
3d0d372de3
|
|
@ -1,5 +1,4 @@
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Http.Triggers;
|
||||
using Elsa.Services;
|
||||
|
|
@ -22,10 +21,10 @@ namespace Elsa.Activities.Http.Middleware
|
|||
var path = httpContext.Request.Path;
|
||||
var method = httpContext.Request.Method;
|
||||
var cancellationToken = httpContext.RequestAborted;
|
||||
|
||||
|
||||
var results = await workflowSelector.SelectWorkflowsAsync<ReceiveHttpRequestTrigger>(
|
||||
x => x.Path == path && x.Method == null || x.Method == method,
|
||||
cancellationToken).ToListAsync(cancellationToken);
|
||||
x => x.Path == path && x.Method == null || x.Method == method,
|
||||
cancellationToken);
|
||||
|
||||
var result = results.FirstOrDefault();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using Elsa.Triggers;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Activities.Http.Triggers
|
||||
{
|
||||
|
|
@ -16,42 +9,15 @@ namespace Elsa.Activities.Http.Triggers
|
|||
{
|
||||
public PathString Path { get; set; }
|
||||
public string? Method { get; set; }
|
||||
public string ActivityId { get; set; }
|
||||
}
|
||||
|
||||
public class ReceiveHttpRequestTriggerProvider : TriggerProvider<ReceiveHttpRequestTrigger>
|
||||
public class ReceiveHttpRequestTriggerProvider : TriggerProvider<ReceiveHttpRequestTrigger, ReceiveHttpRequest>
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IWorkflowFactory _workflowFactory;
|
||||
|
||||
public ReceiveHttpRequestTriggerProvider(IServiceProvider serviceProvider, IWorkflowFactory workflowFactory)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_workflowFactory = workflowFactory;
|
||||
}
|
||||
|
||||
public override async IAsyncEnumerable<ITrigger> GetTriggersAsync(IWorkflowBlueprint workflowBlueprint, [EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
var httpRequestActivities = workflowBlueprint
|
||||
.GetStartActivities()
|
||||
.Where(x => x.Type == nameof(ReceiveHttpRequest))
|
||||
.ToList();
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var workflowInstance = await _workflowFactory.InstantiateAsync(workflowBlueprint, cancellationToken: cancellationToken);
|
||||
var workflowExecutionContext = new WorkflowExecutionContext(scope.ServiceProvider, workflowBlueprint, workflowInstance);
|
||||
|
||||
foreach (var activity in httpRequestActivities)
|
||||
public override async ValueTask<ITrigger> GetTriggerAsync(TriggerProviderContext<ReceiveHttpRequest> context, CancellationToken cancellationToken) =>
|
||||
new ReceiveHttpRequestTrigger
|
||||
{
|
||||
var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, scope.ServiceProvider, activity);
|
||||
|
||||
yield return new ReceiveHttpRequestTrigger
|
||||
{
|
||||
ActivityId = activity.Id,
|
||||
Path = await workflowBlueprint.GetActivityPropertyValue<ReceiveHttpRequest, PathString>(activity.Id, x => x.Path, activityExecutionContext, cancellationToken),
|
||||
Method = await workflowBlueprint.GetActivityPropertyValue<ReceiveHttpRequest, string?>(activity.Id, x => x.Method, activityExecutionContext, cancellationToken)
|
||||
};
|
||||
}
|
||||
}
|
||||
Path = await context.Activity.GetPropertyValueAsync(x => x.Path, cancellationToken),
|
||||
Method = await context.Activity.GetPropertyValueAsync(x => x.Method, cancellationToken)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa
|
||||
|
|
@ -47,6 +49,10 @@ namespace Elsa
|
|||
return query;
|
||||
}
|
||||
|
||||
public static IEnumerable<IActivityBlueprint> GetStartActivities(this IWorkflowBlueprint workflowBlueprint, string activityType) => workflowBlueprint.GetStartActivities().Where(x => x.Type == activityType);
|
||||
public static IEnumerable<IActivityBlueprint> GetStartActivities(this IWorkflowBlueprint workflowBlueprint, Type activityType) => workflowBlueprint.GetStartActivities(activityType.Name);
|
||||
public static IEnumerable<IActivityBlueprint> GetStartActivities<T>(this IWorkflowBlueprint workflowBlueprint) where T : IActivity => workflowBlueprint.GetStartActivities(typeof(T));
|
||||
|
||||
public static IActivityBlueprint? GetActivity(this IWorkflowBlueprint workflowBlueprint, string id) =>
|
||||
workflowBlueprint.Activities.FirstOrDefault(x => x.Id == id);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public class ActivityBlueprintWrapper<TActivity> where TActivity : IActivity
|
||||
{
|
||||
private readonly ActivityExecutionContext _activityExecutionContext;
|
||||
|
||||
public ActivityBlueprintWrapper(ActivityExecutionContext activityExecutionContext)
|
||||
{
|
||||
_activityExecutionContext = activityExecutionContext;
|
||||
}
|
||||
|
||||
public async ValueTask<T> GetPropertyValueAsync<T>(Expression<Func<TActivity, T>> propertyExpression, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var workflowBlueprint = _activityExecutionContext.WorkflowExecutionContext.WorkflowBlueprint;
|
||||
var activityId = _activityExecutionContext.ActivityBlueprint.Id;
|
||||
return await workflowBlueprint.GetActivityPropertyValue(activityId, propertyExpression, _activityExecutionContext, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
namespace Elsa.Triggers
|
||||
{
|
||||
public interface IDescribeTriggersContext
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,5 @@
|
|||
{
|
||||
public interface ITrigger
|
||||
{
|
||||
string ActivityId { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Elsa.Services.Models;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public interface ITriggerProvider
|
||||
{
|
||||
Type ForType();
|
||||
IAsyncEnumerable<ITrigger> GetTriggersAsync(IWorkflowBlueprint workflowBlueprint, CancellationToken cancellationToken = default);
|
||||
Type ForActivityType();
|
||||
ValueTask<ITrigger> GetTriggerAsync(TriggerProviderContext context, CancellationToken cancellationToken = default);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public interface IWorkflowSelector
|
||||
{
|
||||
IAsyncEnumerable<WorkflowSelectorResult> SelectWorkflowsAsync(
|
||||
Task<IEnumerable<WorkflowSelectorResult>> SelectWorkflowsAsync(
|
||||
Type triggerType,
|
||||
Func<ITrigger, bool> evaluate,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
|
|
|||
12
src/core/Elsa.Abstractions/Triggers/TriggerDescriptor.cs
Normal file
12
src/core/Elsa.Abstractions/Triggers/TriggerDescriptor.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public class TriggerDescriptor
|
||||
{
|
||||
public IWorkflowBlueprint WorkflowBlueprint { get; set; } = default!;
|
||||
public string ActivityId { get; set; } = default!;
|
||||
public string ActivityType { get; set; } = default!;
|
||||
public ITrigger Trigger { get; set; } = default!;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Elsa.Services.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Services;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public abstract class TriggerProvider<T> : ITriggerProvider where T : ITrigger
|
||||
public abstract class TriggerProvider<T, TActivity> : ITriggerProvider where T : ITrigger where TActivity : IActivity
|
||||
{
|
||||
public Type ForType() => typeof(T);
|
||||
public abstract IAsyncEnumerable<ITrigger> GetTriggersAsync(IWorkflowBlueprint workflowBlueprint, CancellationToken cancellationToken = default);
|
||||
public Type ForActivityType() => typeof(TActivity);
|
||||
public abstract ValueTask<ITrigger> GetTriggerAsync(TriggerProviderContext<TActivity> context, CancellationToken cancellationToken);
|
||||
|
||||
ValueTask<ITrigger> ITriggerProvider.GetTriggerAsync(TriggerProviderContext context, CancellationToken cancellationToken) =>
|
||||
GetTriggerAsync(new TriggerProviderContext<TActivity>(context.ActivityExecutionContext), cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public class TriggerProviderContext
|
||||
{
|
||||
public TriggerProviderContext(ActivityExecutionContext activityExecutionContext)
|
||||
{
|
||||
ActivityExecutionContext = activityExecutionContext;
|
||||
}
|
||||
|
||||
public ActivityExecutionContext ActivityExecutionContext { get; }
|
||||
public ActivityBlueprintWrapper<TActivity> GetActivity<TActivity>() where TActivity : IActivity => new ActivityBlueprintWrapper<TActivity>(ActivityExecutionContext);
|
||||
}
|
||||
|
||||
public class TriggerProviderContext<T> : TriggerProviderContext where T:IActivity
|
||||
{
|
||||
public TriggerProviderContext(ActivityExecutionContext activityExecutionContext) : base(activityExecutionContext)
|
||||
{
|
||||
}
|
||||
|
||||
public ActivityBlueprintWrapper<T> Activity => GetActivity<T>();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public static class WorkflowSelectorExtensions
|
||||
{
|
||||
public static IAsyncEnumerable<WorkflowSelectorResult> SelectWorkflowsAsync<T>(
|
||||
public static Task<IEnumerable<WorkflowSelectorResult>> SelectWorkflowsAsync<T>(
|
||||
this IWorkflowSelector workflowSelector,
|
||||
Func<T, bool> evaluate,
|
||||
CancellationToken cancellationToken = default) where T : ITrigger =>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Services;
|
||||
using Elsa.Services.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Open.Linq.AsyncExtensions;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
|
|
@ -12,31 +13,71 @@ namespace Elsa.Triggers
|
|||
public class WorkflowSelector : IWorkflowSelector
|
||||
{
|
||||
private readonly IWorkflowRegistry _workflowRegistry;
|
||||
private readonly IWorkflowFactory _workflowFactory;
|
||||
private readonly IEnumerable<ITriggerProvider> _triggerProviders;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private ICollection<TriggerDescriptor>? _descriptors;
|
||||
|
||||
public WorkflowSelector(IWorkflowRegistry workflowRegistry, IEnumerable<ITriggerProvider> triggerProviders)
|
||||
public WorkflowSelector(IWorkflowRegistry workflowRegistry, IWorkflowFactory workflowFactory, IEnumerable<ITriggerProvider> triggerProviders, IServiceProvider serviceProvider)
|
||||
{
|
||||
_workflowRegistry = workflowRegistry;
|
||||
_workflowFactory = workflowFactory;
|
||||
_triggerProviders = triggerProviders;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public async IAsyncEnumerable<WorkflowSelectorResult> SelectWorkflowsAsync(
|
||||
public async Task<IEnumerable<WorkflowSelectorResult>> SelectWorkflowsAsync(
|
||||
Type triggerType,
|
||||
Func<ITrigger, bool> evaluate,
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var providers = _triggerProviders.Where(x => x.ForType() == triggerType).ToList();
|
||||
var workflowBlueprints = await _workflowRegistry.GetWorkflowsAsync(cancellationToken).ToList();
|
||||
_descriptors ??= await BuildDescriptorsAsync(cancellationToken).ToList();
|
||||
|
||||
var query =
|
||||
from descriptor in _descriptors
|
||||
let trigger = descriptor.Trigger
|
||||
where trigger.GetType() == triggerType && evaluate(trigger)
|
||||
select new WorkflowSelectorResult(descriptor.WorkflowBlueprint, descriptor.ActivityId);
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private async Task<IEnumerable<TriggerDescriptor>> BuildDescriptorsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var providers = _triggerProviders.ToList();
|
||||
var workflowBlueprints = await _workflowRegistry.GetWorkflowsAsync(cancellationToken).ToList();
|
||||
var descriptors = new List<TriggerDescriptor>();
|
||||
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
|
||||
foreach (var provider in providers)
|
||||
foreach (var workflowBlueprint in workflowBlueprints)
|
||||
{
|
||||
var triggers = provider.GetTriggersAsync(workflowBlueprint, cancellationToken);
|
||||
var workflowInstance = await _workflowFactory.InstantiateAsync(workflowBlueprint, cancellationToken: cancellationToken);
|
||||
var workflowExecutionContext = new WorkflowExecutionContext(scope.ServiceProvider, workflowBlueprint, workflowInstance);
|
||||
var startActivities = workflowBlueprint.GetStartActivities();
|
||||
|
||||
await foreach (var trigger in triggers.WithCancellation(cancellationToken))
|
||||
if (evaluate(trigger))
|
||||
yield return new WorkflowSelectorResult(workflowBlueprint, trigger.ActivityId);
|
||||
foreach (var startActivity in startActivities)
|
||||
{
|
||||
var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, scope.ServiceProvider, startActivity);
|
||||
var providerContext = new TriggerProviderContext(activityExecutionContext);
|
||||
|
||||
foreach (var provider in providers)
|
||||
{
|
||||
var trigger = await provider.GetTriggerAsync(providerContext, cancellationToken);
|
||||
var descriptor = new TriggerDescriptor
|
||||
{
|
||||
ActivityId = startActivity.Id,
|
||||
ActivityType = startActivity.Type,
|
||||
WorkflowBlueprint = workflowBlueprint,
|
||||
Trigger = trigger,
|
||||
};
|
||||
|
||||
descriptors.Add(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in a new issue