public async Task <int> SaveAsync(AggregateBase aggregate, params KeyValuePair <string, string>[] extraHeaders)
{
var streamName = aggregate.Identifier.ToString();
if (Log.IsEnabled(LogEventLevel.Information))
{
Log.Information("Saving aggregate {streamName}", streamName);
}
var pendingEvents = aggregate.GetPendingEvents();
var originalVersion = aggregate.Version - pendingEvents.Count;
try
{
WriteResult result;
var commitHeaders = CreateCommitHeaders(aggregate, extraHeaders);
var eventsToSave = pendingEvents.Select(x => ToEventData(Guid.NewGuid(), x, commitHeaders));
if (Log.IsEnabled(LogEventLevel.Information))
{
Log.Information("{pendingEventsCount} events to write to stream {streamName}...", pendingEvents.Count, streamName);
}
if (Log.IsEnabled(LogEventLevel.Debug))
{
foreach (var evt in pendingEvents)
{
// Take the hit of serializing twice here as debug logging should only be on in exceptional circumstances
Log.Debug("Event Type: {eventType}. Payload: {payload}", evt.GetType().Name, JsonConvert.SerializeObject(evt));
}
}
var eventBatches = GetEventBatches(eventsToSave);
Debug.Assert(eventBatches.Count > 0);
if (eventBatches.Count == 1)
{
// If just one batch write them straight to the Event Store
result = await _eventStoreConnection.AppendToStreamAsync(streamName, originalVersion, eventBatches[0]);
}
else
{
// If we have more events to save than can be done in one batch according to the WritePageSize, then we need to save them in a transaction to ensure atomicity
using (var transaction = await _eventStoreConnection.StartTransactionAsync(streamName, originalVersion))
{
if (Log.IsEnabled(LogEventLevel.Information))
{
Log.Information("Started transaction {transactionId} for stream {streamName}", transaction.TransactionId, streamName);
}
foreach (var batch in eventBatches)
{
await transaction.WriteAsync(batch);
}
result = await transaction.CommitAsync();
if (Log.IsEnabled(LogEventLevel.Information))
{
Log.Information("Transaction {transactionId} committed", transaction.TransactionId);
}
}
}
aggregate.ClearPendingEvents();
if (Log.IsEnabled(LogEventLevel.Information))
{
Log.Information("Aggregate {streamName} pending events cleaned up", streamName);
}
return(result.NextExpectedVersion);
}
catch (Exception ex)
{
Log.Error($"Failed to write events for stream: {streamName}.", ex);
ExceptionDispatchInfo.Capture(ex).Throw();
}
return(originalVersion + 1);
}