/// <summary>
        /// Schedules the specified command.
        /// </summary>
        /// <param name="scheduledCommand">The scheduled command.</param>
        /// <returns>
        /// A task that is complete when the command has been successfully scheduled.
        /// </returns>
        /// <exception cref="System.NotSupportedException">Non-immediate scheduling is not supported.</exception>
        public virtual async Task Schedule(IScheduledCommand <TAggregate> scheduledCommand)
        {
            if (scheduledCommand.Result is CommandDeduplicated)
            {
                return;
            }

            if (scheduledCommand.Command.CanBeDeliveredDuringScheduling() && scheduledCommand.IsDue())
            {
                if (!await PreconditionHasBeenMet(scheduledCommand))
                {
                    CommandScheduler.DeliverIfPreconditionIsMetSoon(
                        scheduledCommand,
                        Configuration.Current);
                }
                else
                {
                    // resolve the command scheduler so that delivery goes through the whole pipeline
                    await Configuration.Current.CommandScheduler <TAggregate>().Deliver(scheduledCommand);

                    return;
                }
            }

            if (scheduledCommand.Result == null)
            {
                throw new NotSupportedException("Deferred scheduling is not supported by the current command scheduler pipeline configuration.");
            }
        }
Пример #2
0
        public async Task Schedule(IScheduledCommand <TAggregate> scheduledCommand)
        {
            var storedScheduledCommand = await Storage.StoreScheduledCommand(
                scheduledCommand,
                createCommandSchedulerDbContext,
                (scheduledCommandEvent1, db) => ClockNameForEvent(this, scheduledCommandEvent1, db));

            Activity.OnNext(new CommandScheduled(scheduledCommand)
            {
                ClockName = storedScheduledCommand.Clock.Name
            });

            // deliver the command immediately if appropriate
            if (scheduledCommand.IsDue(storedScheduledCommand.Clock))
            {
                // sometimes the command depends on a precondition event that hasn't been saved
                if (!await commandPreconditionVerifier.IsPreconditionSatisfied(scheduledCommand))
                {
                    this.DeliverIfPreconditionIsSatisfiedWithin(
                        TimeSpan.FromSeconds(10),
                        scheduledCommand,
                        eventBus);
                }
                else
                {
                    var scheduler = Configuration.Current.CommandScheduler <TAggregate>();
                    await scheduler.Deliver(scheduledCommand);
                }
            }
        }
Пример #3
0
        internal static async Task <ScheduledCommand> StoreScheduledCommand <TAggregate>(
            IScheduledCommand <TAggregate> scheduledCommand,
            Func <CommandSchedulerDbContext> createDbContext,
            Func <IScheduledCommand <TAggregate>, CommandSchedulerDbContext, Task <string> > clockNameForEvent)
            where TAggregate : class
        {
            ScheduledCommand storedScheduledCommand;

            using (var db = createDbContext())
            {
                var domainTime = Domain.Clock.Now();

                // get or create a clock to schedule the command on
                var clock = scheduledCommand.Clock as Clock;
                if (clock == null)
                {
                    var clockName = await clockNameForEvent(scheduledCommand, db);

                    var clockStartTime = scheduledCommand.Clock?.Now() ??
                                         domainTime;

                    clock = await GetOrAddSchedulerClock(
                        db,
                        clockName,
                        clockStartTime);
                }

                storedScheduledCommand = CreateStoredScheduledCommand(
                    scheduledCommand,
                    domainTime,
                    clock);

                if (scheduledCommand.IsDue() &&
                    !scheduledCommand.Command.RequiresDurableScheduling())
                {
                    storedScheduledCommand.NonDurable = true;
                    return(storedScheduledCommand);
                }

                Debug.WriteLine(
                    $"Storing command '{scheduledCommand.Command.CommandName}' ({scheduledCommand.TargetId}:{storedScheduledCommand.SequenceNumber}) on clock '{clock.Name}'");

                await SaveScheduledCommandToDatabase(db,
                                                     storedScheduledCommand,
                                                     scheduledCommand);
            }

            scheduledCommand.IfTypeIs <ScheduledCommand <TAggregate> >()
            .ThenDo(c => c.SequenceNumber = storedScheduledCommand.SequenceNumber);

            return(storedScheduledCommand);
        }
