示例#1
0
 private void CreateClockIfNotExists()
 {
     try
     {
         using (var con = new SqlConnection(TestSettings.ConnectionStringCommandScheduler))
         {
             con.Open();
             using (
                 var command =
                     new SqlCommand(
                         $"select 1 from [Scheduler].[Clock] where [Name]='{TestSettings.CustomClockName}'",
                         con))
             {
                 var exists = command.ExecuteScalar();
                 if (exists == null)
                 {
                     _configuration
                     .SchedulerClockRepository()
                     .CreateClock(TestSettings.CustomClockName, Clock.Now());
                 }
             }
         }
     }
     catch (Exception e)
     {
         Console.WriteLine(e);
     }
 }
示例#2
0
        public async Task When_ServiceBusCommandQueueSender_is_subscribed_to_the_service_bus_then_messages_are_scheduled_to_trigger_event_based_scheduled_commands()
        {
            VirtualClock.Start(DateTimeOffset.Now.AddHours(-13));

            using (var queueReceiver = CreateQueueReceiver())
            {
                var aggregateIds = Enumerable.Range(1, 5)
                                   .Select(_ => Guid.NewGuid())
                                   .ToArray();

                aggregateIds.ForEach(async id =>
                {
                    var order = CommandSchedulingTests_EventSourced.CreateOrder(orderId: id);

                    // due enough in the future that the scheduler won't apply the commands immediately
                    var due = Clock.Now().AddSeconds(5);
                    order.Apply(new ShipOn(due));

                    Console.WriteLine(new { ShipOrderId = order.Id, due });

                    await Configuration.Current.Repository <Order>().Save(order);
                });

                // reset the clock so that when the messages are delivered, the target commands are now due
                Clock.Reset();

                await queueReceiver.StartReceivingMessages();

                schedulerActivity
                .Select(a => Guid.Parse(a.TargetId))
                .ShouldBeEquivalentTo(aggregateIds);
            }
        }
示例#3
0
        /// <summary>
        ///     Initializes a new instance of the <see cref="Event" /> class.
        /// </summary>
        protected Event()
        {
            var command = CommandContext.Current?.Command;

            if (command != null)
            {
                this.SetActor(command);
                ETag = command.ETag;
            }

            Timestamp = Clock.Now();
        }
示例#4
0
        public async Task When_a_command_has_been_completed_and_a_message_for_it_arrives_the_message_is_also_Completed()
        {
            var aggregateId = Any.Guid();

            // due in the past so that it's scheduled immediately
            var order = CommandSchedulingTests_EventSourced.CreateOrder(orderId: aggregateId)
                        .Apply(new ShipOn(Clock.Now().AddSeconds(-5)));

            queueSender.MessageDeliveryOffsetFromCommandDueTime = TimeSpan.FromSeconds(0);

            await Configuration.Current.Repository <Order>().Save(order);

            using (var receiver = CreateQueueReceiver())
            {
                var receivedMessages = new List <IScheduledCommand>();
                receiver.Messages
                .Where(m => m.IfTypeIs <IScheduledCommand <Order> >()
                       .Then(c => c.TargetId == aggregateId.ToString())
                       .ElseDefault())
                .Subscribe(receivedMessages.Add);

                await receiver.StartReceivingMessages();

                await Task.Delay(TimeSpan.FromSeconds(5));

                receivedMessages.Should()
                .ContainSingle(m => m.IfTypeIs <IScheduledCommand <Order> >()
                               .Then(c => c.TargetId == aggregateId.ToString())
                               .ElseDefault());
            }

            using (var receiver = CreateQueueReceiver())
            {
                var receivedMessages = new List <IScheduledCommand>();

                receiver.Messages
                .Where(m => m.IfTypeIs <IScheduledCommand <Order> >()
                       .Then(c => c.TargetId == aggregateId.ToString())
                       .ElseDefault())
                .Subscribe(receivedMessages.Add);

                await receiver.StartReceivingMessages();

                await Task.Delay(TimeSpan.FromSeconds(10));

                receivedMessages.Count.Should().Be(0);
            }
        }
