Git Product home page Git Product logo

automatonymous's Introduction

MassTransit

MassTransit is a free, open-source distributed application framework for .NET. MassTransit makes it easy to create applications and services that leverage message-based, loosely-coupled asynchronous communication for higher availability, reliability, and scalability.

Mass Transit

MassTransit is Apache 2.0 licensed.

Documentation

Get started by reading through the documentation.

Build Status

Branch Status
master master
develop develop

MassTransit NuGet Packages

Package Name .NET .NET Standard .NET Framework
Main
MassTransit 6.0, 8.0 2.0 4.7.2
MassTransit.Abstractions 6.0, 8.0 2.0 4.7.2
MassTransit.Newtonsoft 6.0, 8.0 2.0 4.7.2
Other
MassTransit.Analyzers 2.0
MassTransit.Templates 6.0
MassTransit.SignalR 6.0, 8.0 4.7.2
MassTransit.Interop.NServiceBus 6.0, 8.0 2.0 4.7.2
MassTransit.TestFramework 6.0, 8.0 2.0 4.7.2
Monitoring
MassTransit.Prometheus 6.0, 8.0 2.0 4.7.2
Persistence
MassTransit.AmazonS3 6.0, 8.0 2.0 4.7.2
MassTransit.Azure.Cosmos 6.0, 8.0 2.0 4.7.2
MassTransit.Azure.Storage 6.0, 8.0 2.0 4.7.2
MassTransit.Azure.Table 6.0, 8.0 2.0 4.7.2
MassTransit.Dapper 6.0, 8.0 2.0 4.7.2
MassTransit.DynamoDb 6.0, 8.0 2.0 4.7.2
MassTransit.EntityFrameworkCore 6.0, 8.0 2.0
MassTransit.EntityFramework 2.1 4.7.2
MassTransit.Marten 6.0, 8.0 2.0 4.7.2
MassTransit.MongoDb 6.0, 8.0 2.0 4.7.2
MassTransit.NHibernate 6.0, 8.0 2.0 4.7.2
MassTransit.Redis 6.0, 8.0 2.0 4.7.2
Scheduling
MassTransit.Hangfire 6.0, 8.0 2.0 4.7.2
MassTransit.Quartz 6.0, 8.0 2.0 4.7.2
Transports
MassTransit.ActiveMQ 6.0, 8.0 2.0 4.7.2
MassTransit.AmazonSQS 6.0, 8.0 2.0 4.7.2
MassTransit.Azure.ServiceBus.Core 6.0, 8.0 2.0 4.7.2
MassTransit.RabbitMQ 6.0, 8.0 2.0 4.7.2
MassTransit.SqlTransport.PostgreSQL 6.0, 8.0 2.0 4.7.2
MassTransit.SqlTransport.SqlServer 6.0, 8.0 2.0 4.7.2
MassTransit.WebJobs.EventHubs 6.0, 8.0 2.0 4.7.2
MassTransit.WebJobs.ServiceBus 6.0, 8.0 2.0 4.7.2
Riders
MassTransit.Kafka 6.0, 8.0 2.0 4.7.2
MassTransit.EventHub 6.0, 8.0 2.0 4.7.2

Discord

Get help live at the MassTransit Discord server.

alt Join the conversation

GitHub Issues

Pay attention

Please do not open an issue on GitHub, unless you have spotted an actual bug in MassTransit.

Use GitHub Discussions to ask questions, bring up ideas, or other general items. Issues are not the place for questions, and will either be converted to a discussion or closed.

This policy is in place to avoid bugs being drowned out in a pile of sensible suggestions for future enhancements and calls for help from people who forget to check back if they get it and so on.

Building from Source

  1. Install the latest .NET 8 SDK
  2. Clone the source down to your machine
    git clone https://github.com/MassTransit/MassTransit.git
  3. Run dotnet build

Contributing

  1. Turn off autocrlf
    git config core.autocrlf false
  2. Hack!
  3. Make a pull request

REQUIREMENTS

  • .NET 8 SDK

CREDITS

Logo Design by The Agile Badger

automatonymous's People

Contributors

alexeyzimarev avatar andymac4182 avatar chrisblock avatar damirainullin avatar drusellers avatar fduman avatar greybird avatar haf avatar igor-toporet avatar jcoulston avatar kernelith avatar moientajik avatar phatboyg avatar tditiecher avatar webprofusion-chrisc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

automatonymous's Issues

Does Automatonymous support commands?

Instead of events being raised between services coming and going I want to use commands where the first service commands the second specific service to do something and wait for response.
saw that you have documentation here
but it is poor and machine state is not used.
I might have said something wrong because I am new to the whole messaging between services idea.
thanks

Expose Observers to derived class of AutomatonymousStateMachine

Make _eventObservers and _stateObservers accessible to derived state mchines.

Or make ConnectEventObserver, ConnectStateObserver accessible to derived state mchines (in constructor).

So that we can connect observers when create a new instance of state mchines.

Failing build

