Skip to content

Commit

Permalink
Version 7.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
JonPSmith committed Nov 17, 2022
1 parent 47faf44 commit f7ae775
Show file tree
Hide file tree
Showing 16 changed files with 130 additions and 302 deletions.
52 changes: 6 additions & 46 deletions EfSchemaCompare/CompareEfSql.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,22 +60,6 @@ public bool CompareEfWithDb(params DbContext[] dbContexts)
return CompareEfWithDb(dbContexts[0].Database.GetDbConnection().ConnectionString, dbContexts);
}

/// <summary>
/// This will compare one or more DbContext against database pointed to the first DbContext
/// using the DesignTimeServices type for T.
/// </summary>
/// <typeparam name="T">Must be the design time provider for the database provider you want to use, e.g. MySqlDesignTimeServices</typeparam>
/// <param name="dbContexts">One or more dbContext instances to be compared with the database</param>
/// <returns>true if any errors found, otherwise false</returns>
public bool CompareEfWithDb<T>(params DbContext[] dbContexts) where T : IDesignTimeServices, new()
{
if (dbContexts == null) throw new ArgumentNullException(nameof(dbContexts));
if (dbContexts.Length == 0)
throw new ArgumentException("You must provide at least one DbContext instance.", nameof(dbContexts));
var designTimeService = new T();
return FinishRestOfCompare(dbContexts[0].Database.GetDbConnection().ConnectionString, dbContexts, designTimeService);
}

/// <summary>
/// This will compare one or more DbContext against database pointed to by the connectionString.
/// </summary>
Expand All @@ -90,38 +74,15 @@ public bool CompareEfWithDb(string configOrConnectionString, params DbContext[]
if (dbContexts.Length == 0)
throw new ArgumentException("You must provide at least one DbContext instance.", nameof(dbContexts));

var designTimeService = dbContexts[0].GetDesignTimeService();
return FinishRestOfCompare(GetConfigurationOrActualString(configOrConnectionString), dbContexts, designTimeService);
}


/// <summary>
/// This will compare one or more DbContext against database pointed to by the connectionString
/// using the DesignTimeServices type for T
/// </summary>
/// <typeparam name="T">Must be the design time provider for the database provider you want to use, e.g. MySqlDesignTimeServices</typeparam>
/// <param name="configOrConnectionString">This should either be a
/// connection string or the name of a connection string in the appsetting.json file.
/// </param>
/// <param name="dbContexts">One or more dbContext instances to be compared with the database</param>
/// <returns>true if any errors found, otherwise false</returns>
public bool CompareEfWithDb<T>(string configOrConnectionString, params DbContext[] dbContexts) where T: IDesignTimeServices, new()
{
if (configOrConnectionString == null) throw new ArgumentNullException(nameof(configOrConnectionString));
if (dbContexts == null) throw new ArgumentNullException(nameof(dbContexts));
if (dbContexts.Length == 0)
throw new ArgumentException("You must provide at least one DbContext instance.", nameof(dbContexts));

var designTimeService = new T();
return FinishRestOfCompare(GetConfigurationOrActualString(configOrConnectionString), dbContexts, designTimeService);
return FinishRestOfCompare(GetConfigurationOrActualString(configOrConnectionString), dbContexts);
}

//------------------------------------------------------
//private methods

