elsa-workflows / elsa-workflows/elsa-core
Add support for generic workflows
- 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
Assessment
This issue has not been assessed yet.