public TDbContext GetDataContext(SynchronizedStorageSession storageSession) { var dbConnection = storageSession.SqlPersistenceSession().Connection; if (_log.IsEnabled(LogEventLevel.Verbose)) { _log.Verbose($"Creating {typeof(TDbContext).FullName} context"); } var dbContext = DbContextThreadAsyncFactory.CreateDbContext(dbConnection, _schema, _builder.Build <TDbContext>); // Use the same underlying ADO.NET transaction dbContext.Database.UseTransaction(storageSession.SqlPersistenceSession().Transaction); // Call SaveChanges before completing storage session storageSession.SqlPersistenceSession().OnSaveChanges(x => { if (_log.IsEnabled(LogEventLevel.Verbose)) { _log.Verbose($"Auto saving changes for {typeof(TDbContext).FullName} context"); } return(dbContext.SaveChangesAsync()); }); return(dbContext); }
internal async Task Save(IContainSagaData sagaData, SynchronizedStorageSession session, object correlationId) { var sqlSession = session.SqlPersistenceSession(); var sagaInfo = sagaInfoCache.GetInfo(sagaData.GetType()); using (var command = sqlDialect.CreateCommand(sqlSession.Connection)) { command.Transaction = sqlSession.Transaction; command.CommandText = sagaInfo.SaveCommand; command.AddParameter("Id", sagaData.Id); var metadata = new Dictionary <string, string>(); if (sagaData.OriginalMessageId != null) { metadata.Add("OriginalMessageId", sagaData.OriginalMessageId); } if (sagaData.Originator != null) { metadata.Add("Originator", sagaData.Originator); } command.AddParameter("Metadata", Serializer.Serialize(metadata)); command.AddJsonParameter("Data", sqlDialect.BuildSagaData(command, sagaInfo, sagaData)); command.AddParameter("PersistenceVersion", StaticVersions.PersistenceVersion); command.AddParameter("SagaTypeVersion", sagaInfo.CurrentVersion); if (correlationId != null) { command.AddParameter("CorrelationId", correlationId); } AddTransitionalParameter(sagaData, sagaInfo, command); await command.ExecuteNonQueryEx().ConfigureAwait(false); } }
public static OrderContext FromCurrentSession(this SynchronizedStorageSession session) { var sqlPersistenceSession = session.SqlPersistenceSession(); var optionsBuilder = new DbContextOptionsBuilder <OrderContext>(); optionsBuilder.UseSqlServer(sqlPersistenceSession.Connection); var context = new OrderContext(optionsBuilder.Options); context.Database.UseTransaction(sqlPersistenceSession.Transaction); return(context); }
internal async Task Complete(IContainSagaData sagaData, SynchronizedStorageSession session, int concurrency) { var sagaInfo = sagaInfoCache.GetInfo(sagaData.GetType()); var sqlSession = session.SqlPersistenceSession(); using (var command = commandBuilder.CreateCommand(sqlSession.Connection)) { command.CommandText = sagaInfo.CompleteCommand; command.Transaction = sqlSession.Transaction; command.AddParameter("Id", sagaData.Id); command.AddParameter("Concurrency", concurrency); await command.ExecuteNonQueryAsync().ConfigureAwait(false); } }
static async Task <Concurrency <TSagaData> > GetSagaData <TSagaData>(SynchronizedStorageSession session, string commandText, RuntimeSagaInfo sagaInfo, ParameterAppender appendParameters) where TSagaData : class, IContainSagaData { var sqlSession = session.SqlPersistenceSession(); using (var command = sagaInfo.CreateCommand(sqlSession.Connection)) { command.CommandText = commandText; command.Transaction = sqlSession.Transaction; var dbCommand = command.InnerCommand; appendParameters(dbCommand.CreateParameter, parameter => dbCommand.Parameters.Add(parameter)); // to avoid loading into memory SequentialAccess is required which means each fields needs to be accessed using (var dataReader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow | CommandBehavior.SequentialAccess).ConfigureAwait(false)) { if (!await dataReader.ReadAsync().ConfigureAwait(false)) { return(default);
internal async Task Complete(IContainSagaData sagaData, SynchronizedStorageSession session, int concurrency) { var sagaInfo = sagaInfoCache.GetInfo(sagaData.GetType()); var sqlSession = session.SqlPersistenceSession(); using (var command = sqlDialect.CreateCommand(sqlSession.Connection)) { command.CommandText = sagaInfo.CompleteCommand; command.Transaction = sqlSession.Transaction; command.AddParameter("Id", sagaData.Id); command.AddParameter("Concurrency", concurrency); var affected = await command.ExecuteNonQueryAsync().ConfigureAwait(false); if (affected != 1) { throw new Exception($"Optimistic concurrency violation when trying to complete saga {sagaInfo.SagaType.FullName} {sagaData.Id}. Expected version {concurrency}."); } } }
static async Task <Concurrency <TSagaData> > GetSagaData <TSagaData>(SynchronizedStorageSession session, string commandText, RuntimeSagaInfo sagaInfo, ParameterAppender appendParameters) where TSagaData : IContainSagaData { var sqlSession = session.SqlPersistenceSession(); using (var command = sagaInfo.CreateCommand(sqlSession.Connection)) { command.CommandText = commandText; command.Transaction = sqlSession.Transaction; appendParameters(command.InnerCommand.CreateParameter, parameter => command.InnerCommand.Parameters.Add(parameter)); // to avoid loading into memory SequentialAccess is required which means each fields needs to be accessed using (var dataReader = await command.ExecuteReaderAsync(CommandBehavior.SingleRow | CommandBehavior.SequentialAccess).ConfigureAwait(false)) { if (!await dataReader.ReadAsync().ConfigureAwait(false)) { return(default(Concurrency <TSagaData>)); } var id = await dataReader.GetGuidAsync(0).ConfigureAwait(false); var sagaTypeVersionString = await dataReader.GetFieldValueAsync <string>(1).ConfigureAwait(false); var sagaTypeVersion = Version.Parse(sagaTypeVersionString); var concurrency = await dataReader.GetFieldValueAsync <int>(2).ConfigureAwait(false); string originator; string originalMessageId; ReadMetadata(dataReader, out originator, out originalMessageId); using (var textReader = dataReader.GetTextReader(4)) { var sagaData = sagaInfo.FromString <TSagaData>(textReader, sagaTypeVersion); sagaData.Id = id; sagaData.Originator = originator; sagaData.OriginalMessageId = originalMessageId; return(new Concurrency <TSagaData>(sagaData, concurrency)); } } } }
internal async Task Update(IContainSagaData sagaData, SynchronizedStorageSession session, int concurrency, CancellationToken cancellationToken = default) { var sqlSession = session.SqlPersistenceSession(); var sagaInfo = sagaInfoCache.GetInfo(sagaData.GetType()); using (var command = sagaInfo.CreateCommand(sqlSession.Connection)) { command.CommandText = sagaInfo.UpdateCommand; command.Transaction = sqlSession.Transaction; command.AddParameter("Id", sagaData.Id); command.AddParameter("PersistenceVersion", StaticVersions.PersistenceVersion); command.AddParameter("SagaTypeVersion", sagaInfo.CurrentVersion); command.AddJsonParameter("Data", sqlDialect.BuildSagaData(command, sagaInfo, sagaData)); command.AddParameter("Concurrency", concurrency); AddTransitionalParameter(sagaData, sagaInfo, command); var affected = await command.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); if (affected != 1) { throw new Exception($"Optimistic concurrency violation when trying to save saga {sagaInfo.SagaType.FullName} {sagaData.Id}. Expected version {concurrency}."); } } }