Esempio n. 1
0
        public Task<AsyncTaskResult<EventAppendResult>> AppendAsync(DomainEventStream eventStream)
        {
            var record = ConvertTo(eventStream);

            return _ioHelper.TryIOFuncAsync<AsyncTaskResult<EventAppendResult>>(async () =>
            {
                try
                {
                    using (var connection = GetConnection())
                    {
                        await connection.InsertAsync(record, _eventTable);
                        return new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.Success);
                    }
                }
                catch (SqlException ex)
                {
                    if (ex.Number == 2627 && ex.Message.Contains(_primaryKeyName))
                    {
                        return new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateEvent);
                    }
                    else if (ex.Number == 2601 && ex.Message.Contains(_commandIndexName))
                    {
                        return new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateCommand);
                    }
                    _logger.Error(string.Format("Append event has sql exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.IOException, ex.Message, EventAppendResult.Failed);
                }
                catch (Exception ex)
                {
                    _logger.Error(string.Format("Append event has unknown exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.Failed, ex.Message, EventAppendResult.Failed);
                }
            }, "AppendEventsAsync");
        }
        public EventAppendResult Append(DomainEventStream eventStream)
        {
            var record = ConvertTo(eventStream);

            return _ioHelper.TryIOFunc(() =>
            {
                using (var connection = GetConnection())
                {
                    try
                    {
                        connection.Insert(record, _eventTable);
                        return EventAppendResult.Success;
                    }
                    catch (SqlException ex)
                    {
                        if (ex.Number == 2627)
                        {
                            if (ex.Message.Contains(_primaryKeyName))
                            {
                                return EventAppendResult.DuplicateEvent;
                            }
                        }
                        throw;
                    }
                }
            }, "AppendEvents");
        }
Esempio n. 3
0
        private EventAppendResult Append(DomainEventStream eventStream)
        {
            var aggregateInfo = _aggregateInfoDict.GetOrAdd(eventStream.AggregateRootId, x => new AggregateInfo());
            var originalStatus = Interlocked.CompareExchange(ref aggregateInfo.Status, Editing, UnEditing);

            if (originalStatus == aggregateInfo.Status)
            {
                return EventAppendResult.DuplicateEvent;
            }

            try
            {
                if (eventStream.Version == aggregateInfo.CurrentVersion + 1)
                {
                    aggregateInfo.EventDict[eventStream.Version] = eventStream;
                    aggregateInfo.CommandDict[eventStream.CommandId] = eventStream;
                    aggregateInfo.CurrentVersion = eventStream.Version;
                    return EventAppendResult.Success;
                }
                return EventAppendResult.DuplicateEvent;
            }
            finally
            {
                Interlocked.Exchange(ref aggregateInfo.Status, UnEditing);
            }
        }
Esempio n. 4
0
 public void PublishDomainEventAsync(ProcessingCommand processingCommand, DomainEventStream eventStream)
 {
     if (eventStream.Items == null || eventStream.Items.Count == 0)
     {
         eventStream.Items = processingCommand.Items;
     }
     var eventStreamMessage = new DomainEventStreamMessage(processingCommand.Message.Id, eventStream.AggregateRootId, eventStream.Version, eventStream.AggregateRootTypeName, eventStream.Events, eventStream.Items);
     PublishDomainEventAsync(processingCommand, eventStreamMessage, 0);
 }
Esempio n. 5
0
        private void AddDataRow(DataTable table, DomainEventStream eventStream)
        {
            var row = table.NewRow();

            row["AggregateRootId"]       = eventStream.AggregateRootId;
            row["AggregateRootTypeName"] = eventStream.AggregateRootTypeName;
            row["CommandId"]             = eventStream.CommandId;
            row["Version"]   = eventStream.Version;
            row["CreatedOn"] = eventStream.Timestamp;
            row["Events"]    = _jsonSerializer.Serialize(_eventSerializer.Serialize(eventStream.Events));
            table.Rows.Add(row);
        }
Esempio n. 6
0
 private StreamRecord ConvertTo(DomainEventStream eventStream)
 {
     return(new StreamRecord
     {
         CommandId = eventStream.CommandId,
         AggregateRootId = eventStream.AggregateRootId,
         AggregateRootTypeName = eventStream.AggregateRootTypeName,
         Version = eventStream.Version,
         CreatedOn = eventStream.Timestamp,
         Events = _jsonSerializer.Serialize(_eventSerializer.Serialize(eventStream.Events))
     });
 }
Esempio n. 7
0
        static void AppendAsyncTest()
        {
            var aggreagateRootId  = ObjectId.GenerateNewStringId();
            var count             = int.Parse(ConfigurationManager.AppSettings["appendAsyncCount"]);
            var printSize         = count / 10;
            var eventStore        = ObjectContainer.Resolve <IEventStore>();
            var createEventStream = new Func <int, DomainEventStream>(version =>
            {
                var evnt = new TestEvent
                {
                    AggregateRootId = aggreagateRootId,
                    Version         = version
                };
                var eventStream = new DomainEventStream(ObjectId.GenerateNewStringId(), aggreagateRootId, "SampleAggregateRootTypeName", version, DateTime.Now, new IDomainEvent[] { evnt });
                return(eventStream);
            });

            Console.WriteLine("start to append test, totalCount:" + count);

            var current    = 0;
            var watch      = Stopwatch.StartNew();
            var waitHandle = new ManualResetEvent(false);

            for (var i = 1; i <= count; i++)
            {
                eventStore.AppendAsync(createEventStream(i)).ContinueWith(t =>
                {
                    if (t.Result.Data == EventAppendResult.DuplicateEvent)
                    {
                        Console.WriteLine("duplicated event stream.");
                        return;
                    }
                    else if (t.Result.Data == EventAppendResult.DuplicateCommand)
                    {
                        Console.WriteLine("duplicated command execution.");
                        return;
                    }
                    var local = Interlocked.Increment(ref current);
                    if (local % printSize == 0)
                    {
                        Console.WriteLine("appended {0}, time:{1}", local, watch.ElapsedMilliseconds);
                        if (local == count)
                        {
                            Console.WriteLine("append throughput: {0} events/s", 1000 * local / watch.ElapsedMilliseconds);
                            waitHandle.Set();
                        }
                    }
                });
            }

            waitHandle.WaitOne();
        }
Esempio n. 8
0
        private void VerifyEvent(DomainEventStream eventStream)
        {
            var current = this as IAggregateRoot;

            if (eventStream.Version > 1 && eventStream.AggregateRootId != current.UniqueId)
            {
                throw new Exception(string.Format("Invalid domain event stream, aggregateRootId:{0}, expected aggregateRootId:{1}", eventStream.AggregateRootId, current.UniqueId));
            }
            if (eventStream.Version != current.Version + 1)
            {
                throw new Exception(string.Format("Invalid domain event stream, version:{0}, expected version:{1}", eventStream.Version, current.Version));
            }
        }
            public void ShouldAppendDomainEventToEndOfStream()
            {
                TestAggregateRoot aggregateRoot = new TestAggregateRoot(Guid.NewGuid());

                var stream = new DomainEventStream(aggregateRoot.Id);

                stream.Should().HaveCount(0);

                var aggregateRootDomainEvent = new AggregateRootChangedDomainEvent(aggregateRoot.Id, Guid.NewGuid());
                IDomainEventStream result    = stream.AppendDomainEvent(aggregateRootDomainEvent);

                result.Should().HaveCount(1);
            }
Esempio n. 10
0
        private void VerifyEvent(DomainEventStream eventStream)
        {
            var current = this as IAggregateRoot;

            if (eventStream.Version > 1 && eventStream.AggregateRootId != current.UniqueId)
            {
                throw new InvalidOperationException(string.Format("Invalid domain event stream, aggregateRootId:{0}, expected aggregateRootId:{1}, type:{2}", eventStream.AggregateRootId, current.UniqueId, current.GetType().FullName));
            }
            if (eventStream.Version != current.Version + 1)
            {
                throw new InvalidOperationException(string.Format("Invalid domain event stream, version:{0}, expected version:{1}, current aggregateRoot type:{2}, id:{3}", eventStream.Version, current.Version, this.GetType().FullName, current.UniqueId));
            }
        }
            public void ShouldThrowIfAggregateRootIdDoesNotMatch()
            {
                TestAggregateRoot aggregateRoot1 = new TestAggregateRoot(Guid.NewGuid());
                TestAggregateRoot aggregateRoot2 = new TestAggregateRoot(Guid.NewGuid());

                var aggregateRoot1Stream = new DomainEventStream(aggregateRoot1.Id);

                aggregateRoot1Stream.Should().HaveCount(0);

                var aggregateRoot2DomainEvent = new AggregateRootChangedDomainEvent(aggregateRoot2.Id, Guid.NewGuid());

                // Append domain event of aggregate 2 to stream of aggregate 1.
                aggregateRoot1Stream.Invoking(s => s.AppendDomainEvent(aggregateRoot2DomainEvent)).Should().Throw <InvalidOperationException>();
            }
Esempio n. 12
0
        void IAggregateRoot.RestoreFromEvents(DomainEventStream eventStream)
        {
            if (eventStream == null)
            {
                return;
            }

            //VerifyEvent(domainEventStream);//Don't VerifyEvent when CompatibleAggregateInitStyle.RepositoryOnly or CompatibleAggregateInitStyle.RepositoryThenEventSourcing

            foreach (var domainEvent in eventStream.Events)
            {
                HandleEvent(domainEvent);
            }
            _version = eventStream.Version;
        }
            public void ShouldBeEqualToNumberOfDomainEvents()
            {
                TestAggregateRoot aggregateRoot = new TestAggregateRoot(Guid.NewGuid());

                // 3 domain events.
                var domainEvent1 = new AggregateRootChangedDomainEvent(aggregateRoot.Id, Guid.NewGuid());
                var domainEvent2 = new AggregateRootChangedDomainEvent(aggregateRoot.Id, Guid.NewGuid());
                var domainEvent3 = new AggregateRootChangedDomainEvent(aggregateRoot.Id, Guid.NewGuid());

                DomainEventStream stream = new DomainEventStream(aggregateRoot.Id, new[]
                {
                    domainEvent1, domainEvent2, domainEvent3
                });

                stream.DomainEventCount.Should().Be(3);
            }
Esempio n. 14
0
        static void Main(string[] args)
        {
            InitializeENodeFramework();

            var aggreagateRootId = ObjectId.GenerateNewStringId();
            var count            = int.Parse(ConfigurationManager.AppSettings["count"]);
            var eventStore       = ObjectContainer.Resolve <IEventStore>();
            var watch            = Stopwatch.StartNew();

            var createEventStream = new Func <int, DomainEventStream>(version =>
            {
                var evnt = new TestEvent
                {
                    AggregateRootId = aggreagateRootId,
                    Version         = version
                };
                var eventStream = new DomainEventStream(ObjectId.GenerateNewStringId(), aggreagateRootId, "SampleAggregateRootTypeName", version, DateTime.Now, new IDomainEvent[] { evnt });
                return(eventStream);
            });

            var current = 0;

            for (var i = 1; i <= count; i++)
            {
                eventStore.AppendAsync(createEventStream(i)).ContinueWith(t =>
                {
                    if (t.Result.Data == EventAppendResult.DuplicateEvent)
                    {
                        Console.WriteLine("duplicated event stream.");
                        return;
                    }
                    else if (t.Result.Data == EventAppendResult.DuplicateCommand)
                    {
                        Console.WriteLine("duplicated command execution.");
                        return;
                    }
                    var local = Interlocked.Increment(ref current);
                    if (local % 1000 == 0)
                    {
                        Console.WriteLine("{0}, time:{1}", local, watch.ElapsedMilliseconds);
                    }
                });
            }

            Console.ReadLine();
        }
Esempio n. 15
0
 private void RefreshAggregateMemoryCache(DomainEventStream aggregateFirstEventStream)
 {
     try
     {
         var aggregateRootType = _aggregateRootTypeCodeProvider.GetType(aggregateFirstEventStream.AggregateRootTypeCode);
         var aggregateRoot     = _memoryCache.Get(aggregateFirstEventStream.AggregateRootId, aggregateRootType);
         if (aggregateRoot == null)
         {
             aggregateRoot = _aggregateRootFactory.CreateAggregateRoot(aggregateRootType);
             aggregateRoot.ReplayEvents(new DomainEventStream[] { aggregateFirstEventStream });
             _memoryCache.Set(aggregateRoot);
             _logger.DebugFormat("Aggregate added into memory, commandId:{0}, aggregateRootType:{1}, aggregateRootId:{2}, aggregateRootVersion:{3}", aggregateFirstEventStream.CommandId, aggregateRootType.Name, aggregateRoot.UniqueId, aggregateRoot.Version);
         }
     }
     catch (Exception ex)
     {
         _logger.Error(string.Format("Refresh memory cache by aggregate first event stream failed, {0}", aggregateFirstEventStream), ex);
     }
 }
Esempio n. 16
0
        static void BatchAppendAsyncTest()
        {
            Console.WriteLine("");

            var aggreagateRootId1 = ObjectId.GenerateNewStringId();
            var aggreagateRootId2 = ObjectId.GenerateNewStringId();
            var count             = int.Parse(ConfigurationManager.AppSettings["batchAppendAsyncount"]);
            var batchSize         = int.Parse(ConfigurationManager.AppSettings["batchSize"]);
            var printSize         = count / 10;
            var finishedCount     = 0;
            var eventStore        = ObjectContainer.Resolve <IEventStore>();
            var eventQueue        = new Queue <DomainEventStream>();

            for (var i = 1; i <= count; i++)
            {
                var aggregateRootId = i % 2 == 0 ? aggreagateRootId1 : aggreagateRootId2;
                var evnt            = new TestEvent
                {
                    AggregateRootId       = aggregateRootId,
                    AggregateRootStringId = aggregateRootId,
                    Version = i
                };
                var eventStream = new DomainEventStream(ObjectId.GenerateNewStringId(), aggregateRootId, "SampleAggregateRootTypeName", DateTime.Now, new IDomainEvent[] { evnt });
                eventQueue.Enqueue(eventStream);
            }

            var watch   = Stopwatch.StartNew();
            var context = new BatchAppendContext
            {
                BatchSize     = batchSize,
                PrintSize     = printSize,
                FinishedCount = finishedCount,
                EventStore    = eventStore,
                EventQueue    = eventQueue,
                Watch         = watch
            };

            Console.WriteLine("start to batch append test, totalCount:" + count);

            DoBatchAppendAsync(context).Wait();
        }
Esempio n. 17
0
        public Task <AsyncTaskResult <EventAppendResult> > AppendAsync(DomainEventStream eventStream)
        {
            if (_currentFailedCount < _expectFailedCount)
            {
                _currentFailedCount++;

                if (_failedType == FailedType.UnKnownException)
                {
                    throw new Exception("AppendAsyncUnKnownException" + _currentFailedCount);
                }
                else if (_failedType == FailedType.IOException)
                {
                    throw new IOException("AppendAsyncIOException" + _currentFailedCount);
                }
                else if (_failedType == FailedType.TaskIOException)
                {
                    return(Task.FromResult(new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Failed, "AppendAsyncError" + _currentFailedCount)));
                }
            }
            return(_inMemoryEventStore.AppendAsync(eventStream));
        }
        public Task <AsyncTaskResult <EventAppendResult> > AppendAsync(DomainEventStream eventStream)
        {
            var record = ConvertTo(eventStream);

            return(_ioHelper.TryIOFuncAsync(async() =>
            {
                try
                {
                    using (var connection = GetConnection())
                    {
                        string sql = string.Format(
                            "INSERT INTO {0} (AggregateRootId,AggregateRootTypeName,Version,CommandId,CreatedOn,Events) VALUES(@AggregateRootId,@AggregateRootTypeName,@Version,@CommandId,@CreatedOn,@Events)",
                            GetTableName(record.AggregateRootId));
                        await connection.ExecuteAsync(sql, record);
                        return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.Success);
                    }
                }
                catch (MySqlException ex)
                {
                    if (ex.Number == 1062 && ex.Message.Contains(_versionIndexName))
                    {
                        return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateEvent);
                    }
                    else if (ex.Number == 1062 && ex.Message.Contains(_commandIndexName))
                    {
                        return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateCommand);
                    }
                    _logger.Error(string.Format("Append event has sql exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.IOException, ex.Message, EventAppendResult.Failed);
                }
                catch (Exception ex)
                {
                    _logger.Error(string.Format("Append event has unknown exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Failed, ex.Message, EventAppendResult.Failed);
                }
            }, "AppendEventsAsync"));
        }
Esempio n. 19
0
        public Task <AsyncTaskResult <EventAppendResult> > AppendAsync(DomainEventStream eventStream)
        {
            var record = ConvertTo(eventStream);

            return(_ioHelper.TryIOFuncAsync <AsyncTaskResult <EventAppendResult> >(async() =>
            {
                try
                {
                    using (var connection = GetConnection())
                    {
                        await connection.InsertToMySqlAsync(record, _tableName);

                        return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.Success);
                    }
                }
                catch (DbException ex)
                {
                    //todo: this error number is from sql server, make sure it's same with mysql database.
                    //if (ex.Number == 2627 && ex.Message.Contains(_primaryKeyName))
                    //{
                    //    return new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateEvent);
                    //}
                    //else if (ex.Number == 2601 && ex.Message.Contains(_commandIndexName))
                    //{
                    //    return new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateCommand);
                    //}

                    _logger.Error(string.Format("Append event has sql exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.IOException, ex.Message, EventAppendResult.Failed);
                }
                catch (Exception ex)
                {
                    _logger.Error(string.Format("Append event has unknown exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Failed, ex.Message, EventAppendResult.Failed);
                }
            }, "AppendEventsAsync"));
        }
            public void ShouldThrowIfAggregateRootIdDoesNotMatch()
            {
                TestAggregateRoot aggregateRoot1             = new TestAggregateRoot(Guid.NewGuid());
                TestAggregateRoot aggregateRoot2             = new TestAggregateRoot(Guid.NewGuid());
                IAggregateRoot    aggregateRoot1ExplicitCast = aggregateRoot1;
                IAggregateRoot    aggregateRoot2ExplicitCast = aggregateRoot2;

                // Apply 3 domain events.
                aggregateRoot1.ChangeMe(Guid.NewGuid());
                aggregateRoot1.ChangeMe(Guid.NewGuid());
                aggregateRoot1.ChangeMe(Guid.NewGuid());

                DomainEventStream stream1 = (DomainEventStream)aggregateRoot1ExplicitCast.GetDomainEventsMarkedForCommit();

                // Apply 3 domain events.
                aggregateRoot2.ChangeMe(Guid.NewGuid());
                aggregateRoot2.ChangeMe(Guid.NewGuid());
                aggregateRoot2.ChangeMe(Guid.NewGuid());

                DomainEventStream stream2 = (DomainEventStream)aggregateRoot2ExplicitCast.GetDomainEventsMarkedForCommit();

                // Append 2 streams.
                stream1.Invoking(s1 => s1.AppendDomainEventStream(stream2)).Should().Throw <InvalidOperationException>();
            }
Esempio n. 21
0
        public DomainEventStream GetAggregateRestoreEventStream(string postId, Post nullObject)
        {
            using (var connection = new SqlConnection(ConfigSettings.Forum_DBConnectionString))
            {
                dynamic          post             = connection.QueryList <dynamic>(new { Id = postId }, Constants.PostTable).SingleOrDefault();
                PostCreatedEvent postCreatedEvent = null;

                postCreatedEvent = new PostCreatedEvent(
                    (string)post.Id, (int)post.Version, (string)post.Subject, (string)post.Body, (string)post.SectionId, (string)post.AuthorId);
                postCreatedEvent.Version   = (int)post.Version;
                postCreatedEvent.Timestamp = (DateTime)post.CreatedOn;

                PostReplyStatisticInfo postReplyStatisticInfo = new PostReplyStatisticInfo(
                    (string)post.MostRecentReplyId, (string)post.MostRecentReplierId, (DateTime)post.LastUpdateTime, (int)post.ReplyCount);
                PostReplyStatisticInfoChangedEvent postReplyStatisticInfoChangedEvent = new PostReplyStatisticInfoChangedEvent(
                    (string)post.Id, (int)post.Version, postReplyStatisticInfo);
                postReplyStatisticInfoChangedEvent.Version   = (int)post.Version;
                postReplyStatisticInfoChangedEvent.Timestamp = (DateTime)post.LastUpdateTime;

                IDomainEvent[]    events      = new IDomainEvent[] { postCreatedEvent, postReplyStatisticInfoChangedEvent };
                DomainEventStream eventStream = new DomainEventStream(postId, postCreatedEvent.Version, events);
                return(eventStream);
            }
        }
Esempio n. 22
0
        public Task <AsyncTaskResult <EventAppendResult> > AppendAsync(DomainEventStream eventStream)
        {
            var record = ConvertTo(eventStream);

            return(_ioHelper.TryIOFuncAsync <AsyncTaskResult <EventAppendResult> >(async() =>
            {
                try
                {
                    using (var connection = GetConnection())
                    {
                        await connection.InsertToMySqlAsync(record, _tableName);

                        return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.Success);
                    }
                }
                catch (MySql.Data.MySqlClient.MySqlException ex)
                {
                    if (ex.Number == 1062 && ex.Message.Contains(_primaryKeyName))
                    {
                        return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateEvent);
                    }
                    else if (ex.Number == 1062 && ex.Message.Contains(_commandIndexName))
                    {
                        return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, EventAppendResult.DuplicateCommand);
                    }

                    _logger.Error(string.Format("Append event has sql exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.IOException, ex.Message, EventAppendResult.Failed);
                }
                catch (Exception ex)
                {
                    _logger.Error(string.Format("Append event has unknown exception, eventStream: {0}", eventStream), ex);
                    return new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Failed, ex.Message, EventAppendResult.Failed);
                }
            }, "AppendEventsAsync"));
        }
        public void update_concurrent_conflict_test()
        {
            var aggregateId = ObjectId.GenerateNewStringId();
            var command     = new CreateTestAggregateCommand
            {
                AggregateRootId = aggregateId,
                Title           = "Sample Note"
            };

            //执行创建聚合根的命令
            var commandResult = _commandService.ExecuteAsync(command).Result;

            Assert.IsNotNull(commandResult);
            Assert.AreEqual(CommandStatus.Success, commandResult.Status);
            var note = _memoryCache.GetAsync <TestAggregate>(aggregateId).Result;

            Assert.IsNotNull(note);
            Assert.AreEqual("Sample Note", note.Title);
            Assert.AreEqual(1, ((IAggregateRoot)note).Version);

            //往EventStore直接插入事件,用于模拟并发冲突的情况
            var eventStream = new DomainEventStream(
                ObjectId.GenerateNewStringId(),
                aggregateId,
                typeof(TestAggregate).FullName,
                DateTime.Now,
                new IDomainEvent[] { new TestAggregateTitleChanged("Changed Title")
                                     {
                                         AggregateRootId = aggregateId, Version = 2
                                     } },
                null);
            var result = _eventStore.BatchAppendAsync(new DomainEventStream[] { eventStream }).Result;

            Assert.IsNotNull(result);
            Assert.AreEqual(aggregateId, result.SuccessAggregateRootIdList[0]);

            _publishedVersionStore.UpdatePublishedVersionAsync("DefaultEventProcessor", typeof(TestAggregate).FullName, aggregateId, 2).Wait();

            var commandList = new List <ICommand>();

            for (var i = 0; i < 50; i++)
            {
                commandList.Add(new ChangeTestAggregateTitleCommand
                {
                    AggregateRootId = aggregateId,
                    Title           = "Changed Note2"
                });
            }

            var waitHandle = new ManualResetEvent(false);
            var count      = 0L;

            foreach (var updateCommand in commandList)
            {
                _commandService.ExecuteAsync(updateCommand).ContinueWith(t =>
                {
                    Assert.IsNotNull(t.Result);
                    var currentCommandResult = t.Result;
                    Assert.IsNotNull(currentCommandResult);
                    Assert.AreEqual(CommandStatus.Success, currentCommandResult.Status);
                    var totalCount = Interlocked.Increment(ref count);
                    if (totalCount == commandList.Count)
                    {
                        waitHandle.Set();
                    }
                });
            }
            waitHandle.WaitOne();
            note = _memoryCache.GetAsync <TestAggregate>(aggregateId).Result;
            Assert.IsNotNull(note);
            Assert.AreEqual(2 + commandList.Count, ((IAggregateRoot)note).Version);
            Assert.AreEqual("Changed Note2", note.Title);
        }
Esempio n. 24
0
 public Task <AsyncTaskResult <EventAppendResult> > AppendAsync(DomainEventStream eventStream)
 {
     return(Task.FromResult(new AsyncTaskResult <EventAppendResult>(AsyncTaskStatus.Success, null, Append(eventStream))));
 }
        private void HandleAggregateDuplicatedCommandAsync(ProcessingCommand processingCommand, IAggregateRoot dirtyAggregateRoot, DomainEventStream eventStream, int retryTimes)
        {
            var command = processingCommand.Message;

            _ioHelper.TryAsyncActionRecursively <AsyncTaskResult <HandledCommand> >("GetCommandAsync",
                                                                                    () => _commandStore.GetAsync(command.Id),
                                                                                    currentRetryTimes => HandleAggregateDuplicatedCommandAsync(processingCommand, dirtyAggregateRoot, eventStream, currentRetryTimes),
                                                                                    result =>
            {
                var existingHandledCommand = result.Data;
                if (existingHandledCommand != null)
                {
                    HandleExistingHandledAggregateAsync(processingCommand, dirtyAggregateRoot, eventStream, existingHandledCommand, 0);
                }
                else
                {
                    //到这里,说明当前command想添加到commandStore中时,提示command重复,但是尝试从commandStore中取出该command时却找不到该command。
                    //出现这种情况,我们就无法再做后续处理了,这种错误理论上不会出现,除非commandStore的Add接口和Get接口出现读写不一致的情况;
                    //我们记录错误日志,然后认为当前command已被处理为失败。
                    var errorMessage = string.Format("Command exist in the command store, but we cannot get it from the command store. commandType:{0}, commandId:{1}",
                                                     command.GetType().Name,
                                                     command.Id);
                    NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, errorMessage);
                }
            },
                                                                                    () => string.Format("[commandId:{0}]", command.Id),
                                                                                    () => NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, "Get command async failed."),
                                                                                    retryTimes);
        }
