/// <summary>
        /// Filters out metadata that is not present in the result set, and matches metadata ordering to resultset.
        /// </summary>
        public static EditColumnMetadata[] FilterColumnMetadata(EditColumnMetadata[] metaColumns, DbColumnWrapper[] resultColumns)
        {
            if (metaColumns.Length == 0)
            {
                return(metaColumns);
            }

            bool escapeColName = FromSqlScript.IsIdentifierBracketed(metaColumns[0].EscapedName);
            Dictionary <string, int> columnNameOrdinalMap = new Dictionary <string, int>(capacity: resultColumns.Length);

            for (int i = 0; i < resultColumns.Length; i++)
            {
                DbColumnWrapper column     = resultColumns[i];
                string          columnName = column.ColumnName;
                if (escapeColName && !FromSqlScript.IsIdentifierBracketed(columnName))
                {
                    columnName = ToSqlScript.FormatIdentifier(columnName);
                }
                columnNameOrdinalMap.Add(columnName, column.ColumnOrdinal ?? i);
            }

            HashSet <string> resultColumnNames = columnNameOrdinalMap.Keys.ToHashSet();

            metaColumns = Array.FindAll(metaColumns, column => resultColumnNames.Contains(column.EscapedName));
            foreach (EditColumnMetadata metaCol in metaColumns)
            {
                metaCol.Ordinal = columnNameOrdinalMap[metaCol.EscapedName];
            }
            Array.Sort(metaColumns, (x, y) => (Comparer <int> .Default).Compare(x.Ordinal, y.Ordinal));

            return(metaColumns);
        }
示例#2
0
        /// <summary>
        /// Generates the INSERT INTO statement that will apply the row creation
        /// </summary>
        /// <returns>INSERT INTO statement</returns>
        public override string GetScript()
        {
            // Process the cells and columns
            List <string> inColumns = new List <string>();
            List <string> inValues  = new List <string>();

            for (int i = 0; i < AssociatedObjectMetadata.Columns.Length; i++)
            {
                DbColumnWrapper column = AssociatedResultSet.Columns[i];
                CellUpdate      cell   = newCells[i];

                // Continue if we're not inserting a value for this column
                if (!IsCellValueProvided(column, cell, DefaultValues[i]))
                {
                    continue;
                }

                // Column is provided
                inColumns.Add(AssociatedObjectMetadata.Columns[i].EscapedName);
                inValues.Add(ToSqlScript.FormatValue(cell.AsDbCellValue, column));
            }

            // Build the insert statement
            return(inValues.Count > 0
                ? string.Format(InsertScriptValuesStatement,
                                AssociatedObjectMetadata.EscapedMultipartName,
                                string.Join(", ", inColumns),
                                string.Join(", ", inValues))
                : string.Format(InsertScriptDefaultStatement, AssociatedObjectMetadata.EscapedMultipartName));
        }
