public async Task Save(ISagaData sagaData, Dictionary<string, string> sagaAuditMetadata)
        {
            using (var connection = await _connectionHelper.GetConnection())
            {
                using (var command = connection.CreateCommand())
                {
                    command.CommandText =
                        $@"

INSERT
    INTO ""{_tableName}"" (""id"", ""revision"", ""data"", ""metadata"")
    VALUES (@id, @revision, @data, @metadata);

";
                    command.Parameters.Add("id", NpgsqlDbType.Uuid).Value = sagaData.Id;
                    command.Parameters.Add("revision", NpgsqlDbType.Integer).Value = sagaData.Revision;
                    command.Parameters.Add("data", NpgsqlDbType.Bytea).Value = _objectSerializer.Serialize(sagaData);
                    command.Parameters.Add("metadata", NpgsqlDbType.Jsonb).Value =
                        _dictionarySerializer.SerializeToString(sagaAuditMetadata);

                    await command.ExecuteNonQueryAsync();
                }
                
                connection.Complete();
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Updates the saga data
        /// </summary>
        public async Task Update(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            var id = GetId(sagaData);

            lock (_lock)
            {
                if (!_data.ContainsKey(id))
                {
                    throw new ConcurrencyException("Saga data with ID {0} no longer exists and cannot be updated", id);
                }

                VerifyCorrelationPropertyUniqueness(sagaData, correlationProperties);

                var existingCopy = _data[id];

                if (existingCopy.Revision != sagaData.Revision)
                {
                    throw new ConcurrencyException("Attempted to update saga data with ID {0} with revision {1}, but the existing data was updated to revision {2}",
                        id, sagaData.Revision, existingCopy.Revision);
                }

                var clone = Clone(sagaData);
                clone.Revision++;
                _data[id] = clone;
                sagaData.Revision++;
            }
        }
Exemplo n.º 3
0
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            if (sagaData.Id == Guid.Empty)
            {
                throw new InvalidOperationException($"Saga data {sagaData.GetType()} has an uninitialized Id property!");
            }

            if (sagaData.Revision != 0)
            {
                throw new InvalidOperationException($"Attempted to insert saga data with ID {sagaData.Id} and revision {sagaData.Revision}, but revision must be 0 on first insert!");
            }

            using (var session = _documentStore.OpenAsyncSession())
            {
                session.Advanced.UseOptimisticConcurrency = true;

                var sagaDataDocumentId = SagaDataDocument.GetIdFromGuid(sagaData.Id);

                var existingSagaDataDocument = await session.LoadAsync<SagaDataDocument>(sagaDataDocumentId);

                if (existingSagaDataDocument != null)
                {
                    throw new ConcurrencyException("Cannot insert document with an id that already exists");
                }

                var sagaDataDocument = new SagaDataDocument(sagaData);
                await session.StoreAsync(sagaDataDocument, sagaDataDocumentId);

                var correlationPropertyDocumentIds = await SaveCorrelationProperties(session, sagaData, correlationProperties, sagaDataDocumentId);
                sagaDataDocument.SagaCorrelationPropertyDocumentIds = correlationPropertyDocumentIds;

                await session.SaveChangesAsync();
            }
        }
        /// <summary>
        /// Saves a snapshot of the saga data along with the given metadata
        /// </summary>
        public async Task Save(ISagaData sagaData, Dictionary<string, string> sagaAuditMetadata)
        {
            using (var connection = await _connectionProvider.GetConnection())
            {
                using (var command = connection.CreateCommand())
                {
                    command.CommandText = string.Format(@"

INSERT INTO [{0}] (
    [id],
    [revision],
    [data],
    [metadata]
) VALUES (
    @id, 
    @revision, 
    @data,
    @metadata
)

", _tableName);
                    command.Parameters.Add("id", SqlDbType.UniqueIdentifier).Value = sagaData.Id;
                    command.Parameters.Add("revision", SqlDbType.Int).Value = sagaData.Revision;
                    command.Parameters.Add("data", SqlDbType.NVarChar).Value = JsonConvert.SerializeObject(sagaData, DataSettings);
                    command.Parameters.Add("metadata", SqlDbType.NVarChar).Value = JsonConvert.SerializeObject(sagaAuditMetadata, MetadataSettings);

                    await command.ExecuteNonQueryAsync();
                }

                await connection.Complete();
            }
        }
Exemplo n.º 5
0
 public void Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
 {
     var indexItems = ReadIndexItems();
     indexItems.RemoveAll(i => i.SagaId == sagaData.Id);
     indexItems.AddRange(GetPropertiesToIndex(sagaData, correlationProperties));
     var path = SagaFilePath(sagaData.Id);
     File.WriteAllText(path, JsonConvert.SerializeObject(sagaData, _serializerSettings));
     WriteSagaIndexItems(indexItems);
 }
Exemplo n.º 6
0
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            if (sagaData.Id == Guid.Empty)
            {
                throw new InvalidOperationException(string.Format("Attempted to insert saga data {0} without an ID", sagaData.GetType()));
            }

            var collection = GetCollection(sagaData.GetType());

            await collection.InsertOneAsync(sagaData.ToBsonDocument()).ConfigureAwait(false);
        }
        public async Task Save(ISagaData sagaData, Dictionary<string, string> sagaAuditMetadata)
        {
            var document = new BsonDocument
            {
                {"_id", new {Id = sagaData.Id, Revision = sagaData.Revision}.ToBsonDocument()},
                {"Metadata", sagaAuditMetadata.ToBsonDocument()},
                {"Data", sagaData.ToBsonDocument()}
            };

            await _snapshots.InsertOneAsync(document);
        }
Exemplo n.º 8
0
 Dictionary<string, string> GetMetadata(ISagaData sagaData, object handler, Message message)
 {
     return new Dictionary<string, string>
     {
         {SagaAuditingMetadataKeys.HandleQueue, _transport.Address},
         {SagaAuditingMetadataKeys.SagaDataType, sagaData.GetType().GetSimpleAssemblyQualifiedName()},
         {SagaAuditingMetadataKeys.SagaHandlerType, handler.GetType().GetSimpleAssemblyQualifiedName()},
         {SagaAuditingMetadataKeys.MessageType, message.GetMessageType()},
         {SagaAuditingMetadataKeys.MessageId, message.GetMessageId()},
         {SagaAuditingMetadataKeys.MachineName, Environment.MachineName},
     };
 }
Exemplo n.º 9
0
        public async Task Save(ISagaData sagaData, Dictionary<string, string> sagaAuditMetadata)
        {
            var logData = new
            {
                Data = sagaData,
                Metadata = sagaAuditMetadata
            };
            
            var jsonText = JsonConvert.SerializeObject(logData, Formatting.None);

            _log.Info(jsonText);
        }
Exemplo n.º 10
0
 internal void AddInstance(ISagaData sagaData)
 {
     lock (_lock)
     {
         var instance = Clone(sagaData);
         if (instance.Id == Guid.Empty)
         {
             instance.Id = Guid.NewGuid();
         }
         _data[instance.Id] = instance;
     }
 }
Exemplo n.º 11
0
        public async Task Delete(ISagaData sagaData)
        {
            var collection = GetCollection(sagaData.GetType());

            var result = await collection.DeleteManyAsync(new BsonDocument("_id", sagaData.Id)).ConfigureAwait(false);

            if (result.DeletedCount != 1)
            {
                throw new ConcurrencyException("Saga data {0} with ID {1} in collection {2} could not be deleted", 
                    sagaData.GetType(), sagaData.Id, collection.CollectionNamespace);
            }
        }
 /// <summary>
 /// Archives the given saga data under its current ID and revision
 /// </summary>
 public async Task Save(ISagaData sagaData, Dictionary<string, string> sagaAuditMetadata)
 {
     var dataRef = $"{sagaData.Id:N}/{sagaData.Revision:0000000000}/data.json";
     var metaDataRef = $"{sagaData.Id:N}/{sagaData.Revision:0000000000}/metadata.json";
     var dataBlob = _container.GetBlockBlobReference(dataRef);
     var metaDataBlob = _container.GetBlockBlobReference(metaDataRef);
     dataBlob.Properties.ContentType = "application/json";
     metaDataBlob.Properties.ContentType = "application/json";
     await dataBlob.UploadTextAsync(JsonConvert.SerializeObject(sagaData, DataSettings), TextEncoding, DefaultAccessCondition, DefaultRequestOptions, DefaultOperationContext);
     await metaDataBlob.UploadTextAsync(JsonConvert.SerializeObject(sagaAuditMetadata, MetadataSettings), TextEncoding, DefaultAccessCondition, DefaultRequestOptions, DefaultOperationContext);
     await dataBlob.SetPropertiesAsync();
     await metaDataBlob.SetPropertiesAsync();
 }
Exemplo n.º 13
0
        public void Delete(ISagaData sagaData)
        {
            var collection = database.GetCollection(collectionName);

            if (Transaction.Current != null)
            {
                var hack = new AmbientTxHack(() => collection.Remove(Query.EQ("_id", sagaData.Id)));
                Transaction.Current.EnlistVolatile(hack, EnlistmentOptions.None);
            }
            else
            {
                collection.Remove(Query.EQ("_id", sagaData.Id));
            }
        }
Exemplo n.º 14
0
        public void Delete(ISagaData sagaData)
        {
            using (var connection = new SqlConnection(connectionString))
            {
                connection.Open();

                using (var command = connection.CreateCommand())
                {
                    command.CommandText = @"delete from sagas where id = @id;
                                            delete from saga_index where saga_id = @id";
                    command.Parameters.AddWithValue("id", sagaData.Id);
                    command.ExecuteNonQuery();
                }
            }
        }
Exemplo n.º 15
0
        public void Delete(ISagaData sagaData)
        {
            var collection = database.GetCollection(collectionName);

            var query = Query.And(Query.EQ("_id", sagaData.Id),
                                  Query.EQ("_rev", sagaData.Revision));

            var safeModeResult = collection.Remove(query, SafeMode.True);

            EnsureResultIsGood(safeModeResult,
                               "delete saga data of type {0} with _id {1} and _rev {2}",
                               sagaData.GetType(),
                               sagaData.Id,
                               sagaData.Revision);
        }
Exemplo n.º 16
0
        public async Task Delete(ISagaData sagaData)
        {
            var collection = GetCollection(sagaData.GetType());

            var result = collection.Remove(Query.EQ("_id", sagaData.Id));

            try
            {
                CheckResult(result, 1);
            }
            catch (Exception exception)
            {
                throw new ConcurrencyException(exception, "Saga data {0} with ID {1} in collection {2} could not be deleted", 
                    sagaData.GetType(), sagaData.Id, collection.Name);
            }
        }
Exemplo n.º 17
0
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            if (sagaData.Id == Guid.Empty)
            {
                throw new InvalidOperationException($"Attempted to insert saga data {sagaData.GetType()} without an ID");
            }

            if (sagaData.Revision != 0)
            {
                throw new InvalidOperationException($"Attempted to insert saga data with ID {sagaData.Id} and revision {sagaData.Revision}, but revision must be 0 on first insert!");
            }

            var collection = GetCollection(sagaData.GetType());

            await collection.InsertOneAsync(sagaData.ToBsonDocument()).ConfigureAwait(false);
        }
