async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; using (var session = _store.DirtyTrackedSession()) { TSaga instance; if (policy.PreInsertInstance(context, out instance)) { PreInsertSagaInstance <T>(session, instance); } if (instance == null) { instance = session.Load <TSaga>(sagaId); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(session, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(context, policy, instance, next, session).ConfigureAwait(false); } } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } Guid sagaId = context.CorrelationId.Value; using (DbContext dbContext = _sagaDbContextFactory()) using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel)) { bool inserted = false; TSaga instance; if (policy.PreInsertInstance(context, out instance)) { inserted = await PreInsertSagaInstance <T>(dbContext, instance, context.CancellationToken).ConfigureAwait(false); } try { if (instance == null) { instance = dbContext.Set <TSaga>().SingleOrDefault(x => x.CorrelationId == sagaId); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(dbContext, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName); } var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); // if (inserted && !sagaConsumeContext.IsCompleted) // dbContext.Set<TSaga>().Update(instance); } await dbContext.SaveChangesAsync().ConfigureAwait(false); transaction.Commit(); } catch (Exception) { transaction.Rollback(); throw; } } }
public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } _logger.Debug($"SAGA: Send {context.Message.GetType().FullName}"); var sagaId = context.CorrelationId.Value; if (policy.PreInsertInstance(context, out var instance)) { await PreInsertSagaInstance <T>(instance, context.MessageId).ConfigureAwait(false); } if (instance == null) { instance = await GetSaga(sagaId); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(_connection, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(context, policy, next, instance).ConfigureAwait(false); } }
public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } TSaga instance; if (policy.PreInsertInstance(context, out instance)) { await PreInsertSagaInstance(context, instance).ConfigureAwait(false); } if (instance == null) { instance = await _collection.Find(x => x.CorrelationId == context.CorrelationId).SingleOrDefaultAsync(context.CancellationToken).ConfigureAwait(false); } if (instance == null) { var missingSagaPipe = new MissingPipe <TSaga, T>(_collection, next, _mongoDbSagaConsumeContextFactory); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(context, policy, next, instance).ConfigureAwait(false); } }
public async Task Send(SagaRepositoryContext <TSaga, T> context) { SagaConsumeContext <TSaga, T> sagaConsumeContext = null; if (_policy.PreInsertInstance(context, out var instance)) { sagaConsumeContext = await context.Insert(instance).ConfigureAwait(false); } sagaConsumeContext ??= await context.Load(_correlationId).ConfigureAwait(false); if (sagaConsumeContext != null) { try { sagaConsumeContext.LogUsed(); await _policy.Existing(sagaConsumeContext, _next).ConfigureAwait(false); if (_policy.IsReadOnly) { await context.Undo(sagaConsumeContext).ConfigureAwait(false); } else { if (sagaConsumeContext.IsCompleted) { await context.Delete(sagaConsumeContext).ConfigureAwait(false); sagaConsumeContext.LogRemoved(); } else { await context.Update(sagaConsumeContext).ConfigureAwait(false); } } } finally { switch (sagaConsumeContext) { case IAsyncDisposable asyncDisposable: await asyncDisposable.DisposeAsync().ConfigureAwait(false); break; case IDisposable disposable: disposable.Dispose(); break; } } } else { await _policy.Missing(context, new MissingSagaPipe <TSaga, T>(context, _next)).ConfigureAwait(false); } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var correlationId = context.CorrelationId.Value; if (policy.PreInsertInstance(context, out TSaga preInsertInstance)) { using (var connection = new SqlConnection(_connectionString)) { await PreInsertSagaInstance <T>(connection, context, preInsertInstance).ConfigureAwait(false); } } using (var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled)) using (var connection = new SqlConnection(_connectionString)) { try { var tableName = GetTableName(); var instance = await connection.QuerySingleOrDefaultAsync <TSaga>( $"SELECT * FROM {tableName} WITH (UPDLOCK, ROWLOCK) WHERE CorrelationId = @correlationId", new { correlationId }).ConfigureAwait(false); if (instance == null) { var missingSagaPipe = new MissingPipe <T>(connection, tableName, next, InsertSagaInstance); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(context, connection, policy, instance, tableName, next).ConfigureAwait(false); } transaction.Complete(); } catch (SagaException sex) { LogContext.Error?.Log(sex, "SAGA:{SagaType} Exception {MessageType}", TypeMetadataCache <TSaga> .ShortName, TypeMetadataCache <T> .ShortName); throw; } catch (Exception ex) { LogContext.Error?.Log(ex, "SAGA:{SagaType} Exception {MessageType}", TypeMetadataCache <TSaga> .ShortName, TypeMetadataCache <T> .ShortName); throw new SagaException(ex.Message, typeof(TSaga), typeof(T), correlationId, ex); } } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; var db = _redisDbFactory(); ITypedDatabase <TSaga> sagas = db.As <TSaga>(); IAsyncDisposable pessimisticLock = null; if (!_optimistic) { pessimisticLock = await db.AcquireLockAsync(sagaId, _lockTimeout, _lockRetryTimeout).ConfigureAwait(false); } try { if (policy.PreInsertInstance(context, out var instance)) { await PreInsertSagaInstance <T>(sagas, instance).ConfigureAwait(false); } if (instance == null) { instance = await sagas.Get(sagaId).ConfigureAwait(false); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(sagas, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(sagas, context, policy, next, instance).ConfigureAwait(false); } } finally { if (!_optimistic) { await pessimisticLock.DisposeAsync().ConfigureAwait(false); } } }
public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } Document document = null; if (policy.PreInsertInstance(context, out var instance)) { document = await PreInsertSagaInstance(context, instance).ConfigureAwait(false); } if (instance == null) { try { ResourceResponse <Document> response = await _client .ReadDocumentAsync(UriFactory.CreateDocumentUri(_databaseName, _collectionName, context.CorrelationId.ToString()), _requestOptions) .ConfigureAwait(false); document = response.Resource; } catch (DocumentClientException e) when(e.StatusCode == System.Net.HttpStatusCode.NotFound) { // Couldn't find the document, swallowing exception } if (document != null) { instance = JsonConvert.DeserializeObject <TSaga>(document.ToString()); } } if (instance == null) { var missingSagaPipe = new MissingPipe <TSaga, T>(_client, _databaseName, _collectionName, next, _documentDbSagaConsumeContextFactory, _requestOptions); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(context, policy, next, instance, document).ConfigureAwait(false); } }
public async Task Send(SagaRepositoryContext <TSaga, T> context) { SagaConsumeContext <TSaga, T> sagaConsumeContext = null; if (_policy.PreInsertInstance(context, out var instance)) { sagaConsumeContext = await context.Insert(instance).ConfigureAwait(false); } if (sagaConsumeContext == null) { sagaConsumeContext = await context.Load(_correlationId).ConfigureAwait(false); } if (sagaConsumeContext != null) { try { sagaConsumeContext.LogUsed(); await _policy.Existing(sagaConsumeContext, _next).ConfigureAwait(false); } finally { switch (sagaConsumeContext) { case IAsyncDisposable asyncDisposable: await asyncDisposable.DisposeAsync().ConfigureAwait(false); break; case IDisposable disposable: disposable.Dispose(); break; } } } else { var missingPipe = new MissingSagaPipe <TSaga, T>(context, _next); await _policy.Missing(context, missingPipe).ConfigureAwait(false); } }
public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; var db = _redisConnection.GetDatabase(); TSaga instance; ITypedDatabase <TSaga> sagas = db.As <TSaga>(); if (policy.PreInsertInstance(context, out instance)) { await PreInsertSagaInstance <T>(sagas, instance).ConfigureAwait(false); } if (instance == null) { using (var distLock = _lockFactory.Create($"redislock:{context.CorrelationId.Value}", _expiry, _wait, _retryTime)) { if (distLock.IsAcquired) { instance = await sagas.Get(sagaId, _redisPrefix).ConfigureAwait(false); } } } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(db, _lockFactory, next, _redisPrefix); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(context, policy, next, instance).ConfigureAwait(false); } }
public async Task Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; TSaga instance; using (var redis = _clientsManager.GetClient()) { var sagas = redis.As <TSaga>(); if (policy.PreInsertInstance(context, out instance)) { await PreInsertSagaInstance <T>(sagas, instance).ConfigureAwait(false); } if (instance == null) { instance = sagas.GetById(sagaId); } } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(_clientsManager, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { await SendToInstance(context, policy, next, instance).ConfigureAwait(false); } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; using (var dbContext = _sagaDbContextFactory()) using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel)) { // Hack for locking row for the duration of the transaction. var tableName = ((IObjectContextAdapter)dbContext).ObjectContext.CreateObjectSet <TSaga>().EntitySet.Name; await dbContext.Database.ExecuteSqlCommandAsync($"select 1 from {tableName} WITH (UPDLOCK, ROWLOCK) WHERE CorrelationId = @p0", sagaId) .ConfigureAwait(false); var inserted = false; TSaga instance; if (policy.PreInsertInstance(context, out instance)) { inserted = await PreInsertSagaInstance <T>(dbContext, instance, context.CancellationToken).ConfigureAwait(false); } try { if (instance == null) { instance = dbContext.Set <TSaga>().SingleOrDefault(x => x.CorrelationId == sagaId); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(dbContext, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName); } var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); } await dbContext.SaveChangesAsync().ConfigureAwait(false); transaction.Commit(); } catch (DbUpdateException ex) { var baseException = ex.GetBaseException() as SqlException; if (baseException != null && baseException.Number == 1205) { // deadlock, no need to rollback } else { try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } } throw; } catch (Exception) { try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } throw; } } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; using (var dbContext = _sagaDbContextFactory()) using (var transaction = await dbContext.Database.BeginTransactionAsync(_isolationLevel, context.CancellationToken).ConfigureAwait(false)) { if (!_optimistic) { // Hack for locking row for the duration of the transaction. var tableName = _relationalEntityMetadataHelper.GetTableName <TSaga>(dbContext); await dbContext.Database.ExecuteSqlCommandAsync( $"select 1 from {tableName} WITH (UPDLOCK, ROWLOCK) WHERE CorrelationId = @p0", new object[] { sagaId }, context.CancellationToken).ConfigureAwait(false); } var inserted = false; TSaga instance; if (policy.PreInsertInstance(context, out instance)) { inserted = await PreInsertSagaInstance <T>(dbContext, instance, context.CancellationToken).ConfigureAwait(false); } try { if (instance == null) { instance = await QuerySagas <T>(dbContext).SingleOrDefaultAsync(x => x.CorrelationId == sagaId, context.CancellationToken).ConfigureAwait(false); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(dbContext, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName); } var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); } await dbContext.SaveChangesAsync(context.CancellationToken).ConfigureAwait(false); transaction.Commit(); } catch (DbUpdateConcurrencyException) { try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } throw; } catch (DbUpdateException ex) { if (IsDeadlockException(ex)) { // deadlock, no need to rollback } else { if (_log.IsErrorEnabled) { _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } } throw; } catch (Exception ex) { if (_log.IsErrorEnabled) { _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } throw; } } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; using (var session = _sessionFactory.OpenSession()) using (var transaction = session.BeginTransaction()) { var inserted = false; TSaga instance; if (policy.PreInsertInstance(context, out instance)) { inserted = PreInsertSagaInstance <T>(session, instance); } try { if (instance == null) { instance = session.Get <TSaga>(sagaId, LockMode.Upgrade); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(session, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName); } var sagaConsumeContext = new NHibernateSagaConsumeContext <TSaga, T>(session, context, instance); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); if (inserted && !sagaConsumeContext.IsCompleted) { session.Update(instance); } } if (transaction.IsActive) { transaction.Commit(); } } catch (Exception ex) { if (_log.IsErrorEnabled) { _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } if (transaction.IsActive) { transaction.Rollback(); } throw; } } }
private async Task SendLogic <T>(IDbContextTransaction transaction, DbContext dbContext, ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class { var sagaId = context.CorrelationId.Value; if (policy.PreInsertInstance(context, out var instance)) { var inserted = await PreInsertSagaInstance <T>(dbContext, instance, context.CancellationToken).ConfigureAwait(false); if (!inserted) { instance = null; // Reset this back to null if the insert failed. We will use the MissingPipe to create instead } } try { if (instance == null) { IQueryable <TSaga> queryable = QuerySagas(dbContext); // Query with a row Lock instead using FromSql. Still a single trip to the DB (unlike EF6, which has to make one dummy call to row lock) var rowLockQuery = _rawSqlLockStatements?.GetRowLockStatement <TSaga>(dbContext); if (rowLockQuery != null) { instance = await queryable.FromSql(rowLockQuery, new object[] { sagaId }).SingleOrDefaultAsync(context.CancellationToken).ConfigureAwait(false); } else { instance = await queryable.SingleOrDefaultAsync(x => x.CorrelationId == sagaId, context.CancellationToken).ConfigureAwait(false); } } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(dbContext, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName); } var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); } await dbContext.SaveChangesAsync(context.CancellationToken).ConfigureAwait(false); transaction.Commit(); } catch (DbUpdateConcurrencyException) { try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } throw; } catch (DbUpdateException ex) { if (IsDeadlockException(ex)) { // deadlock, no need to rollback } else { if (_log.IsErrorEnabled) { _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } } throw; } catch (Exception ex) { if (_log.IsErrorEnabled) { _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } throw; } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; var dbContext = _sagaDbContextFactory.CreateScoped(context); try { using (var transaction = dbContext.Database.BeginTransaction(_isolationLevel)) { if (policy.PreInsertInstance(context, out var instance)) { var inserted = await PreInsertSagaInstance <T>(dbContext, instance, context.CancellationToken).ConfigureAwait(false); if (!inserted) { instance = null; // Reset this back to null if the insert failed. We will use the MissingPipe to create instead } } try { if (instance == null) { // Only perform this additional DB Call for pessimistic concurrency if (_rawSqlLockStatements != null) { var rowLockQuery = _rawSqlLockStatements.GetRowLockStatement <TSaga>(dbContext); await dbContext.Database.ExecuteSqlCommandAsync(rowLockQuery, context.CancellationToken, sagaId).ConfigureAwait(false); } instance = await QuerySagas(dbContext) .SingleOrDefaultAsync(x => x.CorrelationId == sagaId, context.CancellationToken) .ConfigureAwait(false); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(dbContext, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName); } var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); } await dbContext.SaveChangesAsync(context.CancellationToken).ConfigureAwait(false); transaction.Commit(); } catch (DbUpdateConcurrencyException) { try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } throw; } catch (DbUpdateException ex) { if (IsDeadlockException(ex)) { // deadlock, no need to rollback } else { if (_log.IsErrorEnabled) { _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } } throw; } catch (Exception ex) { if (_log.IsErrorEnabled) { _log.Error($"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } try { transaction.Rollback(); } catch (Exception innerException) { if (_log.IsWarnEnabled) { _log.Warn("The transaction rollback failed", innerException); } } throw; } } } finally { _sagaDbContextFactory.Release(dbContext); } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; using (var session = OpenSession()) { var inserted = false; TSaga instance; if (policy.PreInsertInstance(context, out instance)) { inserted = await PreInsertSagaInstance <T>(session, instance); } try { if (instance == null) { instance = await session.LoadAsync <TSaga>(ConvertToSagaId(session, sagaId)) .ConfigureAwait(false); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(session, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA:{0}:{1} Used {2}", TypeMetadataCache <TSaga> .ShortName, instance.CorrelationId, TypeMetadataCache <T> .ShortName); } var sagaConsumeContext = new RavenDbSagaConsumeContext <TSaga, T>(session, context, instance); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); if (inserted && !sagaConsumeContext.IsCompleted) { await session.StoreAsync(instance, GetSagaId(session, instance)).ConfigureAwait(false); } if (_log.IsDebugEnabled) { _log.DebugFormat("SAGA (Send): New saga state: {@Saga}", instance); } } await session.SaveChangesAsync().ConfigureAwait(false); } catch (Exception ex) { if (_log.IsErrorEnabled) { _log.Error( $"SAGA:{TypeMetadataCache<TSaga>.ShortName} Exception {TypeMetadataCache<T>.ShortName}", ex); } throw; } } }
async Task ISagaRepository <TSaga> .Send <T>(ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) { if (!context.CorrelationId.HasValue) { throw new SagaException("The CorrelationId was not specified", typeof(TSaga), typeof(T)); } var sagaId = context.CorrelationId.Value; using (var session = _sessionFactory.OpenSession()) using (var transaction = session.BeginTransaction()) { var inserted = false; if (policy.PreInsertInstance(context, out var instance)) { inserted = await PreInsertSagaInstance(context, session, instance).ConfigureAwait(false); } try { if (instance == null) { instance = await session.GetAsync <TSaga>(sagaId, LockMode.Upgrade).ConfigureAwait(false); } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(session, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { var sagaConsumeContext = new NHibernateSagaConsumeContext <TSaga, T>(session, context, instance); sagaConsumeContext.LogUsed(); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); if (inserted && !sagaConsumeContext.IsCompleted) { await session.UpdateAsync(instance).ConfigureAwait(false); } } if (transaction.IsActive) { await transaction.CommitAsync().ConfigureAwait(false); } } catch (Exception ex) { context.LogFault(this, ex, instance?.CorrelationId); if (transaction.IsActive) { await transaction.RollbackAsync().ConfigureAwait(false); } throw; } } }
async Task SendLogic <T>(IDbContextTransaction transaction, DbContext dbContext, ConsumeContext <T> context, ISagaPolicy <TSaga, T> policy, IPipe <SagaConsumeContext <TSaga, T> > next) where T : class { var sagaId = context.CorrelationId.Value; if (policy.PreInsertInstance(context, out var instance)) { var inserted = await PreInsertSagaInstance(dbContext, context, instance).ConfigureAwait(false); if (!inserted) { instance = null; // Reset this back to null if the insert failed. We will use the MissingPipe to create instead } } try { if (instance == null) { // Query with a row Lock instead using FromSql. Still a single trip to the DB (unlike EF6, which has to make one dummy call to row lock) var rowLockQuery = _rawSqlLockStatements?.GetRowLockStatement <TSaga>(dbContext); if (rowLockQuery != null) { instance = await dbContext.Set <TSaga>().FromSqlRaw(rowLockQuery, new object[] { sagaId }).SingleOrDefaultAsync(context.CancellationToken) .ConfigureAwait(false); } else { instance = await dbContext.Set <TSaga>().SingleOrDefaultAsync(x => x.CorrelationId == sagaId, context.CancellationToken).ConfigureAwait(false); } } if (instance == null) { var missingSagaPipe = new MissingPipe <T>(dbContext, next); await policy.Missing(context, missingSagaPipe).ConfigureAwait(false); } else { var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance); sagaConsumeContext.LogUsed(); await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false); } await dbContext.SaveChangesAsync(context.CancellationToken).ConfigureAwait(false); transaction.Commit(); } catch (DbUpdateConcurrencyException) { try { transaction.Rollback(); } catch (Exception innerException) { LogContext.Warning?.Log(innerException, "Transaction rollback failed"); } throw; } catch (DbUpdateException ex) { if (IsDeadlockException(ex)) { // deadlock, no need to rollback } else { context.LogFault(this, ex, instance?.CorrelationId); try { transaction.Rollback(); } catch (Exception innerException) { LogContext.Warning?.Log(innerException, "Transaction rollback failed"); } } throw; } catch (Exception ex) { context.LogFault(this, ex, instance?.CorrelationId); try { transaction.Rollback(); } catch (Exception innerException) { LogContext.Warning?.Log(innerException, "Transaction rollback failed"); } throw; } }