示例#5
0
        public async Task When_a_command_has_been_completed_and_a_message_for_it_arrives_the_message_is_also_Completed()
        {
            var aggregateId = Any.Guid();

            // due in the past so that it's scheduled immediately
            var order = CommandSchedulingTests.CreateOrder(orderId: aggregateId)
                        .Apply(new ShipOn(Clock.Now().AddSeconds(-5)));

            queueSender.MessageDeliveryOffsetFromCommandDueTime = TimeSpan.FromSeconds(0);

            await orderRepository.Save(order);

            await RunCatchup();

            using (var receiver = CreateQueueReceiver())
            {
                var receivedMessages = new List <IScheduledCommand>();
                receiver.Messages
                .Where(c => c.AggregateId == aggregateId)
                .Subscribe(receivedMessages.Add);

                await receiver.StartReceivingMessages();

                await Task.Delay(TimeSpan.FromSeconds(5));

                receivedMessages.Should().ContainSingle(e => e.AggregateId == aggregateId);
            }

            using (var receiver = CreateQueueReceiver())
            {
                var receivedMessages = new List <IScheduledCommand>();

                receiver.Messages
                .Where(c => c.AggregateId == aggregateId)
                .Subscribe(receivedMessages.Add);

                await receiver.StartReceivingMessages();

                await Task.Delay(TimeSpan.FromSeconds(10));

                receivedMessages.Count().Should().Be(0);
            }
        }
示例#6
0
        public async Task When_a_command_trigger_message_arrives_early_it_is_not_Completed()
        {
            VirtualClock.Start(Clock.Now().AddHours(-1));

            var aggregateId     = Any.Guid();
            var appliedCommands = new List <ICommandSchedulerActivity>();

            scheduler.Activity
            .Where(c => c.ScheduledCommand.AggregateId == aggregateId)
            .Subscribe(appliedCommands.Add);

            using (var receiver = CreateQueueReceiver())
            {
                await receiver.StartReceivingMessages();

                // due enough in the future that the scheduler won't apply the commands immediately
                var order = await CommandSchedulingTests.CreateOrder(orderId : aggregateId)
                            .ApplyAsync(new ShipOn(Clock.Now().AddMinutes(2)));

                await orderRepository.Save(order);

                await RunCatchup();

                await receiver.Messages
                .FirstAsync(c => c.AggregateId == aggregateId)
                .Timeout(TimeSpan.FromMinutes(1));

                await Task.Delay(1000);

                appliedCommands.Should().BeEmpty();
            }

            Clock.Reset();

            using (var receiver = CreateQueueReceiver())
            {
                await receiver.StartReceivingMessages();

                await Task.Delay(1000);

                appliedCommands.Should().Contain(c => c.ScheduledCommand.AggregateId == aggregateId);
            }
        }
示例#7
0
        /// <summary>
        /// Saves a snapshot of the aggregate.
        /// </summary>
        public static async Task SaveSnapshot <TAggregate>(
            this ISnapshotRepository repository,
            TAggregate aggregate)
            where TAggregate : class, IEventSourced
        {
            var snapshotCreator = Configuration.Current.Container.Resolve <ICreateSnapshot <TAggregate> >();

            var snapshot = snapshotCreator.CreateSnapshot(aggregate);

            snapshot.AggregateId       = aggregate.Id;
            snapshot.AggregateTypeName = AggregateType <TAggregate> .EventStreamName;
            snapshot.LastUpdated       = Clock.Now();
            snapshot.Version           = aggregate.Version;
            snapshot.ETags             = aggregate.ETags()
                                         .Where(e => !string.IsNullOrWhiteSpace(e))
                                         .ToArray();

            await repository.SaveSnapshot(snapshot);
        }
        public async Task When_ServiceBusCommandQueueSender_is_subscribed_to_the_service_bus_then_messages_are_scheduled_to_trigger_event_based_scheduled_commands()
        {
            VirtualClock.Start(DateTimeOffset.Now.AddHours(-13));

            using (var queueReceiver = CreateQueueReceiver())
            {
                var aggregateIds = Enumerable.Range(1, 5)
                                   .Select(_ => Guid.NewGuid())
                                   .ToArray();

                aggregateIds.ForEach(id =>
                {
                    var order = CommandSchedulingTests.CreateOrder(orderId: id);

                    // due enough in the future that the scheduler won't apply the commands immediately
                    var due = Clock.Now().AddSeconds(5);
                    order.Apply(new ShipOn(due));

                    Console.WriteLine(new { ShipOrderId = order.Id, due });

                    orderRepository.Save(order);
                });

                RunCatchup();

                // reset the clock so that when the messages are delivered, the target commands are now due
                Clock.Reset();

                queueReceiver.StartReceivingMessages();

                var activity = await scheduler.Activity
                               .Where(a => aggregateIds.Contains(a.ScheduledCommand.AggregateId))
                               .Take(5)
                               .ToList()
                               .Timeout(TimeSpan.FromMinutes(5));

                activity.Select(a => a.ScheduledCommand.AggregateId)
                .ShouldBeEquivalentTo(aggregateIds);
            }
        }