Exemplo n.º 18
0
        public async Task Update(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            var collection = GetCollection(sagaData.GetType());

            var criteria = Builders<BsonDocument>.Filter.And(Builders<BsonDocument>.Filter.Eq("_id", sagaData.Id),
                Builders<BsonDocument>.Filter.Eq("Revision", sagaData.Revision));

            sagaData.Revision++;

            var result = await collection.ReplaceOneAsync(criteria, sagaData.ToBsonDocument(sagaData.GetType())).ConfigureAwait(false);

            if (!result.IsModifiedCountAvailable || result.ModifiedCount != 1)
            {
                throw new ConcurrencyException("Saga data {0} with ID {1} in collection {2} could not be updated!",
                    sagaData.GetType(), sagaData.Id, collection.CollectionNamespace);
            }
        }
Exemplo n.º 19
0
        public async Task Update(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            using (var session = _documentStore.OpenAsyncSession())
            {
                var documentId = SagaDataDocument.GetIdFromGuid(sagaData.Id);
                var existingSagaData = await session.LoadAsync<SagaDataDocument>(documentId);
                sagaData.Revision++;
                existingSagaData.SagaData = sagaData;
                
                await DeleteCorrelationPropertyDataForSaga(existingSagaData, session);

                //add the new saga correlation documents
                var correlationPropertyDocumentIds = await SaveCorrelationProperties(session, sagaData, correlationProperties, existingSagaData.Id);
                existingSagaData.SagaCorrelationPropertyDocumentIds = correlationPropertyDocumentIds;

                await session.SaveChangesAsync();
            }
        }
Exemplo n.º 20
0
        /// <summary>
        /// Saves a snapshot of the saga data along with the given metadata
        /// </summary>
        public async Task Save(ISagaData sagaData, Dictionary<string, string> sagaAuditMetadata)
        {
            var jsonText = JsonConvert.SerializeObject(new Snapshot
            {
                Data = sagaData,
                Metadata = sagaAuditMetadata
            }, Formatting.Indented);

            var snapshotFilePath = Path.Combine(_snapshotDirectory, string.Format("{0:N}-{1}.json", sagaData.Id, sagaData.Revision));

            using (var file = File.OpenWrite(snapshotFilePath))
            {
                using (var writer = new StreamWriter(file, Encoding.UTF8))
                {
                    await writer.WriteAsync(jsonText);
                }
            }
        }
Exemplo n.º 21
0
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            if (sagaData.Id == Guid.Empty)
            {
                throw new InvalidOperationException($"Saga data {sagaData.GetType()} has an uninitialized Id property!");
            }

            using (var session = _documentStore.OpenAsyncSession())
            {
                var sagaDataDocumentId = SagaDataDocument.GetIdFromGuid(sagaData.Id);
                var sagaDataDocument = new SagaDataDocument(sagaData);
                await session.StoreAsync(sagaDataDocument, sagaDataDocumentId);

                var correlationPropertyDocumentIds = await SaveCorrelationProperties(session, sagaData, correlationProperties, sagaDataDocumentId);
                sagaDataDocument.SagaCorrelationPropertyDocumentIds = correlationPropertyDocumentIds;

                await session.SaveChangesAsync();
            }
        }
Exemplo n.º 22
0
        /// <summary>
        /// Inserts the given saga data instance into the index file
        /// </summary>
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            using (new FilesystemExclusiveLock(_lockFile, _log))
            {
                var index = new FilesystemSagaIndex(_basePath);
                var id = GetId(sagaData);
                if (sagaData.Revision != 0)
                {
                    throw new InvalidOperationException($"Attempted to insert saga data with ID {id} and revision {sagaData.Revision}, but revision must be 0 on first insert!");

                }
                var existingSaga = index.FindById(id);
                if (existingSaga != null)
                {
                    throw new ConcurrencyException("Saga data with ID {0} already exists!", id);
                }
                index.Insert(sagaData, correlationProperties);

            }
        }
Exemplo n.º 23
0
 /// <summary>
 /// Updates the given saga data instance in the index file
 /// </summary>
 public async Task Update(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
 {
     using (new FilesystemExclusiveLock(_lockFile, _log))
     {
         var index = new FilesystemSagaIndex(_basePath);
         var id = GetId(sagaData);
         var existingCopy = index.FindById(id);
         if (existingCopy == null)
         {
             throw new ConcurrencyException("Saga data with ID {0} does not exist!", id);
         }
         if (existingCopy.Revision != sagaData.Revision)
         {
             throw new ConcurrencyException("Attempted to update saga data with ID {0} with revision {1}, but the existing data was updated to revision {2}",
                 id, sagaData.Revision, existingCopy.Revision);
         }
         sagaData.Revision++;
         index.Insert(sagaData, correlationProperties);
     }
 }
Exemplo n.º 24
0
        /// <summary>
        /// Saves the given saga data, throwing an exception if the instance already exists
        /// </summary>
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            var id = GetId(sagaData);

            lock (_lock)
            {
                if (_data.ContainsKey(id))
                {
                    throw new ConcurrencyException("Saga data with ID {0} already exists!", id);
                }

                if (sagaData.Revision != 0)
                {
                    throw new InvalidOperationException(string.Format("Attempted to insert saga data with ID {0} and revision {1}, but revision must be 0 on first insert!",
                        id, sagaData.Revision));
                }

                _data[id] = Clone(sagaData);
            }
        }
Exemplo n.º 25
0
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            if (sagaData.Id == Guid.Empty)
            {
                throw new InvalidOperationException(string.Format("Attempted to insert saga data {0} without an ID", sagaData.GetType()));
            }

            var collection = GetCollection(sagaData.GetType());

            var result = collection.Insert(sagaData);

            try
            {
                CheckResult(result,0);
            }
            catch (Exception exception)
            {
                throw new ConcurrencyException(exception, "Saga data {0} with ID {1} in collection {2} could not be inserted!", 
                    sagaData.GetType(), sagaData.Id, collection.Name);
            }
        }
    /// <summary>
    /// Saves a snapshot of the saga data along with the given metadata
    /// </summary>
    public async Task Save(ISagaData sagaData, Dictionary <string, string> sagaAuditMetadata)
    {
        using (var connection = await _connectionProvider.GetConnection())
        {
            using (var command = connection.CreateCommand())
            {
                command.CommandText =
                    $@"

INSERT INTO {_tableName.QualifiedName} (
    [id],
    [revision],
    [data],
    [metadata]
) VALUES (
    @id, 
    @revision, 
    @data,
    @metadata
)

";

                var dataString     = DataSerializer.SerializeToString(sagaData);
                var metadataString = MetadataSerializer.SerializeToString(sagaAuditMetadata);

                command.Parameters.Add("id", SqlDbType.UniqueIdentifier).Value = sagaData.Id;
                command.Parameters.Add("revision", SqlDbType.Int).Value        = sagaData.Revision;
                command.Parameters.Add("data", SqlDbType.NVarChar, MathUtil.GetNextPowerOfTwo(dataString.Length)).Value         = dataString;
                command.Parameters.Add("metadata", SqlDbType.NVarChar, MathUtil.GetNextPowerOfTwo(metadataString.Length)).Value = metadataString;

                Console.WriteLine($"OK WE'RE SAVING SAGA SNAPSHOT {sagaData.Id} rev. {sagaData.Revision} NOW");

                await command.ExecuteNonQueryAsync().ConfigureAwait(false);
            }

            await connection.Complete();
        }
    }
Exemplo n.º 27
0
        // ReSharper restore UnusedMember.Local

        void PerformSaveActions(Saga saga, ISagaData sagaData)
        {
            if (saga.Complete)
            {
                if (!saga.IsNew)
                {
                    storeSagaData.Delete(sagaData);
                }
                return;
            }

            var sagaDataPropertyPathsToIndex = GetSagaDataPropertyPathsToIndex(saga).Distinct().ToArray();

            if (!saga.IsNew)
            {
                storeSagaData.Update(sagaData, sagaDataPropertyPathsToIndex);
            }
            else
            {
                storeSagaData.Insert(sagaData, sagaDataPropertyPathsToIndex);
            }
        }
Exemplo n.º 28
0
        public async Task Update(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            var collection = GetCollection(sagaData.GetType());

            var criteria = Query.And(
                Query.EQ("_id", sagaData.Id),
                Query.EQ("Revision", sagaData.Revision));

            sagaData.Revision++;

            var result = collection.Update(criteria, MongoDB.Driver.Builders.Update.Replace(sagaData));

            try
            {
                CheckResult(result, 1);
            }
            catch (Exception exception)
            {
                throw new ConcurrencyException(exception, "Saga data {0} with ID {1} in collection {2} could not be updated!",
                    sagaData.GetType(), sagaData.Id, collection.Name);
            }
        }
Exemplo n.º 29
0
        public void Save(ISagaData sagaData, string[] sagaDataPropertyPathsToIndex)
        {
            var collection = database.GetCollection(collectionName);

            if (!indexCreated)
            {
                foreach (var propertyToIndex in sagaDataPropertyPathsToIndex)
                {
                    collection.EnsureIndex(IndexKeys.Ascending(propertyToIndex), IndexOptions.SetBackground(false));
                }
                indexCreated = true;
            }

            var criteria = Query.And(Query.EQ("_id", sagaData.Id),
                                     Query.EQ("_rev", sagaData.Revision));

            sagaData.Revision++;
            var update = Update.Replace(sagaData);
            SafeModeResult safeModeResult;
            try
            {
                safeModeResult = collection.Update(criteria, update, UpdateFlags.Upsert, SafeMode.True);

            }
            catch (MongoSafeModeException)
            {
                // in case of race conditions, we get a duplicate key error because the upsert
                // cannot proceed to insert a document with the same _id as an existing document
                // ... therefore, we map the MongoSafeModeException to our own OptimisticLockingException
                throw new OptimisticLockingException(sagaData);
            }

            EnsureResultIsGood(safeModeResult,
                               "save saga data of type {0} with _id {1} and _rev {2}",
                               sagaData.GetType(),
                               sagaData.Id,
                               sagaData.Revision);
        }
Exemplo n.º 30
0
        public void Delete(ISagaData sagaData)
        {
            var connection = getConnection();

            try
            {
                using (var command = connection.CreateCommand())
                {
                    const string updateSagaSql = @"DELETE FROM ""{0}"" WHERE ""id"" = @id AND ""revision"" = @current_revision;";

                    command.CommandText = string.Format(updateSagaSql, sagaTableName);
                    command.Parameters.AddWithValue("id", sagaData.Id);
                    command.Parameters.AddWithValue("current_revision", sagaData.Revision);

                    var rows = command.ExecuteNonQuery();

                    if (rows == 0)
                    {
                        throw new OptimisticLockingException(sagaData);
                    }
                }

                using (var command = connection.CreateCommand())
                {
                    const string deleteSagaIndexSql = @"DELETE FROM ""{0}"" WHERE ""saga_id"" = @id";

                    command.CommandText = string.Format(deleteSagaIndexSql, sagaIndexTableName);
                    command.Parameters.AddWithValue("id", sagaData.Id);
                    command.ExecuteNonQuery();
                }

                commitAction(connection);
            }
            finally
            {
                releaseConnection(connection);
            }
        }
Exemplo n.º 31
0
        void VerifyCorrelationPropertyUniqueness(ISagaData sagaData, IEnumerable <ISagaCorrelationProperty> correlationProperties)
        {
            foreach (var property in correlationProperties)
            {
                var valueFromSagaData = Reflect.Value(sagaData, property.PropertyName);

                foreach (var existingSagaData in _data.Values)
                {
                    if (existingSagaData.Id == sagaData.Id)
                    {
                        continue;
                    }

                    var valueFromExistingInstance = Reflect.Value(existingSagaData, property.PropertyName);

                    if (Equals(valueFromSagaData, valueFromExistingInstance))
                    {
                        throw new ConcurrencyException("Correlation property '{0}' has value '{1}' in existing saga data with ID {2}",
                                                       property.PropertyName, valueFromExistingInstance, existingSagaData.Id);
                    }
                }
            }
        }
Exemplo n.º 32
0
        /// <summary>
        /// Saves the given saga data, throwing an exception if the instance already exists
        /// </summary>
        public async Task Insert(ISagaData sagaData, IEnumerable <ISagaCorrelationProperty> correlationProperties)
        {
            var id = GetId(sagaData);

            lock (_lock)
            {
                if (_data.ContainsKey(id))
                {
                    throw new ConcurrencyException($"Saga data with ID {id} already exists!");
                }

                VerifyCorrelationPropertyUniqueness(sagaData, correlationProperties);

                if (sagaData.Revision != 0)
                {
                    throw new InvalidOperationException($"Attempted to insert saga data with ID {id} and revision {sagaData.Revision}, but revision must be 0 on first insert!");
                }

                var clone = Clone(sagaData);
                _data[id] = clone;
                Created?.Invoke(clone);
            }
        }
Exemplo n.º 33
0
        static async Task ClearSagaIndex(ISagaData sagaData, CloudTable table)
        {
            var partitionKey = sagaData.GetType().Name;
            var op           = TableOperation.Retrieve <DynamicTableEntity>(partitionKey, $"{sagaData.Id:N}_{sagaData.Revision:0000000000}");

            var operationContext    = new OperationContext();
            var tableRequestOptions = new TableRequestOptions {
                RetryPolicy = new ExponentialRetry()
            };

            var res = await table.ExecuteAsync(op, tableRequestOptions, operationContext);

            if (res != null && res.Result != null)
            {
                var index   = (DynamicTableEntity)res.Result;
                var entries = GetIndicies(index, partitionKey);
                foreach (var e in entries)
                {
                    await table.ExecuteAsync(TableOperation.Delete(e), tableRequestOptions, operationContext);
                }
                await table.ExecuteAsync(TableOperation.Delete(index), tableRequestOptions, operationContext);
            }
        }
Exemplo n.º 34
0
        /// <summary>
        /// Saves the given saga data, throwing an exception if the instance already exists
        /// </summary>
        public async Task Insert(ISagaData sagaData, IEnumerable<ISagaCorrelationProperty> correlationProperties)
        {
            var id = GetId(sagaData);

            lock (_lock)
            {
                if (_data.ContainsKey(id))
                {
                    throw new ConcurrencyException("Saga data with ID {0} already exists!", id);
                }

                VerifyCorrelationPropertyUniqueness(sagaData, correlationProperties);

                if (sagaData.Revision != 0)
                {
                    throw new InvalidOperationException($"Attempted to insert saga data with ID {id} and revision {sagaData.Revision}, but revision must be 0 on first insert!");
                }

                var clone = Clone(sagaData);
                _data[id] = clone;
                Created?.Invoke(clone);
            }
        }
Exemplo n.º 35
0
        private async Task <IEnumerable <string> > SaveCorrelationProperties(IAsyncDocumentSession session,
                                                                             ISagaData sagaData, IEnumerable <ISagaCorrelationProperty> correlationProperties, string sagaDataDocumentId)
        {
            var documentIds = new List <string>();

            foreach (var correlationProperty in correlationProperties)
            {
                var propertyName = correlationProperty.PropertyName;
                var value        = sagaData.GetType().GetProperty(propertyName).GetValue(sagaData).ToString();

                var documentId = SagaCorrelationPropertyDocument.GetIdForCorrelationProperty(correlationProperty.SagaDataType, propertyName,
                                                                                             value);

                var existingSagaCorrelationPropertyDocument =
                    await session.LoadAsync <SagaCorrelationPropertyDocument>(documentId);

                if (existingSagaCorrelationPropertyDocument != null)
                {
                    if (existingSagaCorrelationPropertyDocument.SagaDataDocumentId != sagaDataDocumentId)
                    {
                        throw new ConcurrencyException(
                                  $"Could not save correlation properties. The following correlation property already exists with the same value for another saga: {propertyName} - {value}");
                    }
                }
                else
                {
                    var sagaCorrelationPropertyDocument = new SagaCorrelationPropertyDocument(correlationProperty.SagaDataType, propertyName,
                                                                                              value, sagaDataDocumentId);

                    await session.StoreAsync(sagaCorrelationPropertyDocument, documentId);
                }

                documentIds.Add(documentId);
            }

            return(documentIds);
        }
        /// <summary>
        /// Archives the given saga data in MySql under its current ID and revision
        /// </summary>
        public async Task Save(ISagaData sagaData, Dictionary <string, string> sagaAuditMetadata)
        {
            using (var connection = await _connectionHelper.GetConnection())
            {
                using (var command = connection.CreateCommand())
                {
                    command.CommandText =
                        $@"
                            INSERT
                                INTO `{_tableName}` (`id`, `revision`, `data`, `metadata`)
                                VALUES (@id, @revision, @data, @metadata);

                            ";
                    command.Parameters.Add(command.CreateParameter("id", DbType.Guid, sagaData.Id));
                    command.Parameters.Add(command.CreateParameter("revision", DbType.Int32, sagaData.Revision));
                    command.Parameters.Add(command.CreateParameter("data", DbType.Binary, _objectSerializer.Serialize(sagaData)));
                    command.Parameters.Add(command.CreateParameter("metadata", DbType.String, _dictionarySerializer.SerializeToString(sagaAuditMetadata)));

                    await command.ExecuteNonQueryAsync();
                }

                connection.Complete();
            }
        }
Exemplo n.º 37
0
        /// <summary>
        /// Saves the <paramref name="sagaData"/> snapshot and the accompanying <paramref name="sagaAuditMetadata"/>
        /// </summary>
        public async Task Save(ISagaData sagaData, Dictionary <string, string> sagaAuditMetadata)
        {
            using (var connection = _connectionHelper.GetConnection())
            {
                using (var command = connection.CreateCommand())
                {
                    command.CommandText =
                        $@"
                        INSERT
                            INTO {_tableName} (id, revision, data, metadata)
                            VALUES (:id, :revision, :data, :metadata)
                        ";
                    command.Parameters.Add("id", OracleDbType.Raw).Value         = sagaData.Id;
                    command.Parameters.Add("revision", OracleDbType.Int64).Value = sagaData.Revision;
                    command.Parameters.Add("data", OracleDbType.Blob).Value      = _objectSerializer.Serialize(sagaData);
                    command.Parameters.Add("metadata", OracleDbType.Clob).Value  =
                        _dictionarySerializer.SerializeToString(sagaAuditMetadata);

                    await command.ExecuteNonQueryAsync();
                }

                connection.Complete();
            }
        }
Exemplo n.º 38
0
            public TSagaDataToFind Find <TSagaDataToFind>(string sagaDataPropertyPath, object fieldFromMessage) where TSagaDataToFind : class, ISagaData
            {
                var result = innerPersister.Find <TSagaDataToFind>(sagaDataPropertyPath, fieldFromMessage);

                if (result != null)
                {
                    MostRecentlyLoadedSagaData = result;

                    if (Correlated != null)
                    {
                        Correlated(result);
                    }
                }
                else
                {
                    MostRecentlyLoadedSagaData = null;

                    if (CouldNotCorrelate != null)
                    {
                        CouldNotCorrelate();
                    }
                }
                return(result);
            }
Exemplo n.º 39
0
        public void Save(ISagaData sagaData, string[] sagaDataPropertyPathsToIndex)
        {
            var collection = database.GetCollection(collectionName);

            if (!indexCreated)
            {
                foreach (var propertyToIndex in sagaDataPropertyPathsToIndex)
                {
                    collection.EnsureIndex(IndexKeys.Ascending(propertyToIndex), IndexOptions.SetBackground(false));
                }
                indexCreated = true;
            }

            // if an ambient TX is present, enlist the insert to be performed at commit time
            if (Transaction.Current != null)
            {
                var hack = new AmbientTxHack(() => collection.Save(sagaData, SafeMode.True));
                Transaction.Current.EnlistVolatile(hack, EnlistmentOptions.None);
            }
            else
            {
                collection.Save(sagaData, SafeMode.True);
            }
        }
Exemplo n.º 40
0
        /// <summary>
        /// Inserts the saga data
        /// </summary>
        public async Task Insert(ISagaData sagaData, IEnumerable <ISagaCorrelationProperty> correlationProperties)
        {
            //TODO: Implement concurrency
            if (sagaData.Id == Guid.Empty)
            {
                throw new InvalidOperationException($"Saga data {sagaData.GetType()} has an uninitialized Id property!");
            }

            if (sagaData.Revision != 0)
            {
                throw new InvalidOperationException($"Attempted to insert saga data with ID {sagaData.Id} and revision {sagaData.Revision}, but revision must be 0 on first insert!");
            }

            var dataBlob = GetSagaDataBlob(sagaData.Id);

            //if (await dataBlob.ExistsAsync() && dataBlob.Metadata["revision"] != revisionToUpdate.ToString())
            //{
            //    throw new ConcurrencyException("Update of saga with ID {0} did not succeed because someone else beat us to it", sagaData.Id);
            //}

            dataBlob.Properties.ContentType = "application/json";
            dataBlob.Metadata[RevisionKey]  = sagaData.Revision.ToString();

            var jsonText = JsonConvert.SerializeObject(sagaData, Settings);

            await dataBlob.UploadTextAsync(jsonText, TextEncoding, DefaultAccessCondition, DefaultBlobRequestOptions, DefaultOperationContext);

            await dataBlob.SetPropertiesAsync(DefaultAccessCondition,
                                              new BlobRequestOptions { RetryPolicy = new ExponentialRetry() }, new OperationContext());

            await dataBlob.SetMetadataAsync(AccessCondition.GenerateEmptyCondition(),
                                            new BlobRequestOptions { RetryPolicy = new ExponentialRetry() }, new OperationContext());


            await InsertSagaCorrelationProperties(sagaData, correlationProperties);
        }
