private static void writeExecuteMethod(string tableName)
        {
            CodeGenerationStatics.AddSummaryDocComment(
                writer,
                "Executes this " + tableName +
                " modification, persisting all changes. Executes any pre-insert, pre-update, post-insert, or post-update logic that may exist in the class.");
            writer.WriteLine("public void Execute( bool isLongRunning = false ) {");
            writer.WriteLine(DataAccessStatics.GetConnectionExpression(database) + ".ExecuteInTransaction( delegate {");

            // The mod type may change during execute.
            writer.WriteLine("var frozenModType = modType;");

            writer.WriteLine("if( frozenModType == ModificationType.Insert )");
            writer.WriteLine("preInsert();");
            writer.WriteLine("else if( frozenModType == ModificationType.Update )");
            writer.WriteLine("preUpdate();");

            writer.WriteLine("executeInsertOrUpdate( isLongRunning );");

            writer.WriteLine("if( frozenModType == ModificationType.Insert )");
            writer.WriteLine("postInsert();");
            writer.WriteLine("else if( frozenModType == ModificationType.Update )");
            writer.WriteLine("postUpdate();");

            // This must be after the calls to postInsert and postUpdate in case their implementations need to know which column values changed.
            writer.WriteLine("markColumnValuesUnchanged();");

            writer.WriteLine("} );");
            writer.WriteLine("}");
        }
Exemple #2
0
        private static void writeQueryMethod(
            TextWriter writer, Database database, EnterpriseWebLibrary.Configuration.SystemDevelopment.Query query,
            EnterpriseWebLibrary.Configuration.SystemDevelopment.QueryPostSelectFromClause postSelectFromClause)
        {
            // header
            CodeGenerationStatics.AddSummaryDocComment(writer, "Queries the database and returns the full results collection immediately.");
            writer.WriteLine(
                "public static IEnumerable<Row> GetRows" + postSelectFromClause.name + "( " +
                DataAccessStatics.GetMethodParamsFromCommandText(info, query.selectFromClause + " " + postSelectFromClause.Value) + " ) {");


            // body

            var namedParamList       = DataAccessStatics.GetNamedParamList(info, query.selectFromClause + " " + postSelectFromClause.Value);
            var getResultSetFirstArg = namedParamList.Any() ? "new[] { " + StringTools.ConcatenateWithDelimiter(", ", namedParamList.ToArray()) + " }, " : "";

            writer.WriteLine("return Cache.Current." + getQueryCacheName(query, postSelectFromClause) + ".GetResultSet( " + getResultSetFirstArg + "() => {");

            writer.WriteLine("var cmd = " + DataAccessStatics.GetConnectionExpression(database) + ".DatabaseInfo.CreateCommand();");
            writer.WriteLine("cmd.CommandText = selectFromClause + @\"" + postSelectFromClause.Value + "\";");
            DataAccessStatics.WriteAddParamBlockFromCommandText(writer, "cmd", info, query.selectFromClause + " " + postSelectFromClause.Value, database);
            writer.WriteLine("var results = new List<Row>();");
            writer.WriteLine(
                DataAccessStatics.GetConnectionExpression(database) +
                ".ExecuteReaderCommand( cmd, r => { while( r.Read() ) results.Add( new Row( new BasicRow( r ) ) ); } );");

            // Update single-row caches.
            writer.WriteLine("foreach( var i in results )");
            writer.WriteLine("updateSingleRowCaches( i );");

            writer.WriteLine("return results;");

            writer.WriteLine("} );");
            writer.WriteLine("}");
        }
