elsa-workflows / elsa-workflows/elsa-core

Add support for generic workflows

Open
#4,944 2 comments 0 reactions 0 assignees View on GitHub
triaged
Dominant language
C#
Stars
7.9k
Forks
1.5k
Avg merge
15h 22m
Merged PRs (30d)
114

Description

I am trying to add a generic workflow. And then registering them with their types. However it fails for the second type as, Workflows Dictionary uses the type name as the dictionary key which in case of Generics will always be the same.

Background: We are handling event bus messages and building `Domain Event Triggers` around them. To support multitenancy we rolled out our Custom workflows. Now to avoid repetition we converted them to generic workflows.

here is the code,

```csharp
public class EventRouterWorkflow : WorkflowBase
where TEvent : Trigger
where TMessage : IMultiTenantEventMessage
{
private readonly string _id;
private readonly AzureServiceBusOptions _serviceBusOptions;

[UsedImplicitly]
public EventRouterWorkflow(IOptions serviceBusOptions)
{
_serviceBusOptions = serviceBusOptions.Value;
_id = WorkflowIdFromEventName;
}

protected override void Build(IWorkflowBuilder builder)
{
var topicName = _serviceBusOptions.Workflows.FirstOrDefault(w => w.WorkflowId == _id)?.Topic);
var subscriptionName = _serviceBusOptions.Subscriptions.FirstOrDefault(w => w.Topic == topicName)?.Name
builder.Id = _id;
builder.DefinitionId = _id;
builder.Name = _id;
builder.Root = new Sequence
{
Activities =
{
EventListener(topicName, subscriptionName),
DomainEventDispatcher()
}
};
}

private static MessageReceived EventListener(string topicName, string subscriptionName)
{
return new MessageReceived
{
CanStartWorkflow = true,
RunAsynchronously = true,
QueueOrTopic = new Input(topicName),
Subscription = new Input(subscriptionName),
MessageType = new Input(typeof(TMessage)),
};
}
private static CodeActivity DomainEventDispatcher() => new EventDispatcher();
private static string WorkflowIdFromEventName => typeof(TEvent).Name + "RouterWorkflow";
}

public class EventDispatcher : EventDispatcher
where TActivity : Trigger
where TMessage : IMultiTenantEventMessage
{
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
//... similar to SendMessage in elsa but supports multitenancy
var workflowInboxMessage = NewWorkflowInboxMessage.For(eventBookmark, input: input);
//.................
await CompleteAsync(context);
}

private static TMessage GetServiceBusMessage(ActivityExecutionContext context)
{
if (!context.WorkflowInput.TryGetValue("TransportMessage", out var message))
{
throw new InvalidOperationException("Transport message not found");
}

var messageModel = (ReceivedServiceBusMessageModel)message;
return JsonConvert.DeserializeObject(Encoding.Default.GetString(messageModel.Body))
?? throw new InvalidOperationException("Could not deserialize message");
}

private static Dictionary BuildSubWorkflowInput(TMessage message)
{
return new Dictionary { [EventMessageInputParameterName] = message };
}
}

```

We are registering type as,

```
elsa.AddWorkflow>();
//next one will fail.
elsa.AddWorkflow>();

```

This fails because AddWorkflows internally uses a dictionary and and uses a generic type name for the key.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.