Exemplo n.º 41
0
        private void DeclareIndexUsingReturningClause(ISagaData sagaData, AdoNetUnitOfWorkScope scope, IDictionary <string, object> propertiesToIndex)
        {
            var dialect      = scope.Dialect;
            var connection   = scope.Connection;
            var existingKeys = Enumerable.Empty <string>();

            var sagaTypeName = GetSagaTypeName(sagaData.GetType());
            var parameters   = propertiesToIndex
                               .Select((p, i) => new
            {
                PropertyName            = p.Key,
                PropertyValue           = p.Value,
                PropertyNameParameter   = string.Format("n{0}", i),
                PropertyValueParameter  = string.Format("v{0}", i),
                PropertyValuesParameter = string.Format("vs{0}", i)
            })
                               .ToList();

            var tuples = parameters
                         .Select(p => string.Format("({0}, {1}, {2}, {3})",
                                                    dialect.EscapeParameter(SAGAINDEX_ID_COLUMN),
                                                    dialect.EscapeParameter(p.PropertyNameParameter),
                                                    dialect.EscapeParameter(p.PropertyValueParameter),
                                                    dialect.EscapeParameter(p.PropertyValuesParameter)
                                                    ));

            using (var command = connection.CreateCommand())
            {
                command.CommandText = string.Format(
                    "INSERT INTO {0} ({1}, {2}, {3}, {4}) VALUES {5} " +
                    "ON CONFLICT ({1}, {2}) DO UPDATE SET {3} = excluded.{3}, {4} = excluded.{4} " +
                    "RETURNING {2};",
                    dialect.QuoteForTableName(sagaIndexTableName),                          //< 0
                    dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),                        //< 1
                    dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),                       //< 2
                    dialect.QuoteForColumnName(SAGAINDEX_VALUE_COLUMN),                     //< 3
                    dialect.QuoteForColumnName(SAGAINDEX_VALUES_COLUMN),                    //< 4
                    string.Join(", ", tuples)                                               //< 5
                    );

                foreach (var parameter in parameters)
                {
                    var value  = GetIndexValue(parameter.PropertyValue);
                    var values = value == null ? null : ArraysEnabledFor(dialect)
                                                ? (object)GetIndexValues(parameter.PropertyValue)?.ToArray()
                                                : GetConcatenatedIndexValues(GetIndexValues(parameter.PropertyValue));
                    var valuesDbType = ArraysEnabledFor(dialect) ? DbType.Object : DbType.String;

                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyNameParameter), DbType.String, parameter.PropertyName);
                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyValueParameter), DbType.String, value);
                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyValuesParameter), valuesDbType, values);
                }

                command.AddParameter(dialect.EscapeParameter(SAGAINDEX_ID_COLUMN), DbType.Guid, sagaData.Id);

                try
                {
                    using (var reader = command.ExecuteReader())
                    {
                        existingKeys = reader.AsEnumerable <string>(SAGAINDEX_KEY_COLUMN).ToArray();
                    }
                }
                catch (DbException exception)
                {
                    throw new OptimisticLockingException(sagaData, exception);
                }
            }

            var idx = 0;

            using (var command = connection.CreateCommand())
            {
                command.CommandText = string.Format(
                    "DELETE FROM {0} " +
                    "WHERE {1} = {2} AND {3} NOT IN ({4});",
                    dialect.QuoteForTableName(sagaIndexTableName),                              //< 0
                    dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),                            //< 1
                    dialect.EscapeParameter(SAGAINDEX_ID_COLUMN),                               //< 2
                    dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),                           //< 3
                    string.Join(", ", existingKeys.Select(k => dialect.EscapeParameter($"k{idx++}")))
                    );

                for (int i = 0; i < existingKeys.Count(); i++)
                {
                    command.AddParameter(dialect.EscapeParameter($"k{i}"), DbType.StringFixedLength, existingKeys.ElementAt(i).Trim());
                }

                command.AddParameter(dialect.EscapeParameter(SAGAINDEX_ID_COLUMN), DbType.Guid, sagaData.Id);

                try
                {
                    command.ExecuteNonQuery();
                }
                catch (DbException exception)
                {
                    throw new OptimisticLockingException(sagaData, exception);
                }
            }
        }
 /// <summary>
 /// Serializes the given ISagaData object into a string
 /// </summary>
 /// <param name="obj"></param>
 /// <returns></returns>
 public string SerializeToString(ISagaData obj)
 {
     return(base.SerializeToString(obj));
 }
Exemplo n.º 43
0
        private void DeclareIndexUsingTableExpressions(ISagaData sagaData, AdoNetUnitOfWorkScope scope, IDictionary <string, object> propertiesToIndex)
        {
            var dialect    = scope.Dialect;
            var connection = scope.Connection;

            var sagaTypeName = GetSagaTypeName(sagaData.GetType());
            var parameters   = propertiesToIndex
                               .Select((p, i) => new
            {
                PropertyName            = p.Key,
                PropertyValue           = p.Value,
                PropertyNameParameter   = string.Format("n{0}", i),
                PropertyValueParameter  = string.Format("v{0}", i),
                PropertyValuesParameter = string.Format("vs{0}", i)
            })
                               .ToList();

            var tuples = parameters
                         .Select(p => string.Format("({0}, {1}, {2}, {3})",
                                                    dialect.EscapeParameter(SAGAINDEX_ID_COLUMN),
                                                    dialect.EscapeParameter(p.PropertyNameParameter),
                                                    dialect.EscapeParameter(p.PropertyValueParameter),
                                                    dialect.EscapeParameter(p.PropertyValuesParameter)
                                                    ));

            using (var command = connection.CreateCommand())
            {
                command.CommandText = string.Format(
                    "WITH existing AS (" +
                    "INSERT INTO {0} ({1}, {2}, {3}, {4}) VALUES {6} " +
                    "ON CONFLICT ({1}, {2}) DO UPDATE SET {3} = excluded.{3}, {4} = excluded.{4} " +
                    "RETURNING {2}) " +
                    "DELETE FROM {0} " +
                    "WHERE {1} = {5} AND {2} NOT IN (SELECT {2} FROM existing);",
                    dialect.QuoteForTableName(sagaIndexTableName),                          //< 0
                    dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),                        //< 1
                    dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),                       //< 2
                    dialect.QuoteForColumnName(SAGAINDEX_VALUE_COLUMN),                     //< 3
                    dialect.QuoteForColumnName(SAGAINDEX_VALUES_COLUMN),                    //< 4
                    dialect.EscapeParameter(SAGAINDEX_ID_COLUMN),                           //< 5
                    string.Join(", ", tuples)                                               //< 6
                    );

                foreach (var parameter in parameters)
                {
                    var value = GetIndexValue(parameter.PropertyValue);

                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyNameParameter), DbType.String, parameter.PropertyName);
                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyValueParameter), DbType.String, value);

                    var values = ArraysEnabledFor(dialect)
                                                ? (object)GetIndexValues(parameter.PropertyValue)?.ToArray()
                                                : GetConcatenatedIndexValues(GetIndexValues(parameter.PropertyValue));
                    var dbtype = ArraysEnabledFor(dialect) ? DbType.Object : DbType.String;

                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyValuesParameter), dbtype, values);
                }

                command.AddParameter(dialect.EscapeParameter(SAGAINDEX_ID_COLUMN), DbType.Guid, sagaData.Id);

                try
                {
                    command.ExecuteNonQuery();
                }
                catch (DbException exception)
                {
                    throw new OptimisticLockingException(sagaData, exception);
                }
            }
        }
Exemplo n.º 44
0
 /// <summary>
 /// Stores the specified data.
 /// </summary>
 /// <param name="data">The <see cref="ISagaData">data</see> to store.</param>
 /// <param name="correlationProperty">The property used to correlate the stored data.</param>
 /// <param name="cancellationToken">The <see cref="CancellationToken">token</see> that can be used to cancel the operation.</param>
 /// <returns>A <see cref="Task">task</see> representing the asynchronous operation.</returns>
 public virtual Task Store(ISagaData data, CorrelationProperty correlationProperty, CancellationToken cancellationToken)
 {
     Arg.NotNull(data, nameof(data));
     storage.AddOrUpdate(data.Id, data, (key, current) => data);
     return(CompletedTask);
 }
