#.net #state-machine #masstransit #saga
#.net #конечный автомат #masstransit #сага
Вопрос:
Я использую автоматонимичный конечный автомат с MassTransit. Мне понравилось работать с этой машиной состояния / саги, особенно с тем, как она была настроена и настроена, а также с тем, что я могу передавать конечному автомату события, которые реализуют контракты, которые будут использоваться в качестве сообщений.
Вот как это может выглядеть:
//define the statemachine with a State class (ServiceState)
public class ServiceStateMachine :
AutomatonymousStateMachine<ServiceState>{
//define available states
public State Available { get; set; }
public State WaitForItem { get; set; }
//define available events
public Event<RequestItem> RequestItem { get; set; }
//configure the state machine and configure the store to use the ServiceState class
public void ConfigureStateMachineCorrelations(StateMachineSagaRepositoryConfigurator<ServiceState> r)
//bind events to contracts and conditions
r.Correlate(RequestItem,
(state, message) =>
state.CorrelationId == message.CorrelationId)
}
public ServiceStateMachine(IStateMachineActivityFactory activityFactory
{
State(() => Available);
State(() => WaitForItem);
Event(() => RequestItem);
//bind states, events, activities, custom actions...
During(Available,
When(RequestItem)
.Then((state, message) =>
{
state.ServiceId = message.ServiceId; // just an example baby!
})
.TransitionTo(WaitForItem)
.Then(() => _activityFactory.GetActivity<RequestItemActivity, ServiceState>())
}
Какие существуют альтернативные реализации Saga, которые похожи, но не связаны с архитектурами MQ? Я думаю, что я действительно ищу реализацию конечного автомата или Saga, по крайней мере, с постоянным хранилищем в памяти.
Комментарии:
1. Вы можете использовать Automatonymous сам по себе, полностью отдельно от MassTransit. Фактически, интеграция с NHibernate также представляет собой отдельную библиотеку.
2. @ChrisPatterson Не могли бы вы ответить на этот вопрос примером. У меня более или менее тот же вопрос, что и у OP, но мне трудно найти полный пример сохранения. Спасибо!
Ответ №1:
Вы можете использовать Automatonymous, полностью независимую от MassTransit (или любой системы обмена сообщениями). Существуют методы (и функции подъема для выделения конечного автомата или события) для создания событий, как с данными, так и без них.
_machine.RaiseEvent(instance, x => x.RequestItem, itemData);
Простой конечный автомат и реализации экземпляра не имеют понятия хранилища состояния (экземпляра), которое зависит от приложения. Например, вы можете ввести словарь в память или использовать сборку NHibernate, которая упрощает сохранение экземпляра состояния в базе данных SQL (сборка в основном включает помощники для сопоставления CurrentState
свойства, а также несколько других пользовательских типов.
Это пример, который был запрошен онлайн, который я недавно написал в качестве модульного теста для последней ветки (mt3) Automatonymous:
class PhoneStateMachine :
AutomatonymousStateMachine<PrincessModelTelephone>
{
public PhoneStateMachine()
{
InstanceState(x => x.CurrentState);
State(() => OffHook);
State(() => Ringing);
State(() => Connected);
State(() => OnHold, Connected);
State(() => PhoneDestroyed);
Event(() => ServiceEstablished);
Event(() => CallDialed);
Event(() => HungUp);
Event(() => CallConnected);
Event(() => LeftMessage);
Event(() => PlacedOnHold);
Event(() => TakenOffHold);
Event(() => PhoneHurledAgainstWall);
Initially(
When(ServiceEstablished)
.Then(context => context.Instance.Number = context.Data.Digits)
.TransitionTo(OffHook));
During(OffHook,
When(CallDialed)
.TransitionTo(Ringing));
During(Ringing,
When(HungUp)
.TransitionTo(OffHook),
When(CallConnected)
.TransitionTo(Connected));
During(Connected,
When(LeftMessage).TransitionTo(OffHook),
When(HungUp).TransitionTo(OffHook),
When(PlacedOnHold).TransitionTo(OnHold));
During(OnHold,
When(TakenOffHold).TransitionTo(Connected),
When(PhoneHurledAgainstWall).TransitionTo(PhoneDestroyed));
DuringAny(
When(Connected.Enter)
.Then(context => StartCallTimer(context.Instance)),
When(Connected.Leave)
.Then(context => StopCallTimer(context.Instance)));
}
public State OffHook { get; set; }
public State Ringing { get; set; }
public State Connected { get; set; }
public State OnHold { get; set; }
public State PhoneDestroyed { get; set; }
public Event<PhoneServiceEstablished> ServiceEstablished { get; set; }
public Event CallDialed { get; set; }
public Event HungUp { get; set; }
public Event CallConnected { get; set; }
public Event LeftMessage { get; set; }
public Event PlacedOnHold { get; set; }
public Event TakenOffHold { get; set; }
public Event PhoneHurledAgainstWall { get; set; }
void StopCallTimer(PrincessModelTelephone instance)
{
instance.CallTimer.Stop();
}
void StartCallTimer(PrincessModelTelephone instance)
{
instance.CallTimer.Start();
}
}
Он создается и вызывается (модель Princess является экземпляром состояния для примера), как показано ниже:
var phone = new PrincessModelTelephone();
await _machine.RaiseEvent(phone, _machine.ServiceEstablished, new PhoneServiceEstablished {Digits = "555-1212"});
await _machine.RaiseEvent(phone, x => x.CallDialed);
await _machine.RaiseEvent(phone, x => x.CallConnected);
await _machine.RaiseEvent(phone, x => x.PlacedOnHold);
await Task.Delay(10);
await _machine.RaiseEvent(phone, x => x.HungUp);
Я уверен, что есть и другие примеры, но конечный автомат отделен от любых зависимостей, что делает его пригодным для использования в любом месте.