Esempio n. 26
0
        public void update_concurrent_conflict_test()
        {
            var aggregateId = ObjectId.GenerateNewStringId();
            var command     = new CreateTestAggregateCommand
            {
                AggregateRootId = aggregateId,
                Title           = "Sample Note"
            };

            //执行创建聚合根的命令
            var asyncResult = _commandService.ExecuteAsync(command).Result;

            Assert.IsNotNull(asyncResult);
            Assert.AreEqual(AsyncTaskStatus.Success, asyncResult.Status);
            var commandResult = asyncResult.Data;

            Assert.IsNotNull(commandResult);
            Assert.AreEqual(CommandStatus.Success, commandResult.Status);
            var note = _memoryCache.Get <TestAggregate>(aggregateId);

            Assert.IsNotNull(note);
            Assert.AreEqual("Sample Note", note.Title);
            Assert.AreEqual(1, ((IAggregateRoot)note).Version);

            //往EventStore直接插入事件,用于模拟并发冲突的情况
            var eventStream = new DomainEventStream(
                ObjectId.GenerateNewStringId(),
                aggregateId,
                typeof(TestAggregate).FullName,
                2,
                DateTime.Now,
                new IDomainEvent[] { new TestAggregateTitleChanged("Changed Title")
                                     {
                                         AggregateRootId = aggregateId, Version = 2
                                     } },
                null);
            var result = _eventStore.AppendAsync(eventStream).Result;

            Assert.IsNotNull(result);
            Assert.AreEqual(AsyncTaskStatus.Success, result.Status);
            Assert.AreEqual(EventAppendResult.Success, result.Data);

            var result2 = _publishedVersionStore.UpdatePublishedVersionAsync("DefaultEventProcessor", typeof(TestAggregate).FullName, aggregateId, 2).Result;

            Assert.IsNotNull(result2);
            Assert.AreEqual(AsyncTaskStatus.Success, result2.Status);

            //执行修改聚合根的命令
            var command2 = new ChangeTestAggregateTitleCommand
            {
                AggregateRootId = aggregateId,
                Title           = "Changed Note2"
            };

            asyncResult = _commandService.ExecuteAsync(command2).Result;
            Assert.IsNotNull(asyncResult);
            Assert.AreEqual(AsyncTaskStatus.Success, asyncResult.Status);
            commandResult = asyncResult.Data;
            Assert.IsNotNull(commandResult);
            Assert.AreEqual(CommandStatus.Success, commandResult.Status);
            note = _memoryCache.Get <TestAggregate>(aggregateId);
            Assert.IsNotNull(note);
            Assert.AreEqual(3, ((IAggregateRoot)note).Version);
            Assert.AreEqual("Changed Note2", note.Title);
        }
        private void CommitAggregateChanges(ProcessingCommand processingCommand)
        {
            var command = processingCommand.Message;
            var context = processingCommand.CommandExecuteContext;
            var trackedAggregateRoots   = context.GetTrackedAggregateRoots();
            var dirtyAggregateRootCount = 0;
            var dirtyAggregateRoot      = default(IAggregateRoot);
            var changedEvents           = default(IEnumerable <IDomainEvent>);

            foreach (var aggregateRoot in trackedAggregateRoots)
            {
                var events = aggregateRoot.GetChanges();
                if (events.Any())
                {
                    dirtyAggregateRootCount++;
                    if (dirtyAggregateRootCount > 1)
                    {
                        var errorMessage = string.Format("Detected more than one aggregate created or modified by command. commandType:{0}, commandId:{1}",
                                                         command.GetType().Name,
                                                         command.Id);
                        _logger.ErrorFormat(errorMessage);
                        CompleteCommand(processingCommand, CommandStatus.Failed, typeof(string).FullName, errorMessage);
                        return;
                    }
                    dirtyAggregateRoot = aggregateRoot;
                    changedEvents      = events;
                }
            }

            //如果当前command没有对任何聚合根做修改,框架仍然需要尝试获取该command之前是否有产生事件,
            //如果有,则需要将事件再次发布到MQ;如果没有,则完成命令,返回command的结果为NothingChanged。
            //之所以要这样做是因为有可能当前command上次执行的结果可能是事件持久化完成,但是发布到MQ未完成,然后那时正好机器断电宕机了;
            //这种情况下,如果机器重启,当前command对应的聚合根从eventstore恢复的聚合根是被当前command处理过后的;
            //所以如果该command再次被处理,可能对应的聚合根就不会再产生事件了;
            //所以,我们要考虑到这种情况,尝试再次发布该命令产生的事件到MQ;
            //否则,如果我们直接将当前command设置为完成,即对MQ进行ack操作,那该command的事件就永远不会再发布到MQ了,这样就无法保证CQRS数据的最终一致性了。
            if (dirtyAggregateRootCount == 0 || changedEvents == null || changedEvents.Count() == 0)
            {
                ProcessIfNoEventsOfCommand(processingCommand, 0);
                return;
            }

            //接受聚合根的最新修改
            dirtyAggregateRoot.AcceptChanges();

            //刷新聚合根的内存缓存
            _memoryCache.UpdateAggregateRootCache(dirtyAggregateRoot);

            //构造出一个事件流对象
            var commandResult = processingCommand.CommandExecuteContext.GetResult();

            if (commandResult != null)
            {
                processingCommand.Items["CommandResult"] = commandResult;
            }
            var eventStream = new DomainEventStream(
                processingCommand.Message.Id,
                dirtyAggregateRoot.UniqueId,
                _typeNameProvider.GetTypeName(dirtyAggregateRoot.GetType()),
                changedEvents.First().Version,
                _timeProvider.GetCurrentTime(),
                changedEvents,
                processingCommand.Items);

            //异步将事件流提交到EventStore
            _eventService.CommitDomainEventAsync(new EventCommittingContext(dirtyAggregateRoot, eventStream, processingCommand));
        }
