From 3a9e29db4b6412e9501fbe14979b332ec6e8ccda Mon Sep 17 00:00:00 2001 From: Jon P Smith Date: Fri, 8 Nov 2024 15:59:23 +0000 Subject: [PATCH] Added AlwaysRunStage2 (not tested) --- EfSchemaCompare/CompareEfSql.cs | 5 ++++- EfSchemaCompare/CompareEfSqlConfig.cs | 14 ++++++++------ EfSchemaCompare/EfSchemaCompare.csproj | 11 +++++------ README.md | 25 ++++++++++++++++++------- ReleaseNotes.md | 7 +++++++ Test/UnitTests/TestBookContext.cs | 9 ++++++--- Test/UnitTests/TestExtraInDatabase.cs | 1 + 7 files changed, 49 insertions(+), 23 deletions(-) diff --git a/EfSchemaCompare/CompareEfSql.cs b/EfSchemaCompare/CompareEfSql.cs index 22d51d7..82527a6 100644 --- a/EfSchemaCompare/CompareEfSql.cs +++ b/EfSchemaCompare/CompareEfSql.cs @@ -89,7 +89,10 @@ private bool FinishRestOfCompare(string connectionString, DbContext[] dbContexts hasErrors |= stage1Comparer.CompareModelToDatabase(databaseModel); } - if (hasErrors) return true; + //Normally stage 2 isn't run if there were errors that haven't ignored, + //but if AlwaysRunStage2 is true, then stage 2 will run even there are non-ignored errors + if (!_config.AlwaysRunStage2 && hasErrors) + return true; //No errors, so its worth running the second phase var stage2Comparer = new Stage2Comparer(databaseModel, _config); diff --git a/EfSchemaCompare/CompareEfSqlConfig.cs b/EfSchemaCompare/CompareEfSqlConfig.cs index acb375a..6ea8749 100644 --- a/EfSchemaCompare/CompareEfSqlConfig.cs +++ b/EfSchemaCompare/CompareEfSqlConfig.cs @@ -15,12 +15,14 @@ public class CompareEfSqlConfig { private readonly List _logsToIgnore = new List(); - ///// - ///// Set this to StringComparer.CurrentCultureIgnoreCase to change the - ///// This effects the table, schema, column, primary/index/foreignKey constraint names - ///// - //NOTE Turned off CaseComparer as doesn't work with EF Core 5 - //public StringComparer CaseComparer { get; set; } = StringComparer.CurrentCulture; + /// + /// By default, Stage 2 only runs if: either there were no errors in Stage 1, or if you + /// have suppressed all the errors in the first stage. But Stage 2 can be forced to run by either: + /// 1. Set to true + /// 2. Set to "", which says you want to check all tables + /// in the database against your DbContext(s). + /// + public bool AlwaysRunStage2 { get; set; } = false; /// /// This allows you to ignore tables that your EF Core context doesn't use. There are three settings diff --git a/EfSchemaCompare/EfSchemaCompare.csproj b/EfSchemaCompare/EfSchemaCompare.csproj index 2f8bd3c..1a952a1 100644 --- a/EfSchemaCompare/EfSchemaCompare.csproj +++ b/EfSchemaCompare/EfSchemaCompare.csproj @@ -20,16 +20,15 @@ EfCore.SchemaCompare - 8.1.0 - 8.1.0 + 8.2.0 + 8.2.0 Jon P Smith Useful tool if you are changing the schema of your database's schema outside of EF Core' migrations, say by using SQL change scripts. See readme file on github. false - - New feature: Checks Json Mapping data, see README for more. Limitation that only works with the default column name - - Fixed issue #18. i.e "columns that are extra in db are not excluded" is fixed - - IEntityType is obsolete, moved to ITypeBase - see https://github.com/dotnet/efcore/issues/34594 - - Updated vunables NuGets System.Text.Json and Microsoft.Extensions.Caching.Memory + - The ExtraInDatabase errors didn't correctly say what part of the database (e.g. "Table", "Column") was extra. This is fixed. + - NOTE: If you have ignored some ExtraInDatabase errors you our old ignored ExtraInDatabase might need updating to the new (correct format) pattern. + - New boolean 'AlwaysRunStage2' in the CompareEfSqlConfig. If set to 'true', then Stage 2 will always run, even if there are non-ignored errors. Copyright (c) 2020 Jon P Smith. Licenced under MIT licence Entity Framework Core, Database diff --git a/README.md b/README.md index 773917a..2bea82c 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,9 @@ The EfCore.SchemaCompare library (shortened to EfSchemaCompare in the documentat _Thanks to GitHub @bgrauer-atacom and @lweberprb for suggesting that this library could support extra database providers. See the [issue #26](https://github.com/JonPSmith/EfCore.SchemaCompare/pull/26) to see the code that these two people provided to add this feature._ - Versions below 8 support: - - SqlServer - - Sqlite - - Npgsql.EntityFrameworkCore.PostgreSQL + - SqlServer + - Sqlite + - Npgsql.EntityFrameworkCore.PostgreSQL ## What does EfSchemaCompare check? @@ -91,6 +91,8 @@ The fun part is comparing these two sources, especially with all the different t ![EfSchemaCompare diagram](https://github.com/JonPSmith/EfCore.SchemaCompare/blob/master/EfSchemaCompare.png) +The EfSchemaCompare uses two stages: Stage 1 checks your EF Core DbContext matches your database. Stage 2 checks your database for extra tables, columns, etc. that your EF Core DbContext doesn't use. + ## How to use EfSchemaCompare I usually run the EfSchemaCompare code in my unit tests, but that is up to you. @@ -127,6 +129,7 @@ public void CompareViaContext() - If no connection string is found in the `appsetting.json` file, or there is no `appsetting.json`, then it assumes the string is a connection string. See below for an example of both of of these options: + ```c# [Fact] public void CompareBookThenOrderAgainstBookOrderDatabaseViaAppSettings() @@ -168,7 +171,7 @@ The error above says Here is another error coming from stage 2 where it checks the database side, i.e., Unused Tables, Columns and Indexes ```text -EXTRA IN DATABASE: Table 'MyEntites', column name. Found = MyEntityId +EXTRA IN DATABASE: Table 'HeadEntries', column name. Found = DifferentColumnName ``` This says that there is a column called `MyEntityId` in the table `MyEntites` that hasn't got a property in the entity class mapped to the `MyEntites` table. @@ -179,8 +182,6 @@ This says that there is a column called `MyEntityId` in the table `MyEntites` th In a few cases you will get errors that aren't correct (see limitations) or not relevant. In these cases you might want to suppress those errors. There are two way to do this, with the first being the easiest. Both use the `CompareEfSqlConfig` class. -_NOTE: The stage 2 check only runs if: either there was no errors in the first stage, or if you have suppressed all the errors in the first stage._ - ### Suppress errors via `IgnoreTheseErrors` In this approach you capture the error strings you want to ignore and return them as a string, with each error separated by the newline, `'\n'`, character. You feed the errors via the configuration's `IgnoreTheseErrors` method. See an example below @@ -240,11 +241,14 @@ public void CompareSuppressViaViaAddIgnoreCompareLog() //VERIFY hasErrors.ShouldBeFalse(comparer.GetAllErrors); } - ``` ## Other configuration options +You have already seen the class called `CompareEfSqlConfig` for suppressing errors, but there are two other configrations. + +### `TablesToIgnoreCommaDelimited` string property + You have already seen the class called `CompareEfSqlConfig` for suppressing errors. There is one other configuration property called `TablesToIgnoreCommaDelimited`, which allows you to control what table/views in the database are considered. By default (i.e. when `TablesToIgnoreCommaDelimited` is null) then `CompareEfSql` will only look at the tables/views in the database that your EF Core entity classes are mapped to. This provides an simple starting point. The other options are: @@ -268,3 +272,10 @@ var config = new CompareEfSqlConfig TablesToIgnoreCommaDelimited = "Orders,LineItem" }; var comparer = new CompareEfSql(config); +``` + +### `AlwaysRunStage2` boolean property (v8.2.0 or later) + +Getting all the errors in one go can be useful, for instance when you are creating a EF Core DbContext to match a given database. But by default, Stage 2 isn't run if Stage 1 found errors that haven't been register in `config.IgnoreTheseErrors(... your error strings ...)`. + +In version 8.2.0 a new boolean property called `AlwaysRunStage2` in `CompareEfSqlConfig` and if you you set this to `true` then Stage 2 will always run, even if there are non-ignored errors. See [issue #38](https://github.com/JonPSmith/EfCore.SchemaCompare/issues/38) which made me add this new feature. diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 7512059..2a10eb0 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,5 +1,12 @@ # Release notes +## 8.2.0 + +- The ExtraInDatabase errors didn't correctly say what part of the database (e.g. "Table", "Column") was extra. This is fixed. +- NOTE: If you have ignored some ExtraInDatabase errors you our old ignored ExtraInDatabase might need updating to the new (correct format) pattern. +- New boolean 'AlwaysRunStage2' in the CompareEfSqlConfig. If set to 'true', then Stage 2 will always run, even if there are non-ignored errors. See issue #38, which made me add this new feature. + + ## 8.1.0 - New feature: Checks Json Mapping data, see README for more. Limitation that only works with the default column name diff --git a/Test/UnitTests/TestBookContext.cs b/Test/UnitTests/TestBookContext.cs index a4b003f..c00514e 100644 --- a/Test/UnitTests/TestBookContext.cs +++ b/Test/UnitTests/TestBookContext.cs @@ -72,8 +72,12 @@ public void TestTagsOk() var bTags = books.Select(x => new { x.BookId, tagsNames = string.Join(" | ", x.Tags.Select(y => y.TagId)) - - }); + }).ToArray(); + bTags.Length.ShouldEqual(4); + bTags[0].tagsNames.ShouldEqual("Editor's Choice | Refactoring"); + bTags[1].tagsNames.ShouldEqual("Architecture"); + bTags[2].tagsNames.ShouldEqual("Architecture | Editor's Choice"); + bTags[3].tagsNames.ShouldEqual("Quantum Entanglement"); } [Fact] @@ -112,7 +116,6 @@ public void TestDatabaseTablesOk() var options = this.CreateUniqueClassOptions(); using var context = new BookContext(options); context.Database.EnsureClean(); - context.Database.EnsureCreated(); //ATTEMPT var factory = context.GetDatabaseModelFactory(); diff --git a/Test/UnitTests/TestExtraInDatabase.cs b/Test/UnitTests/TestExtraInDatabase.cs index f5d4fe7..09afeea 100644 --- a/Test/UnitTests/TestExtraInDatabase.cs +++ b/Test/UnitTests/TestExtraInDatabase.cs @@ -24,6 +24,7 @@ public TestExtraInDatabase(ITestOutputHelper output) [Fact] public void DecodeCompareTextToCompareLog_ExtraIndexInDatabase_Test() { + //See issue #39 var str = "EXTRA IN DATABASE: Index 'tenants', index constraint name. Found = ix_tenants_belongs_to_database_instance_id"; var compareLog = FindErrorsToIgnore.DecodeCompareTextToCompareLog(str); compareLog.Type.ShouldEqual(CompareType.Index);