/// <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; } } }