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);
        }
예제 #4
0
    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);
        }
    }
예제 #5
0
    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);
예제 #6
0
    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}.");
            }
        }
    }