Skip to content

generic payload for raising an event throws exception #94

@kuldeepGDI

Description

@kuldeepGDI

Hi, I know that this repo is not maintained anymore but nevertheless I have a use case where I use Automatonymous without MT.

I have a sample code here that works but if I use a payload that is coming using generics, i get an exception

Automatonymous.AutomatonymousException: This activity requires a body with the event, but no body was specified.

The working code is from a unit test in the repo.

using Automatonymous;

namespace TestAutomato;

class Instance
{
    public State CurrentState { get; set; }
    public DateTime? Initialized { get; set; }
}


class InstanceStateMachine :
    AutomatonymousStateMachine<Instance>
{
    public InstanceStateMachine()
    {
        During(Initial,
            When(Initialize)
                .TransitionTo(Draft));
        
        During(Draft,
            When(Thing, context => context.Data.PreviousState.Equals(nameof(Draft)))
                .TransitionTo(True)
                .Then(context => context.Raise(SecInitialize)),
            When(Thing, context => !context.Data.PreviousState.Equals(nameof(Draft)))
                .TransitionTo(False));

        DuringAny(
            When(SecInitialize)
                .Then(context => context.Instance.Initialized = DateTime.Now));
    }

    public State Draft { get; private set; }
    public State True { get; private set; }
    public State False { get; private set; }

    public Event<SomeEventFailedContextData> Thing { get; private set; }
    public Event Initialize { get; private set; }
    
    public Event SecInitialize { get; private set; }
}


public record SomeEventFailedContextData(string PreviousState);

then i use this as following

using Automatonymous;
using TestAutomato;

var instance = new Instance();
var machine = new InstanceStateMachine();

await machine.RaiseEvent(instance, machine.Initialize);

await machine.RaiseEvent(instance, machine.Thing, new SomeEventFailedContextData(nameof(InstanceStateMachine.Draft)), CancellationToken.None);

Console.WriteLine(instance.CurrentState);
if (instance.Initialized != null)
{
    Console.WriteLine(instance.Initialized.Value);
}

And this works as expected. however, i want to use the ability to pass payload as generics because i might have many events with their own payload types, so I wanted to use it as following

namespace TestAutomato;

class Instance
{
    public State CurrentState { get; set; }
    public DateTime? Initialized { get; set; }
}


class InstanceStateMachine :
    AutomatonymousStateMachine<Instance>
{
    public InstanceStateMachine()
    {
        During(Initial,
            When(Initialize)
                .TransitionTo(Draft));
        
        During(Draft,
            When(Thing, context => context.Data.PreviousState.Equals(nameof(Draft)))
                .TransitionTo(True)
                .Then(context => context.Raise(SecInitialize)),
            When(Thing, context => !context.Data.PreviousState.Equals(nameof(Draft)))
                .TransitionTo(False));

        DuringAny(
            When(SecInitialize)
                .Then(context => context.Instance.Initialized = DateTime.Now));
    }

    public State Draft { get; private set; }
    public State True { get; private set; }
    public State False { get; private set; }

    public Event<SomeEventFailedContextData> Thing { get; private set; }
    public Event Initialize { get; private set; }
    
    public Event SecInitialize { get; private set; }
}


public record SomeEventFailedContextData(string PreviousState);

public record GenericSpec<T>(T Payload) where T: class;

and then call it as in

await SomeMethod(new GenericSpec<SomeEventFailedContextData>(new SomeEventFailedContextData(nameof(InstanceStateMachine.Draft))));


async Task SomeMethod<T>(GenericSpec<T> data) where T: class
{
    var instance = new Instance();
    var machine = new InstanceStateMachine();

    await machine.RaiseEvent(instance, machine.Initialize);

    await machine.RaiseEvent(instance, machine.Thing, data.Payload, CancellationToken.None);

    Console.WriteLine(instance.CurrentState);
    if (instance.Initialized != null)
    {
        Console.WriteLine(instance.Initialized.Value);
    }
}

which results into the above mentioned error ! Is there no way to do this or achieve this somehow or am I missing some configuration ?

Additionally, I also notice that if i do following

using Automatonymous;
using TestAutomato;

var instance = new Instance();
var machine = new InstanceStateMachine();

await machine.RaiseEvent(instance, machine.Initialize);
var nextEvents = await machine.NextEvents(instance);

var allowedEvent = nextEvents.FirstOrDefault(x => x.Name.Equals(nameof(machine.Thing)));

await machine.RaiseEvent(instance, allowedEvent, new SomeEventFailedContextData(nameof(InstanceStateMachine.Draft)), CancellationToken.None);

Console.WriteLine(instance.CurrentState);
if (instance.Initialized != null)
{
    Console.WriteLine(instance.Initialized.Value);
}

Then as well RaiseEvent throws the same error. So when

  1. I use generic payload i receive this error
  2. If I use Event which is coming from nextEvents FirstOrDefault then I receive same error. However, call to RaiseEvent works fine If the other override of RaiseEvent as following is used.

await machine.RaiseEvent(instance, allowedEvent, CancellationToken.None); //assuming there is no context arguments required for this event in state machine

Thanks

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions