public TableColumn( Identifier tableName, int ordinalPosition, string columnName, string typeDefinition, bool isNullable, Option <string> defaultValue, bool isPrimaryKeyColumn, bool isUniqueKeyColumn, bool isForeignKeyColumn ) : base( tableName, ordinalPosition, columnName, typeDefinition, isNullable, defaultValue ) { TableUrl = UrlRouter.GetTableUrl(tableName); var isKey = isPrimaryKeyColumn || isUniqueKeyColumn || isForeignKeyColumn; ColumnClass = isKey ? @"class=""is-key-column""" : string.Empty; ColumnIcon = BuildColumnIcon(isPrimaryKeyColumn, isUniqueKeyColumn, isForeignKeyColumn); ColumnTitle = BuildColumnTitle(isPrimaryKeyColumn, isUniqueKeyColumn, isForeignKeyColumn); }
protected override IRuleMessage BuildMessage(Option <Identifier> foreignKeyName, Identifier childTableName) { if (childTableName == null) { throw new ArgumentNullException(nameof(childTableName)); } var builder = StringBuilderCache.Acquire(); builder.Append("A foreign key"); foreignKeyName.IfSome(fkName => { builder.Append(" <code>") .Append(HttpUtility.HtmlEncode(fkName.LocalName)) .Append("</code>"); }); var childTableUrl = UrlRouter.GetTableUrl(childTableName); var childTableLink = $"<a href=\"{ childTableUrl }\">{ HttpUtility.HtmlEncode(childTableName.ToVisibleName()) }</a>"; builder.Append(" on ") .Append(childTableLink) .Append(" contains the same column set as the target key."); var message = builder.GetStringAndRelease(); return(new RuleMessage(RuleId, RuleTitle, Level, message)); }
private Option <Uri> GetSynonymTargetUrl(Identifier identifier, SynonymTargets targets) { if (targets.Tables.ContainsKey(identifier)) { return(new Uri(UrlRouter.GetTableUrl(identifier), UriKind.Relative)); } if (targets.Views.ContainsKey(identifier)) { return(new Uri(UrlRouter.GetViewUrl(identifier), UriKind.Relative)); } if (targets.Sequences.ContainsKey(identifier)) { return(new Uri(UrlRouter.GetSequenceUrl(identifier), UriKind.Relative)); } if (targets.Synonyms.ContainsKey(identifier)) { return(new Uri(UrlRouter.GetSynonymUrl(identifier), UriKind.Relative)); } if (targets.Routines.ContainsKey(identifier)) { return(new Uri(UrlRouter.GetRoutineUrl(identifier), UriKind.Relative)); } return(Option <Uri> .None); }
public ChildKey(string constraintName, Identifier childTableName, string childColumnName, string qualifiedParentColumnName, string rootPath) { if (childTableName == null) { throw new ArgumentNullException(nameof(childTableName)); } if (childColumnName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(childColumnName)); } if (qualifiedParentColumnName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(qualifiedParentColumnName)); } if (rootPath == null) { throw new ArgumentNullException(nameof(rootPath)); } ChildTableName = childTableName.ToVisibleName(); ChildTableUrl = rootPath + UrlRouter.GetTableUrl(childTableName); ChildColumnName = childColumnName; var qualifiedChildColumnName = ChildTableName + "." + ChildColumnName; var description = qualifiedChildColumnName + " references " + qualifiedParentColumnName; if (!constraintName.IsNullOrWhiteSpace()) { description += " via " + constraintName; } ConstraintDescription = description; }
protected override IRuleMessage BuildMessage(string columnName, Identifier tableName, Identifier targetTableName) { if (columnName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(columnName)); } if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (targetTableName == null) { throw new ArgumentNullException(nameof(targetTableName)); } var builder = StringBuilderCache.Acquire(); var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var targetTableUrl = UrlRouter.GetTableUrl(targetTableName); var targetTableLink = $"<a href=\"{ targetTableUrl }\">{ HttpUtility.HtmlEncode(targetTableName.ToVisibleName()) }</a>"; builder.Append("The table ") .Append(tableLink) .Append(" has a column <code>") .Append(columnName) .Append("</code> implying a relationship to ") .Append(targetTableLink) .Append(" which is missing a foreign key constraint."); var messageText = builder.GetStringAndRelease(); return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected TableConstraint(Identifier tableName, string constraintName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } TableName = tableName.ToVisibleName(); TableUrl = UrlRouter.GetTableUrl(tableName); ConstraintName = constraintName ?? string.Empty; }
private IReadOnlyCollection <Link> GetReferenceTargetLinks(string rootPath, Identifier objectName, Identifier referenceName) { if (rootPath == null) { throw new ArgumentNullException(nameof(rootPath)); } if (objectName == null) { throw new ArgumentNullException(nameof(objectName)); } if (referenceName == null) { throw new ArgumentNullException(nameof(referenceName)); } var qualifiedReference = QualifyReferenceName(objectName, referenceName); var isSelfReference = string.Equals(objectName.Schema, qualifiedReference.Schema, StringComparison.OrdinalIgnoreCase) && string.Equals(objectName.LocalName, qualifiedReference.LocalName, StringComparison.OrdinalIgnoreCase); if (isSelfReference) { return(Array.Empty <Link>()); } var result = new List <Link>(); var matchingTables = GetMatchingObjects(TableNames, qualifiedReference) .Select(name => new Link(name, new Uri(rootPath + UrlRouter.GetTableUrl(name), UriKind.Relative))); result.AddRange(matchingTables); var matchingViews = GetMatchingObjects(ViewNames, qualifiedReference) .Select(name => new Link(name, new Uri(rootPath + UrlRouter.GetViewUrl(name), UriKind.Relative))); result.AddRange(matchingViews); var matchingSequences = GetMatchingObjects(SequenceNames, qualifiedReference) .Select(name => new Link(name, new Uri(rootPath + UrlRouter.GetSequenceUrl(name), UriKind.Relative))); result.AddRange(matchingSequences); var matchingSynonyms = GetMatchingObjects(SynonymNames, qualifiedReference) .Select(name => new Link(name, new Uri(rootPath + UrlRouter.GetSynonymUrl(name), UriKind.Relative))); result.AddRange(matchingSynonyms); var matchingRoutines = GetMatchingObjects(RoutineNames, qualifiedReference) .Select(name => new Link(name, new Uri(rootPath + UrlRouter.GetRoutineUrl(name), UriKind.Relative))); result.AddRange(matchingRoutines); return(result); }
public Table(Identifier tableName, uint columnCount, ulong rowCount) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } Name = tableName.ToVisibleName(); TableUrl = UrlRouter.GetTableUrl(tableName); ColumnCount = columnCount; RowCount = rowCount; }
protected override IRuleMessage BuildDisabledUniqueKeyMessage(Identifier tableName, Option <Identifier> uniqueKeyName) { var messageKeyName = uniqueKeyName.Match( name => " <code>" + HttpUtility.HtmlEncode(name.LocalName) + "</code>", () => string.Empty ); var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } contains a disabled unique key{ messageKeyName }. Consider enabling or removing the unique key."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } has no candidate (primary or unique) keys. Consider adding one to ensure records are unique."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildTableMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } is also a database keyword and may require quoting to be used. Consider renaming to a non-keyword name."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } is not related to any other table. Consider adding relations or removing the table."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } has a primary key whose column is not the first column in the table."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } has no not-nullable columns present. Consider adding one to ensure that each record contains data."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } has a multi-column primary key. Consider introducing a surrogate primary key."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName, int columnCount) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } has too many columns. It has { columnCount } columns."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } does not have any indexes present, requiring table scans to access records. Consider introducing an index or a primary key or a unique key constraint."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildTableMessage(Identifier tableName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } contains whitespace and requires quoting to be used. Consider renaming to remove any whitespace."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName, string indexName, IEnumerable <string> redundantIndexColumnNames, string otherIndexName, IEnumerable <string> otherIndexColumnNames) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (indexName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(indexName)); } if (redundantIndexColumnNames == null || redundantIndexColumnNames.Empty()) { throw new ArgumentNullException(nameof(redundantIndexColumnNames)); } if (otherIndexName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(otherIndexName)); } if (otherIndexColumnNames == null || otherIndexColumnNames.Empty()) { throw new ArgumentNullException(nameof(otherIndexColumnNames)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var columnNames = redundantIndexColumnNames .Select(columnName => "<code>" + HttpUtility.HtmlEncode(columnName) + "</code>"); var otherColumnNames = otherIndexColumnNames .Select(columnName => "<code>" + HttpUtility.HtmlEncode(columnName) + "</code>"); var builder = StringBuilderCache.Acquire(); builder.Append("The table ") .Append(tableLink) .Append(" has an index <code>") .Append(HttpUtility.HtmlEncode(indexName)) .Append("</code> which may be redundant, as its column set (") .AppendJoin(", ", columnNames) .Append(") is the prefix of another index <code>") .Append(HttpUtility.HtmlEncode(otherIndexName)) .Append("</code> (") .AppendJoin(", ", otherColumnNames) .Append(")."); var messageText = builder.GetStringAndRelease(); return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName, string columnName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (columnName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(columnName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } has a column <code>{ HttpUtility.HtmlEncode(columnName) }</code> with a numeric suffix, indicating denormalization."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName, string columnName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (columnName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(columnName)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } has a column <code>{ HttpUtility.HtmlEncode(columnName) }</code> whose default value is <code>NULL</code>. Consider removing the default value on the column."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildDisabledTriggerMessage(Identifier tableName, string?triggerName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var messageTriggerName = !triggerName.IsNullOrWhiteSpace() ? " <code>" + HttpUtility.HtmlEncode(triggerName) + "</code>" : string.Empty; var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } contains a disabled trigger{ messageTriggerName }. Consider enabling or removing the trigger."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
public ForeignKey( string constraintName, IEnumerable <string> columnNames, Identifier parentTableName, string parentConstraintName, IEnumerable <string> parentColumnNames, ReferentialAction deleteAction, ReferentialAction updateAction, string rootPath ) : base(constraintName) { if (columnNames == null || columnNames.Empty()) { throw new ArgumentNullException(nameof(columnNames)); } if (parentTableName == null) { throw new ArgumentNullException(nameof(parentTableName)); } if (parentColumnNames == null || parentColumnNames.Empty()) { throw new ArgumentNullException(nameof(parentColumnNames)); } if (!deleteAction.IsValid()) { throw new ArgumentException($"The { nameof(ReferentialAction) } provided must be a valid enum.", nameof(deleteAction)); } if (!updateAction.IsValid()) { throw new ArgumentException($"The { nameof(ReferentialAction) } provided must be a valid enum.", nameof(updateAction)); } if (rootPath == null) { throw new ArgumentNullException(nameof(rootPath)); } ChildColumnNames = columnNames.Join(", "); ParentConstraintName = parentConstraintName; ParentTableName = parentTableName.ToVisibleName(); ParentTableUrl = rootPath + UrlRouter.GetTableUrl(parentTableName); ParentColumnNames = parentColumnNames.Join(", "); DeleteActionDescription = _actionDescription[deleteAction]; UpdateActionDescription = _actionDescription[updateAction]; }
protected override IRuleMessage BuildMessage(IReadOnlyCollection <Identifier> cyclePath) { if (cyclePath == null) { throw new ArgumentNullException(nameof(cyclePath)); } var tableNames = cyclePath .Select(tableName => { var tableUrl = UrlRouter.GetTableUrl(tableName); return($"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"); }) .Join(" → "); var message = "Cycle found for the following path: " + tableNames; return(new RuleMessage(RuleId, RuleTitle, Level, message)); }
protected override IRuleMessage BuildDisabledCheckConstraintMessage(Identifier tableName, Option <Identifier> checkName) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } var messageCheckName = checkName.Match( name => " <code>" + HttpUtility.HtmlEncode(name.LocalName) + "</code>", () => string.Empty ); var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var messageText = $"The table { tableLink } contains a disabled check constraint{ messageCheckName }. Consider enabling or removing the check constraint."; return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Option <Identifier> foreignKeyName, Identifier tableName, IEnumerable <string> columnNames) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (columnNames == null || columnNames.Empty()) { throw new ArgumentNullException(nameof(columnNames)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var builder = StringBuilderCache.Acquire(); builder.Append("The table ") .Append(tableLink) .Append(" has a foreign key"); foreignKeyName.IfSome(fkName => { builder.Append(" <code>") .Append(HttpUtility.HtmlEncode(fkName.LocalName)) .Append("</code>"); }); builder.Append(" which is missing an index on the column"); // plural check if (columnNames.Skip(1).Any()) { builder.Append('s'); } var formattedColumnNames = columnNames .Select(columnName => "<code>" + HttpUtility.HtmlEncode(columnName) + "</code>"); builder.Append(' ') .AppendJoin(", ", formattedColumnNames); var messageText = builder.GetStringAndRelease(); return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
protected override IRuleMessage BuildMessage(Identifier tableName, string?indexName, IEnumerable <string> columnNames) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (columnNames == null || columnNames.Empty()) { throw new ArgumentNullException(nameof(columnNames)); } var tableUrl = UrlRouter.GetTableUrl(tableName); var tableLink = $"<a href=\"{ tableUrl }\">{ HttpUtility.HtmlEncode(tableName.ToVisibleName()) }</a>"; var builder = StringBuilderCache.Acquire(); builder.Append("The table ") .Append(tableLink) .Append(" has a unique index"); if (!indexName.IsNullOrWhiteSpace()) { builder.Append(" <code>") .Append(HttpUtility.HtmlEncode(indexName)) .Append("</code>"); } var pluralText = columnNames.Skip(1).Any() ? " which contains nullable columns: " : " which contains a nullable column: "; builder.Append(pluralText); var formattedColumnNames = columnNames .Select(columnName => "<code>" + HttpUtility.HtmlEncode(columnName) + "</code>"); builder.AppendJoin(", ", formattedColumnNames); var messageText = builder.GetStringAndRelease(); return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }
public Index( string?indexName, Identifier tableName, bool isUnique, IEnumerable <string> columnNames, IEnumerable <IndexColumnOrder> columnSorts, IEnumerable <string> includedColumnNames ) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (columnNames == null || columnNames.Empty()) { throw new ArgumentNullException(nameof(columnNames)); } if (columnSorts == null || columnSorts.Empty()) { throw new ArgumentNullException(nameof(columnSorts)); } if (includedColumnNames == null) { throw new ArgumentNullException(nameof(includedColumnNames)); } Name = indexName ?? string.Empty; TableName = tableName.ToVisibleName(); TableUrl = UrlRouter.GetTableUrl(tableName); UniqueText = isUnique ? "✓" : "✗"; ColumnsText = columnNames.Zip( columnSorts.Select(SortToString), (c, s) => c + " " + s ).Join(", "); IncludedColumnsText = includedColumnNames.Join(", "); }
public Trigger( Identifier tableName, Identifier triggerName, string rootPath, string definition, TriggerQueryTiming queryTiming, TriggerEvent triggerEvent ) { if (tableName == null) { throw new ArgumentNullException(nameof(tableName)); } if (triggerName == null) { throw new ArgumentNullException(nameof(triggerName)); } Name = triggerName.ToVisibleName(); TableName = tableName.ToVisibleName(); TableUrl = rootPath + UrlRouter.GetTableUrl(tableName); TriggerUrl = rootPath + UrlRouter.GetTriggerUrl(tableName, triggerName); RootPath = rootPath ?? throw new ArgumentNullException(nameof(rootPath)); Definition = definition ?? throw new ArgumentNullException(nameof(definition)); var queryFlags = queryTiming.GetFlags() .Select(qt => TimingDescriptions[qt]) .OrderBy(qt => qt) .ToList(); var eventFlags = triggerEvent.GetFlags() .Select(te => EventDescriptions[te]) .OrderBy(te => te) .ToList(); QueryTiming = queryFlags.Join(", "); Events = eventFlags.Join(", "); }
protected override IRuleMessage BuildMessage(Option <Identifier> foreignKeyName, Identifier childTableName, Identifier parentTableName) { if (childTableName == null) { throw new ArgumentNullException(nameof(childTableName)); } if (parentTableName == null) { throw new ArgumentNullException(nameof(parentTableName)); } var builder = StringBuilderCache.Acquire(); builder.Append("A foreign key"); foreignKeyName.IfSome(fkName => { builder.Append(" <code>") .Append(HttpUtility.HtmlEncode(fkName.LocalName)) .Append("</code>"); }); var childTableUrl = UrlRouter.GetTableUrl(childTableName); var childTableLink = $"<a href=\"{ childTableUrl }\">{ HttpUtility.HtmlEncode(childTableName.ToVisibleName()) }</a>"; var parentTableUrl = UrlRouter.GetTableUrl(parentTableName); var parentTableLink = $"<a href=\"{ parentTableUrl }\">{ HttpUtility.HtmlEncode(parentTableName.ToVisibleName()) }</a>"; builder.Append(" from ") .Append(childTableLink) .Append(" to ") .Append(parentTableLink) .Append(" contains mismatching column types. These should be the same in order to ensure that foreign keys can always hold the same information as the target key."); var messageText = builder.GetStringAndRelease(); return(new RuleMessage(RuleId, RuleTitle, Level, messageText)); }