01:16:06][Execute build] (RestorePackages target) ->
[01:16:06][Execute build] D:\BuildAgent-02\work\b9767bf84d2a672b\src\.nuget\nuget.targets(58,9): error : The schema version of 'System.Data.SQLite' is incompatible with version 1.7.30402.9028 of NuGet. Please upgrade NuGet to the latest version from http://go.microsoft.com/fwlink/?LinkId=213942. [D:\BuildAgent-02\work\b9767bf84d2a672b\src\NHibernate.AutomatonymousTests\NHibernate.AutomatonymousTests.csproj]
[01:16:06][Execute build] D:\BuildAgent-02\work\b9767bf84d2a672b\src\.nuget\nuget.targets(58,9): error MSB3073: The command ""D:\BuildAgent-02\work\b9767bf84d2a672b\src\.nuget\nuget.exe" install "D:\BuildAgent-02\work\b9767bf84d2a672b\src\NHibernate.AutomatonymousTests\packages.config" -source "" -o "D:\BuildAgent-02\work\b9767bf84d2a672b\src\packages"" exited with code 1. [D:\BuildAgent-02\work\b9767bf84d2a672b\src\NHibernate.AutomatonymousTests\NHibernate.AutomatonymousTests.csproj]
[01:16:06][Execute build] 

Should be enough to commit a new NuGet.exe.

BPMN 2.0 Compatibiltiy

Can we implement a process designed using BPMN 2.0, is there any not supported function?

CompositeEventActivity<TInstance> and GraphStateMachineVisitor<TInstance>

GraphStateMachineVisitor has code that is impossible to execute.

inside method Visit(Activity activity, Action next)
system is trying to cast activity to CompositeEventActivity:
compositeActivity = activity as CompositeEventActivity;

but in case of registering composite event system creates FactoryActivity with CompositeEventActivity so cast is impossible and method GraphStateMachineVisitor.InspectCompositeEventActivity never executes

Add Request support

Hi,

I opened this issue to discuss a notation for adding Request support to the lib. The basic stuff shouldn't be hard to do, it can probably follow the Publish annotation. What might be a bit interesting modeling the timeout.

It could be something like, following MT:

   class StateMachine 
   {
          ctor(){   
               During(Initial,
                    When(Initialized)
                         .Request(new MyRequest(), c => {
                               c.HandleTimeout(() => ...);
                         }
                 ));
            }
   }

