Exemple #1
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( "}" );
        }
        private static void writeCreateForSingleRowUpdateMethod(
            DBConnection cn, string tableName, bool isRevisionHistoryTable, bool isRevisionHistoryClass, string methodNameSuffix)
        {
            // header
            CodeGenerationStatics.AddSummaryDocComment(
                writer,
                "Creates a modification object in single-row update mode with the specified current data. All column values in this object will have HasChanged = false, despite being initialized. This object can then be used to do a piecemeal update of the " +
                tableName + " table.");
            writer.Write(
                "public static " + GetClassName(cn, tableName, isRevisionHistoryTable, isRevisionHistoryClass) + " CreateForSingleRowUpdate" + methodNameSuffix +
                "( ");
            writeColumnParameterDeclarations(columns.AllColumnsExceptRowVersion);
            writer.WriteLine(" ) {");


            // body

            writer.WriteLine(
                "var mod = new " + GetClassName(cn, tableName, isRevisionHistoryTable, isRevisionHistoryClass) + " { modType = ModificationType.Update };");

            // Use the values of key columns as conditions.
            writer.WriteLine("mod.conditions = new List<" + DataAccessStatics.GetTableConditionInterfaceName(cn, database, tableName) + ">();");
            foreach (var column in columns.KeyColumns)
            {
                writer.WriteLine(
                    "mod.conditions.Add( new " + DataAccessStatics.GetEqualityConditionClassName(cn, database, tableName, column) + "( " +
                    EwlStatics.GetCSharpIdentifier(column.CamelCasedName) + " ) );");
            }

            writeColumnValueAssignmentsFromParameters(columns.AllColumnsExceptRowVersion, "mod");
            writer.WriteLine("mod.markColumnValuesUnchanged();");
            writer.WriteLine("return mod;");
            writer.WriteLine("}");
        }
Exemple #3
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
        }
