Esempio n. 1
0
        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(SagaConsumeContext <TSaga, TMessage> context)
            {
                var proxy = new EntityFrameworkSagaConsumeContext <TSaga, TMessage>(_dbContext, context, context.Saga, false);

                proxy.LogAdded();

                await _next.Send(proxy).ConfigureAwait(false);

                if (!proxy.IsCompleted)
                {
                    _dbContext.Set <TSaga>().Add(context.Saga);
                }

                await _dbContext.SaveChangesAsync().ConfigureAwait(false);
            }
            public async Task Send(SagaConsumeContext <TSaga, TMessage> context)
            {
                if (_log.IsDebugEnabled)
                {
                    _log.DebugFormat("SAGA:{0}:{1} Added {2}", TypeMetadataCache <TSaga> .ShortName,
                                     context.Saga.CorrelationId,
                                     TypeMetadataCache <TMessage> .ShortName);
                }

                var proxy = new EntityFrameworkSagaConsumeContext <TSaga, TMessage>(_dbContext, context, context.Saga, false);

                await _next.Send(proxy).ConfigureAwait(false);

                if (!proxy.IsCompleted)
                {
                    _dbContext.Set <TSaga>().Add(context.Saga);
                }

                await _dbContext.SaveChangesAsync().ConfigureAwait(false);
            }
        async Task SendToInstance <T>(SagaQueryConsumeContext <TSaga, T> context, DbContext dbContext, ISagaPolicy <TSaga, T> policy, TSaga instance,
                                      IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            try
            {
                var sagaConsumeContext = new EntityFrameworkSagaConsumeContext <TSaga, T>(dbContext, context, instance);

                sagaConsumeContext.LogUsed();

                await policy.Existing(sagaConsumeContext, next).ConfigureAwait(false);
            }
            catch (SagaException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), instance.CorrelationId, ex);
            }
        }
        async Task SendToInstance <T>(SagaQueryConsumeContext <TSaga, T> context, DbContext dbContext, ISagaPolicy <TSaga, T> policy, TSaga instance,
                                      IPipe <SagaConsumeContext <TSaga, T> > next)
            where T : class
        {
            try
            {
                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);
            }
            catch (SagaException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new SagaException(ex.Message, typeof(TSaga), typeof(T), instance.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;

            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;

            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);
            }
        }