/// <summary> /// Append an event onto the event stream /// </summary> /// <param name="eventToAppend"> /// The event we want to write to the event stream /// </param> /// <param name="expectedTopSequence"> /// The sequence number we expect to be the last in the event stream - if events have been added since this then a /// concurrency error is thrown /// </param> public async Task AppendEvent(object eventToAppend, int?expectedTopSequence = null, EventStreamExistenceConstraint streamConstraint = EventStreamExistenceConstraint.Loose) { if (null != _writer) { // make an event instance of this event and append it to the event stream IAppendResult result = await _writer.AppendEvent(EventInstance.Wrap(eventToAppend), expectedTopSequence.GetValueOrDefault(0), streamConstraint : streamConstraint); if (null != result) { if (null != this._notificationDispatcher) { if (result.NewEventStreamCreated) { await _notificationDispatcher.NewEntityCreated(this, commentary : _context?.Commentary, context : _context); } await _notificationDispatcher.NewEventAppended(this, EventNameAttribute.GetEventName(eventToAppend.GetType()), result.SequenceNumber, commentary : _context?.Commentary, eventPayload : eventToAppend, context : _context); } } } }
public async Task <IAppendResult> AppendEvent(IEvent eventInstance, int expectedTopSequenceNumber = 0, int eventVersionNumber = 1, EventStreamExistenceConstraint streamConstraint = EventStreamExistenceConstraint.Loose) { int nextSequence = 0; // check stream constraints if (streamConstraint != EventStreamExistenceConstraint.Loose) { // find out if the stream exists bool exists = StreamAlreadyExists(); if (streamConstraint == EventStreamExistenceConstraint.MustExist) { if (!exists) { throw new EventStreamWriteException(this, 0, message: $"Stream is constrained to MustExist but has not been created", source: "Table Event Stream Writer"); } } if (streamConstraint == EventStreamExistenceConstraint.MustBeNew) { if (exists) { throw new EventStreamWriteException(this, 0, message: $"Stream is constrained to be new but has already been created", source: "Table Event Stream Writer"); } } } // Read and update the [RECORDID_SEQUENCE] row in a transaction.. nextSequence = await IncrementSequenceNumber(); if (expectedTopSequenceNumber > 0) { if ((1 + expectedTopSequenceNumber) < nextSequence) { //Concurrency error has occured throw new EventStreamWriteException(this, (nextSequence - 1), message: $"Out of sequence write - expected seqeunce number {expectedTopSequenceNumber}, actual {nextSequence - 1}", source: "Table Event Stream Writer"); } } var dteEvent = MakeDynamicTableEntity(eventInstance, nextSequence); if (null != dteEvent) { await base.Table.ExecuteAsync(TableOperation.Insert(dteEvent)); } return(new AppendResult((nextSequence == 1), nextSequence)); }
/// <summary> /// Append the event to the end of the event stream /// </summary> /// <param name="eventInstance"> /// The event to append to the end of the event stream /// </param> /// <param name="expectedTopSequenceNumber"> /// if this is set to > 0 and the event stream is further on then a consistency issue has arisen and the /// event should not be written but rather throw an error /// </param> /// <param name="eventVersionNumber"> /// The version number to add to the event wrapper /// </param> /// <param name="streamConstraint"> /// An additional constrain that must be satisfied by the event stream in order to persist the event /// </param> /// <returns></returns> public async Task <IAppendResult> AppendEvent(IEvent eventInstance, int expectedTopSequenceNumber = 0, int eventVersionNumber = 1, EventStreamExistenceConstraint streamConstraint = EventStreamExistenceConstraint.Loose) { if (base.EventStreamBlob != null) { // acquire a lease for the blob.. string writeStreamLeaseId = null; if (await Exists()) { writeStreamLeaseId = await base.EventStreamBlob.AcquireLeaseAsync(TimeSpan.FromSeconds(15)); } int nextSequence = await base.GetSequenceNumber() + 1; if (expectedTopSequenceNumber > 0) { // check against actual top sequence number if ((expectedTopSequenceNumber + 1) < nextSequence) { throw new EventStreamWriteException(this, (nextSequence - 1), message: $"Out of sequence write - expected seqeunce number {expectedTopSequenceNumber }", source: "Blob Event Stream Writer"); } } string eventName = ""; if (null != eventInstance) { eventName = EventNameAttribute.GetEventName(eventInstance.GetType()); } // create an access condition AccessCondition condition = AccessCondition.GenerateEmptyCondition(); if (streamConstraint == EventStreamExistenceConstraint.MustBeNew) { condition = AccessCondition.GenerateIfNotExistsCondition(); } if (streamConstraint == EventStreamExistenceConstraint.MustExist) { condition = AccessCondition.GenerateIfExistsCondition(); } if (!string.IsNullOrWhiteSpace(writeStreamLeaseId)) { condition.LeaseId = writeStreamLeaseId; } // default the writer context if it is not already set if (null == _writerContext) { _writerContext = WriteContext.DefaultWriterContext(); } BlobBlockJsonWrappedEvent evtToWrite = BlobBlockJsonWrappedEvent.Create(eventName, nextSequence, eventVersionNumber, null, eventInstance, _writerContext); try { // Create it if it doesn't exist and initialsie the metadata await base.Refresh(); Microsoft.Azure.Storage.OperationContext context = new Microsoft.Azure.Storage.OperationContext() { }; await EventStreamBlob.AppendBlockAsync(new System.IO.MemoryStream(Encoding.UTF8.GetBytes(evtToWrite.ToJSonText())), "", condition, null, // use the default blob request options context ); } catch (Microsoft.Azure.Storage.StorageException exBlob) { throw new EventStreamWriteException(this, (nextSequence - 1), message: "Failed to save an event to the event stream", source: "Blob Event Stream Writer", innerException: exBlob); } await IncrementSequence(writeStreamLeaseId); if (!string.IsNullOrWhiteSpace(writeStreamLeaseId)) { // and release the lease await base.EventStreamBlob.ReleaseLeaseAsync(condition); } int sequence = await base.GetSequenceNumber(); return(new AppendResult((sequence == 0), sequence)); } else { return(null); } }