Esempio n. 28
0
 public Task<AsyncTaskResult<EventAppendResult>> AppendAsync(DomainEventStream eventStream)
 {
     return Task.FromResult(new AsyncTaskResult<EventAppendResult>(AsyncTaskStatus.Success, null, Append(eventStream)));
 }
Esempio n. 29
0
        public async Task <AsyncResult <DomainEventAppendResult> > AppendAllAsync(DomainEventStream eventStream)
        {
            Assert.NotNull(eventStream, nameof(eventStream));
            Assert.NotNullOrEmpty(eventStream.AggregateRootId, nameof(eventStream.AggregateRootId));
            Assert.NotNullOrEmpty(eventStream.AggregateRootTypeName, nameof(eventStream.AggregateRootTypeName));
            Assert.LengthGreaterThan(nameof(eventStream.Events), eventStream.Events.Count(), 0);

            var sql          = string.Format(InsertSql, GetShardTableName(eventStream.AggregateRootId));
            var storedEvents = eventStream.Events.Select(it => StoredDomainEventAdapter.ToStoredDomainEvent(it, _serializer));

            try
            {
                using (var connection = new SqlConnection(_options.ConnectionString))
                {
                    await connection.OpenAsync();

                    var transaction = await connection.BeginTransactionAsync();

                    try
                    {
                        using (var copy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction))
                        {
                            copy.BatchSize            = eventStream.Events.Count();
                            copy.BulkCopyTimeout      = _appendEventStreamTimeoutInSeconds;
                            copy.DestinationTableName = GetShardTableName(eventStream.AggregateRootId);
                            await copy.WriteToServerAsync(BuildStoreEventTableStructure());

                            await transaction.CommitAsync();
                        }

                        return(new AsyncResult <DomainEventAppendResult>(AsyncStatus.Success));
                    }
                    catch (Exception)
                    {
                        try
                        {
                            await transaction.RollbackAsync();
                        }
                        catch (Exception ex)
                        {
                            _logger.LogError($"the method{nameof(AppendAllAsync)}'s transaction rollback failed.", ex);
                        }

                        throw;
                    }
                }
            }
            catch (SqlException ex)
            {
                _logger.LogError("Apend all domain events associated with a aggregation has sql exception.", ex);

                if (ex.Number == 2601 && ex.Message.Contains(_versionIndexName))
                {
                    return(new AsyncResult <DomainEventAppendResult>(AsyncStatus.Success, DomainEventAppendResult.DuplicateEvent));
                }
                else if (ex.Number == 2601 && ex.Message.Contains(_commandIndexName))
                {
                    return(new AsyncResult <DomainEventAppendResult>(AsyncStatus.Success, DomainEventAppendResult.DuplicateCommand));
                }
                else
                {
                    return(new AsyncResult <DomainEventAppendResult>(AsyncStatus.Failed));
                }
            }
            catch (Exception ex)
            {
                _logger.LogError("Apend all domain events associated with a aggregation has unknown exception.", ex);

                return(new AsyncResult <DomainEventAppendResult>(AsyncStatus.Failed));
            }
        }
            public void ShouldThrowIfStreamToAppendIsNull()
            {
                DomainEventStream stream = new DomainEventStream(Guid.NewGuid());

                stream.Invoking(s => s.AppendDomainEvent(null)).Should().Throw <ArgumentNullException>();
            }