示例#9
0
        public async Task When_a_command_trigger_message_arrives_early_it_is_not_Completed()
        {
            VirtualClock.Start(Clock.Now().AddHours(-1));

            var aggregateId     = Any.Guid();
            var appliedCommands = new List <ICommandSchedulerActivity>();

            using (var receiver = CreateQueueReceiver())
            {
                await receiver.StartReceivingMessages();

                // due enough in the future that the scheduler won't apply the commands immediately
                var order = await CommandSchedulingTests_EventSourced.CreateOrder(orderId : aggregateId)
                            .ApplyAsync(new ShipOn(Clock.Now().AddMinutes(2)));

                await Configuration.Current.Repository <Order>().Save(order);

                await receiver.Messages
                .OfType <IScheduledCommand <Order> >()
                .FirstAsync(c => c.TargetId == aggregateId.ToString())
                .Timeout(TimeSpan.FromMinutes(1));

                await Task.Delay(1000);

                appliedCommands.Should().BeEmpty();
            }

            Clock.Reset();

            using (var receiver = CreateQueueReceiver())
            {
                await receiver.StartReceivingMessages();

                await Task.Delay(1000);

                // FIX: (When_a_command_trigger_message_arrives_early_it_is_not_Completed) how was this even passing?
//                appliedCommands.Should().Contain(c => c.ScheduledCommand.AggregateId == aggregateId);
            }
        }
示例#10
0
        /// <summary>
        /// Initializes the interface properties of a snapshot.
        /// </summary>
        /// <typeparam name="TAggregate">The type of the aggregate.</typeparam>
        /// <param name="aggregate">The aggregate.</param>
        /// <param name="snapshot">The snapshot.</param>
        /// <exception cref="System.ArgumentNullException">
        /// aggregate
        /// or
        /// snapshot
        /// </exception>
        internal static void InitializeSnapshot <TAggregate>(this TAggregate aggregate, ISnapshot snapshot)
            where TAggregate : class, IEventSourced
        {
            if (aggregate == null)
            {
                throw new ArgumentNullException(nameof(aggregate));
            }
            if (snapshot == null)
            {
                throw new ArgumentNullException(nameof(snapshot));
            }

            if (aggregate.PendingEvents.Any())
            {
                throw new InvalidOperationException("A snapshot can only be created from an aggregate having no pending events. Save the aggregate before creating a snapshot.");
            }

            snapshot.AggregateId       = aggregate.Id;
            snapshot.AggregateTypeName = AggregateType <TAggregate> .EventStreamName;
            snapshot.LastUpdated       = Clock.Now();
            snapshot.Version           = aggregate.Version;
            snapshot.ETags             = aggregate.CreateETagBloomFilter();
        }
        /// <summary>
        /// Writes trace information during command scheduling and delivery for all aggregate types. If no delegates are specified, then output is written to <see cref="System.Diagnostics.Trace" /> on all events.
        /// </summary>
        /// <param name="configuration">The domain configuration.</param>
        /// <param name="onScheduling">An optional delegate to trace information about a command before calling Schedule on the inner scheduler.</param>
        /// <param name="onScheduled">An optional delegate to trace information about a command after calling Schedule on the inner scheduler.</param>
        /// <param name="onDelivering">An optional delegate to trace information about a command before calling Deliver on the inner scheduler.</param>
        /// <param name="onDelivered">An optional delegate to trace information about a command after calling Deliver on the inner scheduler.</param>
        /// <returns>The same configuration object.</returns>
        public static Configuration TraceScheduledCommands(
            this Configuration configuration,
            Action <IScheduledCommand> onScheduling = null,
            Action <IScheduledCommand> onScheduled  = null,
            Action <IScheduledCommand> onDelivering = null,
            Action <IScheduledCommand> onDelivered  = null)
        {
            var traceInitializer = configuration.Container
                                   .Resolve <CommandSchedulerPipelineTraceInitializer>();

            if (onScheduling == null &&
                onScheduled == null &&
                onDelivering == null &&
                onDelivered == null)
            {
                onScheduling = cmd =>
                               Trace.WriteLine($"[Scheduling] @{Clock.Now()}: {cmd}");

                onScheduled = cmd =>
                              Trace.WriteLine($"[Scheduled] @{Clock.Now()}: {cmd}");

                onDelivering = cmd =>
                               Trace.WriteLine($"[Delivering] @{Clock.Now()}: {cmd}");

                onDelivered = cmd =>
                              Trace.WriteLine($"[Delivered] @{Clock.Now()}: {cmd}");
            }

            traceInitializer.OnScheduling(onScheduling);
            traceInitializer.OnScheduled(onScheduled);
            traceInitializer.OnDelivering(onDelivering);
            traceInitializer.OnDelivered(onDelivered);

            traceInitializer.Initialize(configuration);

            return(configuration);
        }