Exemple #4
0
        private static void writeClass( DBConnection cn, string tableName, bool isRevisionHistoryTable, bool isRevisionHistoryClass )
        {
            columns = new TableColumns( cn, tableName, isRevisionHistoryClass );

            writer.WriteLine( "public partial class " + GetClassName( cn, tableName, isRevisionHistoryTable, isRevisionHistoryClass ) + " {" );

            var revisionHistorySuffix = GetRevisionHistorySuffix( isRevisionHistoryClass );

            // Write public static methods.
            writeInsertRowMethod( tableName, revisionHistorySuffix, "", columns.KeyColumns );
            writeInsertRowMethod( tableName, revisionHistorySuffix, "WithoutAdditionalLogic", columns.KeyColumns );
            writeUpdateRowsMethod( cn, tableName, revisionHistorySuffix, "" );
            writeUpdateRowsMethod( cn, tableName, revisionHistorySuffix, "WithoutAdditionalLogic" );
            writeDeleteRowsMethod( cn, tableName, revisionHistorySuffix, true );
            writeDeleteRowsMethod( cn, tableName, revisionHistorySuffix + "WithoutAdditionalLogic", false );
            writePrivateDeleteRowsMethod( cn, tableName, isRevisionHistoryClass );
            writer.WriteLine(
                "static partial void preDelete( List<" + DataAccessStatics.GetTableConditionInterfaceName( cn, database, tableName ) + "> conditions, ref " +
                getPostDeleteCallClassName( cn, tableName ) + " postDeleteCall );" );

            writer.WriteLine( "private ModificationType modType;" );
            writer.WriteLine( "private List<" + DataAccessStatics.GetTableConditionInterfaceName( cn, database, tableName ) + "> conditions;" );

            foreach( var column in columns.AllColumnsExceptRowVersion )
                writeFieldsAndPropertiesForColumn( column );

            foreach( var column in columns.DataColumns.Where( i => !columns.KeyColumns.Contains( i ) ) )
                FormItemStatics.WriteFormItemGetters( writer, column.GetModificationField() );

            // Write constructors.
            writeCreateForInsertMethod( cn, tableName, isRevisionHistoryTable, isRevisionHistoryClass, revisionHistorySuffix );
            writeCreateForUpdateMethod( cn, tableName, isRevisionHistoryTable, isRevisionHistoryClass, revisionHistorySuffix );
            if( columns.DataColumns.Any() )
                writeCreateForSingleRowUpdateMethod( cn, tableName, isRevisionHistoryTable, isRevisionHistoryClass, revisionHistorySuffix );
            writeGetConditionListMethod( cn, tableName );
            writer.WriteLine( "private " + GetClassName( cn, tableName, isRevisionHistoryTable, isRevisionHistoryClass ) + "() {}" );

            if( columns.DataColumns.Any() )
                writeSetAllDataMethod();

            // Write execute methods and helpers.
            writeExecuteMethod( tableName );
            writer.WriteLine( "partial void preInsert();" );
            writer.WriteLine( "partial void preUpdate();" );
            writeExecuteWithoutAdditionalLogicMethod( tableName );
            writeExecuteInsertOrUpdateMethod( cn, tableName, isRevisionHistoryClass, columns.KeyColumns, columns.IdentityColumn );
            writeAddColumnModificationsMethod( columns.AllNonIdentityColumnsExceptRowVersion );
            if( isRevisionHistoryClass ) {
                writeCopyLatestRevisionsMethod( cn, tableName, columns.AllNonIdentityColumnsExceptRowVersion );
                DataAccessStatics.WriteGetLatestRevisionsConditionMethod( writer, columns.PrimaryKeyAndRevisionIdColumn.Name );
            }
            writeRethrowAsEwfExceptionIfNecessary();
            writer.WriteLine(
                "static partial void populateConstraintNamesToViolationErrorMessages( Dictionary<string,string> constraintNamesToViolationErrorMessages );" );
            writer.WriteLine( "partial void postInsert();" );
            writer.WriteLine( "partial void postUpdate();" );
            writeMarkColumnValuesUnchangedMethod();

            writer.WriteLine( "}" );
        }
        private static void writeGetRowsMethod(
            DBConnection cn, TextWriter writer, IDatabase database, string table, TableColumns tableColumns, bool isSmallTable, bool tableUsesRowVersionedCaching,
            bool isRevisionHistoryTable, bool excludePreviousRevisions, int?commandTimeoutSeconds)
        {
            // header
            var methodName = "GetRows" + (isSmallTable ? "MatchingConditions" : "") +
                             (isRevisionHistoryTable && !excludePreviousRevisions ? "IncludingPreviousRevisions" : "");

            CodeGenerationStatics.AddSummaryDocComment(
                writer,
                "Retrieves the rows from the table that match the specified conditions, ordered in a stable way." +
                (isSmallTable ? " Since the table is specified as small, you should only use this method if you cannot filter the rows in code." : ""));
            writer.WriteLine(
                "public static IEnumerable<Row> " + methodName + "( params " + DataAccessStatics.GetTableConditionInterfaceName(cn, database, table) + "[] conditions ) {");


            // body

            // If it's a primary key query, use RowsByPk if possible.
            foreach (var i in tableColumns.KeyColumns)
            {
                var equalityConditionClassName = DataAccessStatics.GetEqualityConditionClassName(cn, database, table, i);
                writer.WriteLine("var {0}Condition = conditions.OfType<{1}>().FirstOrDefault();".FormatWith(i.CamelCasedName, equalityConditionClassName));
            }
            writer.WriteLine("var cache = Cache.Current;");
            var pkConditionVariableNames = tableColumns.KeyColumns.Select(i => i.CamelCasedName + "Condition");

            writer.WriteLine(
                "var isPkQuery = " + StringTools.ConcatenateWithDelimiter(" && ", pkConditionVariableNames.Select(i => i + " != null").ToArray()) +
                " && conditions.Count() == " + tableColumns.KeyColumns.Count() + ";");
            writer.WriteLine("if( isPkQuery ) {");
            writer.WriteLine("Row row;");
            writer.WriteLine(
                "if( cache." + (excludePreviousRevisions ? "LatestRevision" : "") + $"RowsByPk.TryGetValue( {TypeNames.Tuple}.Create( " +
                StringTools.ConcatenateWithDelimiter(", ", pkConditionVariableNames.Select(i => i + ".Value").ToArray()) + " ), out row ) )");
            writer.WriteLine("return new [] {row};");
            writer.WriteLine("}");

            var commandConditionsExpression = "conditions.Select( i => i.CommandCondition )";

            if (excludePreviousRevisions)
            {
                commandConditionsExpression += ".Concat( new [] {getLatestRevisionsCondition()} )";
            }
            writer.WriteLine("return cache.Queries.GetResultSet( " + commandConditionsExpression + ", commandConditions => {");
            writeResultSetCreatorBody(
                cn,
                writer,
                database,
                table,
                tableColumns,
                tableUsesRowVersionedCaching,
                excludePreviousRevisions,
                "!isPkQuery",
                commandTimeoutSeconds);
            writer.WriteLine("} );");

            writer.WriteLine("}");
        }
 private static void writeGetConditionListMethod(DBConnection cn, string tableName)
 {
     writer.WriteLine(
         "private static List<" + DataAccessStatics.GetTableConditionInterfaceName(cn, database, tableName) + "> getConditionList( " +
         getConditionParameterDeclarations(cn, tableName) + " ) {");
     writer.WriteLine("var conditions = new List<" + DataAccessStatics.GetTableConditionInterfaceName(cn, database, tableName) + ">();");
     writer.WriteLine("conditions.Add( requiredCondition );");
     writer.WriteLine("foreach( var condition in additionalConditions )");
     writer.WriteLine("conditions.Add( condition );");
     writer.WriteLine("return conditions;");
     writer.WriteLine("}");
 }