Esempio n. 31
0
 private void RefreshAggregateMemoryCache(DomainEventStream aggregateFirstEventStream)
 {
     try
     {
         var aggregateRootType = _typeNameProvider.GetType(aggregateFirstEventStream.AggregateRootTypeName);
         var aggregateRoot = _memoryCache.Get(aggregateFirstEventStream.AggregateRootId, aggregateRootType);
         if (aggregateRoot == null)
         {
             aggregateRoot = _aggregateRootFactory.CreateAggregateRoot(aggregateRootType);
             aggregateRoot.ReplayEvents(new DomainEventStream[] { aggregateFirstEventStream });
             _memoryCache.Set(aggregateRoot);
             if (_logger.IsDebugEnabled)
             {
                 _logger.DebugFormat("Aggregate added into memory, commandId:{0}, aggregateRootType:{1}, aggregateRootId:{2}, aggregateRootVersion:{3}", aggregateFirstEventStream.CommandId, aggregateRootType.Name, aggregateRoot.UniqueId, aggregateRoot.Version);
             }
         }
     }
     catch (Exception ex)
     {
         _logger.Error(string.Format("Refresh memory cache by aggregate first event stream failed, {0}", aggregateFirstEventStream), ex);
     }
 }
Esempio n. 32
0
 private void AddDataRow(DataTable table, DomainEventStream eventStream)
 {
     var row = table.NewRow();
     row["AggregateRootId"] = eventStream.AggregateRootId;
     row["AggregateRootTypeName"] = eventStream.AggregateRootTypeName;
     row["CommandId"] = eventStream.CommandId;
     row["Version"] = eventStream.Version;
     row["CreatedOn"] = eventStream.Timestamp;
     row["Events"] = _jsonSerializer.Serialize(_eventSerializer.Serialize(eventStream.Events));
     table.Rows.Add(row);
 }
