appccelerate / statemachine

A .net library that lets you build state machines (hierarchical, async with fluent definition syntax and reporting capabilities).
Apache License 2.0
488 stars 128 forks source link

Question: Intended usage pattern for Actions with multiple machines #63

Closed adrianhand closed 4 years ago

adrianhand commented 4 years ago

If I employ the StateMachineDefinitionBuilder (as I believe is intended) by using CreatePassiveStateMachine more than once, so as to have multiple instances of my state machine created at any one moment, how can I tell which instance was the source of a call to Execute()?

To reference the example in the documentation, if I expand upon it to create a collection of Elevators, when the fsm executes this.AnnounceOverload, I want to know which of my many elevator FSM objects was the source of the execution.

Thank you!

ursenzler commented 4 years ago

I see currently no way how to achieve this without having multiple definitions. The split between definition and runtime is quite new and was introduced to define a machine once and instantiate it (the same machine instance) again and again - something typical in e.g. web backends for every request.

You can give a state machine a name .CreatePassiveStateMachine("myName"), but currently that can only be used in reporting.

The state machine syntax would probably have to be extended with overloads that accept an action with a parameter that identifies the state machine. Another question is whether the name itself is good enough to identify the state machine.

For the moment you probably have to use a parameterized definition: have multiple definitions with different actions that already know to which state machine they belong. Instantiate 1 machine per definition.

adrianhand commented 4 years ago

Thank you for your reply and considerations, ursenzler.

I have experimented further, this time with version 4, and can work around it to a degree by subclassing PassiveStateMachine and adding an identifying member; thats imperfect but it does help.

So two questions, from a computation/correctness perspective, am I mugging myself by sticking with version 4? Or alternatively, can I subclass v5 in a way I've not figured out? More specifically I can adequately derive a class from v5 PassiveStateMachine but obvs the builder pattern outputs only the base implementations with .CreatePassiveStateMachine.

Im also digging into the source to see if I can pass sender as an event argument but obviously this is not without complexity!

ursenzler commented 4 years ago

Maybe - for a quick fix - it's simplest that you copy the builder classes to your own code and use your derived class in this custom builder.

ursenzler commented 4 years ago

There will also be some changes in the next version (due to support for nullable-reference types) that would make it probably different for you to upgrade if you invest too much into finding a workaround.

adrianhand commented 4 years ago

Thank you for your guidance, ursenzler - I have got it working now with Defintion and DefintionBuilder duplicated to deliver instances of my derived class and as you describe whilst imperfect it works a treat. Theres not so much code to it that it will be an inconvenience when future updates force it to be reworked.

I am afraid forking to pass a sender (or its name) in the execute overload is beyond me in the time I have available so fwiw would love to see that in the future here - I think its a good fit for the builder pattern used now, expectant as it is of there being multiple machine instances flying around.

Thank you for your thoughts and guidance!

ursenzler commented 4 years ago

Glad to hear that it worked. I'll consider this feature for future versions.