示例#3
0
        /// <summary>
        /// Generates a WHERE clause that uses the key columns of the table to uniquely identity
        /// the row that will be updated.
        /// </summary>
        /// <param name="parameterize">
        /// Whether or not to generate a parameterized where clause. If <c>true</c> verbatim values
        /// will be replaced with paremeters (like @Param12). The parameters must be added to the
        /// SqlCommand used to execute the commit.
        /// </param>
        /// <returns>A <see cref="WhereClause"/> object</returns>
        protected WhereClause GetWhereClause(bool parameterize)
        {
            WhereClause output = new WhereClause();

            if (!AssociatedObjectMetadata.KeyColumns.Any())
            {
                throw new InvalidOperationException(SR.EditDataColumnNoKeyColumns);
            }

            IList <DbCellValue> row = AssociatedResultSet.GetRow(RowId);

            foreach (EditColumnMetadata col in AssociatedObjectMetadata.KeyColumns)
            {
                // Put together a clause for the value of the cell
                DbCellValue cellData = row[col.Ordinal];
                string      cellDataClause;
                if (cellData.IsNull)
                {
                    cellDataClause = "IS NULL";
                }
                else
                {
                    if (cellData.RawObject is byte[] ||
                        col.DbColumn.DataTypeName.Equals("TEXT", StringComparison.OrdinalIgnoreCase) ||
                        col.DbColumn.DataTypeName.Equals("NTEXT", StringComparison.OrdinalIgnoreCase))
                    {
                        // Special cases for byte[] and TEXT/NTEXT types
                        cellDataClause = "IS NOT NULL";
                    }
                    else
                    {
                        // General case is to just use the value from the cell
                        if (parameterize)
                        {
                            // Add a parameter and parameterized clause component
                            // NOTE: We include the row ID to make sure the parameter is unique if
                            //       we execute multiple row edits at once.
                            string paramName = $"@Param{RowId}{col.Ordinal}";
                            cellDataClause = $"= {paramName}";
                            SqlParameter parameter = new SqlParameter(paramName, col.DbColumn.SqlDbType)
                            {
                                Value = cellData.RawObject
                            };
                            output.Parameters.Add(parameter);
                        }
                        else
                        {
                            // Add the clause component with the formatted value
                            cellDataClause = $"= {ToSqlScript.FormatValue(cellData, col.DbColumn)}";
                        }
                    }
                }

                string completeComponent = $"({col.EscapedName} {cellDataClause})";
                output.ClauseComponents.Add(completeComponent);
            }

            return(output);
        }
示例#4
0
        public void FormatColumnType(bool useSemanticEquivalent, DbColumn input, string expectedOutput)
        {
            // If: I supply the input columns
            string output = ToSqlScript.FormatColumnType(input, useSemanticEquivalent);

            // Then: The output should match the expected output
            Assert.Equal(expectedOutput, output);
        }
示例#5
0
        public void NullDbCellTest()
        {
            // If: I attempt to format a null db cell
            // Then: It should throw
            DbColumn column = new FormatterTestDbColumn(null);

            Assert.Throws <ArgumentNullException>(() => ToSqlScript.FormatValue(null, column));
        }
示例#6
0
        public void UnsupportedColumnTest()
        {
            // If: I attempt to format an unsupported datatype
            // Then: It should throw
            DbColumn column = new FormatterTestDbColumn("unsupported");

            Assert.Throws <ArgumentOutOfRangeException>(() => ToSqlScript.FormatValue(new DbCellValue(), column));
        }
示例#7
0
        [InlineData("t]]est", "[t]]]]est]")]    // Multiple escape characters
        public void FormatIdentifierTest(string value, string expectedOutput)
        {
            // If: I attempt to format a value as an identifier
            string output = ToSqlScript.FormatIdentifier(value);

            // Then: The output should match the expected output
            Assert.Equal(expectedOutput, output);
        }
示例#8
0
        public void FormatMultipartIdentifierArrayTest(string expectedOutput, string[] splits)
        {
            // If: I attempt to format a value as a multipart identifier
            string output = ToSqlScript.FormatMultipartIdentifier(splits);

            // Then: The output should match the expected output
            Assert.Equal(expectedOutput, output);
        }
示例#9
0
        public void NullTest()
        {
            // If: I attempt to format a db cell that contains null
            // Then: I should get the null string back
            DbColumn column          = new FormatterTestDbColumn(null);
            string   formattedString = ToSqlScript.FormatValue(new DbCellValue(), new FormatterTestDbColumn(null));

            Assert.Equal(ToSqlScript.NullString, formattedString);
        }
示例#10
0
        public void DoubleTest()
        {
            // Setup: Build a column and cell for the approx numeric type column
            DbColumn    column = new FormatterTestDbColumn("FLOAT");
            DbCellValue cell   = new DbCellValue {
                RawObject = 3.14159d
            };

            // If: I attempt to format a approx numeric type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should be able to be converted back into a double
            Assert.Equal(cell.RawObject, double.Parse(output));
        }
