void Database.RestoreNewTransactionLogs( string folderPath )
        {
            var lastRestoredTransactionLogFileName = "";
            ExecuteDbMethod(
                cn => {
                    var command = new InlineSelect(
                        "TransactionLogFileName".ToSingleElementArray(),
                        "from RsisLogBackups",
                        false,
                        orderByClause: "order by RsisLogBackupId desc" );
                    command.Execute(
                        cn,
                        r => {
                            if( r.Read() )
                                lastRestoredTransactionLogFileName = r.GetString( 0 );
                        } );
                } );

            // We want all logs whose ID is greater than that of the last restored log file.
            var newLogFileNames = GetLogFilesOrderedByNewest( folderPath, lastRestoredTransactionLogFileName );

            // The following commands must be executed against the master database because there can't be any active connections when doing a restore (including the connection you are using
            // to run the command).
            executeDbMethodAgainstMaster(
                cn => {
                    try {
                        executeLongRunningCommand( cn, "ALTER DATABASE " + info.Database + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE" );

                        foreach( var logFileName in newLogFileNames.Reverse() ) {
                            var filePath = EwlStatics.CombinePaths( folderPath, logFileName );

                            // We do not want this to be in a transaction (it probably wouldn't even work because having a transaction writes to the transaction log, and we can't write anything to a
                            // SQL Server database in standby mode.
                            try {
                                executeLongRunningCommand( cn, "RESTORE LOG " + info.Database + " FROM DISK = '" + filePath + "' WITH STANDBY = '" + getStandbyFilePath() + "'" );
                            }
                            catch( Exception e ) {
                                var sqlException = e.GetBaseException() as SqlException;
                                if( sqlException != null ) {
                                    // 3117: The log or differential backup cannot be restored because no files are ready to rollforward.
                                    if( sqlException.Number == 3117 ) {
                                        throw new UserCorrectableException(
                                            "Failed to restore log, probably because we failed to do a full restore with force new package download after creating a log-shipping-enabled backup on the live server.",
                                            e );
                                    }

                                    // 4305: The log in this backup set begins at LSN X, which is too recent to apply to the database. An earlier log backup that includes LSN Y can be restored.
                                    if( sqlException.Number == 4305 ) {
                                        throw new UserCorrectableException(
                                            "Failed to restore log because the oldest log available for download from the live server is still too new to restore on this standby server. This happens if the standby server falls so far behind that the live server starts cleaning up old logs before they are downloaded.",
                                            e );
                                    }
                                }

                                throw;
                            }
                        }
                    }
                    finally {
                        // Sometimes the database isn't ready to go yet and this command will fail. So, we retry.
                        EwlStatics.Retry(
                            () => executeLongRunningCommand( cn, "ALTER DATABASE " + info.Database + " SET MULTI_USER" ),
                            "Database is in Restoring state and is not recovering." );
                    }
                } );
        }
        public string GetLogSummary( string folderPath )
        {
            var summary = "";
            ExecuteDbMethod(
                cn => {
                    var cutOffDateTime = DateTime.Now.AddHours( -24 );

                    var command = new InlineSelect( "count( * )".ToSingleElementArray(), "from RsisLogBackups", false );
                    command.AddCondition(
                        new InequalityCondition(
                            InequalityCondition.Operator.GreaterThan,
                            new InlineDbCommandColumnValue( "DateAndTimeSaved", new DbParameterValue( cutOffDateTime, "DateTime2" ) ) ) );
                    var numberOfLogsRestored = 0;
                    command.Execute(
                        cn,
                        r => {
                            r.Read();
                            numberOfLogsRestored = r.GetInt32( 0 );
                        } );

                    summary = "In the last 24 hours, " + numberOfLogsRestored + " logs were successfully restored.";
                    if( Directory.Exists( folderPath ) ) {
                        var logsDownloaded = new DirectoryInfo( folderPath ).GetFiles().Where( f => f.LastWriteTime > cutOffDateTime ).ToList();
                        var totalSizeInBytes = logsDownloaded.Sum( f => f.Length );
                        summary += " " + logsDownloaded.Count() + " logs were downloaded, with a total size of " + FormattingMethods.GetFormattedBytes( totalSizeInBytes ) + ".";
                    }
                } );
            return summary;
        }
        internal static void Generate(
            DBConnection cn, TextWriter writer, string baseNamespace, Database database, Configuration.SystemDevelopment.Database configuration)
        {
            if( configuration.rowConstantTables == null )
                return;

            writer.WriteLine( "namespace " + baseNamespace + "." + database.SecondaryDatabaseName + "RowConstants {" );
            foreach( var table in configuration.rowConstantTables ) {
                Column valueColumn;
                var orderIsSpecified = !table.orderByColumn.IsNullOrWhiteSpace();
                var values = new List<string>();
                var names = new List<string>();
                try {
                    var columns = new TableColumns( cn, table.tableName, false );
                    valueColumn = columns.AllColumnsExceptRowVersion.Single( column => column.Name.ToLower() == table.valueColumn.ToLower() );
                    var nameColumn = columns.AllColumnsExceptRowVersion.Single( column => column.Name.ToLower() == table.nameColumn.ToLower() );

                    var cmd = new InlineSelect(
                        new[] { valueColumn.Name, nameColumn.Name },
                        "FROM " + table.tableName,
                        false,
                        orderByClause: orderIsSpecified ? "ORDER BY " + table.orderByColumn : "" );
                    cmd.Execute(
                        cn,
                        reader => {
                            while( reader.Read() ) {
                                if( reader.IsDBNull( reader.GetOrdinal( valueColumn.Name ) ) )
                                    values.Add( valueColumn.NullValueExpression.Any() ? valueColumn.NullValueExpression : "null" );
                                else {
                                    var valueString = valueColumn.ConvertIncomingValue( reader[ valueColumn.Name ] ).ToString();
                                    values.Add( valueColumn.DataTypeName == typeof( string ).ToString() ? "\"{0}\"".FormatWith( valueString ) : valueString );
                                }
                                names.Add( nameColumn.ConvertIncomingValue( reader[ nameColumn.Name ] ).ToString() );
                            }
                        } );
                }
                catch( Exception e ) {
                    throw new UserCorrectableException(
                        "Column or data retrieval failed for the " + table.tableName + " row constant table. Make sure the table and the value, name, and order by columns exist.",
                        e );
                }

                CodeGenerationStatics.AddSummaryDocComment( writer, "Provides constants copied from the " + table.tableName + " table." );
                var className = table.tableName.TableNameToPascal( cn ) + "Rows";
                writer.WriteLine( "public class " + className + " {" );

                // constants
                for( var i = 0; i < values.Count; i++ ) {
                    CodeGenerationStatics.AddSummaryDocComment( writer, "Constant generated from row in database table." );
                    // It's important that row constants actually *be* constants (instead of static readonly) so they can be used in switch statements.
                    writer.WriteLine( "public const " + valueColumn.DataTypeName + " " + EwlStatics.GetCSharpIdentifier( names[ i ] ) + " = " + values[ i ] + ";" );
                }

                // one to one map
                var dictionaryType = "OneToOneMap<" + valueColumn.DataTypeName + ", string>";
                writer.WriteLine( "private static readonly " + dictionaryType + " " + dictionaryName + " = new " + dictionaryType + "();" );

                writeStaticConstructor( writer, className, names, values, valueColumn.DataTypeName );

                // methods
                writeGetNameFromValueMethod( writer, valueColumn.DataTypeName );
                writeGetValueFromNameMethod( writer, valueColumn.DataTypeName );
                if( orderIsSpecified ) {
                    writeGetValuesToNamesMethod( writer, valueColumn.DataTypeName );
                    writeFillListControlMethod( writer, valueColumn );
                }

                writer.WriteLine( "}" ); // class
            }
            writer.WriteLine( "}" ); // namespace
        }