Exemple #7
0
        private static void writePrivateDeleteRowsMethod(DBConnection cn, string tableName, bool isRevisionHistoryClass, int?commandTimeoutSeconds)
        {
            // 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.DataAccessStateCurrentDatabaseConnectionExpression + ".ExecuteInTransaction( () => {");
            }

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

            writer.WriteLine($@"var delete = new {TypeNames.InlineDelete}( ""{tableName}"", {commandTimeoutSeconds?.ToString() ?? "null"} );");
            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.DataAccessStateCurrentDatabaseConnectionExpression + " );");
            writer.WriteLine("}");               // try
            writer.WriteLine("catch(" + nameof(Exception) + " e) {");
            writer.WriteLine("rethrowAsDataModificationExceptionIfNecessary( e );");
            writer.WriteLine("throw;");
            writer.WriteLine("}");               // catch

            if (isRevisionHistoryClass)
            {
                writer.WriteLine("} );");                   // cn.ExecuteInTransaction
            }
            writer.WriteLine("}");
        }
        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
        }
 private static string getConditionParameterDeclarations(DBConnection cn, string tableName)
 {
     return("" + DataAccessStatics.GetTableConditionInterfaceName(cn, database, tableName) + " requiredCondition, params " +
            DataAccessStatics.GetTableConditionInterfaceName(cn, database, tableName) + "[] additionalConditions");
 }
        private static void writeResultSetCreatorBody(
            DBConnection cn, TextWriter writer, IDatabase database, string table, TableColumns tableColumns, bool tableUsesRowVersionedCaching,
            bool excludesPreviousRevisions, string cacheQueryInDbExpression, int?commandTimeoutSeconds)
        {
            if (tableUsesRowVersionedCaching)
            {
                writer.WriteLine("var results = new List<Row>();");
                writer.WriteLine(DataAccessStatics.DataAccessStateCurrentDatabaseConnectionExpression + ".ExecuteInTransaction( () => {");

                // 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,
                            commandTimeoutSeconds)));
                writer.WriteLine(getCommandConditionAddingStatement("keyCommand"));
                writer.WriteLine($"var keys = new List<{TypeNames.Tuple}<{getPkAndVersionTupleTypeArguments( cn, tableColumns )}>>();");
                var concatenateWithDelimiter = StringTools.ConcatenateWithDelimiter(
                    ", ",
                    tableColumns.KeyColumns.Select((c, i) => c.GetDataReaderValueExpression("r", ordinalOverride: i)));
                var o = cn.DatabaseInfo is OracleInfo
                                                ? "({0})r.GetValue( {1} )".FormatWith(oracleRowVersionDataType, tableColumns.KeyColumns.Count())
                                                : tableColumns.RowVersionColumn.GetDataReaderValueExpression("r", ordinalOverride: tableColumns.KeyColumns.Count());
                writer.WriteLine(
                    "keyCommand.Execute( " + DataAccessStatics.DataAccessStateCurrentDatabaseConnectionExpression + ", r => { while( r.Read() ) keys.Add( " +
                    $"{TypeNames.Tuple}.Create( {concatenateWithDelimiter}, {o} )" + " ); } );");

                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.CodeBlock(
                    "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", commandTimeoutSeconds)));
                    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.DataAccessStateCurrentDatabaseConnectionExpression +
                        ", r => { while( r.Read() ) singleRowResults.Add( new BasicRow( r ) ); } );");
                    writer.WriteLine("return singleRowResults.Single();");
                    writer.WriteLine("} ) ) );");
                    writer.WriteLine("}");
                });
                // Otherwise, execute the full query.
                writer.CodeBlock(
                    "else {",
                    () => {
                    writer.WriteLine(
                        "var command = {0};".FormatWith(
                            getInlineSelectExpression(
                                table,
                                tableColumns,
                                cn.DatabaseInfo is OracleInfo ? "\"{0}.*\", \"ORA_ROWSCN\"".FormatWith(table) : "\"*\"",
                                cacheQueryInDbExpression,
                                commandTimeoutSeconds)));
                    writer.WriteLine(getCommandConditionAddingStatement("command"));
                    writer.WriteLine("command.Execute( " + DataAccessStatics.DataAccessStateCurrentDatabaseConnectionExpression + ", 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("} );");
            }
            else
            {
                writer.WriteLine(
                    "var command = {0};".FormatWith(getInlineSelectExpression(table, tableColumns, @"""*""", cacheQueryInDbExpression, commandTimeoutSeconds)));
                writer.WriteLine(getCommandConditionAddingStatement("command"));
                writer.WriteLine("var results = new List<Row>();");
                writer.WriteLine(
                    "command.Execute( " + DataAccessStatics.DataAccessStateCurrentDatabaseConnectionExpression +
                    ", 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." + Utility.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;");
        }