示例#11
0
        public void IntegerNumericTest(string dataType)
        {
            // Setup: Build a column and cell for the integer type column
            DbColumn    column = new FormatterTestDbColumn(dataType);
            DbCellValue cell   = new DbCellValue {
                RawObject = (long)123
            };

            // If: I attempt to format an integer type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should be able to be converted back into a long
            Assert.Equal(cell.RawObject, long.Parse(output));
        }
示例#12
0
        public void StringTypeTest(string datatype)
        {
            // Setup: Build a column and cell for the string type column
            DbColumn    column = new FormatterTestDbColumn(datatype);
            DbCellValue cell   = new DbCellValue {
                RawObject = "test string"
            };

            // If: I attempt to format a string type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should match the output string
            Assert.Equal("N'test string'", output);
        }
示例#13
0
        [InlineData("'", "N''''")]                              // Test with escaped character
        public void StringFormattingTest(string input, string expectedOutput)
        {
            // Setup: Build a column and cell for the string type column
            // NOTE: We're using VARCHAR because it's very general purpose.
            DbColumn    column = new FormatterTestDbColumn("VARCHAR");
            DbCellValue cell   = new DbCellValue {
                RawObject = input
            };

            // If: I attempt to format a string type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should be quoted and escaped properly
            Assert.Equal(expectedOutput, output);
        }
示例#14
0
        public void DecimalTest(string dataType, string regex, int?precision, int?scale)
        {
            // Setup: Build a column and cell for the decimal type column
            DbColumn    column = new FormatterTestDbColumn(dataType, precision, scale);
            DbCellValue cell   = new DbCellValue {
                RawObject = 123.45m
            };

            // If: I attempt to format a decimal type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: It should match a something like CAST(123.45 AS MONEY)
            Regex castRegex = new Regex($@"CAST\([\d\.]+ AS {regex}", RegexOptions.IgnoreCase);

            Assert.True(castRegex.IsMatch(output));
        }
示例#15
0
        public void GuidTest()
        {
            // Setup: Build a column and cell for the string type column
            DbColumn    column = new FormatterTestDbColumn("UNIQUEIDENTIFIER");
            DbCellValue cell   = new DbCellValue {
                RawObject = Guid.NewGuid()
            };

            // If: I attempt to format a string type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should match the output string
            Regex regex = new Regex(@"N'[0-9A-F]{8}(-[0-9A-F]{4}){3}-[0-9A-F]{12}'", RegexOptions.IgnoreCase);

            Assert.True(regex.IsMatch(output));
        }
示例#16
0
        public void TimeTest()
        {
            // Setup: Build a column and cell for the time type column
            DbColumn    column = new FormatterTestDbColumn("TIME");
            DbCellValue cell   = new DbCellValue {
                RawObject = TimeSpan.FromHours(12)
            };

            // If: I attempt to format a time type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should be able to be converted back into a timespan
            Regex    dateTimeRegex = new Regex("N'(.*)'");
            TimeSpan outputDateTime;

            Assert.True(TimeSpan.TryParse(dateTimeRegex.Match(output).Groups[1].Value, out outputDateTime));
        }
示例#17
0
        public void BinaryTest(string datatype)
        {
            // Setup: Build a column and cell for the string type column
            DbColumn    column = new FormatterTestDbColumn(datatype);
            DbCellValue cell   = new DbCellValue
            {
                RawObject = new byte[] { 0x42, 0x45, 0x4e, 0x49, 0x53, 0x43, 0x4f, 0x4f, 0x4c }
            };

            // If: I attempt to format a string type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should match the output string
            Regex regex = new Regex("0x[0-9A-F]+", RegexOptions.IgnoreCase);

            Assert.True(regex.IsMatch(output));
        }
