Plans for OrchestrationBase<TInput,TOutput,TEvent>?
Hey there, I have been using this package lately and it made working with the Durable Task Framework much more easier.
I only have one thing that I was missing and was thinking about maybe contributing on it for the Extensions.
To have the Event type of the Orchestration configurable as well instead of it always being a string.
The issue I have been facing being:
I am sending a custom record as an Event that gets correctly serialized to a JSON string. The DurableTaskFramework than tries to Deserialize the JSON string back to a string and there it fails. (This is what is going on in the end under the hood)
var eventarg = new ManualUserEventArgs("WHO",true,"WHAT");
var check = JsonSerializer.Serialize(eventarg, JsonSerializerOptions.Default);
var checkString = JsonSerializer.Deserialize(check,typeof(String), JsonSerializerOptions.Default);
public record ManualUserEventArgs(
string ByWho,
bool IsApproval,
string? Details){}
It would be easier to just be able to specify the TEvent on my Orchestration and not have to serialize the event differently than the rest of the objects. Probably the same would go for the CustomStatus.
Another topic would be an Orchestration working with different types of Events. We could set the TEvent to object and based on the type we could activate different CompletionSources.
Let me know if I can help build those abstractions or if there is a better way to work around this simple issue. For now I am sending the ManualUserEventArgs.ToString() and inside the OnEvent method I deserialize it manually to the shared event type.
@theiszeduard supporting strongly typed events would be a nice feature to have. I am not sure I want to add it to the type signature though, as it doesn't add much value and makes orchestrations that support multiple events cumbersome. I am thinking there might be a better way to declare event types.
An idea I played around with was to use attributes:
public class MyOrchestration : OrchestrationBase<Input, Output>
{
[OrchestrationEvent("eventName")]
private void OnEvent(OrchestrationContext context, EventType input)
{
}
}
The base implementation of OrchestrationBase.RaiseEvent could then scan for instance methods with OrchestrationEventAttribute, discover its parameter type, and then deserialize and dispatch accordingly.
In the meantime I am sending my events as such : "EventType {valid Json serialization of the Event}"
The HubClient sends the event:
_hubClient.RaiseEvent(ochestration, nameof(EventArg), $"{nameof(EventArg) {JsonSerializer.Serialize(eventarg, JsonSerializerOptions.Default)}"
Inside the OnEvent I simply remove the eventName at the beginning of the string and The rest I deserialize to the needed type and act accordingly.
If I have the time I will check the example you sent me, it might be more prone to errors than the current implementation I have.
Thank you for the quick response. I really enjoyed working with this extension.
From my side you can def. close the issue/discussion if there is nothing else to be added.
@theiszeduard I'll leave the issue open as a reminder to open another issue with a strongly-typed event handling design.
Personally, what I have done is just override OnEvent(OrchestrationContext context, string name, string input) and perform manual deserialization myself:
public override RaiseEvent(OrchestrationContext context, string name, string input)
{
switch (name)
{
case "myEvent":
MyEvent myEvent = context.MessageDataConverter.Deserialize<MyEvent>(input);
default:
// no op - unhandled event.
}
}
I think this works, but I can't remember the exact code I used in the past. May need some tweaking.
@theiszeduard I'll leave the issue open as a reminder to open another issue with a strongly-typed event handling design.
Personally, what I have done is just override
OnEvent(OrchestrationContext context, string name, string input)and perform manual deserialization myself:public override OnEvent(OrchestrationContext context, string name, string input) { switch (name) { case "myEvent": MyEvent myEvent = context.MessageDataConverter.Deserialize<MyEvent>(input); default: // no op - unhandled event. } }I think this works, but I can't remember the exact code I used in the past. May need some tweaking.
The issue here is not how you deserialize the input inside the method. If you raise the event as:
_hubClient.RaiseEvent(orchestration, nameof(EventArgs), new EventArgs());
DTFx won't be able to reach the OnEvent method inside the Orchestration. It fails whenever it tries to deserialize the Json from the Queue to an actual string.
(I am also Using the SjtDataConverter.cs class from the samples) so that might also be a place where the Deserialization to string can be caught and handled but it feels more like a hack to fix it there :).
Oh my bad - you need to override RaiseEvent, not OnEvent.
Closing as RaiseEvent should work with my example