Exemple #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="DatabaseEntity{T}" /> class.
 /// </summary>
 /// <param name="sqlSchema">The schema.</param>
 /// <param name="name">The name.</param>
 protected DatabaseSchemaEntity([NotNull] SqlSchema sqlSchema, [NotNull] string name)
     : base(string.Format("{0}.{1}", sqlSchema.FullName, name))
 {
     if (string.IsNullOrEmpty(name))
     {
         throw new ArgumentNullException("name");
     }
     if (sqlSchema == null)
     {
         throw new ArgumentNullException("sqlSchema");
     }
     SqlSchema = sqlSchema;
 }
        /// <summary>
        /// Initializes a new instance of the <see cref="SqlProgramDefinition" /> class.
        /// </summary>
        /// <param name="type">The type of program.</param>
        /// <param name="sqlSchema">The schema.</param>
        /// <param name="name">The <see cref="SqlProgramDefinition.Name">program name</see>.</param>
        /// <param name="parameters">The parameters.</param>
        internal SqlProgramDefinition(
            SqlObjectType type,
            [NotNull] SqlSchema sqlSchema,
            [NotNull] string name,
            [NotNull] params SqlProgramParameter[] parameters)
            : base(sqlSchema, name)
        {
            if (parameters == null)
            {
                throw new ArgumentNullException("parameters");
            }

            Type       = type;
            Name       = name;
            Parameters = parameters;
            // ReSharper disable once PossibleNullReferenceException
            _parametersByName = parameters.ToDictionary(p => p.FullName, StringComparer.InvariantCultureIgnoreCase);
        }
 /// <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="SqlTableDefinition" /> class.
        /// </summary>
        /// <param name="type">The object <see cref="Type">type</see>.</param>
        /// <param name="sqlSchema">The schema.</param>
        /// <param name="name">The table/view name.</param>
        /// <param name="columns">The columns.</param>
        /// <param name="tableType">Type of the table.</param>
        internal SqlTableDefinition(
            SqlObjectType type,
            [NotNull] SqlSchema sqlSchema,
            [NotNull] string name,
            [NotNull] SqlColumn[] columns,
            [CanBeNull] SqlTableType tableType)
            : base(sqlSchema, name)
            // ReSharper restore PossibleNullReferenceException
        {
            if (columns == null)
            {
                throw new ArgumentNullException("columns");
            }

            Type      = type;
            Name      = name;
            TableType = tableType;
            Columns   = columns;
            Dictionary <string, SqlColumn> columnsByName = new Dictionary <string, SqlColumn>(
                columns.Length,
                StringComparer.InvariantCultureIgnoreCase);

            _columnsByName = columnsByName;
            SqlMetaData    = new SqlMetaData[columns.Length];
            if (tableType != null)
            {
                tableType.TableDefinition = this;
            }

            int i = 0;

            foreach (SqlColumn column in columns)
            {
                Debug.Assert(column != null);
                SqlMetaData[i++] = column.SqlMetaData;
                columnsByName[column.FullName] = column;
            }
        }
        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;
                }
            }
        }