示例#18
0
        public void DateTimeOffsetTest()
        {
            // Setup: Build a column and cell for the datetime offset type column
            DbColumn    column = new FormatterTestDbColumn("DATETIMEOFFSET");
            DbCellValue cell   = new DbCellValue {
                RawObject = DateTimeOffset.Now
            };

            // If: I attempt to format a datetime offset type column
            string output = ToSqlScript.FormatValue(cell, column);

            // Then: The output string should be able to be converted back into a datetime offset
            Regex          dateTimeRegex = new Regex("N'(.*)'");
            DateTimeOffset outputDateTime;

            Assert.True(DateTimeOffset.TryParse(dateTimeRegex.Match(output).Groups[1].Value, out outputDateTime));
        }
示例#19
0
        /// <summary>
        /// Constructs an update statement to change the associated row.
        /// </summary>
        /// <returns>An UPDATE statement</returns>
        public override string GetScript()
        {
            // Build the "SET" portion of the statement
            var setComponents = cellUpdates.Values.Select(cellUpdate =>
            {
                string formattedColumnName = ToSqlScript.FormatIdentifier(cellUpdate.Column.ColumnName);
                string formattedValue      = ToSqlScript.FormatValue(cellUpdate.Value, cellUpdate.Column);
                return($"{formattedColumnName} = {formattedValue}");
            });
            string setClause = string.Join(", ", setComponents);

            // Put everything together into a single query
            string whereClause           = GetWhereClause(false).CommandText;
            string updateStatementFormat = AssociatedObjectMetadata.IsMemoryOptimized
                ? UpdateScriptMemOptimized
                : UpdateScript;

            return(string.Format(updateStatementFormat,
                                 AssociatedObjectMetadata.EscapedMultipartName,
                                 setClause,
                                 whereClause
                                 ));
        }
        /// <summary>
        /// Generates a edit-ready metadata object using SMO
        /// </summary>
        /// <param name="connection">Connection to use for getting metadata</param>
        /// <param name="objectName">Name of the object to return metadata for</param>
        /// <param name="objectType">Type of the object to return metadata for</param>
        /// <returns>Metadata about the object requested</returns>
        public TableMetadata GetObjectMetadata(DbConnection connection, string schemaName, string objectName, string objectType)
        {
            // Get a connection to the database for SMO purposes
            SqlConnection sqlConn = connection as SqlConnection;

            if (sqlConn == null)
            {
                // It's not actually a SqlConnection, so let's try a reliable SQL connection
                ReliableSqlConnection reliableConn = connection as ReliableSqlConnection;
                if (reliableConn == null)
                {
                    // If we don't have connection we can use with SMO, just give up on using SMO
                    return(null);
                }

                // We have a reliable connection, use the underlying connection
                sqlConn = reliableConn.GetUnderlyingConnection();
            }

            // Connect with SMO and get the metadata for the table
            ServerConnection serverConnection;

            if (sqlConn.AccessToken == null)
            {
                serverConnection = new ServerConnection(sqlConn);
            }
            else
            {
                serverConnection = new ServerConnection(sqlConn, new AzureAccessToken(sqlConn.AccessToken));
            }
            Server   server   = new Server(serverConnection);
            Database database = server.Databases[sqlConn.Database];
            TableViewTableTypeBase smoResult;

            switch (objectType.ToLowerInvariant())
            {
            case "table":
                Table table = string.IsNullOrEmpty(schemaName) ? new Table(database, objectName) : new Table(database, objectName, schemaName);
                table.Refresh();
                smoResult = table;
                break;

            case "view":
                View view = string.IsNullOrEmpty(schemaName) ? new View(database, objectName) : new View(database, objectName, schemaName);
                view.Refresh();
                smoResult = view;
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(objectType), SR.EditDataUnsupportedObjectType(objectType));
            }
            if (smoResult == null)
            {
                throw new ArgumentOutOfRangeException(nameof(objectName), SR.EditDataObjectMetadataNotFound);
            }

            // Generate the edit column metadata
            List <ColumnMetadata> editColumns = new List <ColumnMetadata>();

            for (int i = 0; i < smoResult.Columns.Count; i++)
            {
                Column smoColumn = smoResult.Columns[i];

                // The default value may be escaped
                string defaultValue = smoColumn.DefaultConstraint == null
                    ? null
                    : FromSqlScript.UnwrapLiteral(smoColumn.DefaultConstraint.Text);

                ColumnMetadata column = new ColumnMetadata
                {
                    DefaultValue = defaultValue,
                    EscapedName  = ToSqlScript.FormatIdentifier(smoColumn.Name),
                    Ordinal      = i
                };
                editColumns.Add(column);
            }

            // Only tables can be memory-optimized
            Table smoTable          = smoResult as Table;
            bool  isMemoryOptimized = smoTable != null && smoTable.IsMemoryOptimized;

            // Escape the parts of the name
            string[] objectNameParts      = { smoResult.Schema, smoResult.Name };
            string   escapedMultipartName = ToSqlScript.FormatMultipartIdentifier(objectNameParts);

            return(new TableMetadata
            {
                Columns = editColumns.ToArray(),
                EscapedMultipartName = escapedMultipartName,
                IsMemoryOptimized = isMemoryOptimized,
            });
        }