Exemple #3
0
        internal static void Generate(DBConnection cn, TextWriter writer, string baseNamespace, Database database)
        {
            writer.WriteLine("namespace " + baseNamespace + "." + database.SecondaryDatabaseName + "Sequences {");

            var cmd = cn.DatabaseInfo.CreateCommand();

            cmd.CommandText = "SELECT * FROM USER_SEQUENCES";
            cn.ExecuteReaderCommand(cmd,
                                    reader => {
                while (reader.Read())
                {
                    var sequenceName = reader["SEQUENCE_NAME"].ToString();
                    writer.WriteLine();
                    writer.WriteLine("public class " + sequenceName + " {");
                    writer.WriteLine("public static decimal GetNextValue() {");
                    writer.WriteLine("DbCommand cmd = " + DataAccessStatics.GetConnectionExpression(database) + ".DatabaseInfo.CreateCommand();");
                    writer.WriteLine("cmd.CommandText = \"SELECT " + sequenceName + ".NEXTVAL FROM DUAL\";");
                    writer.WriteLine("return (decimal)" + DataAccessStatics.GetConnectionExpression(database) + ".ExecuteScalarCommand( cmd );");
                    writer.WriteLine("}");
                    writer.WriteLine("}");
                }
            });

            writer.WriteLine();
            writer.WriteLine("}");
        }
Exemple #4
0
        private static void writePrivateDeleteRowsMethod( DBConnection cn, string tableName, bool isRevisionHistoryClass )
        {
            // NOTE: For revision history tables, we should have the delete method automatically clean up the revisions table (but not user transactions) for us when doing direct-with-revision-bypass deletions.

            writer.WriteLine( "private static int deleteRows( List<" + DataAccessStatics.GetTableConditionInterfaceName( cn, database, tableName ) + "> conditions ) {" );
            if( isRevisionHistoryClass )
                writer.WriteLine( "return " + DataAccessStatics.GetConnectionExpression( database ) + ".ExecuteInTransaction( () => {" );

            if( isRevisionHistoryClass )
                writer.WriteLine( "copyLatestRevisions( conditions );" );

            writer.WriteLine( "var delete = new InlineDelete( \"" + tableName + "\" );" );
            writer.WriteLine( "conditions.ForEach( condition => delete.AddCondition( condition.CommandCondition ) );" );

            if( isRevisionHistoryClass )
                writer.WriteLine( "delete.AddCondition( getLatestRevisionsCondition() );" );

            writer.WriteLine( "try {" );
            writer.WriteLine( "return delete.Execute( " + DataAccessStatics.GetConnectionExpression( database ) + " );" );
            writer.WriteLine( "}" ); // try
            writer.WriteLine( "catch( System.Exception e ) {" );
            writer.WriteLine( "rethrowAsDataModificationExceptionIfNecessary( e );" );
            writer.WriteLine( "throw;" );
            writer.WriteLine( "}" ); // catch

            if( isRevisionHistoryClass )
                writer.WriteLine( "} );" ); // cn.ExecuteInTransaction
            writer.WriteLine( "}" );
        }
Exemple #5
0
        private static void writeDeleteRowsMethod( DBConnection cn, string tableName, string methodNameSuffix, bool executeAdditionalLogic )
        {
            CodeGenerationStatics.AddSummaryDocComment(
                writer,
                "<para>Deletes the rows that match the specified conditions and returns the number of rows deleted.</para>" +
                "<para>WARNING: After calling this method, delete referenced rows in other tables that are no longer needed.</para>" );
            writer.WriteLine( "public static int DeleteRows" + methodNameSuffix + "( " + getConditionParameterDeclarations( cn, tableName ) + " ) {" );
            if( executeAdditionalLogic )
                writer.WriteLine( "return " + DataAccessStatics.GetConnectionExpression( database ) + ".ExecuteInTransaction( () => {" );

            writer.WriteLine( "var conditions = getConditionList( requiredCondition, additionalConditions );" );

            if( executeAdditionalLogic ) {
                writer.WriteLine( getPostDeleteCallClassName( cn, tableName ) + " postDeleteCall = null;" );
                writer.WriteLine( "preDelete( conditions, ref postDeleteCall );" );
            }

            writer.WriteLine( "var rowsDeleted = deleteRows( conditions );" );

            if( executeAdditionalLogic ) {
                writer.WriteLine( "if( postDeleteCall != null )" );
                writer.WriteLine( "postDeleteCall.Execute();" );
            }

            writer.WriteLine( "return rowsDeleted;" );

            if( executeAdditionalLogic )
                writer.WriteLine( "} );" ); // cn.ExecuteInTransaction
            writer.WriteLine( "}" );
        }