Esempio n. 33
0
 private StreamRecord ConvertTo(DomainEventStream eventStream)
 {
     return new StreamRecord
     {
         CommandId = eventStream.CommandId,
         AggregateRootId = eventStream.AggregateRootId,
         AggregateRootTypeName = eventStream.AggregateRootTypeName,
         Version = eventStream.Version,
         CreatedOn = eventStream.Timestamp,
         Events = _jsonSerializer.Serialize(_eventSerializer.Serialize(eventStream.Events))
     };
 }
Esempio n. 34
0
        public async Task Should_Query_Aggregate_Events_Async()
        {
            //Arrange
            var eventStream1 = GetTestDomainEventStream(ObjectId.GenerateNewStringId());
            var eventStream2 = new DomainEventStream(ObjectId.GenerateNewStringId(), eventStream1.AggregateRootId, "typename", DateTime.Now, new List <DomainEvent <string> >()
            {
                new Test1DomainEvent()
                {
                    AggregateRootId = eventStream1.AggregateRootId,
                    Name            = "test1",
                    Version         = 2,
                },
                new Test2DomainEvent()
                {
                    AggregateRootId = eventStream1.AggregateRootId,
                    Name            = "test2",
                    Version         = 2,
                },
                new Test3DomainEvent()
                {
                    AggregateRootId = eventStream1.AggregateRootId,
                    Name            = "test3",
                    Version         = 2,
                },
            });
            var eventStream3 = new DomainEventStream(ObjectId.GenerateNewStringId(), eventStream1.AggregateRootId, "typename", DateTime.Now, new List <DomainEvent <string> >()
            {
                new Test1DomainEvent()
                {
                    AggregateRootId = eventStream1.AggregateRootId,
                    Name            = "test1",
                    Version         = 3,
                },
                new Test2DomainEvent()
                {
                    AggregateRootId = eventStream1.AggregateRootId,
                    Name            = "test2",
                    Version         = 3,
                },
                new Test3DomainEvent()
                {
                    AggregateRootId = eventStream1.AggregateRootId,
                    Name            = "test3",
                    Version         = 3,
                },
            });

            await _store.BatchAppendAsync(new List <DomainEventStream> {
                eventStream1
            });

            await _store.BatchAppendAsync(new List <DomainEventStream> {
                eventStream2
            });

            await _store.BatchAppendAsync(new List <DomainEventStream> {
                eventStream3
            });

            //Act
            var events = await _store.QueryAggregateEventsAsync(eventStream1.AggregateRootId, eventStream1.AggregateRootTypeName, 1, 3);

            events.Count().ShouldBe(3);
        }
