private void LookForUnusedColumns(IReadOnlyList <CompareLog> firstStageLogs, CompareLog log) { var logger = new CompareLogger2(CompareType.Column, null, _logs, _ignoreList, () => _hasErrors = true); var tableDict = _databaseModel.Tables.ToDictionary(x => x.FormSchemaTableFromDatabase(_databaseModel.DefaultSchema), _caseComparer); //because of table splitting and TPH we need to groups properties by table name to correctly say what columns are missed var entityColsGrouped = firstStageLogs.SelectMany(p => p.SubLogs) .Where(x => x.State == CompareState.Ok && x.Type == CompareType.Entity) .GroupBy(x => x.Expected, y => y.SubLogs .Where(x => x.State == CompareState.Ok && x.Type == CompareType.Property) .Select(p => p.Expected)); var entityColsByTableDict = entityColsGrouped.ToDictionary(x => x.Key, y => y.SelectMany(x => x.ToList()), _caseComparer); foreach (var entityLog in firstStageLogs.SelectMany(p => p.SubLogs) .Where(x => x.State == CompareState.Ok && x.Type == CompareType.Entity)) { if (tableDict.ContainsKey(entityLog.Expected)) { var dbColNames = tableDict[entityLog.Expected].Columns.Select(x => x.Name); var colsNotUsed = dbColNames.Where(p => !entityColsByTableDict[entityLog.Expected].Contains(p, _caseComparer)); foreach (var colName in colsNotUsed) { logger.ExtraInDatabase(colName, CompareAttributes.ColumnName, entityLog.Expected); } } } }
private void LookForUnusedTables(IReadOnlyList <CompareLog> firstStageLogs, CompareLog log) { var logger = new CompareLogger2(CompareType.Table, null, log.SubLogs, _ignoreList, () => _hasErrors = true); var databaseTableNames = _databaseModel.Tables.Select(x => x.FormSchemaTableFromDatabase(_databaseModel.DefaultSchema)); var allEntityTableNames = firstStageLogs.SelectMany(p => p.SubLogs) .Where(x => x.State == CompareState.Ok && x.Type == CompareType.Entity) .Select(p => p.Expected).OrderBy(p => p).Distinct().ToList(); var tablesNotUsed = databaseTableNames.Where(p => !allEntityTableNames.Contains(p, _caseComparer)); foreach (var tableName in tablesNotUsed) { logger.ExtraInDatabase(null, CompareAttributes.NotSet, tableName); } }
private void LookForUnusedIndexes(IReadOnlyList <CompareLog> firstStageLogs, CompareLog log) { var logger = new CompareLogger2(CompareType.Index, null, _logs, _ignoreList, () => _hasErrors = true); var tableDict = _databaseModel.Tables.ToDictionary(x => x.FormSchemaTableFromDatabase(_databaseModel.DefaultSchema), _caseComparer); foreach (var entityLog in firstStageLogs.SelectMany(p => p.SubLogs) .Where(x => x.State == CompareState.Ok && x.Type == CompareType.Entity)) { if (tableDict.ContainsKey(entityLog.Expected)) { var indexCol = tableDict[entityLog.Expected].Indexes.Select(x => x.Name); var allEfIndexNames = entityLog.SubLogs .Where(x => x.State == CompareState.Ok && x.Type == CompareType.Index) .Select(p => p.Expected).OrderBy(p => p).Distinct().ToList(); var indexesNotUsed = indexCol.Where(p => !allEfIndexNames.Contains(p, _caseComparer)); foreach (var indexName in indexesNotUsed) { logger.ExtraInDatabase(indexName, CompareAttributes.IndexConstraintName, entityLog.Expected); } } } }
private void CompareColumns(CompareLog log, IEntityType entityType, DatabaseTable table) { var isView = entityType.GetTableName() == null; var primaryKeyDict = table.PrimaryKey?.Columns.ToDictionary(x => x.Name, _caseComparer) ?? new Dictionary <string, DatabaseColumn>(); var efPKeyConstraintName = isView ? NoPrimaryKey : entityType.FindPrimaryKey()?.GetName() ?? NoPrimaryKey; bool pKeyError = false; var pKeyLogger = new CompareLogger2(CompareType.PrimaryKey, efPKeyConstraintName, log.SubLogs, _ignoreList, () => { pKeyError = true; //extra set of pKeyError _hasErrors = true; }); if (!isView) { pKeyLogger.CheckDifferent(efPKeyConstraintName, table.PrimaryKey?.Name ?? NoPrimaryKey, CompareAttributes.ConstraintName, _caseComparison); } var columnDict = table.Columns.ToDictionary(x => x.Name, _caseComparer); // SQL Server only feature. Will not affect other databases var temporalColumnIgnores = table.GetAnnotations() #pragma warning disable EF1001 // Internal EF Core API usage. .Where(a => a.Name == SqlServerAnnotationNames.TemporalPeriodStartPropertyName || a.Name == SqlServerAnnotationNames.TemporalPeriodEndPropertyName) #pragma warning restore EF1001 // Internal EF Core API usage. .Select(a => (string)a.Value) .ToArray(); //This finds all the Owned Types and THP foreach (var property in entityType.GetProperties()) { // Ignore temporal shadow properties (SQL Server) if (property.IsShadowProperty() && temporalColumnIgnores.Contains(property.Name)) { continue; } var colLogger = new CompareLogger2(CompareType.Property, property.Name, log.SubLogs, _ignoreList, () => _hasErrors = true); var columnName = GetColumnNameTakingIntoAccountSchema(property, table, isView); if (columnName == null) { //This catches properties in TPH, split tables, and Owned Types where the properties are not mapped to the current table continue; } if (columnDict.ContainsKey(columnName)) { var reColumn = GetRelationalColumn(columnName, table, isView); var error = ComparePropertyToColumn(reColumn, colLogger, property, columnDict[columnName], isView); //check for primary key if (property.IsPrimaryKey() && //This remove TPH, Owned Types primary key checks !isView != primaryKeyDict.ContainsKey(columnName)) { if (!primaryKeyDict.ContainsKey(columnName)) { pKeyLogger.NotInDatabase(columnName, CompareAttributes.ColumnName); error = true; } else { pKeyLogger.ExtraInDatabase(columnName, CompareAttributes.ColumnName, table.PrimaryKey.Name); } } if (!error) { //There were no errors noted, so we mark it as OK colLogger.MarkAsOk(columnName); } } else { colLogger.NotInDatabase(GetColumnNameTakingIntoAccountSchema(property, table), CompareAttributes.ColumnName); } } if (!pKeyError) { pKeyLogger.MarkAsOk(efPKeyConstraintName); } }
private void CompareColumns(CompareLog log, IEntityType entityType, DatabaseTable table) { var isView = entityType.GetTableName() == null; var primaryKeyDict = table.PrimaryKey?.Columns.ToDictionary(x => x.Name, _caseComparer) ?? new Dictionary <string, DatabaseColumn>(); var efPKeyConstraintName = isView ? NoPrimaryKey : entityType.FindPrimaryKey()?.GetName() ?? NoPrimaryKey; bool pKeyError = false; var pKeyLogger = new CompareLogger2(CompareType.PrimaryKey, efPKeyConstraintName, log.SubLogs, _ignoreList, () => { pKeyError = true; //extra set of pKeyError _hasErrors = true; }); if (!isView) { pKeyLogger.CheckDifferent(efPKeyConstraintName, table.PrimaryKey?.Name ?? NoPrimaryKey, CompareAttributes.ConstraintName, _caseComparison); } var columnDict = table.Columns.ToDictionary(x => x.Name, _caseComparer); //This finds all the Owned Types and THP foreach (var property in entityType.GetProperties()) { var colLogger = new CompareLogger2(CompareType.Property, property.Name, log.SubLogs, _ignoreList, () => _hasErrors = true); var columnName = GetColumnNameTakingIntoAccountSchema(property, table, isView); if (columnName == null) { //This catches properties in TPH, split tables, and Owned Types where the properties are not mapped to the current table continue; } if (columnDict.ContainsKey(columnName)) { var reColumn = GetRelationalColumn(columnName, table, isView); var error = ComparePropertyToColumn(reColumn, colLogger, property, columnDict[columnName], isView); //check for primary key if (property.IsPrimaryKey() && //This remove TPH, Owned Types primary key checks !isView != primaryKeyDict.ContainsKey(columnName)) { if (!primaryKeyDict.ContainsKey(columnName)) { pKeyLogger.NotInDatabase(columnName, CompareAttributes.ColumnName); error = true; } else { pKeyLogger.ExtraInDatabase(columnName, CompareAttributes.ColumnName, table.PrimaryKey.Name); } } if (!error) { //There were no errors noted, so we mark it as OK colLogger.MarkAsOk(columnName); } } else { colLogger.NotInDatabase(GetColumnNameTakingIntoAccountSchema(property, table), CompareAttributes.ColumnName); } } if (!pKeyError) { pKeyLogger.MarkAsOk(efPKeyConstraintName); } }