Exemple #6
0
        private static void writeCopyLatestRevisionsMethod( DBConnection cn, string tableName, IEnumerable<Column> nonIdentityColumns )
        {
            writer.WriteLine(
                "private static void copyLatestRevisions( List<" + DataAccessStatics.GetTableConditionInterfaceName( cn, database, tableName ) + "> conditions ) {" );

            writer.WriteLine( "var revisionHistorySetup = (RevisionHistoryProvider)DataAccessStatics.SystemProvider;" );

            writer.WriteLine(
                "var command = new InlineSelect( \"" + columns.PrimaryKeyAndRevisionIdColumn.Name + "\".ToSingleElementArray(), \"FROM " + tableName + "\", false );" );
            writer.WriteLine( "conditions.ForEach( condition => command.AddCondition( condition.CommandCondition ) );" );
            writer.WriteLine( "command.AddCondition( getLatestRevisionsCondition() );" );
            writer.WriteLine( "var latestRevisionIds = new List<int>();" );
            writer.WriteLine(
                "command.Execute( " + DataAccessStatics.GetConnectionExpression( database ) +
                ", r => { while( r.Read() ) latestRevisionIds.Add( System.Convert.ToInt32( r[0] ) ); } );" );
            writer.WriteLine( "foreach( var latestRevisionId in latestRevisionIds ) {" );

            // Get the latest revision.
            writer.WriteLine( "var latestRevision = revisionHistorySetup.GetRevision( latestRevisionId );" );

            // If this condition is true, we've already modified the row in this transaction. If we were to copy it, we'd end up with two revisions of the same entity
            // in the same user transaction, which we don't support.
            writer.WriteLine( "if( latestRevision.UserTransactionId == " + DataAccessStatics.GetConnectionExpression( database ) + ".GetUserTransactionId() )" );
            writer.WriteLine( "continue;" );

            // Update the latest revision with a new user transaction.
            writer.WriteLine(
                "revisionHistorySetup.UpdateRevision( latestRevisionId, latestRevisionId, " + DataAccessStatics.GetConnectionExpression( database ) +
                ".GetUserTransactionId(), latestRevisionId );" );

            // Insert a copy of the latest revision with a new ID. This will represent the revision of the data before it was changed.
            writer.WriteLine( "var copiedRevisionId = revisionHistorySetup.GetNextMainSequenceValue();" );
            writer.WriteLine( "revisionHistorySetup.InsertRevision( copiedRevisionId, latestRevisionId, latestRevision.UserTransactionId );" );

            // Insert a copy of the data row and make it correspond to the copy of the latest revision.
            writer.WriteLine( "var copyCommand = " + DataAccessStatics.GetConnectionExpression( database ) + ".DatabaseInfo.CreateCommand();" );
            writer.WriteLine( "copyCommand.CommandText = \"INSERT INTO " + tableName + " SELECT \";" );
            foreach( var column in nonIdentityColumns ) {
                if( column == columns.PrimaryKeyAndRevisionIdColumn ) {
                    writer.WriteLine( "var revisionIdParameter = new DbCommandParameter( \"copiedRevisionId\", new DbParameterValue( copiedRevisionId ) );" );
                    writer.WriteLine(
                        "copyCommand.CommandText += revisionIdParameter.GetNameForCommandText( " + DataAccessStatics.GetConnectionExpression( database ) +
                        ".DatabaseInfo ) + \", \";" );
                    writer.WriteLine(
                        "copyCommand.Parameters.Add( revisionIdParameter.GetAdoDotNetParameter( " + DataAccessStatics.GetConnectionExpression( database ) + ".DatabaseInfo ) );" );
                }
                else
                    writer.WriteLine( "copyCommand.CommandText += \"" + column.Name + ", \";" );
            }
            writer.WriteLine( "copyCommand.CommandText = copyCommand.CommandText.Remove( copyCommand.CommandText.Length - 2 );" );
            writer.WriteLine( "copyCommand.CommandText += \" FROM " + tableName + " WHERE \";" );
            writer.WriteLine(
                "( new EqualityCondition( new InlineDbCommandColumnValue( \"" + columns.PrimaryKeyAndRevisionIdColumn.Name +
                "\", new DbParameterValue( latestRevisionId ) ) ) as InlineDbCommandCondition ).AddToCommand( copyCommand, " +
                DataAccessStatics.GetConnectionExpression( database ) + ".DatabaseInfo, \"latestRevisionId\" );" );
            writer.WriteLine( DataAccessStatics.GetConnectionExpression( database ) + ".ExecuteNonQueryCommand( copyCommand );" );

            writer.WriteLine( "}" ); // foreach
            writer.WriteLine( "}" ); // method
        }
        private static void writeDeleteRowsMethod(
            DBConnection cn, string tableName, string methodNameSuffix, bool includeIsLongRunningParameter, bool executeAdditionalLogic)
        {
            CodeGenerationStatics.AddSummaryDocComment(
                writer,
                "<para>Deletes the rows that match the specified conditions and returns the number of rows deleted.</para>" +
                "<para>WARNING: After calling this method, delete referenced rows in other tables that are no longer needed.</para>");
            writer.WriteLine(
                "public static int DeleteRows{0}( {1} ) {{".FormatWith(
                    methodNameSuffix,
                    StringTools.ConcatenateWithDelimiter(
                        ", ",
                        includeIsLongRunningParameter ? "bool isLongRunning" : "",
                        getConditionParameterDeclarations(cn, tableName))));
            if (executeAdditionalLogic)
            {
                writer.WriteLine("return " + DataAccessStatics.GetConnectionExpression(database) + ".ExecuteInTransaction( () => {");
            }

            writer.WriteLine("var conditions = getConditionList( requiredCondition, additionalConditions );");

            if (executeAdditionalLogic)
            {
                writer.WriteLine(getPostDeleteCallClassName(cn, tableName) + " postDeleteCall = null;");
                writer.WriteLine("preDelete( conditions, ref postDeleteCall );");
            }

            writer.WriteLine("var rowsDeleted = deleteRows( conditions, {0} );".FormatWith(includeIsLongRunningParameter ? "isLongRunning" : "false"));

            if (executeAdditionalLogic)
            {
                writer.WriteLine("if( postDeleteCall != null )");
                writer.WriteLine("postDeleteCall.Execute();");
            }

            writer.WriteLine("return rowsDeleted;");

            if (executeAdditionalLogic)
            {
                writer.WriteLine("} );");                   // cn.ExecuteInTransaction
            }
            writer.WriteLine("}");
        }