Esempio n. 35
0
 private void UpdateAggregateMemoryCacheToLatestVersion(DomainEventStream eventStream)
 {
     try
     {
         _memoryCache.RefreshAggregateFromEventStore(eventStream.AggregateRootTypeName, eventStream.AggregateRootId);
     }
     catch (Exception ex)
     {
         _logger.Error(string.Format("Try to refresh aggregate in-memory from event store failed, eventStream: {0}", eventStream), ex);
     }
 }
 private void CommitAggregateChangesAsync(ProcessingCommand processingCommand, IAggregateRoot dirtyAggregateRoot, DomainEventStream eventStream, HandledCommand handledCommand, int retryTimes)
 {
     _ioHelper.TryAsyncActionRecursively <AsyncTaskResult <CommandAddResult> >("AddCommandAsync",
                                                                               () => _commandStore.AddAsync(handledCommand),
                                                                               currentRetryTimes => CommitAggregateChangesAsync(processingCommand, dirtyAggregateRoot, eventStream, handledCommand, currentRetryTimes),
                                                                               result =>
     {
         var commandAddResult = result.Data;
         if (commandAddResult == CommandAddResult.Success)
         {
             _eventService.CommitDomainEventAsync(new EventCommittingContext(dirtyAggregateRoot, eventStream, processingCommand));
         }
         else if (commandAddResult == CommandAddResult.DuplicateCommand)
         {
             HandleAggregateDuplicatedCommandAsync(processingCommand, dirtyAggregateRoot, eventStream, 0);
         }
         else
         {
             NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, "Add command async failed.");
         }
     },
                                                                               () => string.Format("[handledCommand:{0}]", handledCommand),
                                                                               () => NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, "Add command async failed."),
                                                                               retryTimes);
 }