示例#21
0
        /// <summary>
        /// Generates a edit-ready metadata object using SMO
        /// </summary>
        /// <param name="connection">Connection to use for getting metadata</param>
        /// <param name="objectNamedParts">Split and unwrapped name parts</param>
        /// <param name="objectType">Type of the object to return metadata for</param>
        /// <returns>Metadata about the object requested</returns>
        public EditTableMetadata GetObjectMetadata(DbConnection connection, string[] objectNamedParts, string objectType)
        {
            Validate.IsNotNull(nameof(objectNamedParts), objectNamedParts);
            if (objectNamedParts.Length <= 0)
            {
                throw new ArgumentNullException(nameof(objectNamedParts), SR.EditDataMetadataObjectNameRequired);
            }
            if (objectNamedParts.Length > 2)
            {
                throw new InvalidOperationException(SR.EditDataMetadataTooManyIdentifiers);
            }

            // Get a connection to the database for SMO purposes
            SqlConnection sqlConn = connection as SqlConnection;

            if (sqlConn == null)
            {
                // It's not actually a SqlConnection, so let's try a reliable SQL connection
                ReliableSqlConnection reliableConn = connection as ReliableSqlConnection;
                if (reliableConn == null)
                {
                    // If we don't have connection we can use with SMO, just give up on using SMO
                    return(null);
                }

                // We have a reliable connection, use the underlying connection
                sqlConn = reliableConn.GetUnderlyingConnection();
            }

            // Connect with SMO and get the metadata for the table
            ServerConnection serverConnection;

            if (sqlConn.AccessToken == null)
            {
                serverConnection = new ServerConnection(sqlConn);
            }
            else
            {
                serverConnection = new ServerConnection(sqlConn, new AzureAccessToken(sqlConn.AccessToken));
            }
            Server   server = new Server(serverConnection);
            Database db     = new Database(server, sqlConn.Database);

            TableViewTableTypeBase smoResult;

            switch (objectType.ToLowerInvariant())
            {
            case "table":
                smoResult = objectNamedParts.Length == 1
                        ? new Table(db, objectNamedParts[0])                        // No schema provided
                        : new Table(db, objectNamedParts[1], objectNamedParts[0]);  // Schema provided
                break;

            case "view":
                smoResult = objectNamedParts.Length == 1
                        ? new View(db, objectNamedParts[0])                         // No schema provided
                        : new View(db, objectNamedParts[1], objectNamedParts[0]);   // Schema provided
                break;

            default:
                throw new ArgumentOutOfRangeException(nameof(objectType), SR.EditDataUnsupportedObjectType(objectType));
            }

            // A bug in SMO makes it necessary to call refresh to attain certain properties (such as IsMemoryOptimized)
            smoResult.Refresh();
            if (smoResult.State != SqlSmoState.Existing)
            {
                throw new ArgumentOutOfRangeException(nameof(objectNamedParts), SR.EditDataObjectNotFound);
            }

            // Generate the edit column metadata
            List <EditColumnMetadata> editColumns = new List <EditColumnMetadata>();

            for (int i = 0; i < smoResult.Columns.Count; i++)
            {
                Column smoColumn = smoResult.Columns[i];

                string defaultValue = null;
                try
                {
                    // The default value may be escaped
                    defaultValue = smoColumn.DefaultConstraint == null
                        ? null
                        : FromSqlScript.UnwrapLiteral(smoColumn.DefaultConstraint.Text);
                }
                catch (PropertyCannotBeRetrievedException)
                {
                    // This exception will be thrown when the user doesn't have view definition privilege,
                    // we can ignore it and use null as the default value;
                }

                EditColumnMetadata column = new EditColumnMetadata
                {
                    DefaultValue  = defaultValue,
                    EscapedName   = ToSqlScript.FormatIdentifier(smoColumn.Name),
                    Ordinal       = i,
                    IsHierarchyId = smoColumn.DataType.SqlDataType == SqlDataType.HierarchyId,
                };
                editColumns.Add(column);
            }

            // Only tables can be memory-optimized
            Table smoTable          = smoResult as Table;
            bool  isMemoryOptimized = false;

            // TODO: Remove IsSupported check once SMO fixes broken IsMemoryOptimized scenario (TFS #10871823)
            if (smoTable != null)
            {
                isMemoryOptimized = smoTable.IsSupportedProperty("IsMemoryOptimized") && smoTable.IsMemoryOptimized;
            }

            // Escape the parts of the name
            string[] objectNameParts      = { smoResult.Schema, smoResult.Name };
            string   escapedMultipartName = ToSqlScript.FormatMultipartIdentifier(objectNameParts);

            return(new EditTableMetadata
            {
                Columns = editColumns.ToArray(),
                EscapedMultipartName = escapedMultipartName,
                IsMemoryOptimized = isMemoryOptimized
            });
        }