Exemple #8
0
        private static void writeMethod(TextWriter writer, Database database, RedStapler.StandardLibrary.Configuration.SystemDevelopment.CustomModification mod)
        {
            writer.WriteLine("public static void " + mod.name + "( " +
                             DataAccessStatics.GetMethodParamsFromCommandText(info, StringTools.ConcatenateWithDelimiter("; ", mod.commands)) + " ) {");

            writer.WriteLine(DataAccessStatics.GetConnectionExpression(database) + ".ExecuteInTransaction( delegate {");
            var cnt = 0;

            foreach (var command in mod.commands)
            {
                var commandVariableName = "cmd" + cnt++;
                writer.WriteLine("DbCommand " + commandVariableName + " = " + DataAccessStatics.GetConnectionExpression(database) + ".DatabaseInfo.CreateCommand();");
                writer.WriteLine(commandVariableName + ".CommandText = @\"" + command + "\";");
                DataAccessStatics.WriteAddParamBlockFromCommandText(writer, commandVariableName, info, command, database);
                writer.WriteLine(DataAccessStatics.GetConnectionExpression(database) + ".ExecuteNonQueryCommand( " + commandVariableName + " );");
            }
            writer.WriteLine("} );");            // execute in transaction call

            writer.WriteLine("}");               // method
        }
        private static void writeExecuteInsertOrUpdateMethod(
            DBConnection cn, string tableName, bool isRevisionHistoryClass, IEnumerable <Column> keyColumns, Column identityColumn)
        {
            writer.WriteLine("private void executeInsertOrUpdate( bool isLongRunning ) {");
            writer.WriteLine("try {");
            if (isRevisionHistoryClass)
            {
                writer.WriteLine(DataAccessStatics.GetConnectionExpression(database) + ".ExecuteInTransaction( delegate {");
            }


            // insert

            writer.WriteLine("if( modType == ModificationType.Insert ) {");

            // If this is a revision history table, write code to insert a new revision when a row is inserted into this table.
            if (isRevisionHistoryClass)
            {
                writer.WriteLine("var revisionHistorySetup = RevisionHistoryStatics.SystemProvider;");
                writer.WriteLine(getColumnFieldName(columns.PrimaryKeyAndRevisionIdColumn) + ".Value = revisionHistorySetup.GetNextMainSequenceValue();");
                writer.WriteLine(
                    "revisionHistorySetup.InsertRevision( System.Convert.ToInt32( " + getColumnFieldName(columns.PrimaryKeyAndRevisionIdColumn) +
                    ".Value ), System.Convert.ToInt32( " + getColumnFieldName(columns.PrimaryKeyAndRevisionIdColumn) + ".Value ), " +
                    DataAccessStatics.GetConnectionExpression(database) + ".GetUserTransactionId() );");
            }

            writer.WriteLine("var insert = new InlineInsert( \"" + tableName + "\" );");
            writer.WriteLine("insert.AddColumnModifications( getColumnModificationValues() );");
            if (identityColumn != null)
            {
                // One reason the ChangeType call is necessary: SQL Server identities always come back as decimal, and you can't cast a boxed decimal to an int.
                writer.WriteLine(
                    "{0}.Value = {1};".FormatWith(
                        getColumnFieldName(identityColumn),
                        identityColumn.GetIncomingValueConversionExpression(
                            "EwlStatics.ChangeType( insert.Execute( {0}, isLongRunning: isLongRunning ), typeof( {1} ) )".FormatWith(
                                DataAccessStatics.GetConnectionExpression(database),
                                identityColumn.UnconvertedDataTypeName))));
            }
            else
            {
                writer.WriteLine("insert.Execute( {0}, isLongRunning: isLongRunning );".FormatWith(DataAccessStatics.GetConnectionExpression(database)));
            }

            // Future calls to Execute should perform updates, not inserts. Use the values of key columns as conditions.
            writer.WriteLine("modType = ModificationType.Update;");
            writer.WriteLine("conditions = new List<" + DataAccessStatics.GetTableConditionInterfaceName(cn, database, tableName) + ">();");
            foreach (var column in keyColumns)
            {
                writer.WriteLine(
                    "conditions.Add( new " + DataAccessStatics.GetEqualityConditionClassName(cn, database, tableName, column) + "( " +
                    EwlStatics.GetCSharpIdentifier(column.PascalCasedNameExceptForOracle) + " ) );");
            }

            writer.WriteLine("}");               // if insert


            // update
            writer.WriteLine("else {");
            writer.WriteLine("var modificationValues = getColumnModificationValues();");
            writer.WriteLine("if( modificationValues.Any() ) {");
            if (isRevisionHistoryClass)
            {
                writer.WriteLine("copyLatestRevisions( conditions, isLongRunning );");
            }
            writer.WriteLine("var update = new InlineUpdate( \"" + tableName + "\" );");
            writer.WriteLine("update.AddColumnModifications( modificationValues );");
            writer.WriteLine("conditions.ForEach( condition => update.AddCondition( condition.CommandCondition ) );");
            if (isRevisionHistoryClass)
            {
                writer.WriteLine("update.AddCondition( getLatestRevisionsCondition() );");
            }
            writer.WriteLine("update.Execute( {0}, isLongRunning: isLongRunning );".FormatWith(DataAccessStatics.GetConnectionExpression(database)));
            writer.WriteLine("}");
            writer.WriteLine("}");               // else

            if (isRevisionHistoryClass)
            {
                writer.WriteLine("} );");        // cn.ExecuteInTransaction
            }
            writer.WriteLine("}");               // try

            writer.WriteLine("catch( System.Exception e ) {");
            writer.WriteLine("rethrowAsDataModificationExceptionIfNecessary( e );");
            writer.WriteLine("throw;");
            writer.WriteLine("}");               // catch

            writer.WriteLine("}");               // method
        }
