elsa-workflows / elsa-workflows/elsa-extensions
[FEAT] Add ExecuteMethod Activity
- Dominant language
- C#
- Stars
- 49
- Forks
- 48
- Avg merge
- 21h 35m
- Merged PRs (30d)
- 20
Description
## Feature Request
### Problem Overview
Provide a way to invoke an existing C# method within a developer's codebase to make Elsa more extensible. This will allow the developer to easily invoke existing business methods without creating a custom coded activity every time there is another piece of logic they wish to call.
(Note that this differs from elsa-workflows/elsa-core#6305 which is for invoking CLI commands).
### Proposed Solution
Here are the high-level steps:
1. A developer decorates every method in their codebase with the `[ElsaExecutableMethod]` attribute they wish to have exposed in Elsa to choose to execute.
2. Then in `program.cs` the developer registers the assemblies containing the classes of the methods they wish to include for execution - so a similar experience to how activities are registered with Elsa.
3. At runtime, the user can drag the `ExecuteMethod` activity on to the canvas in Elsa Studio and choose from a dropdown list of methods to execute (the `[ElsaExecutableMethod]` from earlier)
Here is an initial rough draft of what I think it could look like. The `MethodName` still needs to be connected to the private method and the `[ElsaExecutableMethod]` attribute would still need to be implemented and connected to the dropdown. The method names would be the fully qualified class name and method name along with the parameter types so it's easier to fill out the `Parameters` input (e.g. `MyCompany.MyProduct.SomeService.DoAThing(int id, bool active)`):
```csharp
[Activity(
@namespace: nameof(Elsa),
Category = "Code",
DisplayName = "Invoke C# Method",
Description = "Executes the specified C# method decorated with [ElsaInvokableMethod]."
)]
[FlowNode(["Done", "Noop"])]
public class ExecuteMethodActivity : CodeActivity
{
[Input(
DisplayName = "Service Key",
Description = "An object that specifies the optional key of the service object to get."
)]
public Input ServiceKey { get; set; } = null!;
[Input(
DisplayName = "Create Service Scope",
Description = "Create a service scope for the execution of this activity."
)]
public Input CreateServiceScope { get; set; } = null!;
[Required]
[Input(DisplayName = "Method Name")]
public Input MethodName { get; set; } = null!;
[Required]
[Input(DisplayName = nameof(Parameters))]
public Input Parameters { get; set; } = null!;
public Output Response { get; set; } = null!;
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
object? serviceKey = ServiceKey.Get(context);
bool createServiceScope = CreateServiceScope.Get(context);
string methodName = MethodName.Get(context);
object?[]? parameters = Parameters.Get(context);
MethodInfo method = GetMethodInfo(methodName);
Type type = method.DeclaringType!;
IServiceScope? scope = createServiceScope ? context.WorkflowExecutionContext.ServiceProvider.CreateScope() : null;
object service;
if (scope != null)
{
service = serviceKey is null
? scope.ServiceProvider.GetRequiredService(type)
: scope.ServiceProvider.GetRequiredKeyedService(type, serviceKey);
}
else
{
service = serviceKey is null
? context.GetRequiredService(type)
: context.WorkflowExecutionContext.ServiceProvider.GetRequiredKeyedService(type, serviceKey);
}
object? response = method.Invoke(service, parameters);
Response.Set(context, response);
await context.CompleteActivityWithOutcomesAsync("Done");
}
private MethodInfo GetMethodInfo(string methodName)
{
MethodInfo? method = GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if (method is null)
{
throw new MissingMethodException($"Method '{methodName}' not found.");
}
return method;
}
public List GetMethodNamesForSelection()
{
return null;
}
}
```
### Use Cases
- I have lots of existing C# methods in my business tier and I want to integrate them with Elsa as another convenient entry point without adding a lot of glue code to create custom activities just to make calls to those methods.
Contributor guide
Assessment
This issue has not been assessed yet.