Esempio n. 1
0
        private void CreateIndex(ISagaData sagaData, AdoNetUnitOfWorkScope scope, IEnumerable <KeyValuePair <string, string> > 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)
            })
                               .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} ({1}, {2}, {3}, {4}) values ({5}, {6}, {7}, {8})",
                                          dialect.QuoteForTableName(sagaIndexTableName),
                                          dialect.QuoteForColumnName(SAGAINDEX_TYPE_COLUMN),
                                          dialect.QuoteForColumnName(SAGAINDEX_KEY_COLUMN),
                                          dialect.QuoteForColumnName(SAGAINDEX_VALUE_COLUMN),
                                          dialect.QuoteForColumnName(SAGAINDEX_ID_COLUMN),
                                          dialect.EscapeParameter(SAGAINDEX_TYPE_COLUMN),
                                          dialect.EscapeParameter(a.PropertyNameParameter),
                                          dialect.EscapeParameter(a.PropertyValueParameter),
                                          dialect.EscapeParameter(SAGAINDEX_ID_COLUMN)
                                          ));

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

                command.CommandText = sql;

                foreach (var parameter in parameters)
                {
                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyNameParameter), DbType.String, parameter.PropertyName);
                    command.AddParameter(dialect.EscapeParameter(parameter.PropertyValueParameter), DbType.String, parameter.PropertyValue);
                }

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

                try
                {
                    command.ExecuteNonQuery();
                }
                catch (DbException exception)
                {
                    throw new OptimisticLockingException(sagaData, exception);
                }
            }
        }
Esempio n. 2
0
		public AdoNetUnitOfWorkScope GetScope()
		{
			var result = new AdoNetUnitOfWorkScope(this, _factory.Dialect, Connection);

			if (_autodispose) {
				result.OnDispose +=
					() =>
					{
						if (!_aborted) Commit();
						this.Dispose();
					};
			}

			return result;
		}
        private void DeclareIndex(ISagaData sagaData, AdoNetUnitOfWorkScope scope, IDictionary <string, object> propertiesToIndex)
        {
            var dialect = scope.Dialect;

            if (dialect.SupportsOnConflictClause && dialect.SupportsReturningClause && dialect.SupportsTableExpressions)
            {
                DeclareIndexUsingTableExpressions(sagaData, scope, propertiesToIndex);
            }
            else if (dialect.SupportsOnConflictClause && dialect.SupportsReturningClause)
            {
                DeclareIndexUsingReturningClause(sagaData, scope, propertiesToIndex);
            }
            else
            {
                DeclareIndexUnoptimized(sagaData, scope, propertiesToIndex);
            }
        }
Esempio n. 4
0
        public AdoNetUnitOfWorkScope GetScope()
        {
            var result = new AdoNetUnitOfWorkScope(this, _factory.Dialect, Connection);

            if (_autodispose)
            {
                result.OnDispose +=
                    () =>
                {
                    if (!_aborted)
                    {
                        Commit();
                    }
                    this.Dispose();
                };
            }

            return(result);
        }
        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);
                    }
                }
            }
        }
        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);
                }
            }
        }
        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);
                }
            }
        }