Esempio n. 37
0
        public void create_concurrent_conflict_and_then_update_many_times_test2()
        {
            var aggregateId = ObjectId.GenerateNewStringId();
            var commandId   = ObjectId.GenerateNewStringId();

            //往EventStore直接插入事件,用于模拟并发冲突的情况
            var eventStream = new DomainEventStream(
                commandId,
                aggregateId,
                typeof(TestAggregate).FullName,
                1,
                DateTime.Now,
                new IDomainEvent[] { new TestAggregateTitleChanged("Note Title")
                                     {
                                         AggregateRootId = aggregateId, Version = 1
                                     } },
                null);
            var result = _eventStore.AppendAsync(eventStream).Result;

            Assert.IsNotNull(result);
            Assert.AreEqual(AsyncTaskStatus.Success, result.Status);
            Assert.AreEqual(EventAppendResult.Success, result.Data);

            var result2 = _publishedVersionStore.UpdatePublishedVersionAsync("DefaultEventProcessor", typeof(TestAggregate).FullName, aggregateId, 1).Result;

            Assert.IsNotNull(result2);
            Assert.AreEqual(AsyncTaskStatus.Success, result2.Status);

            var commandList = new List <ICommand>();

            commandList.Add(new CreateTestAggregateCommand
            {
                Id = commandId,
                AggregateRootId = aggregateId,
                Title           = "Sample Note"
            });
            for (var i = 0; i < 50; i++)
            {
                commandList.Add(new ChangeTestAggregateTitleCommand
                {
                    AggregateRootId = aggregateId,
                    Title           = "Changed Note Title"
                });
            }

            var waitHandle           = new ManualResetEvent(false);
            var count                = 0L;
            var createCommandSuccess = false;

            foreach (var updateCommand in commandList)
            {
                _commandService.ExecuteAsync(updateCommand).ContinueWith(t =>
                {
                    Assert.IsNotNull(t.Result);
                    Assert.AreEqual(AsyncTaskStatus.Success, t.Result.Status);
                    var commandResult = t.Result.Data;
                    Assert.IsNotNull(commandResult);
                    Assert.AreEqual(CommandStatus.Success, commandResult.Status);
                    if (commandResult.CommandId != commandId)
                    {
                        var totalCount = Interlocked.Increment(ref count);
                        if (totalCount == commandList.Count - 1)
                        {
                            waitHandle.Set();
                        }
                    }
                    else
                    {
                        createCommandSuccess = true;
                    }
                });
            }
            waitHandle.WaitOne();
            var note = _memoryCache.GetAsync <TestAggregate>(aggregateId).Result;

            Assert.IsNotNull(note);
            Assert.AreEqual(true, createCommandSuccess);
            Assert.AreEqual(commandList.Count, ((IAggregateRoot)note).Version);
        }
        private void HandleExistingHandledAggregateAsync(ProcessingCommand processingCommand, IAggregateRoot dirtyAggregateRoot, DomainEventStream eventStream, HandledCommand existingHandledCommand, int retryTimes)
        {
            var command = processingCommand.Message;

            _ioHelper.TryAsyncActionRecursively <AsyncTaskResult <DomainEventStream> >("FindEventStreamByCommandIdAsync",
                                                                                       () => _eventStore.FindAsync(existingHandledCommand.AggregateRootId, command.Id),
                                                                                       currentRetryTimes => HandleExistingHandledAggregateAsync(processingCommand, dirtyAggregateRoot, eventStream, existingHandledCommand, currentRetryTimes),
                                                                                       result =>
            {
                var existingEventStream = result.Data;
                if (existingEventStream != null)
                {
                    //如果当前command已经被持久化过了,且该command产生的事件也已经被持久化了,则只要再做一遍发布事件的操作
                    _eventService.PublishDomainEventAsync(processingCommand, existingEventStream);
                }
                else
                {
                    //如果当前command已经被持久化过了,但事件没有被持久化,则需要重新提交当前command所产生的事件;
                    _eventService.CommitDomainEventAsync(new EventCommittingContext(dirtyAggregateRoot, eventStream, processingCommand));
                }
            },
                                                                                       () => string.Format("[aggregateRootId:{0}, commandId:{1}]", existingHandledCommand.AggregateRootId, command.Id),
                                                                                       () => NotifyCommandExecuted(processingCommand, CommandStatus.Failed, null, "Find event stream by command id async failed."),
                                                                                       retryTimes);
        }