Exemple #10
0
        private static void writeResultSetCreatorBody(
            DBConnection cn, TextWriter writer, Database database, string table, TableColumns tableColumns, bool tableUsesRowVersionedCaching,
            bool excludesPreviousRevisions, string cacheQueryInDbExpression)
        {
            if (tableUsesRowVersionedCaching)
            {
                writer.WriteLine("var results = new List<Row>();");
                writer.WriteLine(DataAccessStatics.GetConnectionExpression(database) + ".ExecuteInTransaction( delegate {");

                // Query for the cache keys of the results.
                writer.WriteLine(
                    "var keyCommand = {0};".FormatWith(
                        getInlineSelectExpression(
                            table,
                            tableColumns,
                            "{0}, \"{1}\"".FormatWith(
                                StringTools.ConcatenateWithDelimiter(", ", tableColumns.KeyColumns.Select(i => "\"{0}\"".FormatWith(i.Name)).ToArray()),
                                cn.DatabaseInfo is OracleInfo ? "ORA_ROWSCN" : tableColumns.RowVersionColumn.Name),
                            cacheQueryInDbExpression)));
                writer.WriteLine(getCommandConditionAddingStatement("keyCommand"));
                writer.WriteLine("var keys = new List<System.Tuple<{0}>>();".FormatWith(getPkAndVersionTupleTypeArguments(cn, tableColumns)));
                writer.WriteLine(
                    "keyCommand.Execute( " + DataAccessStatics.GetConnectionExpression(database) + ", r => { while( r.Read() ) keys.Add( " +
                    "System.Tuple.Create( {0}, {1} )".FormatWith(
                        StringTools.ConcatenateWithDelimiter(
                            ", ",
                            tableColumns.KeyColumns.Select((c, i) => c.GetDataReaderValueExpression("r", ordinalOverride: i)).ToArray()),
                        cn.DatabaseInfo is OracleInfo
                                                        ? "({0})r.GetValue( {1} )".FormatWith(oracleRowVersionDataType, tableColumns.KeyColumns.Count())
                                                        : tableColumns.RowVersionColumn.GetDataReaderValueExpression("r", ordinalOverride: tableColumns.KeyColumns.Count())) + " ); } );");

                writer.WriteLine("var rowsByPkAndVersion = getRowsByPkAndVersion();");
                writer.WriteLine("var cachedKeyCount = keys.Where( i => rowsByPkAndVersion.ContainsKey( i ) ).Count();");

                // If all but a few results are cached, execute a single-row query for each missing result.
                writer.WriteLine("if( cachedKeyCount >= keys.Count() - 1 || cachedKeyCount >= keys.Count() * .99 ) {");
                writer.WriteLine("foreach( var key in keys ) {");
                writer.WriteLine("results.Add( new Row( rowsByPkAndVersion.GetOrAdd( key, () => {");
                writer.WriteLine("var singleRowCommand = {0};".FormatWith(getInlineSelectExpression(table, tableColumns, "\"*\"", "false")));
                foreach (var i in tableColumns.KeyColumns.Select((c, i) => new { column = c, index = i }))
                {
                    writer.WriteLine(
                        "singleRowCommand.AddCondition( ( ({0})new {1}( key.Item{2} ) ).CommandCondition );".FormatWith(
                            DataAccessStatics.GetTableConditionInterfaceName(cn, database, table),
                            DataAccessStatics.GetEqualityConditionClassName(cn, database, table, i.column),
                            i.index + 1));
                }
                writer.WriteLine("var singleRowResults = new List<BasicRow>();");
                writer.WriteLine(
                    "singleRowCommand.Execute( " + DataAccessStatics.GetConnectionExpression(database) +
                    ", r => { while( r.Read() ) singleRowResults.Add( new BasicRow( r ) ); } );");
                writer.WriteLine("return singleRowResults.Single();");
                writer.WriteLine("} ) ) );");
                writer.WriteLine("}");
                writer.WriteLine("}");

                // Otherwise, execute the full query.
                writer.WriteLine("else {");
                writer.WriteLine(
                    "var command = {0};".FormatWith(
                        getInlineSelectExpression(
                            table,
                            tableColumns,
                            cn.DatabaseInfo is OracleInfo ? "\"{0}.*\", \"ORA_ROWSCN\"".FormatWith(table) : "\"*\"",
                            cacheQueryInDbExpression)));
                writer.WriteLine(getCommandConditionAddingStatement("command"));
                writer.WriteLine("command.Execute( " + DataAccessStatics.GetConnectionExpression(database) + ", r => {");
                writer.WriteLine(
                    "while( r.Read() ) results.Add( new Row( rowsByPkAndVersion.GetOrAdd( System.Tuple.Create( {0}, {1} ), () => new BasicRow( r ) ) ) );".FormatWith(
                        StringTools.ConcatenateWithDelimiter(", ", tableColumns.KeyColumns.Select(i => i.GetDataReaderValueExpression("r")).ToArray()),
                        cn.DatabaseInfo is OracleInfo
                                                        ? "({0})r.GetValue( {1} )".FormatWith(oracleRowVersionDataType, tableColumns.AllColumns.Count())
                                                        : tableColumns.RowVersionColumn.GetDataReaderValueExpression("r")));
                writer.WriteLine("} );");
                writer.WriteLine("}");

                writer.WriteLine("} );");
            }
            else
            {
                writer.WriteLine("var command = {0};".FormatWith(getInlineSelectExpression(table, tableColumns, "\"*\"", cacheQueryInDbExpression)));
                writer.WriteLine(getCommandConditionAddingStatement("command"));
                writer.WriteLine("var results = new List<Row>();");
                writer.WriteLine(
                    "command.Execute( " + DataAccessStatics.GetConnectionExpression(database) +
                    ", r => { while( r.Read() ) results.Add( new Row( new BasicRow( r ) ) ); } );");
            }

            // Add all results to RowsByPk.
            writer.WriteLine("foreach( var i in results ) {");
            var pkTupleCreationArgs = tableColumns.KeyColumns.Select(i => "i." + EwlStatics.GetCSharpIdentifier(i.PascalCasedNameExceptForOracle));
            var pkTuple             = "System.Tuple.Create( " + StringTools.ConcatenateWithDelimiter(", ", pkTupleCreationArgs.ToArray()) + " )";

            writer.WriteLine("cache.RowsByPk[ " + pkTuple + " ] = i;");
            if (excludesPreviousRevisions)
            {
                writer.WriteLine("cache.LatestRevisionRowsByPk[ " + pkTuple + " ] = i;");
            }
            writer.WriteLine("}");

            writer.WriteLine("return results;");
        }
        internal static void Generate(DBConnection cn, TextWriter writer, string baseNamespace, Database database)
        {
            writer.WriteLine("namespace " + baseNamespace + " {");
            writer.WriteLine("public static class " + database.SecondaryDatabaseName + "Procedures {");
            foreach (var procedure in database.GetProcedures())
            {
                var parameters = database.GetProcedureParameters(procedure);

                // header
                CodeGenerationStatics.AddSummaryDocComment(writer, "Executes the " + procedure + " procedure.");
                var parameterDeclarations = parameters.Select(
                    i => (i.Direction == ParameterDirection.Output ? "out " : i.Direction == ParameterDirection.InputOutput ? "ref " : "") + i.DataTypeName + " " +
                    i.Name);
                writer.WriteLine("public static void " + procedure + "( " + StringTools.ConcatenateWithDelimiter(", ", parameterDeclarations.ToArray()) + " ) {");

                // body
                writer.WriteLine("var cmd = new SprocExecution( \"" + procedure + "\" );");
                foreach (var parameter in parameters)
                {
                    if (parameter.Direction == ParameterDirection.Input)
                    {
                        writer.WriteLine("cmd.AddParameter( " + getDbCommandParameterCreationExpression(parameter) + " );");
                    }
                    else
                    {
                        writer.WriteLine("var " + parameter.Name + "Parameter = " + getDbCommandParameterCreationExpression(parameter) + ";");
                        writer.WriteLine(
                            parameter.Name + "Parameter.GetAdoDotNetParameter( " + DataAccessStatics.GetConnectionExpression(database) +
                            ".DatabaseInfo ).Direction = ParameterDirection." + parameter.Direction + ";");
                        writer.WriteLine("cmd.AddParameter( " + parameter.Name + "Parameter );");
                    }
                }
                foreach (var parameter in parameters.Where(parameter => parameter.Direction != ParameterDirection.Input))
                {
                    writer.WriteLine(parameter.DataTypeName + " " + parameter.Name + "Local = default( " + parameter.DataTypeName + " );");
                }
                writer.WriteLine("cmd.ExecuteReader( " + DataAccessStatics.GetConnectionExpression(database) + ", r => {");
                foreach (var parameter in parameters.Where(parameter => parameter.Direction != ParameterDirection.Input))
                {
                    var adoDotNetParameterValueExpression = "{0}Parameter.GetAdoDotNetParameter( {1}.DatabaseInfo ).Value".FormatWith(
                        parameter.Name,
                        DataAccessStatics.GetConnectionExpression(database));


                    // We are not sure if this is handling null correctly. When a null comes back via an "out" parameter, we're not sure whether it is represented with
                    // DBNull.Value or in another way. This issue can be resolved as soon as we have a system with stored procedures that we can use for testing.

                    // NOTE: This is a hack. We would like to use a simple cast to convert the value of the database parameter to the method parameter's type, but we
                    // can't because the types in Oracle.DataAccess.Types, like OracleDecimal, do not support any kind of conversion to .NET types when they are boxed.
                    writer.WriteLine(
                        "{0}Local = {1};".FormatWith(
                            parameter.Name,
                            parameter.GetIncomingValueConversionExpression(
                                "EwlStatics.ChangeType( {0}.ToString(), typeof( {1} ) )".FormatWith(
                                    adoDotNetParameterValueExpression,
                                    parameter.UnconvertedDataTypeName))));
                    //writer.WriteLine( "{0}Local = {1};".FormatWith( parameter.Name, parameter.GetIncomingValueConversionExpression( adoDotNetParameterValueExpression ) ) );
                }
                writer.WriteLine("} );");
                foreach (var parameter in parameters.Where(parameter => parameter.Direction != ParameterDirection.Input))
                {
                    writer.WriteLine(parameter.Name + " = " + parameter.Name + "Local;");
                }
                writer.WriteLine("}");
            }
            writer.WriteLine("}");
            writer.WriteLine("}");
        }