示例#22
0
        /// <summary>
        /// Constructs a new cell update based on the the string value provided and the column
        /// for the cell.
        /// </summary>
        /// <param name="column">Column the cell will be under</param>
        /// <param name="valueAsString">The string from the client to convert to an object</param>
        public CellUpdate(DbColumnWrapper column, string valueAsString)
        {
            Validate.IsNotNull(nameof(column), column);
            Validate.IsNotNull(nameof(valueAsString), valueAsString);

            // Store the state that won't be changed
            try
            {
                Column = column;
                Type columnType = column.DataType;

                // Check for null
                if (valueAsString == NullString)
                {
                    ProcessNullValue();
                }
                else if (columnType == typeof(byte[]))
                {
                    // Binary columns need special attention
                    ProcessBinaryCell(valueAsString);
                }
                else if (columnType == typeof(string))
                {
                    ProcessTextCell(valueAsString);
                }
                else if (columnType == typeof(Guid))
                {
                    Value         = Guid.Parse(valueAsString);
                    ValueAsString = Value.ToString();
                }
                else if (columnType == typeof(TimeSpan))
                {
                    ProcessTimespanColumn(valueAsString);
                }
                else if (columnType == typeof(DateTimeOffset))
                {
                    Value         = DateTimeOffset.Parse(valueAsString, CultureInfo.CurrentCulture);
                    ValueAsString = Value.ToString();
                }
                else if (columnType == typeof(bool))
                {
                    ProcessBooleanCell(valueAsString);
                }
                // @TODO: Microsoft.SqlServer.Types.SqlHierarchyId
                else
                {
                    // Attempt to go straight to the destination type, if we know what it is, otherwise
                    // leave it as a string
                    Value = columnType != null
                        ? Convert.ChangeType(valueAsString, columnType, CultureInfo.CurrentCulture)
                        : valueAsString;

                    ValueAsString = Value.ToString();
                }
            }
            catch (FormatException fe)
            {
                // Pretty up the exception so the user can learn a bit from it
                // NOTE: Other formatting errors raised by helpers are InvalidOperationException to
                //       avoid being prettied here
                throw new FormatException(SR.EditDataInvalidFormat(column.ColumnName, ToSqlScript.FormatColumnType(column)), fe);
            }
        }
