Example #1
0
 /// <summary>
 ///   Initializes a new instance of the <see cref="SqlTableType"/> class.
 /// </summary>
 /// <param name="baseType">The base type.</param>
 /// <param name="sqlSchema">The schema name.</param>
 /// <param name="name">The table name.</param>
 /// <param name="defaultSize">The default size information.</param>
 /// <param name="isNullable">
 ///   If set to <see langword="true"/> the value can be <see langword="null"/>.
 /// </param>
 /// <param name="isUserDefined">
 ///   If set to <see langword="true"/> the value is a user defined type.
 /// </param>
 /// <param name="isClr">
 ///   If set to <see langword="true"/> the value is a CLR type.
 /// </param>
 // ReSharper disable once NotNullMemberIsNotInitialized
 internal SqlTableType(
     [CanBeNull] SqlType baseType,
     [NotNull] SqlSchema sqlSchema,
     [NotNull] string name,
     SqlTypeSize defaultSize,
     bool isNullable,
     bool isUserDefined,
     bool isClr)
     : base(
         baseType,
         sqlSchema,
         name,
         defaultSize,
         isNullable,
         isUserDefined,
         isClr,
         true)
 {
 }
            /// <summary>
            /// Initializes a new instance of the <see cref="TableDefinitionData" /> class.
            /// </summary>
            /// <param name="type">The type.</param>
            /// <param name="schemaID">The schema identifier.</param>
            /// <param name="name">The name.</param>
            /// <param name="ordinal">The ordinal.</param>
            /// <param name="columnName">Name of the column.</param>
            /// <param name="columnType">Type of the column.</param>
            /// <param name="columnSize">Size of the column.</param>
            /// <param name="isNullable">if set to <see langword="true" /> [is nullable].</param>
            /// <param name="tableTypeID">The ID of the associated <see cref="SqlType"/> if this table defines a <see cref="SqlType"/>.</param>
            public TableDefinitionData(
                SqlObjectType type,
                int schemaID,
                [NotNull] string name,
                int ordinal,
                [NotNull] string columnName,
                [NotNull] SqlType columnType,
                SqlTypeSize columnSize,
                bool isNullable,
                int? tableTypeID)
            {
                if (name == null) throw new ArgumentNullException("name");
                if (columnName == null) throw new ArgumentNullException("columnName");
                if (columnType == null) throw new ArgumentNullException("columnType");

                Type = type;
                SchemaID = schemaID;
                Name = name;
                Column = new SqlColumn(ordinal, columnName, columnType, columnSize, isNullable);
                TableTypeID = tableTypeID;
            }
        private async Task<DatabaseSchema> Load(bool forceReload, CancellationToken cancellationToken)
        {
            Instant requested = TimeHelpers.Clock.Now;
            using (await _lock.LockAsync(cancellationToken).ConfigureAwait(false))
            {
                // Check to see if the currently loaded schema is acceptable.
                CurrentSchema current = _current;

                // ReSharper disable once ConditionIsAlwaysTrueOrFalse
                if ((current != null) &&
                    (!forceReload || (current.Loaded > requested)))
                {
                    // Rethrow load errors.
                    if (current.ExceptionDispatchInfo != null)
                        current.ExceptionDispatchInfo.Throw();

                    Debug.Assert(current.Schema != null);
                    return this;
                }

                // Create dictionaries
                Dictionary<int, SqlSchema> sqlSchemas = new Dictionary<int, SqlSchema>();
                Dictionary<int, SqlType> typesByID = new Dictionary<int, SqlType>();
                Dictionary<string, SqlType> typesByName =
                    new Dictionary<string, SqlType>(StringComparer.InvariantCultureIgnoreCase);
                Dictionary<string, SqlProgramDefinition> programDefinitions =
                    new Dictionary<string, SqlProgramDefinition>(StringComparer.InvariantCultureIgnoreCase);
                Dictionary<string, SqlTableDefinition> tables =
                    new Dictionary<string, SqlTableDefinition>(StringComparer.InvariantCultureIgnoreCase);

                try
                {
                    // Open a connection
                    using (SqlConnection sqlConnection = new SqlConnection(ConnectionString))
                    {
                        // ReSharper disable once PossibleNullReferenceException
                        await sqlConnection.OpenAsync(cancellationToken).ConfigureAwait(false);

                        Version version;
                        if (!Version.TryParse(sqlConnection.ServerVersion, out version))
                            throw new DatabaseSchemaException(
                                () => Resources.DatabaseSchema_Load_CouldNotParseVersionInformation);
                        Debug.Assert(version != null);

                        if (version.Major < 9)
                            throw new DatabaseSchemaException(
                                () => Resources.DatabaseSchema_Load_VersionNotSupported,
                                version);

                        string sql = version.Major == 9 ? SQLResources.RetrieveSchema9 : SQLResources.RetrieveSchema10;

                        // Create the command first, as we will reuse on each connection.
                        using (
                            SqlCommand command = new SqlCommand(sql, sqlConnection) { CommandType = CommandType.Text })
                            // Execute command
                        using (SqlDataReader reader =
                            // ReSharper disable once PossibleNullReferenceException
                            await
                                command.ExecuteReaderAsync(CommandBehavior.SequentialAccess, cancellationToken)
                                    .ConfigureAwait(false))
                        {
                            /*
                             * Load SQL Schemas
                             */
                            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
                            {
                                SqlSchema sqlSchema = new SqlSchema(reader.GetInt32(0), reader.GetString(1));
                                sqlSchemas.Add(sqlSchema.ID, sqlSchema);
                            }

                            if (sqlSchemas.Count < 1)
                                throw new DatabaseSchemaException(
                                    () => Resources.DatabaseSchema_Load_CouldNotRetrieveSchemas);

                            /*
                             * Load types
                             */
                            if (!(await reader.NextResultAsync(cancellationToken).ConfigureAwait(false)))
                                throw new DatabaseSchemaException(
                                    () => Resources.DatabaseSchema_Load_RanOutOfResultsRetrievingTypes);

                            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
                            {
                                int schemaId = reader.GetInt32(0);
                                SqlSchema sqlSchema;
                                if (!sqlSchemas.TryGetValue(schemaId, out sqlSchema) ||
                                    (sqlSchema == null))
                                    throw new DatabaseSchemaException(
                                        () => Resources.DatabaseSchema_Load_CouldNotFindSchema,
                                        schemaId);
                                int id = reader.GetInt32(1);
                                string name = reader.GetString(2).ToLower();
                                SqlType baseType;
                                if (reader.IsDBNull(3))
                                    baseType = null;
                                else
                                {
                                    // NB SQL returns types in dependency order
                                    // i.e. base types are always seen first, so this code is much easier.
                                    int baseId = reader.GetInt32(3);
                                    typesByID.TryGetValue(baseId, out baseType);
                                }

                                short maxLength = reader.GetInt16(4);
                                byte precision = reader.GetByte(5);
                                byte scale = reader.GetByte(6);
                                bool isNullable = reader.GetBoolean(7);
                                bool isUserDefined = reader.GetBoolean(8);
                                bool isCLR = reader.GetBoolean(9);
                                bool isTable = reader.GetBoolean(10);

                                // Create type
                                SqlType type = isTable
                                    ? new SqlTableType(
                                        baseType,
                                        sqlSchema,
                                        name,
                                        new SqlTypeSize(maxLength, precision, scale),
                                        isNullable,
                                        isUserDefined,
                                        isCLR)
                                    : new SqlType(
                                        baseType,
                                        sqlSchema,
                                        name,
                                        new SqlTypeSize(maxLength, precision, scale),
                                        isNullable,
                                        isUserDefined,
                                        isCLR);

                                // Add to dictionary
                                typesByName.Add(type.FullName, type);
                                if (!typesByName.ContainsKey(type.Name))
                                    typesByName.Add(type.Name, type);
                                typesByID.Add(id, type);
                            }

                            if (typesByName.Count < 1)
                                throw new DatabaseSchemaException(
                                    () => Resources.DatabaseSchema_Load_CouldNotRetrieveTypes);

                            /*
                             * Load program definitions
                             */
                            if (!(await reader.NextResultAsync(cancellationToken).ConfigureAwait(false)))
                                throw new DatabaseSchemaException(
                                    () => Resources.DatabaseSchema_Load_RanOutOfResultsRetrievingPrograms);

                            List<ProgramDefinitionData> programDefinitionData = new List<ProgramDefinitionData>();
                            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
                            {
                                SqlObjectType type;
                                string typeString = reader.GetString(0) ?? string.Empty;
                                if (!ExtendedEnum<SqlObjectType>.TryParse(typeString, true, out type))
                                    throw new DatabaseSchemaException(
                                        () => Resources.DatabaseSchema_Load_CouldNotFindTypeWhenLoadingPrograms,
                                        typeString);

                                int schemaId = reader.GetInt32(1);
                                SqlSchema sqlSchema;
                                if (!sqlSchemas.TryGetValue(schemaId, out sqlSchema))
                                    throw new DatabaseSchemaException(
                                        () => Resources.DatabaseSchema_Load_CouldNotFindSchemaWhenLoadingPrograms,
                                        schemaId);
                                string name = reader.GetString(2).ToLower();

                                // If we have a null ordinal, we have no parameters.
                                if (reader.IsDBNull(3))
                                {
                                    programDefinitionData.Add(new ProgramDefinitionData(type, schemaId, name));
                                    continue;
                                }

                                int ordinal = reader.GetInt32(3);
                                string parameterName = reader.GetString(4).ToLower();
                                int typeId = reader.GetInt32(5);
                                SqlType parameterType;
                                if (!typesByID.TryGetValue(typeId, out parameterType) ||
                                    (parameterType == null))
                                    throw new DatabaseSchemaException(
                                        () => Resources.DatabaseSchema_Load_ParameterTypeNotFound,
                                        parameterName,
                                        typeId,
                                        name);

                                short maxLength = reader.GetInt16(6);
                                byte precision = reader.GetByte(7);
                                byte scale = reader.GetByte(8);
                                SqlTypeSize parameterSize = new SqlTypeSize(maxLength, precision, scale);

                                bool isOutput = reader.GetBoolean(9);
                                ParameterDirection parameterDirection;
                                if (!isOutput)
                                    parameterDirection = ParameterDirection.Input;
                                else if (parameterName == string.Empty)
                                    parameterDirection = ParameterDirection.ReturnValue;
                                else
                                    parameterDirection = ParameterDirection.InputOutput;

                                bool parameterIsReadOnly = reader.GetBoolean(10);
                                programDefinitionData.Add(
                                    new ProgramDefinitionData(
                                        type,
                                        schemaId,
                                        name,
                                        ordinal,
                                        parameterName,
                                        parameterType,
                                        parameterSize,
                                        parameterDirection,
                                        parameterIsReadOnly));
                            }

                            // Create unique program definitions.
                            foreach (SqlProgramDefinition program in programDefinitionData
                                // ReSharper disable once PossibleNullReferenceException
                                .GroupBy(d => d.ToString())
                                .Select(
                                    g =>
                                    {
                                        Debug.Assert(g != null);

                                        // Get columns ordered by ordinal.
                                        SqlProgramParameter[] parameters = g
                                            // ReSharper disable once PossibleNullReferenceException
                                            .Select(d => d.Parameter)
                                            .Where(p => p != null)
                                            .OrderBy(p => p.Ordinal)
                                            .ToArray();

                                        ProgramDefinitionData first = g.First();
                                        Debug.Assert(first != null);
                                        Debug.Assert(first.Name != null);

                                        Debug.Assert(sqlSchemas != null);

                                        SqlSchema sqlSchema;
                                        if (!sqlSchemas.TryGetValue(first.SchemaID, out sqlSchema))
                                            throw new DatabaseSchemaException(
                                                () =>
                                                    Resources
                                                    .DatabaseSchema_Load_CouldNotFindSchemaLoadingTablesAndViews,
                                                first.SchemaID);
                                        Debug.Assert(sqlSchema != null);

                                        return new SqlProgramDefinition(
                                            first.Type,
                                            sqlSchema,
                                            first.Name,
                                            parameters);
                                    }))
                            {
                                Debug.Assert(program != null);
                                programDefinitions[program.FullName] = program;

                                if (!programDefinitions.ContainsKey(program.Name))
                                    programDefinitions.Add(program.Name, program);
                            }

                            /*
                             * Load tables and views
                             */
                            if (!(await reader.NextResultAsync(cancellationToken).ConfigureAwait(false)))
                                throw new DatabaseSchemaException(
                                    () => Resources.DatabaseSchema_Load_RanOutOfTablesAndViews);

                            // Read raw data in.
                            List<TableDefinitionData> tableDefinitionData = new List<TableDefinitionData>();
                            while (await reader.ReadAsync(cancellationToken).ConfigureAwait(false))
                            {
                                SqlObjectType type;
                                string typeString = reader.GetString(0) ?? string.Empty;
                                if (!ExtendedEnum<SqlObjectType>.TryParse(typeString, true, out type))
                                    throw new DatabaseSchemaException(
                                        () => Resources.DatabaseSchema_Load_CouldNotFindObjectType,
                                        typeString);
                                int schemaId = reader.GetInt32(1);
                                string name = reader.GetString(2).ToLower();
                                int ordinal = reader.GetInt32(3);
                                string columnName = reader.GetString(4).ToLower();
                                int typeId = reader.GetInt32(5);
                                SqlType sqlType;
                                if (!typesByID.TryGetValue(typeId, out sqlType) ||
                                    (sqlType == null))
                                    throw new DatabaseSchemaException(
                                        () => Resources.DatabaseSchema_Load_ColumnTypeNotFound,
                                        columnName,
                                        typeId,
                                        name);

                                short maxLength = reader.GetInt16(6);
                                byte precision = reader.GetByte(7);
                                byte scale = reader.GetByte(8);
                                SqlTypeSize sqlTypeSize = new SqlTypeSize(maxLength, precision, scale);

                                bool isNullable = reader.GetBoolean(9);

                                int? tableType = reader.IsDBNull(10) ? null : (int?)reader.GetInt32(10);

                                tableDefinitionData.Add(
                                    new TableDefinitionData(
                                        type,
                                        schemaId,
                                        name,
                                        ordinal,
                                        columnName,
                                        sqlType,
                                        sqlTypeSize,
                                        isNullable,
                                        tableType));
                            }

                            // Create unique table definitions.
                            foreach (SqlTableDefinition table in tableDefinitionData
                                // ReSharper disable once PossibleNullReferenceException
                                .GroupBy(d => d.ToString())
                                .Select(
                                    g =>
                                    {
                                        Debug.Assert(g != null);

                                        // Get columns ordered by ordinal.
                                        SqlColumn[] columns = g
                                            // ReSharper disable PossibleNullReferenceException
                                            .Select(d => d.Column)
                                            .OrderBy(c => c.Ordinal)
                                            // ReSharper restore PossibleNullReferenceException
                                            .ToArray();
                                        Debug.Assert(columns.Length > 0);

                                        TableDefinitionData first = g.First();
                                        Debug.Assert(first != null);
                                        Debug.Assert(first.Name != null);
                                        Debug.Assert(sqlSchemas != null);

                                        SqlSchema sqlSchema;
                                        if (!sqlSchemas.TryGetValue(first.SchemaID, out sqlSchema))
                                            throw new DatabaseSchemaException(
                                                () =>
                                                    Resources
                                                    .DatabaseSchema_Load_CouldNotFindSchemaLoadingTablesAndViews,
                                                first.SchemaID);
                                        Debug.Assert(sqlSchema != null);

                                        SqlTableType tableType;
                                        if (first.TableTypeID != null)
                                        {
                                            Debug.Assert(typesByID != null);

                                            SqlType tType;
                                            if (!typesByID.TryGetValue(first.TableTypeID.Value, out tType))
                                                throw new DatabaseSchemaException(
                                                    () => Resources.DatabaseSchema_Load_TableTypeNotFound,
                                                    first.TableTypeID.Value,
                                                    first.Name);
                                            tableType = tType as SqlTableType;
                                            if (tableType == null)
                                                throw new DatabaseSchemaException(
                                                    () => Resources.DatabaseSchema_Load_TypeNotTableType,
                                                    first.TableTypeID.Value,
                                                    first.Name);
                                        }
                                        else tableType = null;

                                        return new SqlTableDefinition(
                                            first.Type,
                                            sqlSchema,
                                            first.Name,
                                            columns,
                                            tableType);
                                    }))
                            {
                                Debug.Assert(table != null);
                                tables[table.FullName] = table;

                                if (!tables.ContainsKey(table.Name))
                                    tables.Add(table.Name, table);
                            }
                        }
                    }

                    // Update the current schema.
                    _current = new CurrentSchema(Schema.GetOrAdd(sqlSchemas, programDefinitions, tables, typesByName));

                    // Always return this
                    return this;
                }
                    // In the event of an error we don't set the loaded flag - this allows retries.
                catch (DatabaseSchemaException databaseSchemaException)
                {
                    // Capture the exception in the current schema.
                    _current = new CurrentSchema(ExceptionDispatchInfo.Capture(databaseSchemaException));
                    throw;
                }
                catch (Exception exception)
                {
                    // Wrap exception in Database exception.
                    DatabaseSchemaException databaseSchemaException = new DatabaseSchemaException(
                        exception,
                        LoggingLevel.Critical,
                        () => Resources.DatabaseSchema_Load_ErrorOccurred);
                    // Capture the exception in the current schema.
                    _current = new CurrentSchema(ExceptionDispatchInfo.Capture(databaseSchemaException));
                    throw databaseSchemaException;
                }
            }
        }
            /// <summary>
            /// Initializes a new instance of the <see cref="ProgramDefinitionData" /> class.
            /// </summary>
            /// <param name="type">The type.</param>
            /// <param name="schemaID">The schema identifier.</param>
            /// <param name="name">The name.</param>
            /// <param name="ordinal">The ordinal.</param>
            /// <param name="parameterName">Name of the parameter.</param>
            /// <param name="parameterType">Type of the parameter.</param>
            /// <param name="parameterSize">Size of the parameter.</param>
            /// <param name="parameterDirection">The parameter direction.</param>
            /// <param name="isReadonly">if set to <see langword="true" /> [is readonly].</param>
            public ProgramDefinitionData(
                SqlObjectType type,
                int schemaID,
                [NotNull] string name,
                int ordinal,
                [NotNull] string parameterName,
                [NotNull] SqlType parameterType,
                SqlTypeSize parameterSize,
                ParameterDirection parameterDirection,
                bool isReadonly)
            {
                if (name == null) throw new ArgumentNullException("name");
                if (parameterName == null) throw new ArgumentNullException("parameterName");
                if (parameterType == null) throw new ArgumentNullException("parameterType");

                Type = type;
                SchemaID = schemaID;
                Name = name;
                Parameter = new SqlProgramParameter(
                    ordinal,
                    parameterName,
                    parameterType,
                    parameterSize,
                    parameterDirection,
                    isReadonly);
            }