Пример #4
0
        /// <summary>
        /// Schedules the specified command.
        /// </summary>
        /// <param name="scheduledCommand">The scheduled command.</param>
        /// <returns>
        /// A task that is complete when the command has been successfully scheduled.
        /// </returns>
        /// <exception cref="System.NotSupportedException">Non-immediate scheduling is not supported.</exception>
        public virtual async Task Schedule(IScheduledCommand <TAggregate> scheduledCommand)
        {
            if (scheduledCommand.IsDue() &&
                await VerifyPrecondition(scheduledCommand))
            {
                // resolve the command scheduler so that delivery goes through the whole pipeline
                await Configuration.Current.CommandScheduler <TAggregate>().Deliver(scheduledCommand);

                return;
            }

            if (scheduledCommand.Result == null)
            {
                throw new NotSupportedException("Deferred scheduling is not supported.");
            }
        }
        private async Task Schedule(IScheduledCommand <TAggregate> cmd, Func <IScheduledCommand <TAggregate>, Task> next)
        {
            var storedCommand = await Storage.StoreScheduledCommand(
                cmd,
                createDbContext,
                GetClockName);

            if (cmd.IsDue(storedCommand.Clock))
            {
                var preconditionVerifier = Configuration.Current.Container.Resolve <ICommandPreconditionVerifier>();

                // sometimes the command depends on a precondition event that hasn't been saved
                if (await preconditionVerifier.IsPreconditionSatisfied(cmd))
                {
                    await Configuration.Current.CommandScheduler <TAggregate>().Deliver(cmd);
                }
            }

            await next(cmd);
        }
Пример #6
0
        internal static async Task <ScheduledCommand> StoreScheduledCommand <TAggregate>(
            IScheduledCommand <TAggregate> scheduledCommand,
            Func <CommandSchedulerDbContext> createDbContext,
            Func <IScheduledCommand <TAggregate>, CommandSchedulerDbContext, Task <string> > clockNameForEvent) where TAggregate : class, IEventSourced
        {
            ScheduledCommand storedScheduledCommand;

            using (var db = createDbContext())
            {
                var domainTime = Domain.Clock.Now();

                // get or create a clock to schedule the command on
                var clockName = await clockNameForEvent(scheduledCommand, db);

                var schedulerClock = await db.Clocks.SingleOrDefaultAsync(c => c.Name == clockName);

                if (schedulerClock == null)
                {
                    Debug.WriteLine(String.Format("Creating clock '{0}' @ {1}", clockName, domainTime));

                    schedulerClock = new Clock
                    {
                        Name      = clockName,
                        UtcNow    = domainTime,
                        StartTime = domainTime
                    };
                    db.Clocks.Add(schedulerClock);
                    await db.SaveChangesAsync();
                }

                storedScheduledCommand = new ScheduledCommand
                {
                    AggregateId       = scheduledCommand.AggregateId,
                    SequenceNumber    = scheduledCommand.SequenceNumber,
                    AggregateType     = AggregateType <TAggregate> .EventStreamName,
                    SerializedCommand = scheduledCommand.ToJson(),
                    CreatedTime       = domainTime,
                    DueTime           = scheduledCommand.DueTime,
                    Clock             = schedulerClock
                };

                if (scheduledCommand.IsDue(storedScheduledCommand.Clock) &&
                    !scheduledCommand.Command.RequiresDurableScheduling)
                {
                    storedScheduledCommand.NonDurable = true;
                    return(storedScheduledCommand);
                }

                Debug.WriteLine(String.Format("Storing command '{0}' ({1}:{2}) on clock '{3}'",
                                              scheduledCommand.Command.CommandName,
                                              scheduledCommand.AggregateId,
                                              scheduledCommand.SequenceNumber,
                                              clockName));

                db.ScheduledCommands.Add(storedScheduledCommand);

                while (true)
                {
                    try
                    {
                        db.SaveChanges();
                        scheduledCommand.Result = new CommandScheduled(scheduledCommand)
                        {
                            ClockName = schedulerClock.Name
                        };
                        break;
                    }
                    catch (DbUpdateException exception)
                    {
                        if (exception.IsConcurrencyException())
                        {
                            if (storedScheduledCommand.SequenceNumber < 0)
                            {
                                // for scheduler-assigned sequence numbers, decrement and retry
                                storedScheduledCommand.SequenceNumber--;
                            }
                            else
                            {
                                // this is not a scheduler-assigned sequence number, so the concurrency exception indicates an actual issue
                                break;
                            }
                        }
                        else
                        {
                            throw;
                        }
                    }
                }
            }

            return(storedScheduledCommand);
        }