private bool FinishRestOfCompare(string connectionString, DbContext[] dbContexts, IDesignTimeServices designTimeService)
private bool FinishRestOfCompare(string connectionString, DbContext[] dbContexts)
{
var databaseModel = GetDatabaseModelViaScaffolder(dbContexts, connectionString, designTimeService);
var databaseModel = GetDatabaseModelViaScaffolder(dbContexts, connectionString);
bool hasErrors = false;
foreach (var context in dbContexts)
{
Expand All @@ -138,12 +99,11 @@ private bool FinishRestOfCompare(string connectionString, DbContext[] dbContexts
return hasErrors;
}

private DatabaseModel GetDatabaseModelViaScaffolder(DbContext[] contexts, string connectionString, IDesignTimeServices designTimeService)
private DatabaseModel GetDatabaseModelViaScaffolder(DbContext[] contexts, string connectionString)
{
var serviceProvider = designTimeService.GetDesignTimeProvider();
var factory = (IDatabaseModelFactory) serviceProvider.GetService(typeof(IDatabaseModelFactory));
var databaseFactory = contexts[0].GetDatabaseModelFactory();

var databaseModel = factory.Create(connectionString,
var databaseModel = databaseFactory.Create(connectionString,
new DatabaseModelFactoryOptions(new string[] { }, new string[] { }));
RemoveAnyTableToIgnore(databaseModel, contexts);
return databaseModel;
Expand Down
1 change: 1 addition & 0 deletions EfSchemaCompare/EfSchemaCompare.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.0" />
</ItemGroup>

<PropertyGroup>
Expand Down
42 changes: 42 additions & 0 deletions EfSchemaCompare/Internal/DatabaseModelFinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) 2022 Jon P Smith, GitHub: JonPSmith, web: http://www.thereformedprogrammer.net/
// Licensed under MIT license. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore;
using System;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Scaffolding;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Sqlite.Scaffolding.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Scaffolding.Internal;
using Npgsql.EntityFrameworkCore.PostgreSQL.Scaffolding.Internal;

namespace EfSchemaCompare.Internal;

internal static class DatabaseModelFinder
{
private const string SqlServerProviderName = "Microsoft.EntityFrameworkCore.SqlServer";
private const string SqliteProviderName = "Microsoft.EntityFrameworkCore.Sqlite";
private const string PostgresSqlProviderName = "Npgsql.EntityFrameworkCore.PostgreSQL";

public static IDatabaseModelFactory GetDatabaseModelFactory(this DbContext context)
{

var providerName = context.Database.ProviderName;

var logger = context.GetService<IDiagnosticsLogger<DbLoggerCategory.Scaffolding>>();

if (providerName == SqlServerProviderName)
return new SqlServerDatabaseModelFactory(logger);
if (providerName == SqliteProviderName)
{
var typeMapper = context.GetService<IRelationalTypeMappingSource>();
return new SqliteDatabaseModelFactory(logger, typeMapper);
}
if (providerName == PostgresSqlProviderName)
return new NpgsqlDatabaseModelFactory(logger);

throw new InvalidOperationException("Your database provider isn't supported by the EfCore.SchemaCompare library. " +
"Please provide an issue about the database type you would like and I may be able to add it.");
}
}
74 changes: 0 additions & 74 deletions EfSchemaCompare/Internal/DesignProvider.cs

This file was deleted.

29 changes: 4 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The EfCore.SchemaCompare library (shortened to EfSchemaCompare in the documentat
2. [List of limitations](#List-of-limitations)
3. [Introduction to how EfSchemaCompare works](#Introduction-to-how-EfSchemaCompare-works)
4. [How to use EfSchemaCompare](#How-to-use-EfSchemaCompare)
5. [Different parameters to the `CompareEfWithDb` method](#different-parameters-to-the-compareefwithdb-method)
5. [Understanding the error messages](#Understanding-the-error-messages)
6. [How to suppress certain error messages](#How-to-suppress-certain-error-messages)
7. [Other configuration options](#Other-configuration-options)
Expand Down Expand Up @@ -54,8 +55,9 @@ The EfCore.SchemaCompare library (shortened to EfSchemaCompare in the documentat
## List of limitations

- The EF Core's scaffolder doesn't read in any index on the foreign key (the scaffolder assumes EF Core will do that by default). That means I can't check that there is an index on a foreign key.
- Cannot correctly check Table-per-Type classes beacause EF Core doesn't currently hold that data. This is tracked by [Ef Core #19811](https://github.com/dotnet/efcore/issues/19811).
- Cannot compare database tables/columns using InvariantCultureIgnoreCase. That is a EF Core 5 limitation.
- Cannot correctly check Table-per-Type or Table-per-Class classes because EF Core doesn't currently hold that data. This is tracked by [Ef Core #19811](https://github.com/dotnet/efcore/issues/19811).
- Cannot compare database tables/columns using InvariantCultureIgnoreCase. That is a EF Core 5+ limitation.
- At the moment this library only supports SQL Server, Sqlite and PostgresSql.

The following are things I haven't bothered to check.

Expand Down Expand Up @@ -133,29 +135,6 @@ public void CompareBookThenOrderAgainstBookOrderDatabaseViaAppSettings()
}
```

### Using with database provider not installed in `EfCore.SchemaCompare` library

The `EfCore.SchemaCompare` library only contains the SqlServer and Sqlite database providers. But if you want to run the compare with a specific database provider you can do that using the version with takes in a `IDesignTimeServices` for your database provider which contains the reverse engineering feature. For instance if you wanted to check a PostgreSql database you would use the following code shown below.

```c#
[Fact]
public void TestCompareEfSqlPostgreSql()
{
//SETUP
using (var context = new BookContext(_options))
{
var comparer = new CompareEfSql();

//ATTEMPT
//This will use the database provider design time type you gave to get the database information
var hasErrors = comparer.CompareEfWithDb<NpgsqlDesignTimeServices>(context));

//VERIFY
hasErrors.ShouldBeFalse(comparer.GetAllErrors);
}
}
```

## Understanding the error messages

The `comparer.GetAllErrors` property will return a string with each error separated by the `Environment.NewLine` string. Below is an example of an error
Expand Down
4 changes: 4 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Release notes

## 7.0.0

- Updated to EF Core 7.0

## 6.0.0

- Updated to EF Core 6.0 (thanks to Wade Baglin, GitHub @pleb)
Expand Down
35 changes: 0 additions & 35 deletions Test/UnitTests/ComparerBooksExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,41 +92,6 @@ public void CompareSuppressViaViaAddIgnoreCompareLog()
hasErrors.ShouldBeFalse(comparer.GetAllErrors);
}

[Fact]
public void CompareViaType()
{
//SETUP
var options = this.CreateUniqueClassOptions<BookContext>();
using var context = new BookContext(options);
context.Database.EnsureClean();

var comparer = new CompareEfSql();

//ATTEMPT
var hasErrors = comparer.CompareEfWithDb<SqlServerDesignTimeServices>(context);

//VERIFY
hasErrors.ShouldBeFalse(comparer.GetAllErrors);
}

[Fact]
public void CompareViaTypeWithConnection()
{
//SETUP
var options = this.CreateUniqueClassOptions<BookContext>();
using var context = new BookContext(options);
context.Database.EnsureClean();

var comparer = new CompareEfSql();

//ATTEMPT
var hasErrors =
comparer.CompareEfWithDb<SqlServerDesignTimeServices>(context.Database.GetConnectionString(), context);

//VERIFY
hasErrors.ShouldBeFalse(comparer.GetAllErrors);
}

[Fact]
public void CompareBadConnection()
{
Expand Down
4 changes: 1 addition & 3 deletions Test/UnitTests/OldTestSupportIssueTests/Issue012Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ public void TestTwoTablesWithSameNameButDifferentSchemas()
//SETUP
using (var context = new Issue012DbContext(_options))
{
var dtService = context.GetDesignTimeService();
var serviceProvider = dtService.GetDesignTimeProvider();
var factory = serviceProvider.GetService<IDatabaseModelFactory>();
var factory = context.GetDatabaseModelFactory();
var database = factory.Create(_connectionString,
new DatabaseModelFactoryOptions(new string[] { }, new string[] { }));

Expand Down
4 changes: 1 addition & 3 deletions Test/UnitTests/Stage1ComparerBooksDiff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,7 @@ public void CompareSelfTestEfCoreContext()
//SETUP
using (var context = new BookContext(_options))
{
var dtService = context.GetDesignTimeService();
var serviceProvider = dtService.GetDesignTimeProvider();
var factory = serviceProvider.GetService<IDatabaseModelFactory>();
var factory = context.GetDatabaseModelFactory();
var database = factory.Create(_connectionString,
new DatabaseModelFactoryOptions(new string[] { }, new string[] { }));

Expand Down
4 changes: 2 additions & 2 deletions Test/UnitTests/Stage1ComparerMyEntityDiff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ public Stage1ComparerMyEntityDiff(ITestOutputHelper output)
{
_output = output;

var serviceProvider = new SqlServerDesignTimeServices().GetDesignTimeProvider();
var factory = serviceProvider.GetService<IDatabaseModelFactory>();


var options = this.CreateUniqueClassOptions<MyEntityDbContext>(
builder => builder.ReplaceService<IModelCacheKeyFactory, MyEntityModelCacheKeyFactory>());
using (var context = new MyEntityDbContext(options, MyEntityDbContext.Configs.NormalTable))
{
context.Database.EnsureClean();
var factory = context.GetDatabaseModelFactory();
var connectionString = context.Database.GetDbConnection().ConnectionString;
databaseModel = factory.Create(connectionString,
new DatabaseModelFactoryOptions(new string[] { }, new string[] { }));
Expand Down
8 changes: 2 additions & 6 deletions Test/UnitTests/Stage2ComparerMyEntityDiff.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,12 @@ public Stage2ComparerMyEntityDiff(ITestOutputHelper output)
_output = output;
var options = this
.CreateUniqueClassOptions<MyEntityDbContext>();
var serviceProvider = new SqlServerDesignTimeServices().GetDesignTimeProvider();
var factory = serviceProvider.GetService<IDatabaseModelFactory>();

using (var context = new MyEntityDbContext(options, MyEntityDbContext.Configs.NormalTable))
{
var connectionString = context.Database.GetDbConnection().ConnectionString;
context.Database.EnsureClean();

var factory = context.GetDatabaseModelFactory();
_databaseModel = factory.Create(connectionString,
new DatabaseModelFactoryOptions(new string[] { }, new string[] { }));
}
Expand Down Expand Up @@ -110,9 +108,7 @@ public void ExtraIndexConstaint()
builder => builder.ReplaceService<IModelCacheKeyFactory, MyEntityModelCacheKeyFactory>());
using (var context = new MyEntityDbContext(options, MyEntityDbContext.Configs.HasIndex))
{
var dtService = context.GetDesignTimeService();
var serviceProvider = dtService.GetDesignTimeProvider();
var factory = serviceProvider.GetService<IDatabaseModelFactory>();
var factory = context.GetDatabaseModelFactory();
var connectionString = context.Database.GetDbConnection().ConnectionString;

context.Database.EnsureClean();
Expand Down
Loading

0 comments on commit f7ae775

Please sign in to comment.