Esempio n. 39
0
        public void create_concurrent_conflict_not_enable_batch_insert_test()
        {
            _eventStore.SupportBatchAppendEvent = false;

            try
            {
                var aggregateId = ObjectId.GenerateNewStringId();
                var commandId   = ObjectId.GenerateNewStringId();

                //往EventStore直接插入事件,用于模拟并发冲突的情况
                var eventStream = new DomainEventStream(
                    commandId,
                    aggregateId,
                    typeof(TestAggregate).FullName,
                    1,
                    DateTime.Now,
                    new IDomainEvent[] { new TestAggregateTitleChanged("Note Title")
                                         {
                                             AggregateRootId = aggregateId, Version = 1
                                         } },
                    null);
                var result = _eventStore.AppendAsync(eventStream).Result;
                Assert.IsNotNull(result);
                Assert.AreEqual(AsyncTaskStatus.Success, result.Status);
                Assert.AreEqual(EventAppendResult.Success, result.Data);
                var result2 = _publishedVersionStore.UpdatePublishedVersionAsync("DefaultEventProcessor", typeof(TestAggregate).FullName, aggregateId, 1).Result;
                Assert.IsNotNull(result2);
                Assert.AreEqual(AsyncTaskStatus.Success, result2.Status);

                //执行创建聚合根的命令
                var command = new CreateTestAggregateCommand
                {
                    AggregateRootId = aggregateId,
                    Title           = "Sample Note"
                };
                var asyncResult = _commandService.ExecuteAsync(command).Result;
                Assert.IsNotNull(asyncResult);
                Assert.AreEqual(AsyncTaskStatus.Success, asyncResult.Status);
                var commandResult = asyncResult.Data;
                Assert.IsNotNull(commandResult);
                Assert.AreEqual(CommandStatus.Failed, commandResult.Status);
                Assert.AreEqual("Duplicate aggregate creation.", commandResult.Result);
                var note = _memoryCache.GetAsync <TestAggregate>(aggregateId).Result;
                Assert.IsNotNull(note);
                Assert.AreEqual("Note Title", note.Title);
                Assert.AreEqual(1, ((IAggregateRoot)note).Version);

                //执行创建聚合根的命令
                command = new CreateTestAggregateCommand
                {
                    Id = commandId,
                    AggregateRootId = aggregateId,
                    Title           = "Sample Note"
                };
                asyncResult = _commandService.ExecuteAsync(command).Result;
                Assert.IsNotNull(asyncResult);
                Assert.AreEqual(AsyncTaskStatus.Success, asyncResult.Status);
                commandResult = asyncResult.Data;
                Assert.IsNotNull(commandResult);
                Assert.AreEqual(CommandStatus.Success, commandResult.Status);
                note = _memoryCache.GetAsync <TestAggregate>(aggregateId).Result;
                Assert.IsNotNull(note);
                Assert.AreEqual("Note Title", note.Title);
                Assert.AreEqual(1, ((IAggregateRoot)note).Version);
            }
            finally
            {
                _eventStore.SupportBatchAppendEvent = true;
            }
        }