private static async Task <Table> GetTableSchemaImplementationAsync(this SqlConnection connection,
                                                                            SqlTransaction transaction, string table, bool cache, CancellationToken cancellationToken)
        {
            // Validate parameters.
            Debug.Assert(connection != null);
            Debug.Assert(!string.IsNullOrWhiteSpace(table));

            // Create the key.
            var key = new TableSchemaKey {
                ConnectionString = connection.ConnectionString,
                Table            = table
            };

            // If not caching, then get the schema table and return.
            if (!cache)
            {
                return(await connection.GetTableSchemaAsync(transaction, key, cancellationToken).ConfigureAwait(false));
            }

            // Lock, get or add.
            using (await TablesLock.LockAsync(cancellationToken).ConfigureAwait(false))
            {
                // The table.
                if (!Tables.TryGetValue(key, out Table value))
                {
                    // Set value.
                    value = await connection.GetTableSchemaAsync(transaction, key, cancellationToken).
                            ConfigureAwait(false);

                    // Add.
                    Tables.Add(key, value);
                }

                // Return the value.
                return(value);
            }
        }
        private static async Task <Table> GetTableSchemaAsync(this SqlConnection connection,
                                                              SqlTransaction transaction, TableSchemaKey key, CancellationToken cancellationToken)
        {
            // Validate parameters.
            Debug.Assert(connection != null);

            // The sql.
            const string sql = @"
select 
    s.name as schema_name, t.name as table_name, t.create_date, t.modify_date 
from 
    sys.tables as t 
        inner join sys.schemas as s on 
            s.schema_id = t.schema_id 
where 
    t.object_id = object_id(@table);
select
	c.column_id,
	c.name,
	c.is_identity,
	c.is_nullable,
	c.is_computed
from 
	sys.columns as c
where 
	c.object_id = object_id(@table);
select
	i.index_id,
	i.name,
	i.is_primary_key,
	ic.column_id,
	ic.key_ordinal,
	ic.is_descending_key,
	ic.is_included_column
from 
	sys.indexes as i
		inner join sys.index_columns as ic on
			ic.object_id = i.object_id and
			ic.index_id = i.index_id
where 
	i.object_id = object_id(@table);
";

            // Query multiple.
            using (SqlMapper.GridReader gridReader = await connection.QueryMultipleAsync(sql,
                                                                                         new { table = key.Table }, transaction: transaction, commandType: CommandType.Text).ConfigureAwait(false))
            {
                // Get the items.
                TableRecord tableRecord = (await gridReader.ReadAsync <TableRecord>().ConfigureAwait(false)).
                                          Single();

                // Set the column records.
                IReadOnlyCollection <ColumnRecord> columnRecords = (await gridReader.ReadAsync <ColumnRecord>().
                                                                    ConfigureAwait(false)).ToReadOnlyCollection();

                // Set the index records.
                IReadOnlyCollection <IndexRecord> indexRecords = (await gridReader.ReadAsync <IndexRecord>().
                                                                  ConfigureAwait(false)).ToReadOnlyCollection();

                // Start creating everything, the columns first.
                IReadOnlyDictionary <int, Column> columns = columnRecords.
                                                            Select(c => new Column(c.column_id, c.name, c.is_identity, c.is_nullable, c.is_computed)).
                                                            ToReadOnlyDictionary(c => c.Id);

                // Get the indexes and index columns.
                IReadOnlyCollection <Index> indexes = indexRecords.
                                                      // Group the indexes first.
                                                      GroupBy(i => new { i.index_id, i.name, i.is_primary_key }).

                                                      // Select the index for each group.
                                                      Select(g => new Index(g.Key.index_id, g.Key.name, g.Key.is_primary_key,
                                                                            // Get the individual columns.
                                                                            g.Select(c => new IndexColumn(columns[c.column_id], c.key_ordinal, c.is_descending_key,
                                                                                                          c.is_included_column)))).
                                                      // Materialize.
                                                      ToReadOnlyCollection();

                // Assemble the table and return.
                return(new Table(tableRecord.schema_name, tableRecord.table_name, tableRecord.create_date,
                                 tableRecord.modify_date, columns.Values.OrderBy(c => c.Id), indexes));
            }
        }