In more tricky cases it could be useful to separate state machine configurations from one another for different entity states. This could be done by using IStateMachineConfiguration instances:
publicinterfaceIIssueStateMachineConfiguration:IStateMachineConfiguration<Issue,IssueState,IssueAction>{}publicabstractclassIssueStateMachineConfiguration:StateMachineConfiguration,IIssueStateMachineConfiguration{protectedreadonlyICurrentUserService CurrentUserService;protectedIssueStateMachineConfigurator(ICurrentUserService currentUserService)=> CurrentUserService = currentUserService;} // Configuration for IssueState.Draft.publicclassDraftIssueStateMachineConfiguration:IssueStateMachineConfiguration{publicDraftIssueStateMachineConfiguration(ICurrentUserService currentUserService) : base(currentUserService) {}protectedoverridevoidConfigure(Stateless.StateMachine<IssueState,IssueAction> stateMachine,Issue entity) { // Getting current user.ICurrentUser currentUser =CurrentUserService.CurrentUser; // Getting info about current user for specified issue.bool isAdmin =currentUser.HasRole("Admin");bool isAuthor =issue.AuthorId==currentUser.Id;bool isAssignee =issue.AssigneeId==currentUser.Id;stateMachine.Configure(IssueState.Draft) .PermitIf(IssueAction.Submit,IssueState.Submitted, () => isAuthor) .InternalTransitionIf(IssueAction.Edit, () => isAuthor) .InternalTransitionIf(IssueAction.Delete, () => isAuthor); }}/* ... and other configuration for other states here ...*/
That could be really useful when each configuration contains relatively big and unique set of dependencies, so it won't be needed to invoke (inject) any dependence for each state of entity, but decide what configuration and what dependencies do we need in a run-time:
// Represents configuration dispatcher.publicclassIssueStateMachineConfigurator:IStateMachineConfigurator<Issue,IssueState,IssueAction>{ // This factory will allow you to get instance of IIssueStateMachineConfiguration for any specific state.privatereadonlyFunc<IssueState,IIssueStateMachineConfiguration> _factory;publicIssueStateMachineConfigurator(Func<IssueState,IIssueStateMachineConfiguration> factory) => _factory = factory; public async Task ConfigureAsync(Stateless.StateMachine<IssueState, IssueAction> stateMachine, Issue entity, CancellationToken cancellationToken)
{ // Get configuration for this specific state invoking only its dependencies.var configuration =_factory(entity.State); // Configuring only part of configuration needed for current entity state.awaitconfiguration.ConfigureAsync(stateMachine, entity, cancellationToken); }}
The factory trick above with Func<IssueState, IIssueStateMachineConfiguration> could be done easily with Structr.Abstractions package:
services.AddFactory<IssueState,IIssueStateMachineConfiguration>(newDictionary<IssueState,Type>{ { IssueState.Draft,typeof(DraftIssueStateMachineConfiguration) },/* ... and other configuration for other states here ...*/});