示例#23
0
 public void NullDbColumnTest()
 {
     // If: I attempt to format a null db column
     // Then: It should throw
     Assert.Throws <ArgumentNullException>(() => ToSqlScript.FormatValue(new DbCellValue(), null));
 }
示例#24
0
        /// <summary>
        /// Generates a command that can be executed to update a row -- and return the contents of
        /// the updated row.
        /// </summary>
        /// <param name="connection">The connection the command should be associated with</param>
        /// <returns>Command to update the row</returns>
        public override DbCommand GetCommand(DbConnection connection)
        {
            Validate.IsNotNull(nameof(connection), connection);

            // Process the cells and columns
            List <string>       declareColumns   = new List <string>();
            List <SqlParameter> inParameters     = new List <SqlParameter>();
            List <string>       setComponents    = new List <string>();
            List <string>       outClauseColumns = new List <string>();
            List <string>       selectColumns    = new List <string>();

            for (int i = 0; i < AssociatedObjectMetadata.Columns.Length; i++)
            {
                EditColumnMetadata metadata = AssociatedObjectMetadata.Columns[i];

                // Add the output columns regardless of whether the column is read only
                declareColumns.Add($"{metadata.EscapedName} {ToSqlScript.FormatColumnType(metadata.DbColumn, useSemanticEquivalent: true)}");
                outClauseColumns.Add($"inserted.{metadata.EscapedName}");
                selectColumns.Add(metadata.EscapedName);

                // If we have a new value for the column, proccess it now
                CellUpdate cellUpdate;
                if (cellUpdates.TryGetValue(i, out cellUpdate))
                {
                    string paramName = $"@Value{RowId}_{i}";
                    setComponents.Add($"{metadata.EscapedName} = {paramName}");
                    inParameters.Add(new SqlParameter(paramName, AssociatedResultSet.Columns[i].SqlDbType)
                    {
                        Value = cellUpdate.Value
                    });
                }
            }

            // Put everything together into a single query
            // Step 1) Build a temp table for inserting output values into
            string tempTableName    = $"@Update{RowId}Output";
            string declareStatement = string.Format(DeclareStatement, tempTableName, string.Join(", ", declareColumns));

            // Step 2) Build the update statement
            WhereClause whereClause = GetWhereClause(true);

            string updateStatementFormat = AssociatedObjectMetadata.IsMemoryOptimized
                ? UpdateOutputMemOptimized
                : UpdateOutput;
            string updateStatement = string.Format(updateStatementFormat,
                                                   AssociatedObjectMetadata.EscapedMultipartName,
                                                   string.Join(", ", setComponents),
                                                   string.Join(", ", outClauseColumns),
                                                   tempTableName,
                                                   whereClause.CommandText);


            string validateScript = string.Format(CultureInfo.InvariantCulture, validateUpdateOnlyOneRow,
                                                  AssociatedObjectMetadata.EscapedMultipartName,
                                                  whereClause.CommandText);

            // Step 3) Build the select statement
            string selectStatement = string.Format(SelectStatement, string.Join(", ", selectColumns), tempTableName);

            // Step 4) Put it all together into a results object
            StringBuilder query = new StringBuilder();

            query.AppendLine(declareStatement);
            query.AppendLine(validateScript);
            query.AppendLine(updateStatement);
            query.AppendLine(selectStatement);
            query.Append("END");

            // Build the command
            DbCommand command = connection.CreateCommand();

            command.CommandText = query.ToString();
            command.CommandType = CommandType.Text;
            command.Parameters.AddRange(inParameters.ToArray());
            command.Parameters.AddRange(whereClause.Parameters.ToArray());

            return(command);
        }