Exemplo n.º 45
0
        ISagaData Clone(ISagaData sagaData)
        {
            var serializedObject = JsonConvert.SerializeObject(sagaData, _serializerSettings);

            return(JsonConvert.DeserializeObject <ISagaData>(serializedObject, _serializerSettings));
        }
Exemplo n.º 46
0
 public Task Store(ISagaData data, CorrelationProperty correlationProperty, CancellationToken cancellationToken) =>
 throw new NotSupportedException(SR.NoConfiguredSagaStorage);
Exemplo n.º 47
0
            public async Task Update(ISagaData sagaData, IEnumerable <ISagaCorrelationProperty> correlationProperties)
            {
                await _innerSagaStorage.Update(sagaData, correlationProperties);

                _persistentSagaData[sagaData.Id] = sagaData;
            }
Exemplo n.º 48
0
        public async Task Delete(ISagaData sagaData)
        {
            await Task.Delay(_delayMilliseconds);

            await _sagaStorage.Delete(sagaData);
        }
Exemplo n.º 49
0
 public SagaDataDocument(ISagaData sagaData)
 {
     SagaData = sagaData;
 }
Exemplo n.º 50
0
 /// <summary>
 /// Adds to the invoker a piece of saga data that has been determined to be relevant for the invocation
 /// </summary>
 public abstract void SetSagaData(ISagaData sagaData);
Exemplo n.º 51
0
        public async Task Update(ISagaData sagaData, IEnumerable <ISagaCorrelationProperty> correlationProperties)
        {
            await Task.Delay(_delayMilliseconds);

            await _sagaStorage.Update(sagaData, correlationProperties);
        }
Exemplo n.º 52
0
 public void Update(ISagaData sagaData, string[] sagaDataPropertyPathsToIndex)
 {
     innerPersister.Update(sagaData, sagaDataPropertyPathsToIndex);
 }
Exemplo n.º 53
0
 void ISagaInstance.AttachNew(ISagaData data) => AttachNew((TData)data);
Exemplo n.º 54
0
 void ISagaInstance.AttachExisting(ISagaData data) => AttachExisting((TData)data);
Exemplo n.º 55
0
        private void DeclareIndexUnoptimized(ISagaData sagaData, AdoNetUnitOfWorkScope scope, IDictionary <string, object> propertiesToIndex)
        {
            var connection   = scope.Connection;
            var dialect      = scope.Dialect;
            var sagaTypeName = GetSagaTypeName(sagaData.GetType());

            var idxTbl    = dialect.QuoteForTableName(sagaIndexTableName);
            var idCol     = dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN);
            var keyCol    = dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN);
            var valueCol  = dialect.QuoteForColumnName(SAGAINDEX_VALUE_COLUMN);
            var valuesCol = dialect.QuoteForColumnName(SAGAINDEX_VALUE_COLUMN);

            var idParam = dialect.EscapeParameter(SAGAINDEX_ID_COLUMN);

            var existingKeys = Enumerable.Empty <string>();

            // Let's fetch existing keys..
            using (var command = connection.CreateCommand())
            {
                command.CommandText = string.Format(
                    "SELECT {1} FROM {0} WHERE {2} = {3};",
                    dialect.QuoteForTableName(sagaIndexTableName),                          //< 0
                    dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),                       //< 1
                    dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),                        //< 2
                    dialect.EscapeParameter(SAGAINDEX_ID_COLUMN)                            //< 3
                    );

                command.AddParameter(dialect.EscapeParameter(SAGAINDEX_ID_COLUMN), DbType.Guid, sagaData.Id);

                try
                {
                    using (var reader = command.ExecuteReader())
                    {
                        existingKeys = reader.AsEnumerable <string>(SAGAINDEX_KEY_COLUMN).ToArray();
                    }
                }
                catch (DbException exception)
                {
                    throw new OptimisticLockingException(sagaData, exception);
                }
            }

            // For each exisring key, update it's value..
            foreach (var key in existingKeys.Where(k => propertiesToIndex.Any(p => p.Key == k)))
            {
                using (var command = connection.CreateCommand())
                {
                    command.CommandText = string.Format(
                        "UPDATE {0} SET {1} = {2}, {3} = {4} " +
                        "WHERE {5} = {6} AND {7} = {8};",
                        dialect.QuoteForTableName(sagaIndexTableName),                              //< 0
                        dialect.QuoteForColumnName(SAGAINDEX_VALUE_COLUMN),                         //< 1
                        dialect.EscapeParameter(SAGAINDEX_VALUE_COLUMN),                            //< 2
                        dialect.QuoteForColumnName(SAGAINDEX_VALUES_COLUMN),                        //< 3
                        dialect.EscapeParameter(SAGAINDEX_VALUES_COLUMN),                           //< 4
                        dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),                            //< 5
                        dialect.EscapeParameter(SAGAINDEX_ID_COLUMN),                               //< 6
                        dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),                           //< 7
                        dialect.EscapeParameter(SAGAINDEX_KEY_COLUMN)                               //< 8
                        );

                    var value  = GetIndexValue(propertiesToIndex[key]);
                    var values = ArraysEnabledFor(dialect)
                                                ? (object)GetIndexValues(propertiesToIndex[key])?.ToArray()
                                                : GetConcatenatedIndexValues(GetIndexValues(propertiesToIndex[key]));
                    var valuesDbType = ArraysEnabledFor(dialect) ? DbType.Object : DbType.String;

                    command.AddParameter(dialect.EscapeParameter(SAGAINDEX_ID_COLUMN), DbType.Guid, sagaData.Id);
                    command.AddParameter(dialect.EscapeParameter(SAGAINDEX_KEY_COLUMN), DbType.String, key);
                    command.AddParameter(dialect.EscapeParameter(SAGAINDEX_VALUE_COLUMN), DbType.String, value);
                    command.AddParameter(dialect.EscapeParameter(SAGAINDEX_VALUES_COLUMN), valuesDbType, values);

                    try
                    {
                        command.ExecuteNonQuery();
                    }
                    catch (DbException exception)
                    {
                        throw new OptimisticLockingException(sagaData, exception);
                    }
                }
            }

            var removedKeys = existingKeys.Where(x => !propertiesToIndex.ContainsKey(x)).ToArray();

            if (removedKeys.Length > 0)
            {
                // Remove no longer needed keys..
                using (var command = connection.CreateCommand())
                {
                    command.CommandText = string.Format(
                        "DELETE FROM {0} WHERE {1} = {2} AND {3} IN ({4});",
                        dialect.QuoteForTableName(sagaIndexTableName),                              //< 0
                        dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),                            //< 1
                        dialect.EscapeParameter(SAGAINDEX_ID_COLUMN),                               //< 2
                        dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),                           //< 3
                        string.Join(", ", existingKeys.Select((x, i) => dialect.EscapeParameter($"k{i}")))
                        );

                    command.AddParameter(dialect.EscapeParameter(SAGAINDEX_ID_COLUMN), DbType.Guid, sagaData.Id);

                    for (int i = 0; i < existingKeys.Count(); i++)
                    {
                        command.AddParameter(dialect.EscapeParameter($"k{i}"), DbType.StringFixedLength, existingKeys.ElementAt(i).Trim());
                    }

                    try
                    {
                        command.ExecuteNonQuery();
                    }
                    catch (DbException exception)
                    {
                        throw new OptimisticLockingException(sagaData, exception);
                    }
                }
            }

            var parameters = propertiesToIndex
                             .Where(x => !existingKeys.Contains(x.Key))
                             .Select((p, i) => new
            {
                PropertyName            = p.Key,
                PropertyValue           = p.Value,
                PropertyNameParameter   = string.Format("n{0}", i),
                PropertyValueParameter  = string.Format("v{0}", i),
                PropertyValuesParameter = string.Format("vs{0}", i)
            })
                             .ToList();

            if (parameters.Count > 0)
            {
                // Insert new keys..
                using (var command = connection.CreateCommand())
                {
                    var tuples = parameters.Select(p => string.Format("({0}, {1}, {2}, {3})",
                                                                      idParam,
                                                                      dialect.EscapeParameter(p.PropertyNameParameter),
                                                                      dialect.EscapeParameter(p.PropertyValueParameter),
                                                                      dialect.EscapeParameter(p.PropertyValuesParameter)
                                                                      ));

                    command.CommandText = string.Format(
                        "INSERT INTO {0} ({1}, {2}, {3}, {4}) VALUES {5};",
                        dialect.QuoteForTableName(sagaIndexTableName),                              //< 0
                        dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),                            //< 1
                        dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),                           //< 2
                        dialect.QuoteForColumnName(SAGAINDEX_VALUE_COLUMN),                         //< 3
                        dialect.QuoteForColumnName(SAGAINDEX_VALUES_COLUMN),                        //< 4
                        string.Join(", ", tuples)                                                   //< 5
                        );

                    foreach (var parameter in parameters)
                    {
                        var value  = GetIndexValue(parameter.PropertyValue);
                        var values = ArraysEnabledFor(dialect)
                                                        ? (object)GetIndexValues(parameter.PropertyValue)?.ToArray()
                                                        : GetConcatenatedIndexValues(GetIndexValues(parameter.PropertyValue));
                        var valuesDbType = ArraysEnabledFor(dialect) ? DbType.Object : DbType.String;

                        command.AddParameter(dialect.EscapeParameter(parameter.PropertyNameParameter), DbType.String, parameter.PropertyName);
                        command.AddParameter(dialect.EscapeParameter(parameter.PropertyValueParameter), DbType.String, value);
                        command.AddParameter(dialect.EscapeParameter(parameter.PropertyValuesParameter), valuesDbType, values);
                    }

                    command.AddParameter(dialect.EscapeParameter(SAGAINDEX_ID_COLUMN), DbType.Guid, sagaData.Id);

                    try
                    {
                        command.ExecuteNonQuery();
                    }
                    catch (DbException exception)
                    {
                        throw new OptimisticLockingException(sagaData, exception);
                    }
                }
            }
        }