or a bit more statemachine-ish approach and perhaps:

   class StateMachine 
   {
         enum Timeouts {
              First,
              Second 
        }

          ctor(){   
               During(Initial,
                    When(Initialized)
                         .Request(new MyRequest(), c => {
                               c.SetTimeoutTag( Timeouts.First );
                         }
                 )
                When(Timeout, Timeouts.First)
                    .Then(...);
            }
   }

Any thoughts on this?

cheers,
Ernst

Activity Exception runs infinite loop in RabbitMQ

Hi,

I created a sample Prescription workflow with a part of the statemachine implementation as below.

During(Created, Valid,
When(ValidatePrescriptionEvent)
.Activity(x => x.OfType())
.TransitionTo(Valid)
.RespondAsync(context =>
context.Init(new
{
PrescriptionNumber = context.Instance.CorrelationId,
CustomerNumber = context.Instance.CustomerNumber,
State = context.Instance.CurrentState == Created.Name ? Valid.Name : context.Instance.CurrentState,
Drugs = context.Instance.PresciptionDrugs
})
)
.Catch(ex =>
ex.TransitionTo(Invalid)
.RespondAsync(context =>
context.Init(new
{
PrescriptionNumber = context.Instance.CorrelationId,
Message = context.Exception.Message
})
)
),
When(SetValidPrescriptionEvent)
.TransitionTo(Valid)
);

I created an activity file to implement the ValidatePrescriptionActivity that implements Execute as below

public async Task Execute(BehaviorContext<PrescriptionStatus, ValidatePrescription> context, Behavior<PrescriptionStatus, ValidatePrescription> next)
{
if (DateTime.Now.Minute % 2 == 0)
{
await next.Execute(context);
}
**else
{
Console.WriteLine("Invalid Prescription");

            throw new InvalidOperationException();
        }**
    }

The intention is to throw an exception when the current min is Odd. The state changes to Invalid as intended, but there is an infinite loop that runs on the RabbitMQ and eventually failing Redis due to Concurrency.

Below are my error messages coming from the consumer console:

warn: MassTransit.ReceiveTransport[0]
Retrying 00:00:01.7600000: Unable to lock saga: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba_lock)
MassTransit.MassTransitException: Unable to lock saga: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba_lock)
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.SagaLock.<Lock>g__LockAsync|6_0() at MassTransit.Policies.PipeRetryExtensions.Retry[T](IRetryPolicy retryPolicy, Func1 retryMethod, CancellationToken cancellationToken)
dbug: MassTransit.ReceiveTransport[0]
SAGA:Prescription.Components.StateMachine.PrescriptionStatus:f6be58b0-13d0-4a80-a6aa-5562d6fa29ba Used Sample.Contracts.Interfaces.Drugs.InvalidPrescription
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/Sample.Contracts.Interfaces.Drugs:InvalidPrescription 00010000-4100-0250-a8f5-08d8fe86e5b4 Sample.Contracts.Interfaces.Drugs.InvalidPrescription
dbug: MassTransit.ReceiveTransport[0]
SAGA:Prescription.Components.StateMachine.PrescriptionStatus:f6be58b0-13d0-4a80-a6aa-5562d6fa29ba Used Sample.Contracts.Interfaces.Drugs.InvalidPrescription
warn: MassTransit.ReceiveTransport[0]
Retrying 00:00:01.1430000: Unable to lock saga: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba_lock)
MassTransit.MassTransitException: Unable to lock saga: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba_lock)
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.SagaLock.<Lock>g__LockAsync|6_0() at MassTransit.Policies.PipeRetryExtensions.Retry[T](IRetryPolicy retryPolicy, Func1 retryMethod, CancellationToken cancellationToken)
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-56e6-08d8fe86e535 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-811a-08d8fe86e567 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-5746-08d8fe86e535 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-5396-08d8fe86e535 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-56f9-08d8fe86e535 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-8ec1-08d8fe86e538 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-f364-08d8fe86e5a9 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/MassTransit:Fault--Sample.Contracts.Interfaces.Drugs:InvalidPrescription-- 00010000-4100-0250-4b57-08d8fe86e5d5 MassTransit.Fault<Sample.Contracts.Interfaces.Drugs.InvalidPrescription>
dbug: MassTransit.ReceiveTransport[0]
SEND rabbitmq://localhost/Sample.Contracts.Interfaces.Drugs:InvalidPrescription 00010000-4100-0250-3262-08d8fe86e5d7 Sample.Contracts.Interfaces.Drugs.InvalidPrescription
dbug: MassTransit.ReceiveTransport[0]
SAGA:Prescription.Components.StateMachine.PrescriptionStatus:f6be58b0-13d0-4a80-a6aa-5562d6fa29ba Used Sample.Contracts.Interfaces.Drugs.InvalidPrescription
fail: MassTransit.ReceiveTransport[0]
R-FAULT rabbitmq://localhost/prescription-status 00010000-4100-0250-43d6-08d8fe86e4eb Sample.Contracts.Interfaces.Drugs.InvalidPrescription Prescription.Components.StateMachine.PrescriptionStatus(00:00:00.6500925)
MassTransit.SagaException: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba) Saga exception: Saga update failed
---> MassTransit.RedisIntegration.RedisSagaConcurrencyException: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba) Saga exception: Saga version conflict
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context)
--- End of inner exception stack trace ---
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context)
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context)
at MassTransit.Saga.SendSagaPipe2.Send(SagaRepositoryContext2 context)
at MassTransit.Saga.SendSagaPipe2.Send(SagaRepositoryContext2 context)
at MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory1.Send[T](ConsumeContext1 context, IPipe1 next) at MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory1.Send[T](ConsumeContext1 context, IPipe1 next)
at MassTransit.ExtensionsDependencyInjectionIntegration.ScopeProviders.DependencyInjectionSagaRepositoryContextFactory1.<>c__DisplayClass6_01.<g__CreateScope|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at MassTransit.Saga.Pipeline.Filters.CorrelatedSagaFilter2.GreenPipes.IFilter<MassTransit.ConsumeContext<TMessage>>.Send(ConsumeContext1 context, IPipe1 next) **fail**: MassTransit.ReceiveTransport[0] R-FAULT rabbitmq://localhost/prescription-status 00010000-4100-0250-5e39-08d8fe86e557 Sample.Contracts.Interfaces.Drugs.InvalidPrescription Prescription.Components.StateMachine.PrescriptionStatus(00:00:00.7106839) MassTransit.SagaException: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba) Saga exception: Saga update failed ---> MassTransit.RedisIntegration.RedisSagaConcurrencyException: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba) Saga exception: Saga version conflict at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context) --- End of inner exception stack trace --- at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context) at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context) at MassTransit.Saga.SendSagaPipe2.Send(SagaRepositoryContext2 context) at MassTransit.Saga.SendSagaPipe2.Send(SagaRepositoryContext2 context) at MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory1.Send[T](ConsumeContext1 context, IPipe1 next)
at MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory1.Send[T](ConsumeContext1 context, IPipe1 next) at MassTransit.ExtensionsDependencyInjectionIntegration.ScopeProviders.DependencyInjectionSagaRepositoryContextFactory1.<>c__DisplayClass6_01.<<Send>g__CreateScope|0>d.MoveNext() --- End of stack trace from previous location where exception was thrown --- at MassTransit.Saga.Pipeline.Filters.CorrelatedSagaFilter2.GreenPipes.IFilter<MassTransit.ConsumeContext>.Send(ConsumeContext1 context, IPipe1 next)
fail: MassTransit.ReceiveTransport[0]
R-FAULT rabbitmq://localhost/prescription-status 00010000-4100-0250-0c36-08d8fe86e3cf Sample.Contracts.Interfaces.Drugs.InvalidPrescription Prescription.Components.StateMachine.PrescriptionStatus(00:00:00.8804586)
MassTransit.SagaException: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba) Saga exception: Saga update failed
---> MassTransit.RedisIntegration.RedisSagaConcurrencyException: Prescription.Components.StateMachine.PrescriptionStatus(f6be58b0-13d0-4a80-a6aa-5562d6fa29ba) Saga exception: Saga version conflict
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context)
--- End of inner exception stack trace ---
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context)
at MassTransit.RedisIntegration.Contexts.RedisDatabaseContext1.Update(SagaConsumeContext1 context)
at MassTransit.Saga.SendSagaPipe2.Send(SagaRepositoryContext2 context)
at MassTransit.Saga.SendSagaPipe2.Send(SagaRepositoryContext2 context)
at MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory1.Send[T](ConsumeContext1 context, IPipe1 next) at MassTransit.RedisIntegration.Contexts.RedisSagaRepositoryContextFactory1.Send[T](ConsumeContext1 context, IPipe1 next)
at MassTransit.ExtensionsDependencyInjectionIntegration.ScopeProviders.DependencyInjectionSagaRepositoryContextFactory1.<>c__DisplayClass6_01.<g__CreateScope|0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at MassTransit.Saga.Pipeline.Filters.CorrelatedSagaFilter2.GreenPipes.IFilter<MassTransit.ConsumeContext<TMessage>>.Send(ConsumeContext1 context, IPipe`1 next)

Filtered events don't trigger OnUnhandledEvent

Hey,

I've got a user input processing machine as follows:

            During(
               UserInput,
               When(Key1)
                   .TransitionTo(Menu1),
               When(Key2, ctx => ctx.Instance.IsSpecialUser)
                   .TransitionTo(Menu2),
               When(KeyBack)
                   .Finalize()
           );
           OnUnhandledEvent(
              /* force a re-prompt */
           )

As stated, user can input 1 or BACK, and special user can input also 2. When invalid key is pressed, user is re-prompted in error handler.
But if When(Key2, .. is used, even if condition fails event in no longer treated as unhandled and doesn't trigger an error.

Is it by design and how to undandle/discard an event then?

Thank you

How to handle exceptions in an activity

Hi,

I like Automatonymous up to know, it's only a pitty that there is not much documentation. I can't figure out how to handle exceptions in activities.

When an exception occurs I want to know this after the raise of the event.

Visualizer use Ignore like When

In state machine I have

During(Failed,
                Ignore(CreateEntitiesFaulted)
                );

but generated dot file has edges from Failed state to CreateEntitiesFaulted event.

[Question] Scheduled events lost with service restart

I have set up a Saga that will continue to send users emails daily for a week or until they verify their email address. Everything is working as expected except the scheduled events are being lost if I restart my service. Is there a way to have these persisted so they will be picked up again when the service restarts?

My saga looks like this

public class ResendVerifyEmailReminderStateMachine : MassTransitStateMachine<ResendVerifyEmailReminderState>
{
    public State Active { get; private set; }

    //Init event
    public Event<UserAccountCreated> UserAccountCreated { get; private set; }

    public Event<UserEmailConfirmed> UserEmailConfirmed { get; private set; }

    //Final event
    public Event<MaxVerifyEmailRemindersSent> MarkedAsCompleted { get; private set; }

    public Schedule<ResendVerifyEmailReminderState, ResendVerifyEmailTimeout> ResendConfirmationEmail
    {
        get;
        private set;
    }

    public ResendVerifyEmailReminderStateMachine()
    {
        InstanceState(x => x.CurrentState);

        Event(() => UserAccountCreated, x => x.CorrelateById(context => context.Message.UserId));
        Event(() => UserEmailConfirmed, x => x.CorrelateById(context => context.Message.UserId));
        Event(() => MarkedAsCompleted, x => x.CorrelateById(context => context.Message.UserId));

        Schedule(() => ResendConfirmationEmail, x => x.UserId, x =>
        {
            x.Delay = TimeSpan.FromSeconds(10);
            x.Received = e => e.CorrelateById(context => context.Message.UserId);
        });

        Initially(
            When(UserAccountCreated)
                .Then(context =>
                {
                    context.Instance.Email = context.Data.Email;
                    context.Instance.UserId = context.Data.UserId;
                    context.Instance.FullName = context.Data.FullName;
                })
                .Schedule(ResendConfirmationEmail, context => new ResendVerifyEmailTimeout
                {
                    UserId = context.Data.UserId
                })
                .TransitionTo(Active)
        );

        During(Active,

            When(ResendConfirmationEmail.Received)
                .Then(HandleTimeout)
                .Schedule(ResendConfirmationEmail, context => new ResendVerifyEmailTimeout
                {
                    UserId = context.Data.UserId
                }),

            When(UserEmailConfirmed)
                .Unschedule(ResendConfirmationEmail)
                .Finalize(),

            When(MarkedAsCompleted)
                .Finalize()
        );



        SetCompletedWhenFinalized();
    }

    private static void HandleTimeout(
        BehaviorContext<ResendVerifyEmailReminderState, ResendVerifyEmailTimeout> context)
    {
        context.Instance.ReminderEmailsSent++;
        context.Instance.LastModified = DateTime.UtcNow;

        if (context.Instance.ReminderEmailsSent > 7)
        {
            context.Publish(new MaxVerifyEmailRemindersSent {UserId = context.Instance.CorrelationId});
        }
        else
        {
            context.Publish(new VerifyEmailTokenExpired
            {
                Email = context.Instance.Email,
                FullName = context.Instance.FullName
            });
        }
    }
}

Receive endpoint is setup as:

private static void CreateSchedulerEndpoint(IContainer container, IRabbitMqBusFactoryConfigurator cfg,
            IRabbitMqHost host)
    {
        cfg.ReceiveEndpoint(host, UserQueues.ScheduledEndpoint.QueueName, configurator =>
        {
            cfg.UseMessageScheduler(configurator.InputAddress);

            configurator.LoadFrom(container);

            configurator.UseInMemoryOutbox();
        });
    }

And scheduler setup which is called in the .Start() of my service

private static IScheduler CreateScheduler()
{
    var schedulerFactory = new StdSchedulerFactory();
    var scheduler = MassTransit.Util.TaskUtil.Await(() => schedulerFactory.GetScheduler());

    return scheduler;
}

Am I missing something?

Thanks in advance.

How well is Automatonymous optimized?

Automatonymous looks really interesting to me, but It seems like I'm forced to do async/await everywhere, and I'm not sure how it's performance is, with that in mind.
I don't really care for Automatonymous doing a lot of IO work, maybe here and there in combination with Mass Transit, but a lot of in-memory usage would be used. Is this a use case Automatonymous was designed for at all?

Dynamic states

I completely understand the examples in the Wiki, but was curious if there was a way to have user configurable state machines (eg, user defines 5 states and the rules, and I can build how that stuff gets created/loaded if necessary) and have Automatonymous handle the transitions and the rules etc?

The quick start code will not compile

The Relationship class implements StateMachineInstance that does not seem to exist. The then clause in the When(Introduce) will not compile either.

Edges in visualizer shared between states.

I'm working on creating a state machine with Automatonymous and I'm running into an issues with the visualizer. I'd like to run the graph by the client but the graph doesn't really make any sense since the Events/transitions are being shared between states in the graph.

For example, State 1 and state 2 transition to state 3 but they both use the same event which then appears as its own node on the graph. Not to0 big of an issue with 3 states and 1 transition since it only results in 4 vertexes and 3 edges but I have ~30 states and 8 events and the graph then has 130 edges in the edge collection and turns it into a soup of lines that aren't very useful since you can't tell the state->event->state relationships. I'd like to conceptually make each transition appear to be unique in the graph. This would essentially just make the event a 'label' and not a full blown state with no outline in the graph.

I'm looking at modifying the graphing code to make the edges unique but I just wanted to make sure that I'm not totally off-base in regards to this being a problem. It seems like the graphing would break down on any moderately large state machine and that I may not have been the first person to run into this.

Any suggestions?
state

How to use instance specific state type?

In my instance's type, how do i use my custom state type instead of "string" or Automatonymous.State for

public enum HumorState 
{
   Grinny,
   Smiley,
   Laughy,
   LOL
}
class RidiculousPerson
{
   public string Name { get; set; }
   public int Age { get; set; }
   public HumorState State { get; set; }
}

I've not figured out how to integrate this kind of type to statemachine via InstanceState method.
I'm using 3.5.0.0(?! hmm. I though I pulled Automatonymous.3.5.5-develop but dll version displayed in VisualStudio properties window does mismatch.)

Any hint?

CompositeEvent raises in inapropriate order

Hi,
it seems that composite event raises right before the last event in composition.
I faced this bug working with MT saga and it was already discussed here. But your suggestion to move handler configuration doesn't work for me.
I modified When_combining_events_into_a_single_event fixture (in Combine_Spec.cs) like this

    [TestFixture]
    public class When_combining_events_into_a_single_event
    {
        [Test]
        public async void Should_have_called_combined_event()
        {
            _machine = new TestStateMachine();
            _instance = new Instance();
            await _machine.RaiseEvent(_instance, _machine.Start);

            await _machine.RaiseEvent(_instance, _machine.First);
            await _machine.RaiseEvent(_instance, _machine.Second);

            Assert.IsTrue(_instance.Called);
        }

        [Test]
        public async Task Should_have_called_combined_event_after_all_events()
        {
            _machine = new TestStateMachine();
            _instance = new Instance();
            await _machine.RaiseEvent(_instance, _machine.Start);

            await _machine.RaiseEvent(_instance, _machine.First);
            await _machine.RaiseEvent(_instance, _machine.Second);

            Assert.IsTrue(_instance.CalledAfterAll);
        }

        [Test]
        public async void Should_not_call_for_one_event()
        {
            _machine = new TestStateMachine();
            _instance = new Instance();
            await _machine.RaiseEvent(_instance, _machine.Start);

            await _machine.RaiseEvent(_instance, _machine.First);

            Assert.IsFalse(_instance.Called);
        }

        [Test]
        public async void Should_not_call_for_one_other_event()
        {
            _machine = new TestStateMachine();
            _instance = new Instance();
            await _machine.RaiseEvent(_instance, _machine.Start);

            await _machine.RaiseEvent(_instance, _machine.Second);

            Assert.IsFalse(_instance.Called);
        }

        TestStateMachine _machine;
        Instance _instance;


        class Instance
        {
            public CompositeEventStatus CompositeStatus { get; set; }
            public bool Called { get; set; }
            public bool CalledAfterAll { get; set; }
            public State CurrentState { get; set; }
        }


        sealed class TestStateMachine :
            AutomatonymousStateMachine<Instance>
        {
            public TestStateMachine()
            {
                CompositeEvent(() => Third, x => x.CompositeStatus, First, Second);

                Initially(
                    When(Start)
                        .TransitionTo(Waiting));

                During(Waiting,
                    When(First)
                        .Then(context =>
                        {
                            context.Instance.CalledAfterAll = false;
                        }),
                    When(Second)
                        .Then(context =>
                        {
                            context.Instance.CalledAfterAll = false;
                        }),
                    When(Third)
                        .Then(context =>
                        {
                            context.Instance.Called = true;
                            context.Instance.CalledAfterAll = true;
                        })
                        .Finalize());
            }

            public State Waiting { get; private set; }

            public Event Start { get; private set; }

            public Event First { get; private set; }
            public Event Second { get; private set; }
            public Event Third { get; private set; }
        }
    }

and failing test Should_have_called_combined_event_after_all_events() proves this.

Extend During with more states

Currently when using During and some event that can happen in more than one state, we must duplicate the event handling in each state. Some events are even possible in all states and we get even more duplication, leading to extensive code changes in case the logic needs to be changed.

Wouldn't it be nicer to have During(State[], ...) and DuringAny(..)?

License file missing

Please include a license file. At the moment it is unclear under what license the code is released.

Catch in IfElse leads to Automatonymous.EventExecutionException

Is this a bug report?

Yes

Can you also reproduce the problem with the latest version?

Yes

Environment

  1. Dotnet version: .NET 5.0
  2. Operating system: Windows 10 x64
  3. IDE (Rider, VS Code, Visual Studio): Rider

Steps to Reproduce

  1. Create MassTransitStateMachine with IfElse.
  2. In else clause create an activity that throws an exception.
  3. Catch that exception.

Expected Behavior

Catching in IfElse doesn't result in an exception.

Actual Behavior

After catching exception is thrown:

 ---> Automatonymous.EventExecutionException: The CreateModelRequested<CreateModelRequestedEvent> (Event) execution faulted
 ---> System.Exception: Exception of type 'System.Exception' was thrown.
   at WebApplication.ThrowExceptionActivity.Execute(BehaviorContext`2 context, Behavior`2 next) in C:\git\masstransit-ifelse-bug\WebApplication\Saga\ThrowExceptionActivity.cs:line 23
   at Automatonymous.Behaviors.ActivityBehavior`1.Automatonymous.Behavior<TInstance>.Execute[T](BehaviorContext`2 context)
   --- End of inner exception stack trace ---
   at Automatonymous.Behaviors.ExceptionBehavior`2.Automatonymous.Behavior<TInstance,TData>.Faulted[TException](BehaviorExceptionContext`3 context)
   at Automatonymous.Activities.ExceptionBehavior`3.Automatonymous.Behavior<TInstance>.Execute(BehaviorContext`1 context)
   at Automatonymous.Activities.TransitionActivity`1.Automatonymous.Activity<TInstance>.Execute(BehaviorContext`1 context, Behavior`1 next)
   at Automatonymous.Behaviors.ActivityBehavior`1.Automatonymous.Behavior<TInstance>.Execute[T](BehaviorContext`2 context)
   at Automatonymous.Activities.ConditionActivity`2.Automatonymous.Activity<TInstance>.Execute[T](BehaviorContext`2 context, Behavior`2 next)
   at Automatonymous.Activities.ActionActivity`2.Automatonymous.Activity<TInstance,TData>.Execute(BehaviorContext`2 context, Behavior`2 next)
   at Automatonymous.Behaviors.ActivityBehavior`1.Automatonymous.Behavior<TInstance>.Execute[T](BehaviorContext`2 context)
   --- End of inner exception stack trace ---
   at Automatonymous.Behaviors.ExceptionBehavior`2.Automatonymous.Behavior<TInstance,TData>.Faulted[TException](BehaviorExceptionContext`3 context)
   at Automatonymous.Activities.ConditionActivity`2.Automatonymous.Activity<TInstance>.Faulted[T,TException](BehaviorExceptionContext`3 context, Behavior`2 next)
   at Automatonymous.Behaviors.LastBehavior`1.Automatonymous.Behavior<TInstance>.Faulted[T,TException](BehaviorExceptionContext`3 context)
   at Automatonymous.Behaviors.DataBehavior`2.Automatonymous.Behavior<TInstance,TData>.Faulted[TException](BehaviorExceptionContext`3 context)
   at Automatonymous.Behaviors.ExceptionTypeCache.CachedConfigurator`1.Automatonymous.Behaviors.ExceptionTypeCache.CachedConfigurator.Faulted[TInstance,TData](Behavior`2 behavior, BehaviorContext`2 context, Exception exceptio
n)
   at Automatonymous.Behaviors.ExceptionTypeCache.Faulted[TInstance,TData](Behavior`2 behavior, BehaviorContext`2 context, Exception exception)
   at Automatonymous.Behaviors.ActivityBehavior`1.Automatonymous.Behavior<TInstance>.Execute[T](BehaviorContext`2 context)
   at Automatonymous.States.StateMachineState`1.Automatonymous.State<TInstance>.Raise[T](EventContext`2 context)
   at Automatonymous.States.StateMachineState`1.Automatonymous.State<TInstance>.Raise[T](EventContext`2 context)
   at Automatonymous.AutomatonymousStateMachine`1.Automatonymous.StateMachine<TInstance>.RaiseEvent[T](EventContext`2 context)
   at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next)
   at Automatonymous.Pipeline.StateMachineSagaMessageFilter`2.Send(SagaConsumeContext`2 context, IPipe`1 next)
   at MassTransit.Saga.MissingSagaPipe`2.Send(SagaConsumeContext`2 context)
   at MassTransit.Saga.MissingSagaPipe`2.Send(SagaConsumeContext`2 context)
   at MassTransit.Saga.MissingSagaPipe`2.Send(SagaConsumeContext`2 context)
   at MassTransit.Saga.SendSagaPipe`2.Send(SagaRepositoryContext`2 context)
   at MassTransit.EntityFrameworkCoreIntegration.Saga.Context.EntityFrameworkSagaRepositoryContextFactory`1.<>c__DisplayClass5_0`1.<<Send>b__1>d.MoveNext()
--- End of stack trace from previous location ---
   at MassTransit.EntityFrameworkCoreIntegration.Saga.Context.EntityFrameworkSagaRepositoryContextFactory`1.<>c__DisplayClass8_0.<<WithinTransaction>g__Create|0>d.MoveNext()
--- End of stack trace from previous location ---
   at MassTransit.EntityFrameworkCoreIntegration.Saga.Context.EntityFrameworkSagaRepositoryContextFactory`1.WithinTransaction[T](DbContext context, CancellationToken cancellationToken, Func`1 callback)
   at MassTransit.EntityFrameworkCoreIntegration.Saga.Context.EntityFrameworkSagaRepositoryContextFactory`1.WithinTransaction[T](DbContext context, CancellationToken cancellationToken, Func`1 callback)
   at MassTransit.EntityFrameworkCoreIntegration.Saga.Context.EntityFrameworkSagaRepositoryContextFactory`1.WithinTransaction[T](DbContext context, CancellationToken cancellationToken, Func`1 callback)
   at MassTransit.EntityFrameworkCoreIntegration.Saga.Context.EntityFrameworkSagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, IPipe`1 next)
   at MassTransit.EntityFrameworkCoreIntegration.Saga.Context.EntityFrameworkSagaRepositoryContextFactory`1.Send[T](ConsumeContext`1 context, IPipe`1 next)
   at MassTransit.ExtensionsDependencyInjectionIntegration.ScopeProviders.DependencyInjectionSagaRepositoryContextFactory`1.<>c__DisplayClass6_0`1.<<Send>g__CreateScope|0>d.MoveNext()
--- End of stack trace from previous location ---
   at MassTransit.Saga.Pipeline.Filters.CorrelatedSagaFilter`2.GreenPipes.IFilter<MassTransit.ConsumeContext<TMessage>>.Send(ConsumeContext`1 context, IPipe`1 next)

Reproducible Demo

https://github.com/qrogi/masstransit-ifelse-bug
A simple web API with get method, that initiating the saga through RabbitMQ.
Persistence with PostgreSQL, don't forget to change connectionString.

Feature Request : Add a utility to the state machine to allow consuming code to subscribe to any state change

Copied from my original request :

I would like to add an implementation in my state machine definition that would allow me to execute some code when ever the state of a saga instance changes. That is regardless of what the current state of the saga.

In the state machine definition it would be nice if we can define something like :
WhenEnter(State.Any,Callback ... or WhenEnterAnyState(Callback ... and something similar. That way we can define a common code to execute whenever the state of the saga changes.

It is worth mentioning here that, in the current version of MT(3.1.0.0) I was able to achieve something similar by creating my own version version of the "TransitionTo" extension method. like below (I hope it doesn't make you cringe too much)

public static EventActivityBinder< TInstance, TData > TransitionToAndBlah< TInstance, TData >(
            this EventActivityBinder< TInstance, TData > source,
            State toState) where TInstance : MrCycle
        {
            return source.TransitionTo( toState ).Add( new ActionActivity< TInstance, TData >(MyCallBack);
        }

Scheduled expirationId / tokenId usage

Could someone explain meaning of expirationId / tokenId in scheduled event ?

Schedule(() => CartExpired, x => x.ExpirationId ??????, x => { ..... });

Documentation says "This is an identifier that is used by the state machine's scheduling feature, to capture the scheduled message identifier". But it's not clear if this is property which saga needs only for internal needs or you can use it somehow to control scheduled event.

p.s. if there is better description somewhere please point.

Thanks.

Is there any option to have Fault<> consumer in MassTransitStateMachine?

I have a saga which subscribes to response events which are produced by external services. In other words, saga publishes message, external event handler subscribed and responds to saga. These external event handlers have also Fault<> consumers. So, the question is, can saga be configured subscribe to these fault consumers ?

3.0.5-beta Nuget Package not working

If you install with the latest prerelease I am only getting 3.0.4-beta, which is causing a whole heap of issues with trying to checkout MassTransit 3.0.11-beta as it as a dependency that cannot be resolved.

The 3.0.5 package is listed on nuget but that is not what is being installed when running a nuget install.

Enhancement: Let Interfaces start with a cappital I

It would be great if all Interfaces could start with a cappital I to be compatibel to the dotnet base classes.
public interface State
would change to
public interface IState

I find it hard to navigate in the code without this

Unable to determine if transition is allowed

StateMachine.NextEvents() tells me what events I can issue to attempt a transition. Is there a way the machine is able to tell me if an event is actually permissable without executing the transition?

The introspection test The_next_events_should_be_known shows that the machine is able to enumerate the event correctly, but this test has no guard conditions in the state machine.

If one of the transitions was amended to support:

During(Greeted,
        When(Handshake, (c => c.Instance.Data.Grip = GripType.Perfect)
                .TransitionTo(Loved),
        When(Handshake, (c => c.Instance.Data.Grip = (GripType.TooHard || GripType.TooLimp)
                .TransitionTo(Pissed),

Given that calls are required to pass the object containing state - how would you obtain the result that there are only 2 were valid events given the guard constraints now placed on Greeted and constrained with "GripType.Perfect" .... the transition to "Pissed" is not viable?

Missing release 4.1.6

MassTransit.Automatonymous-5.5.0-develop.1948 depends on Automatonymous (>= 4.1.6) but that version is not yet available on NuGet.org?

Event Filter Executes Multiple Times

If an activity filter is specified:

When(MyEvent, context => context.Data.IsValid)

The filter is executed multiple times, based upon how many activities are added to the event behavior. The engine should potentially group and execute the activities to avoid multiple filter evaluations.

It would also be useful to be able to evaluate the filter outside of the execution to determine if the event would be handled by the instance.

How to handle multiple requests of the same type?

I'd like to have an ability to store multiple requests with the same signature in my MassTransitStateMachine. e.g. something like this:

class Workflow : MassTransitStateMachine<Work> 
{
    public List<Request<Work, ProcessRenderTask, ProcessRenderTaskFinished>> RenderRequest { get; private set; }
}

is it possible? or maybe some workaround to implement such behavior?

How to re-use services from multiple sagas?

There's something I struggle with, but it may be just limited understanding, can you please advise.
I have two sagas (say, CreateFoo and UpdateFoo).
There's a service (BarService) that both sagas would use to fetch - say - a list of available Bars.
My naive thinking would make me implement something like BarService.GetBars(), responding to a message called GetBars.
However, when I register outbound message delivery (message types to queues) I can only send the GetBars message to one of the queues. (CreateFoo and UpdateFoo are implemented in the same process).
It also means I have to implement BarService.GetBars() twice, to respond to the same message from the two different sagas.

Similarly, the response message (say, GetBarsResult) can only be consumed by a single saga. So BarService has to be aware where the request came from and send the response to the mathing response queue.

So the current understanding is that I have to create GetBars and GetBarsResult twice, in different namespaces for each saga.

Is this a correct understanding or am I missing something? Is it possible to re-use the same message type for both sagas somehow? I understand it would require something like correlating messages to multiple sagas somehow. Is it possible?

Saga starts with non-initial event

Let's say we have a saga with composite events :

private void SetupStateMachine()
{
  this.InstanceState(x => x.CurrentState);

  this.Event(
      () => this.FirstEvent,
      x =>
      {
          x.CorrelateById(ctx => new Guid(ctx.Message.Id));

          x.SetSagaFactory(
              context =>
              {
                  var now = DateTime.UtcNow;
                  return new SagaInstance
                  {
                      CorrelationId = new Guid(context.Message.Id),
                      Id = context.Message.Id,
                      CreatedOn = now,
                      UpdatedOn = now
                  };
              });
      });

  this.Event(() => this.SecondEvent, x => { x.CorrelateById(ctx => new Guid(ctx.Message.Id)); });
  this.Event(() => this.ThirdEvent, x => { x.CorrelateById(ctx => new Guid(ctx.Message.Id)); });
  this.Event(() => this.FourthEvent, x => { x.CorrelateById(ctx => new Guid(ctx.Message.Id)); });

  this.CompositeEvent(
      () =>
          this.FinalEvent,
      x => x.DataAvailable,
      CompositeEventOptions.IncludeInitial,
      this.SecondEvent,
      this.ThirdEvent,
      this.FourthEvent);

  this.Initially(
      this.When(this.FirstEvent)
          .Then(
              context =>
              {
                  context.Instance.UpdatedOn = DateTime.UtcNow;
              })
          .TransitionTo(this.ProcessingState));

  this.During(
      this.ProcessingState,
      this.When(this.SecondEvent).Then(
          context =>
          {
              context.Instance.UpdatedOn = DateTime.UtcNow;
          }),
      this.When(this.ThirdEvent).Then(
          context =>
          {
              context.Instance.UpdatedOn = DateTime.UtcNow;
          }),
      this.When(this.FourthEvent).Then(
          context =>
          {
              context.Instance.UpdatedOn = DateTime.UtcNow;
          }),
      this.When(this.FinalEvent)
          .Finalize(),
      this.Ignore(this.FirstEvent));

  this.SetCompletedWhenFinalized();
}

The way our system is design, it's possible that the Second, Third or Fourth event occurs before the FirstEvent. In that case, the saga seems to start and be persist but is not initilize properly (for instance, the document id stays null). We would expect the Second, Third and Fourth events to wait in their queue until the saga with the proper correlation id gets created.

Is there any way we could change our code in order to resolve this issue.

Automatonymous.NHibernateIntegration.dll v3.6.0 is not signed

I get an error in Visual Studio that the file Automatonymous.NHibernateIntegration.dll is not signed.

I can fix the issue for myself, but it would be nice to fix it for everyone, but I don’t see a v3.6.0 tag in this repo. Am I missing something?

Confirmation of the signing issue:

C:\NuGetPackages\Automatonymous.NHibernate.3.6.0\lib\net452>sn -vf Automatonymous.NHibernateIntegration.dll

Microsoft (R) .NET Framework Strong Name Utility  Version 4.0.30319.17929
Copyright (c) Microsoft Corporation.  All rights reserved.

Automatonymous.NHibernateIntegration.dll does not represent a strongly named assembly

How to subscribe to any state change

I would like to add an implementation in my state machine definition that would allow me to execute some code when ever the state of a saga instance changes. That is regardless of what the current state of the saga.

I see there that there are some methods that would allow you to subscribe to state changes on from or to a predetermined state. I am referring to the methods WhenEnter,WhenLeave .. on AutomatonymousStateMachine class. In order to use this I would have to explicitly tell it what state to consider.

So my question is ,

-Is there a utility that would allow me to subscribe to any state change and allow me to execute some piece of code when ever that happens?

I hope this makes sense.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.