示例#25
0
        /// <summary>
        /// Generates a command that can be executed to insert a new row -- and return the newly
        /// inserted row.
        /// </summary>
        /// <param name="connection">The connection the command should be associated with</param>
        /// <returns>Command to insert the new row</returns>
        public override DbCommand GetCommand(DbConnection connection)
        {
            Validate.IsNotNull(nameof(connection), connection);

            // Process the cells and columns
            List <string>       declareColumns       = new List <string>();
            List <string>       inColumnNames        = new List <string>();
            List <string>       outClauseColumnNames = new List <string>();
            List <string>       inValues             = new List <string>();
            List <SqlParameter> inParameters         = new List <SqlParameter>();
            List <string>       selectColumns        = new List <string>();

            for (int i = 0; i < AssociatedObjectMetadata.Columns.Length; i++)
            {
                DbColumnWrapper    column   = AssociatedResultSet.Columns[i];
                EditColumnMetadata metadata = AssociatedObjectMetadata.Columns[i];
                CellUpdate         cell     = newCells[i];

                // Add the output columns regardless of whether the column is read only
                outClauseColumnNames.Add($"inserted.{metadata.EscapedName}");
                declareColumns.Add($"{metadata.EscapedName} {ToSqlScript.FormatColumnType(column, useSemanticEquivalent: true)}");
                selectColumns.Add(metadata.EscapedName);

                // Continue if we're not inserting a value for this column
                if (!IsCellValueProvided(column, cell, DefaultValues[i]))
                {
                    continue;
                }

                // Add the input column
                inColumnNames.Add(metadata.EscapedName);

                // Add the input values as parameters
                string paramName = $"@Value{RowId}_{i}";
                inValues.Add(paramName);
                inParameters.Add(new SqlParameter(paramName, column.SqlDbType)
                {
                    Value = cell.Value
                });
            }

            // Put everything together into a single query
            // Step 1) Build a temp table for inserting output values into
            string tempTableName    = $"@Insert{RowId}Output";
            string declareStatement = string.Format(DeclareStatement, tempTableName, string.Join(", ", declareColumns));

            // Step 2) Build the insert statement
            string joinedOutClauseNames = string.Join(", ", outClauseColumnNames);
            string insertStatement      = inValues.Count > 0
                ? string.Format(InsertOutputValuesStatement,
                                AssociatedObjectMetadata.EscapedMultipartName,
                                string.Join(", ", inColumnNames),
                                joinedOutClauseNames,
                                tempTableName,
                                string.Join(", ", inValues))
                : string.Format(InsertOutputDefaultStatement,
                                AssociatedObjectMetadata.EscapedMultipartName,
                                joinedOutClauseNames,
                                tempTableName);

            // Step 3) Build the select statement
            string selectStatement = string.Format(SelectStatement, string.Join(", ", selectColumns), tempTableName);

            // Step 4) Put it all together into a results object
            StringBuilder query = new StringBuilder();

            query.AppendLine(declareStatement);
            query.AppendLine(insertStatement);
            query.Append(selectStatement);

            // Build the command
            DbCommand command = connection.CreateCommand();

            command.CommandText = query.ToString();
            command.CommandType = CommandType.Text;
            command.Parameters.AddRange(inParameters.ToArray());

            return(command);
        }
示例#26
0
 public void FormatIdentifierNull()
 {
     // If: I attempt to format null as an identifier
     // Then: I should get an exception thrown
     Assert.Throws <ArgumentNullException>(() => ToSqlScript.FormatIdentifier(null));
 }
示例#27
0
        internal void UpdateColumnInformationWithMetadata(DbColumnWrapper[] columns)
        {
            if (columns == null || this.objectMetadata == null)
            {
                return;
            }

            foreach (DbColumnWrapper col in columns)
            {
                var columnMetadata = objectMetadata.Columns.FirstOrDefault(cm => { return(cm.EscapedName == ToSqlScript.FormatIdentifier(col.ColumnName)); });
                col.IsHierarchyId = columnMetadata != null && columnMetadata.IsHierarchyId;
            }
        }