Exemplo n.º 56
0
 /// <summary>
 /// Stores the specified saga data.
 /// </summary>
 /// <param name="data">The data to store.</param>
 /// <param name="correlationProperty">The property used to correlate the stored data.</param>
 /// <param name="cancellationToken">The <see cref="CancellationToken">token</see> that can be used to cancel the operation.</param>
 /// <returns>A <see cref="Task">task</see> repesenting the asynchronous operation.</returns>
 public Task Store(ISagaData data, CorrelationProperty correlationProperty, CancellationToken cancellationToken)
 {
     Arg.NotNull(data, nameof(data));
     Arg.NotNull(correlationProperty, nameof(correlationProperty));
     return(Configuration.Store(data, correlationProperty, cancellationToken));
 }
Exemplo n.º 57
0
            public RelevantSagaInfo(ISagaData sagaData, IEnumerable<CorrelationProperty> correlationProperties, Saga saga)
            {
                SagaData = sagaData;

                // only keep necessary correlation properties, i.e.
                CorrelationProperties = correlationProperties
                    .GroupBy(p => p.PropertyName)
                    .Select(g => g.First())
                    .ToList();

                Saga = saga;
            }
Exemplo n.º 58
0
        void CreateIndex(IEnumerable <KeyValuePair <string, string> > propertiesToIndex, ConnectionHolder connection, ISagaData sagaData)
        {
            var sagaTypeName = GetSagaTypeName(sagaData.GetType());
            var parameters   = propertiesToIndex
                               .Select((p, i) => new
            {
                PropertyName           = p.Key,
                PropertyValue          = p.Value ?? "",
                PropertyNameParameter  = string.Format("@n{0}", i),
                PropertyValueParameter = string.Format("@v{0}", i)
            })
                               .ToList();

            // lastly, generate new index
            using (var command = connection.CreateCommand())
            {
                // generate batch insert with SQL for each entry in the index
                var inserts = parameters
                              .Select(a => string.Format(
                                          @"                      insert into [{0}]
                                                            ([saga_type], [key], [value], [saga_id]) 
                                                        values 
                                                            (@saga_type, {1}, {2}, @saga_id)",
                                          sagaIndexTableName, a.PropertyNameParameter, a.PropertyValueParameter))
                              .ToList();

                var sql = string.Join(";" + Environment.NewLine, inserts);

                command.CommandText = sql;

                foreach (var parameter in parameters)
                {
                    command.Parameters.Add(parameter.PropertyNameParameter, SqlDbType.NVarChar).Value  = parameter.PropertyName;
                    command.Parameters.Add(parameter.PropertyValueParameter, SqlDbType.NVarChar).Value = parameter.PropertyValue;
                }

                command.Parameters.Add("saga_type", SqlDbType.NVarChar).Value       = sagaTypeName;
                command.Parameters.Add("saga_id", SqlDbType.UniqueIdentifier).Value = sagaData.Id;

                try
                {
                    command.ExecuteNonQuery();
                }
                catch (SqlException sqlException)
                {
                    if (sqlException.Number == SqlServerMagic.PrimaryKeyViolationNumber)
                    {
                        throw new OptimisticLockingException(sagaData, sqlException);
                    }

                    throw;
                }
            }
        }
Exemplo n.º 59
0
 public Task Complete(ISagaData data, CancellationToken cancellationToken) => throw new NotSupportedException(SR.NoConfiguredSagaStorage);
Exemplo n.º 60
0
 internal abstract Task InvokeConflictResolution(ISagaData otherSagaData);