private void LookForUnusedColumns(IReadOnlyList <CompareLog> firstStageLogs, CompareLog log) { var logger = new CompareLogger(CompareType.Column, null, _logs, _ignoreList, () => _hasErrors = true); var tableDict = _databaseModel.Tables.ToDictionary(x => x.FormSchemaTable(_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); } } } }
public bool CompareModelToDatabase(DatabaseModel databaseModel) { var dbLogger = new CompareLogger(CompareType.DbContext, _dbContextName, _logs, _ignoreList, () => _hasErrors = true); //Check things about the database, such as sequences dbLogger.MarkAsOk(_dbContextName); CheckDatabaseOk(_logs.Last(), _model.Relational(), databaseModel); var tableDict = databaseModel.Tables.ToDictionary(x => x.Name); foreach (var entityType in _model.GetEntityTypes()) { var eRel = entityType.Relational(); var logger = new CompareLogger(CompareType.Entity, entityType.ClrType.Name, _logs.Last().SubLogs, _ignoreList, () => _hasErrors = true); if (tableDict.ContainsKey(eRel.TableName)) { var databaseTable = tableDict[eRel.TableName]; //Checks for table matching var log = logger.MarkAsOk(eRel.TableName); logger.CheckDifferent(entityType.FindPrimaryKey().Relational().Name, databaseTable.PrimaryKey.Name, CompareAttributes.ConstraintName); CompareColumns(log, entityType, databaseTable); CompareForeignKeys(log, entityType, databaseTable); CompareIndexes(log, entityType, databaseTable); } else { logger.NotInDatabase(entityType.Relational().FormSchemaTable(), CompareAttributes.TableName); } } return(_hasErrors); }
private void CompareColumns(CompareLog log, IEntityType entityType, DatabaseTable table) { var columnDict = table.Columns.ToDictionary(x => x.Name, _caseComparer); var primaryKeyDict = table.PrimaryKey?.Columns.ToDictionary(x => x.Name, _caseComparer) ?? new Dictionary <string, DatabaseColumn>(); var efPKeyConstraintName = entityType.FindPrimaryKey().Relational().Name; bool pKeyError = false; var pKeyLogger = new CompareLogger(CompareType.PrimaryKey, efPKeyConstraintName, log.SubLogs, _ignoreList, () => { pKeyError = true; //extra set of pKeyError _hasErrors = true; }); pKeyLogger.CheckDifferent(efPKeyConstraintName, table.PrimaryKey?.Name ?? NoPrimaryKey, CompareAttributes.ConstraintName, _caseComparison); foreach (var property in entityType.GetProperties()) { var pRel = property.Relational(); var colLogger = new CompareLogger(CompareType.Property, property.Name, log.SubLogs, _ignoreList, () => _hasErrors = true); if (columnDict.ContainsKey(pRel.ColumnName)) { if (!IgnorePrimaryKeyFoundInOwnedTypes(entityType.DefiningEntityType, table, property, entityType.FindPrimaryKey())) { var error = ComparePropertyToColumn(colLogger, property, columnDict[pRel.ColumnName]); //check for primary key if (property.IsPrimaryKey() != primaryKeyDict.ContainsKey(pRel.ColumnName)) { if (!primaryKeyDict.ContainsKey(pRel.ColumnName)) { pKeyLogger.NotInDatabase(pRel.ColumnName, CompareAttributes.ColumnName); error = true; } else { pKeyLogger.ExtraInDatabase(pRel.ColumnName, CompareAttributes.ColumnName, table.PrimaryKey.Name); } } if (!error) { //There were no errors noted, so we mark it as OK colLogger.MarkAsOk(pRel.ColumnName); } } } else { colLogger.NotInDatabase(pRel.ColumnName, CompareAttributes.ColumnName); } } if (!pKeyError) { pKeyLogger.MarkAsOk(efPKeyConstraintName); } }
public bool CompareLogsToDatabase(IReadOnlyList <CompareLog> firstStageLogs) { var logger = new CompareLogger(CompareType.Database, _databaseModel.DatabaseName, _logs, _ignoreList, () => _hasErrors = true); logger.MarkAsOk(null); LookForUnusedTables(firstStageLogs, _logs.Last()); LookForUnusedColumns(firstStageLogs, _logs.Last()); LookForUnusedIndexes(firstStageLogs, _logs.Last()); return(_hasErrors); }
private bool ComparePropertyToColumn(CompareLogger logger, IProperty property, DatabaseColumn column) { var pRel = property.Relational(); var error = logger.CheckDifferent(pRel.ColumnType, column.StoreType, CompareAttributes.ColumnType); error |= logger.CheckDifferent(property.IsNullable.NullableAsString(), column.IsNullable.NullableAsString(), CompareAttributes.Nullability); error |= logger.CheckDifferent((pRel.DefaultValueSql ?? pRel.DefaultValue?.ToString()).RemoveUnnecessaryBrackets(), column.DefaultValueSql.RemoveUnnecessaryBrackets(), CompareAttributes.DefaultValueSql); error |= logger.CheckDifferent(pRel.ComputedColumnSql.RemoveUnnecessaryBrackets(), column.ComputedColumnSql.RemoveUnnecessaryBrackets(), CompareAttributes.ComputedColumnSql); error |= CheckValueGenerated(logger, property, column); return(error); }
private bool ComparePropertyToColumn(CompareLogger logger, IProperty property, DatabaseColumn column) { var error = logger.CheckDifferent(property.GetColumnType(), column.StoreType, CompareAttributes.ColumnType, _caseComparison); error |= logger.CheckDifferent(property.IsNullable.NullableAsString(), column.IsNullable.NullableAsString(), CompareAttributes.Nullability, _caseComparison); error |= logger.CheckDifferent(property.GetComputedColumnSql().RemoveUnnecessaryBrackets(), column.ComputedColumnSql.RemoveUnnecessaryBrackets(), CompareAttributes.ComputedColumnSql, _caseComparison); var defaultValue = property.GetDefaultValueSql() ?? property.GetDefaultValue()?.ToString(); error |= logger.CheckDifferent(defaultValue.RemoveUnnecessaryBrackets(), column.DefaultValueSql.RemoveUnnecessaryBrackets(), CompareAttributes.DefaultValueSql, _caseComparison); error |= CheckValueGenerated(logger, property, column); return(error); }
private void LookForUnusedTables(IReadOnlyList <CompareLog> firstStageLogs, CompareLog log) { var logger = new CompareLogger(CompareType.Table, null, log.SubLogs, _ignoreList, () => _hasErrors = true); var databaseTableNames = _databaseModel.Tables.Select(x => x.FormSchemaTable(_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 bool CheckValueGenerated(CompareLogger logger, IProperty property, DatabaseColumn column) { var colValGen = column.ValueGenerated.ConvertNullableValueGenerated(column.DefaultValueSql); if (colValGen == ValueGenerated.Never.ToString() //There is a case where the property is part of the primary key and the key is not set in the database && property.ValueGenerated == ValueGenerated.OnAdd && property.IsKey() //We assume that a integer of some form should be provided by the database && !IntegerTypes.Contains(property.ClrType)) { return(false); } return(logger.CheckDifferent(property.ValueGenerated.ToString(), colValGen, CompareAttributes.ValueGenerated)); }
private void CompareForeignKeys(CompareLog log, IEntityType entityType, DatabaseTable table) { var fKeyDict = table.ForeignKeys.ToDictionary(x => x.Name); foreach (var entityFKey in entityType.GetForeignKeys()) { var entityFKeyprops = entityFKey.Properties; var constraintName = entityFKey.Relational().Name; var logger = new CompareLogger(CompareType.ForeignKey, constraintName, log.SubLogs, _ignoreList, () => _hasErrors = true); if (IgnoreForeignKeyIfInSameTable(entityType, entityFKey, table)) { continue; } if (fKeyDict.ContainsKey(constraintName)) { //Now check every foreign key var error = false; var thisKeyCols = fKeyDict[constraintName].Columns.ToDictionary(x => x.Name); foreach (var fKeyProp in entityFKeyprops) { var pRel = fKeyProp.Relational(); if (!thisKeyCols.ContainsKey(pRel.ColumnName)) { logger.NotInDatabase(pRel.ColumnName); error = true; } } error |= logger.CheckDifferent(entityFKey.DeleteBehavior.ToString(), fKeyDict[constraintName].OnDelete.ConvertReferentialActionToDeleteBehavior(entityFKey.DeleteBehavior), CompareAttributes.DeleteBehaviour); if (!error) { logger.MarkAsOk(constraintName); } } else { logger.NotInDatabase(constraintName, CompareAttributes.ConstraintName); } } }
private void LookForUnusedIndexes(IReadOnlyList <CompareLog> firstStageLogs, CompareLog log) { var logger = new CompareLogger(CompareType.Index, null, _logs, _ignoreList, () => _hasErrors = true); var tableDict = _databaseModel.Tables.ToDictionary(x => x.FormSchemaTable(_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); } } } }
public bool CompareModelToDatabase(DatabaseModel databaseModel) { var dbLogger = new CompareLogger(CompareType.DbContext, _dbContextName, _logs, _ignoreList, () => _hasErrors = true); //Check things about the database, such as sequences dbLogger.MarkAsOk(_dbContextName); CheckDatabaseOk(_logs.Last(), _model.Relational(), databaseModel); var tableDict = databaseModel.Tables.ToDictionary(x => x.FormSchemaTable(databaseModel.DefaultSchema), _caseComparer); var dbQueries = _model.GetEntityTypes().Where(x => x.IsQueryType).ToList(); if (dbQueries.Any()) { dbLogger.Warning("EfSchemaCompare does not check DbQuery types", null, string.Join(", ", dbQueries.Select(x => x.ClrType.Name))); } foreach (var entityType in _model.GetEntityTypes().Where(x => !x.IsQueryType)) { var eRel = entityType.Relational(); var logger = new CompareLogger(CompareType.Entity, entityType.ClrType.Name, _logs.Last().SubLogs, _ignoreList, () => _hasErrors = true); if (tableDict.ContainsKey(eRel.FormSchemaTable())) { var databaseTable = tableDict[eRel.FormSchemaTable()]; //Checks for table matching var log = logger.MarkAsOk(eRel.FormSchemaTable()); logger.CheckDifferent(entityType.FindPrimaryKey()?.Relational().Name ?? NoPrimaryKey, databaseTable.PrimaryKey?.Name ?? NoPrimaryKey, CompareAttributes.ConstraintName, _caseComparison); CompareColumns(log, entityType, databaseTable); CompareForeignKeys(log, entityType, databaseTable); CompareIndexes(log, entityType, databaseTable); } else { logger.NotInDatabase(entityType.Relational().FormSchemaTable(), CompareAttributes.TableName); } } return(_hasErrors); }
private void CompareIndexes(CompareLog log, IEntityType entityType, DatabaseTable table) { var indexDict = table.Indexes.ToDictionary(x => x.Name); foreach (var entityIdx in entityType.GetIndexes()) { var entityIdxprops = entityIdx.Properties; var logger = new CompareLogger(CompareType.Index, entityIdxprops.CombinedColNames(), log.SubLogs, _ignoreList, () => _hasErrors = true); var constraintName = entityIdx.Relational().Name; if (indexDict.ContainsKey(constraintName)) { //Now check every column in an index var error = false; var thisIdxCols = indexDict[constraintName].Columns.ToDictionary(x => x.Name); foreach (var idxProp in entityIdxprops) { var pRel = idxProp.Relational(); if (!thisIdxCols.ContainsKey(pRel.ColumnName)) { logger.NotInDatabase(pRel.ColumnName); error = true; } } error |= logger.CheckDifferent(entityIdx.IsUnique.ToString(), indexDict[constraintName].IsUnique.ToString(), CompareAttributes.Unique); if (!error) { logger.MarkAsOk(constraintName); } } else { logger.NotInDatabase(constraintName, CompareAttributes.ConstraintName); } } }