Git Product home page Git Product logo

efcore.testsupport's People

Contributors

chrisbbe avatar dependabot[bot] avatar jonpsmith avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

efcore.testsupport's Issues

EfSchemaCompare: Would like to to handle the nullability of the inherited classes

If you have a THP class in your model, then EfSchemaCompare will show an error on non-nullable properties in the non-base entities. That's because EF Core makes the columns nullable because those properties may not be used.

I'm not sure if I can catch that and stop these errors, but its worth having a look. Not urgent, but nice to have.

Suggestion: Extend EfCoreLogDecoder to decode CommandError as well as CommandExecuted

A small suggestion for this handy little feature.

if (log.EventId.Name != RelationalEventId.CommandError.Name && log.EventId.Name != RelationalEventId.CommandExecuted.Name)

My use case was encountering a foreign key constraint error from sqlite when constructing a test database to run a unit test against - of course sqlite being unable to tell me which constraint I was violating. With a copy of the class with that small change I was able to pull the sql being executed and find what I was doing wrong. Maybe helpful in other situations too?

Only Supports Sqlite and SQL Server

Can the readme and wiki be updated to have obvious upfront statements about supported databases somewhere?

I completely failed to check supported providers. I was totally excited to use this until I got the lovely "This is not a database provider that we currently support." ::SAD_FACE::

Is the level of effort required to support other providers such as Postgres too steep?

Comparison always fails for default values on BIT NOT NULL (boolean) fields

Tested using EfCore.TestSupport nuget package 3.1.0

Consider a table

CREATE TABLE [dbo].[Foos]
(
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[IsFoosable] [bit] NOT NULL DEFAULT 1,
	CONSTRAINT [PK_Foos] PRIMARY KEY CLUSTERED (Id)
)

Consider an entity

public class Foo
{
    [Key]
    public int Id { get; set; }

    public bool IsFoosable { get; set; }
}

Consider a context

public class FooContext : DbContext
{
    public DbSet<Foo> Foos { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Entity<Foo>()
            .Property(x => x.IsFoosable)
            .HasDefaultValue(true);

    }
}

Then the test

            //--------- Arrange ---------//
            string connectionString = ...;

            var config = new CompareEfSqlConfig();

            var comparer = new CompareEfSql(config);

            using (var fooContext = CreateFooContext())
            {
                //--------- Act ---------//
                var hasErrors = comparer.CompareEfWithDb(connectionString, fooContext);

                //--------- Assert ---------//
                Assert.False(hasErrors, comparer.GetAllErrors);
            }

Will fail with:

Message: 
    DIFFERENT: Foo->Property 'IsFoosable', default value sql. Expected = True, found = 1

If the context instead does:

   ...
        modelBuilder.Entity<Foo>()
            .Property(x => x.IsFoosable)
            .HasDefaultValue(1);
   ...

The failure is still

Message: 
    DIFFERENT: Foo->Property 'IsFoosable', default value sql. Expected = True, found = 1

So there appears to be no way to configure a default value for boolean / bit not null that EfCore.TestSupport will consider to be comparable.

Support EF Core 5

CompareEfWithDb method throws the following exception on EF Core 5 Preview 7:
System.InvalidOperationException : Unable to resolve service for type 'Microsoft.EntityFrameworkCore.Diagnostics.IDbContextLogger' while attempting to activate 'Microsoft.EntityFrameworkCore.Internal.DiagnosticsLogger`1[Microsoft.EntityFrameworkCore.DbLoggerCategory+Scaffolding]'.
Exception doesn't have a stacktrace

Ability to compare only those tables that exist in the EF Context.

I know that TablesToIgnoreCommaDelimited already exists, but I could not undertand how I had to use it properly. My contexts contains only a small amount of table. Should I have enumerate all tables from the DB, that do not exist in the context?
So It would be greate to have such option to declare only required tables.
I've impemented the following solution:
Added new config properties:

        public bool CompareOnlyExistingTables { get; set; }
        internal string TablesToCompareCommaDelimited { get; set; }

Updated 2 methods:

private DatabaseModel GetDatabaseModelViaScaffolder(DbContext context, string configOrConnectionString, IDesignTimeServices designTimeService)
        {
            var serviceProvider = designTimeService.GetDesignTimeProvider();
            var factory = serviceProvider.GetService<IDatabaseModelFactory>();
            var connectionString = configOrConnectionString == null
                ? context.Database.GetDbConnection().ConnectionString
                : GetConfigurationOrActualString(configOrConnectionString);
            //FIXED
            _config.TablesToCompareCommaDelimited = _config.CompareOnlyExistingTables ? string.Join(",", context.Model.GetEntityTypes()
                                                                                                         .Select(r => string.IsNullOrEmpty(r.Relational().Schema) ? r.Relational().TableName :                                                                                                         $"{r.Relational().Schema}.{r.Relational().TableName}")) : string.Empty;
            var databaseModel = factory.Create(connectionString, new string[] { }, new string[] { });
            RemoveAnyTabletoIgnore(databaseModel);
            return databaseModel;
        }

        private void RemoveAnyTabletoIgnore(DatabaseModel databaseModel)
        {
            //FIXED
            List<DatabaseTable> CreateTableList(string list)
            {
                var tableList = new List<DatabaseTable>();
                foreach (var tableToIgnore in list.Split(',')
                    .Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)))
                {
                    var split = tableToIgnore.Split('.').Select(x => x.Trim()).ToArray();
                    var schema = split.Length == 1 ? databaseModel.DefaultSchema : split[0];
                    var tableName = split.Length == 1 ? split[0] : split[1];
                    var tableToRemove = databaseModel.Tables
                        .SingleOrDefault(x => x.Schema.Equals(schema, StringComparison.InvariantCultureIgnoreCase)
                                           && x.Name.Equals(tableName, StringComparison.InvariantCultureIgnoreCase));
                    if (tableToRemove == null)
                        throw new InvalidOperationException(
                            $"The TablesToIgnoreCommaDelimited config property contains a table name of '{tableToIgnore}', which was not found in the database");
                    tableList.Add(tableToRemove);
                }
                return tableList;
            }
            if (_config.TablesToIgnoreCommaDelimited != null)
            {
                foreach (var tableToRemove in CreateTableList(_config.TablesToIgnoreCommaDelimited))
                {
                    databaseModel.Tables.Remove(tableToRemove);
                }
            }
            if (!string.IsNullOrEmpty(_config.TablesToCompareCommaDelimited))
            {
                var tablesToRemove = databaseModel.Tables.Where(r => !CreateTableList(_config.TablesToCompareCommaDelimited).Contains(r)).ToList();
                foreach (var tableToRemove in tablesToRemove)
                {
                    databaseModel.Tables.Remove(tableToRemove);
                }
            }
        }

Missing logic for verifying DateTime columns

I got this error in a call to CompareEfWithDb:
DIFFERENT: CommandGroup->Property 'CreateDate', default value sql. Expected = , found = '0001-01-01T00:00:00.0000000'\nDIFFERENT: CommandGroup->Property 'CreateDate', value generated. Expected = Never, found = OnAdd

I may not configure it correctly, but this CreateDate field is a standard DateTime field, with default configuration of EF Core 2.2:
public DateTime CreateDate { get; set; }

In SQL Server:
[CreateDate] datetime2 NOT NULL

Support EF Core 3.1 for .NET Standard 2.0

EF Core 3.1 works on .NET Standard 2.0 (i.e. you can use it along with the .NET Framework).

The library doesn't with .NET Framework 4.7.2 and EF Core 3.1, because the project references EF Core 2.0 for .NET Standard 2.0, although it can use 3.0.

Migration from .NET Framework to .NET Core 5.0 is going to be a long one and it would be good to support framework for some time.

When I tried it, it failed because of different factory methods in 2.0 and 3.1

Did not find '\bin\' in the assembly path C:\Users\username\.nuget\packages\efcore.testsupport\...

Hi,

Firstly thank you very much for this useful lib, it'll help us a lot.

I'm facing an issue when calling creating seed data with Assembly.GetCallingAssembly()).Location, when referencing the project from NuGet.org the Assembly.GetCallingAssembly()).Location points to C:\Users\username\.nuget\packages\efcore.testsupport\2.0.0\lib\netstandard2.0\TestSupport.dll and TestSupport was unable to find \bin\ part in it and threw an exception.

Everything works fine when I reference directly your project in my solution.

My setup

  • Visual Studio 2019 Pro 16.1.6
  • Microsoft.NET.Test.Sdk 16.2.0
  • MSTest.TestAdapter 1.4.0
  • MSTest.TestFramework 1.4.0
  • EfCore.TestSupport 2.0.0
  • Windows 10 1803

The issue is present with built-in Visual Studio's test runner and with dotnet test, didn't tested with others.

To Reproduce

  1. Create new MSTest project
  2. Reference a project with a valid EFCore DbContext
  3. Create an extract test method as described in Wiki (https://github.com/JonPSmith/EfCore.TestSupport/wiki/Seed-from-Production---Extract-stage)
  4. Run the test

Expected behavior

A seed JSON file is created in current test's folder

Current behavior

An exception is thrown.

System.Exception: Did not find '\bin' in the assembly. Do you need to provide the callingAssembly parameter?
Stack Trace:
at TestSupport.Helpers.TestData.GetCallingAssemblyTopLevelDir(Assembly callingAssembly)
at TestSupport.Helpers.TestData.GetTestDataDir(String alternateTestDir, Assembly callingAssembly)
at TestSupport.SeedDatabase.SeedJsonHelpers.FormJsonFilePath(String fileSuffix)
at TestSupport.SeedDatabase.SeedJsonHelpers.WriteJsonToJsonFile(String fileSuffix, String json)
at Trianon.HR.Data.EFCore.Tests.FilterTests.ExampleExtract() in C:\ieu\development\HR.Data\tests\Trianon.HR.Data.EFCore.Tests\FilterTests.cs:line 29

I saw that issues #4 and #14 face the same issue that should have been fixed since then but it seems it's still here...

I'll send you a PR to try to provide a way to avoid this issue in the future.

Mapping to Views

Hi Jon,

This is probably more of a feature request than issue but is there a way to get the CompareEFWithDB Method to check for Views as well as tables. EF Core doesn't actually care which you use and it can be especially useful when wanting to get data from other databases or linked servers, where a table would not be appropriate.

It would also need the issue with missing Primary Keys to be solved however (issue #32 ) as obviously the view does not have a Primary Key associated with it.

Worth noting that you can work around this by both including the View in TablesToIgnoreCommaDelimited and then subsequently also adding the error string "NOT IN DATABASE: Entity 'XXXXX', table name. Expected = XXXXView" but it's a bit messy!

Any help would be greatly appreciated as have been really loving this library so far :)

Many thanks,
Louis

GetCallingAssemblyTopLevelDir method via Nuget gives wrong results

I've been trying to use the CreateUniqueClassOptions method to set up a SQL Server xUnit test via your Nuget package. I cannot get it to work properly. I believe I have discovered that the cause is the use of use of
var pathToManipulate = (callingAssembly ?? Assembly.GetCallingAssembly()).Location;

in the GetCallingAssemblyTopLevelDir method. It appears that the result of this is not as expected, since the assembly being referenced is the Nuget package, which does not have the "bin" directory that you reference. As such, it causes a problem with your indexOfPart var returning zero.

Also, I think your logic here might be wonky:
var indexOfPart = pathToManipulate.IndexOf(binDir, StringComparison.OrdinalIgnoreCase) + 1; if (indexOfPart < 0) throw new Exception($"Did not find '{binDir}' in the ApplicationBasePath");

If the value for "bin" is not found, then your IndexOf will be -1. You then add 1 to that, which would result in zero. You then check to see if the result is LESS than zero. You need to check if it equals zero. Right now, it blows up in the Substring function with a length of -1.

EFCore CompareEfSql errors when schema has incorrect case

Hi,

We're finishing up a migration from EF6 to EF Core, and using CompareEfSql to validate the upgraded builder (especially the corner cases), and it's been a great tool to assist with this.

However we're having a small issue where the real schema was initially created all lowercase version, but the schema has since been changed to be pascal case. Further, either EF6 pascal cased the foreign key names, or perhaps another developer manually ignored the change when the schema was changed to pascal case.

The result is a mix of schema.Table and FK_Schema_ColumnId that is perfectly legal for Sql Server, and EF Core. The updated configuration works without errors.

public void Configure(EntityTypeBuilder<Terminal> builder)
{
    builder.ToTable(table: "Terminal", schema: "Store");

I realize we can just ignore these errors individually, but as with #9 it would be great to add a case-insensitive option when comparing schema (and/or table).

NOT IN DATABASE: Entity 'Terminal', table name. Expected = Store.Terminal

but once ignored, we then get the reverse error

EXTRA IN DATABASE: YourDatabaseName->Table 'store.Terminal'`

We would like it if the following does not fail

CREATE TABLE [purchase].[Order] ....

public void Configure(EntityTypeBuilder<Order> builder)
{
    builder.ToTable(table: "Order", schema: "Purchase"); // originally purchase
}

[Test]
public void ShouldMatchSchema()
{
    using (var context = new MyDbContext())
    {
        var config = new CompareEfSqlConfig();

        config.SchemaIgnoreCase = true;


        var comparer = new CompareEfSql(config);

        //ATTEMPT
        //This will compare EF Core model of the database 
        //with the database that the context's connection points to
        var hasErrors = comparer.CompareEfWithDb(context);

        //VERIFY
        //The CompareEfWithDb method returns true if there were errors.
        //The comparer.GetAllErrors property returns a string
        //where each error is on a separate line
        Assert.That(hasErrors, Is.False, comparer.GetAllErrors);
    }
}

Thanks again for the great tool!

CreateUniqueMethodOptions with temporal tables

Hello,

we are using the CreateUniqueMethodOptions for testing. Now we added a temporal table and we are getting following error while executing the tests:

Microsoft.Data.SqlClient.SqlException: Failed to delete the XXX table because this is not a supported operation for temporal tables with system versioning.

If I delete the table on my own, the test works fine once. But then, I will get the error again...

Does this method supports temporal tables und if yes, how?

Thanks,
Jenny

EfSchemaCompare: Wrong difference reported on generated value

Entity:
public class NormativeReference
{
[Key, MaxLength (50)]
public string NormativeReferenceId { get; set; }
public string Name { get; set; }
}

SQL (SQL Server 2014):
CREATE TABLE [dbo].[NormativeReferences] (
[NormativeReferenceId] NVARCHAR (50) NOT NULL,
[Name] NVARCHAR (MAX) NULL,
CONSTRAINT [PK_NormativeReferences] PRIMARY KEY CLUSTERED ([NormativeReferenceId] ASC)
);

CompareEfWithDb returns:
DIFFERENT: NormativeReference->Property 'NormativeReferenceId', value generated. Expected = OnAdd, found = Never

AppSettings.GetConfiguration doesn't allow for environmental variable overrides

Inside various pipeline based software, it is necessary to allow configuration to be overridable by Environmental Variables. as a means to help match up services. Inside AppSettings.GetConfiguration, would it be possible to add environment variables such that "ASPNETCORE_ConnectionStrings__PostgreSqlConnection" can override the connection string?

Issues with possible unintended re-use of SQLite in-memory DbContext?

Does SQLite in-memory by default re-use DbContext instances?

I'm not sure if this is a more general SQLite issue or related specifically to EfCore.TestSupport.

Here's my general test setup...

public class MyDbFixture
{
   public MyDbFixture()
   {
        // A custom `IDateTimeAdapter` to make testing easier with .NET DateTime
        var now = DateTime.UtcNow;
        TestDateTime = new Mock<IDateTimeAdapter>();
        TestDateTime
             .SetupGet(dt => dt.UtcNow)
             .Returns(now);
   }
   
   // All db tests will use this method to create the DbContext
   // The same mocked instance of `IDateTimeAdapter` is passed in, used for setting default values for date columns
   public MyDbContext CreateDbContext()
   {
      var options = SqliteInMemory.CreateOptions<MyDbContext>();
      var context = new MyDbContext(options, TestDateTime);
      context.Database.EnsureDeleted();
      context.Database.EnsureCreated();
      return context;
   }
}

Running the following tests presents no problem....

public class MyTests1
{
   public MyTestsA(MyDbFixture fixture)
   {
      _fixture = fixture;
   }
   
   public async Task MyTest1()
   {
      using var dbContext = _fixture.CreateDbContext()

      .... do testing stuff
   }

   public async Task MyTest2()
   {
      using var dbContext = _fixture.CreateDbContext()

      .... do testing stuff
   }
}

However, adding this test will cause the previous tests to occasionally fail when all tests are run together

public class MyTestsB
{
   public async Task MyOtherTest()
   {
        var options = SqliteInMemory.CreateOptions<MyDbContext>();
        
        // No Mock Setup for IDateTimeAdapter
        using var context = new OrderPollingDbContext(options, new Mock<IDateTimeAdapter>().Object);

        context.Database.EnsureDeleted();
        context.Database.EnsureCreated();
        
        .... do testing stuff
   }
}

I notice the following:

  • Occasionally MyTestsA will have a MyDbContext IDateTimeAdapter.UtcNow of DateTime.MinValue. This is unexpected.
  • When I disable MyTestsB, MyTestsA stop failing.
  • MyTestsA only continue to pass, even if DbContext is reused, because each test is using the same Mock<IDateTimeAdapter>

'Did not find '\bin\' in the assembly. Do you need to provide the callingAssembly parameter?'

Hey Jon,

I'm getting the following exception from a call to CreateUniqueClassOptions:
'Did not find '\bin\' in the assembly. Do you need to provide the callingAssembly parameter?'

Stack:

TestSupport.dll!TestSupport.Helpers.TestData.GetCallingAssemblyTopLevelDir(System.Reflection.Assembly callingAssembly)
TestSupport.dll!TestSupport.Helpers.AppSettings.GetConfiguration(System.Reflection.Assembly callingAssembly, string settingsFilename)
TestSupport.dll!TestSupport.Helpers.AppSettings.GetUniqueDatabaseConnectionString(object testClass, string optionalMethodName, char seperator)
TestSupport.dll!TestSupport.EfHelpers.SqlServerHelpers.CreateOptionWithDatabaseName<Entities.ProprietaryContext>(object callingClass, bool throwOnClientServerWarning, string callingMember)
TestSupport.dll!TestSupport.EfHelpers.SqlServerHelpers.CreateUniqueClassOptions<Entities.ProprietaryContext>(object callingClass, bool throwOnClientServerWarning)

Calling code:

[Fact]
public void NoCompany()
{
      var options = this.CreateUniqueClassOptions<ProprietaryContext>();
...

I see the exception on Helpers/TestData.cs:158, but am not getting anywhere without symbols.

Any thoughts?

Thanks!

Ability to pass paramater to CreateOptions

The problem is I need to add to the dbcontextoptionsbuilder the net topology options but currently I can't because all methods don't have the proper lambda like the constructor while using the adddbcontext option

Comparison fails for a default value with the default C# value

Tested using EfCore.TestSupport nuget package 3.1.0

If entity framework is configured to have a Sql default that is the same as the C# default(T), then comparison incorrectly fails with an error similar to:

DIFFERENT: FooEntity->Property 'MyNumberWithDefault', default value sql. Expected = 0, found = <null>
DIFFERENT: FooEntity->Property 'MyNumberWithDefault', value generated. Expected = OnAdd, found = Never

Duplication:

    public class FooTest
    {
        public class FooEntity
        {
            [Key]
            public int Id { get; set; }

            public int MyNumberWithDefault { get; set; }
        }

        public class FooContext : DbContext
        {
            public FooContext(DbContextOptions dbContextOptions) : base(dbContextOptions) { }

            public DbSet<FooEntity> FooEntity { get; set; }

            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                base.OnModelCreating(modelBuilder);

                modelBuilder.Entity<FooEntity>()
                    .Property(x => x.MyNumberWithDefault)
                    //NOTE: Change this to any other value besides 0, and the test passes
                    .HasDefaultValue(0);
            }
        }

        [Fact]
        public async Task ShouldAllowDefaultDefaultValue()
        {
            //----- Arrange -----//
            var dbContextOptions = new DbContextOptionsBuilder<FooContext>().UseSqlServer(this.GetUniqueDatabaseConnectionString());

            using (var fooContext = new FooContext(dbContextOptions.Options))
            {
                await fooContext.Database.EnsureDeletedAsync();
                await fooContext.Database.EnsureCreatedAsync();
            }

            //----- Act -----//
            var config = new CompareEfSqlConfig();

            var comparer = new CompareEfSql(config);

            var hasErrors = comparer.CompareEfWithDb(new FooContext(dbContextOptions.Options));

            //----- Act -----//
            Assert.False(hasErrors, comparer.GetAllErrors);
        }
    }

Method not found: 'Microsoft.EntityFrameworkCore.Scaffolding.Metadata.DatabaseModel

After upgrading my solution and test project to .NET Core 3.0, I get this error

System.MissingMethodException : Method not found: 'Microsoft.EntityFrameworkCore.Scaffolding.Metadata.DatabaseModel Microsoft.EntityFrameworkCore.Scaffolding.IDatabaseModelFactory.Create(System.String, System.Collections.Generic.IEnumerable1<System.String>, System.Collections.Generic.IEnumerable1<System.String>)'.
at TestSupport.EfSchemeCompare.CompareEfSql.GetDatabaseModelViaScaffolder(DbContext[] contexts, String configOrConnectionString, IDesignTimeServices designTimeService)
at TestSupport.EfSchemeCompare.CompareEfSql.FinishRestOfCompare(String configOrConnectionString, DbContext[] dbContexts, IDesignTimeServices designTimeService)
at TestSupport.EfSchemeCompare.CompareEfSql.CompareEfWithDb(String configOrConnectionString, DbContext[] dbContexts)
at TestSupport.EfSchemeCompare.CompareEfSql.CompareEfWithDb(DbContext[] dbContexts)

Is there a plan on updating the project to .NET Core 3?

Columns checks are case sensitive

As SQL Server is in most cases case insensitive for its structure, it may be a good idea to make the checks case insensitive by default?

Currently a property named Metadata will not match a column named MetaData, and the script will returns that the column is missing.

EfSchemaCompare: Error with default values on string properties

  • Created a simple dbcontext
  • Use code first migrations to create a sql script
  • Run that script in a new empty database (Sql Server)
  • Run the example EfTestSchemaCompare test
  • Get this error

DIFFERENT: Message->Property 'MessageContent', default value sql. Expected = , found = N''

Basically any property on an entity which is configured with some variation of .IsRequired().HasDefaultValue("") is giving this error.

Using .IsRequired().HasDefaultValue("").HasDefaultValueSql("N''") does not result in any error, but I don't think that should be necessary?

I believe that there shouldn't be any reported errors if EF Core is in full control of the database structure, but I could just be misunderstanding what the expected behavior is.

Using Sql Server
Latest version of Entity Framework packages (2.2.3)
Latest EfCore.TestSupport (1.8.0)

Context:

public class EfTestContext : DbContext
{
    public EfTestContext(DbContextOptions<EfTestContext> options) : base(options)
    {
    }
	
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Message>()
            .ToTable("Message")
            .Property(b => b.MessageContent).IsRequired().HasDefaultValue("");
    }

    public DbSet<Message> Messages { get; set; }
}

public class Message
{
    public int MessageId { get; set; }
    public string MessageContent { get; set; }
}

Unit Test:

[Fact]
public void EfTestSchemaCompare()
{
    //SETUP
    DbContextOptionsBuilder<EfTestContext> builder = new DbContextOptionsBuilder<EfTestContext>();
    builder.UseSqlServer("Server=.\\SQLEXPRESS;Database=EfTest;Integrated Security=true;");
	
    using (EfTestContext context = new EfTestContext(builder.Options))
    {
        CompareEfSql comparer = new CompareEfSql();

        //ATTEMPT
        //This will compare EF Core model of the database with the database that the context's connection points to
        bool hasErrors = comparer.CompareEfWithDb(context); 

        //VERIFY
        //The CompareEfWithDb method returns true if there were errors. 
        //The comparer.GetAllErrors property returns a string, with each error on a separate line
        hasErrors.ShouldBeFalse(comparer.GetAllErrors);
    }
}

Sql:

IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL
BEGIN
    CREATE TABLE [__EFMigrationsHistory] (
        [MigrationId] nvarchar(150) NOT NULL,
        [ProductVersion] nvarchar(32) NOT NULL,
        CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId])
    );
END;

GO

IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20190407183740_initial')
BEGIN
    CREATE TABLE [Message] (
        [MessageId] int NOT NULL IDENTITY,
        [MessageContent] nvarchar(max) NOT NULL DEFAULT N'',
        CONSTRAINT [PK_Message] PRIMARY KEY ([MessageId])
    );
END;

GO

IF NOT EXISTS(SELECT * FROM [__EFMigrationsHistory] WHERE [MigrationId] = N'20190407183740_initial')
BEGIN
    INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
    VALUES (N'20190407183740_initial', N'2.2.3-servicing-35854');
END;

GO

Exception occurs when checking tables without PK

Hi Jon, I very like your tool, but I faced with some issue during using it.
Firstly, exception occurs when checking tables without PK (null reference exception).
Unfortunatelly, I have not saved a log. But I've made a quick fix by using null propagation, for example:

public bool CompareModelToDatabase(DatabaseModel databaseModel)
{
//.....
logger.CheckDifferent(entityType.FindPrimaryKey().Relational().Name, databaseTable.PrimaryKey**?**.Name,
                        CompareAttributes.ConstraintName);
//.....
}

And

private void CompareColumns(CompareLog log, IEntityType entityType, DatabaseTable table)
 {
      var columnDict = table.Columns.ToDictionary(x => x.Name);
      var primaryKeyDict = table.PrimaryKey == null ? new Dictionary<string, DatabaseColumn>() : table.PrimaryKey.Columns.ToDictionary(x => x.Name);
//....
  pKeyLogger.CheckDifferent(efPKeyConstraintName, table.PrimaryKey?.Name, CompareAttributes.ConstraintName);

This is just a quick fix, because the absence of PK is identified as “DIFFERENCE”.
Thanks.

Support EF Core 5

Currently tests are failing becouse seems that library tries to call internal EF Core method that are not available now. Do you plan to update the library?

Use secrets.json

For our integration tests, we created a DbContext like this:

DbContextOptions<TestDbContext>? options = this.CreateUniqueMethodOptions<TestDbContext>();
await using TestDbContext dbContext = new(options);

How is it possible to use the connection string from the secrets.json?

Support for EF Core 8.0

Hello there,

Just a heads up. I updated to use EF Core 8.0 and the tests which rely on this TestSupport library fail with the MissingMethodException:

Exception has occurred: CLR/System.MissingMethodException
Exception thrown: 'System.MissingMethodException' in Microsoft.EntityFrameworkCore.Relational.dll: 'Method not found: 'Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping Microsoft.EntityFrameworkCore.Storage.RelationalTypeMapping.Clone(System.String, System.Nullable`1<Int32>)'.'
   at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteTypeMappingSource.FindMapping(RelationalTypeMappingInfo& mappingInfo)
   at Microsoft.EntityFrameworkCore.Storage.RelationalTypeMappingSource.<>c.<FindMappingWithConversion>b__8_0(ValueTuple`4 k, RelationalTypeMappingSource self)
   at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd[TArg](TKey key, Func`3 valueFactory, TArg factoryArgument)
   at Microsoft.EntityFrameworkCore.Storage.RelationalTypeMappingSource.FindMappingWithConversion(RelationalTypeMappingInfo mappingInfo, Type providerClrType, ValueConverter customConverter)
   at Microsoft.EntityFrameworkCore.Storage.RelationalTypeMappingSource.FindMapping(Type type, IModel model, CoreTypeMapping elementMapping)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.MemberClassifier.IsCandidateNavigationPropertyType(Type targetType, MemberInfo memberInfo, Model model, Nullable`1& shouldBeOwned)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.MemberClassifier.FindCandidateNavigationPropertyType(MemberInfo memberInfo, IConventionModel model, Nullable`1& shouldBeOwned)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.MemberClassifier.GetNavigationCandidates(IConventionEntityType entityType)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.ForeignKeyAttributeConvention.ProcessEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnEntityTypeAddedNode.Run(ConventionDispatcher dispatcher)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.DelayedConventionScope.Run(ConventionDispatcher dispatcher)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Run()
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Dispose()
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelInitialized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelInitialized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model..ctor(ConventionSet conventions, ModelDependencies modelDependencies, ModelConfiguration modelConfiguration)
   at Microsoft.EntityFrameworkCore.ModelBuilder..ctor(ConventionSet conventions, ModelDependencies modelDependencies, ModelConfiguration modelConfiguration)
   at Microsoft.EntityFrameworkCore.ModelConfigurationBuilder.CreateModelBuilder(ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_4(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService(IInfrastructure`1 accessor, Type serviceType)
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.get_Dependencies()
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.EnsureCreated()
   at Birder.Tests.Controller.PostFollowUserAsyncTests.<PostFollowUserAsync_ReturnsOkObject_WhenRequestIsValid>d__7.MoveNext() in C:\Users\user\source\repos\birder-server\Birder.Tests\Controller\NetworkController\PostFollowUserAsyncTests.cs:line 185

If I revert the EF Core packages back to version 7.0.14 (keeping the target framework net8) then the tests PASS again.

For example:

<TargetFramework>net8.0</TargetFramework>
   ...
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.14" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.14">

Think it's just a case of adding the new dependencies to EfCore.TestSupport .csproj.

Please let me know if you need any further information.

(and thank you for the library which is excellent :) )

CompareEfSql: An item with the same key has already been added. Key: System.CultureAwareComparer

Hi,

I am using nuget EfCore.TestSupport with latest 3.1.0 version.

I get unhandled exception thrown when constructing new CompareEfSql().

I run similar test to test CompareViaContext in ComparerBooks class.
If I run it on windows it completes without error but on linux (ubuntu 18.04) it throws following error:

System.TypeInitializationException : The type initializer for 'TestSupport.EfSchemeCompare.Internal.CompareHelpers' threw an exception.
 ---- System.ArgumentException : An item with the same key has already been added. Key: System.CultureAwareComparer

Full stack trace:

at TestSupport.EfSchemeCompare.Internal.CompareHelpers.GetStringComparison(StringComparer caseComparer) in /builds/web/notifications/notifications-api/TestSupport/EfSchemeCompare/Internal/CompareHelpers.cs:line 43
    at TestSupport.EfSchemeCompare.Internal.Stage1Comparer..ctor(IModel model, String dbContextName, CompareEfSqlConfig config, List`1 logs) in /builds/web/notifications/notifications-api/TestSupport/EfSchemeCompare/Internal/Stage1Comparer.cs:line 36
    at TestSupport.EfSchemeCompare.CompareEfSql.FinishRestOfCompare(String configOrConnectionString, DbContext[] dbContexts, IDesignTimeServices designTimeService) in /builds/web/notifications/notifications-api/TestSupport/EfSchemeCompare/CompareEfSql.cs:line 131
    at TestSupport.EfSchemeCompare.CompareEfSql.CompareEfWithDb(String configOrConnectionString, DbContext[] dbContexts) in /builds/web/notifications/notifications-api/TestSupport/EfSchemeCompare/CompareEfSql.cs:line 97
    at TestSupport.EfSchemeCompare.CompareEfSql.CompareEfWithDb(DbContext[] dbContexts) in /builds/web/notifications/notifications-api/TestSupport/EfSchemeCompare/CompareEfSql.cs:line 63
    at Notifications.DatabaseSchemaTests.DatabaseSchemaTests.CompareViaContext() in /builds/web/notifications/notifications-api/test/Notifications.DatabaseSchemaTests/DatabaseSchemaTests.cs:line 29
 ----- Inner Stack Trace -----
    at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
    at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
    at TestSupport.EfSchemeCompare.Internal.CompareHelpers..cctor() in /builds/web/notifications/notifications-api/TestSupport/EfSchemeCompare/Internal/CompareHelpers.cs:line 46

It seems that some keys in dictionary ComparerToComparison are equal to each other which is strange.

AppSettings.GetUniqueDatabaseConnectionString not always unique

GetUniqueDatabaseConnectionString doesn't always return a unique result.

For example, if I need to create two databases in a single test, then the default behavior won't generate two distinct connection strings. This is easily worked around by adding extra "jitter" to the test name in addition to e,g, $"{nameof(MyTestMethod)}_1" and $"{nameof(MyTestMethod)}_2"

A bit more significant concern is that if for some reason a test database fails to get cleaned up at the end of execution, GetUniqueDatabaseConnectionString will return the same connection string the next test run, possibly causing the test to fail in exciting and exotic ways.

This can also be worked around by adding extra code to check for the existence of the database before executing the rest of the test - but that is extra code to write, or by adding uniqueness "jitter" to the testName, e.g. this.GetUniqueDatabaseConnectionString($"{nameof(MyTestMethod)}_{Guid.NewGuid()}");

However, it caught me by surprise the first time that happened, and I wonder if the utility should provide out-of-the-box support for generating truly unique database names, or perhaps ensuring that the database doesn't already exist before returning the "unique" connection string?

Or perhaps just update the documentation at https://github.com/JonPSmith/EfCore.TestSupport/wiki/3b.-Creating-connection-strings#the-getuniquedatabaseconnectionstring-extention-method to more clearly indicate that a test database left over from a previous run may cause issues?

Error while saving with Update method

When using update to save the entity the test will fail. If you just change properties and save everuthing works fine.

The erros says: Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException : Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

GetAllErrors() can run into problems with line endings

public string GetAllErrors => string.Join("\n", CompareLog.ListAllErrors(Logs));

On my setup, Rider 2019.1.2. on Windows 10 1809, the GetAllErrors() method can cause multiline test assertions to fail because they are expecting lines to be separated by '\r\n' rather than by '\n'.

I can get them to pass by replacing the '\n' separator in the string.Join() method with Environment.NewLine.

Exception when same table name different schema

Running this against our database we are getting an exception when running
var hasErrors = comparer.CompareEfWithDb(context);

Exception:

System.ArgumentException: An item with the same key has already been added. Key: Job
   at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey](List`1 source, Func`2 keySelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey](IEnumerable`1 source, Func`2 keySelector, IEqualityComparer`1 comparer)
   at TestSupport.EfSchemeCompare.Internal.Stage1Comparer.CompareModelToDatabase(DatabaseModel databaseModel)
   at TestSupport.EfSchemeCompare.CompareEfSql.FinishRestOfCompare(String configOrConnectionString, DbContext[] dbContexts, IDesignTimeServices designTimeService)
   at Gravity.EF.Test.DummyTestFixture.CompareViaContext()

After checking our database turns out we have two tables:
HangFire.Job (external table)
replication.Job (another external table)

Is there any way of ignoring some schemas? or specifying the default EF schema?
Any suggestion on handling these are much appreciated.

Thanks for this amazing lib.

Missing primary key in SQL table throws NullReferenceException

Hi,

I ran into minor bug with nuget EfCore.TestSupport 3.1.1 version (latest).

I get unhandled NullReferenceException thrown in Stage1Comparer.CompareModelToDatabase(DatabaseModel databaseModel) line 297.

The problem occurs when a primary key is missing in a SQL table but is present in EF Core model.

repro:

CREATE TABLE [MyEntities] (
    [MyEntityId] int NOT NULL
);
public class MyEntity
{
    public int MyEntityId { get; set; }
}
...
public DbSet<MyEntity> MyEntities { get; set; }

I would expect a warning message explaining missing primary key in a SQL table


I found out that there is similar issue #8 but is closed. The issue was fixed with check for primary key in EF Core model. IMO similar check should be for SQL model.

System.IO.Pipelines

After installing EfCore.TestSupport I started getting build warnings regarding System.IO.Pipelines:

Microsoft.Common.CurrentVersion.targets(2389,5): Warning MSB3277 : Found conflicts between different versions of "System.IO.Pipelines" that could not be resolved. There was a conflict between "System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" and "System.IO.Pipelines, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51". "System.IO.Pipelines, Version=6.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" was chosen because it was primary and "System.IO.Pipelines, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" was not.

I have checked NuGet Package Manager and can see that EfCore.TestSupport is the only package that references System.IO.Pipelines 6

image

Do you have any suggestions how to resolve this?

Logs output behavior

I'm not sure if this is an issue or this is the way you want it to show logs result.

STEP 1:

  • You have a database with 3 tables
  • You have an empty DbContext

You run the comparer and you have this logs in error:
EXTRA IN DATABASE: ht->Table 'Address'
EXTRA IN DATABASE: ht->Table 'Customers'
EXTRA IN DATABASE: ht->Table 'Employees'

STEP 2:

  • I add an entity (Address) with a missing constraint

Here are the logs:
DIFFERENT: Address->Property 'Address1', column type. Expected = nvarchar(max), found = nvarchar(200)

I would have expected something like:
EXTRA IN DATABASE: ht->Table 'Customers'
EXTRA IN DATABASE: ht->Table 'Employees'
DIFFERENT: Address->Property 'Address1', column type. Expected = nvarchar(max), found = nvarchar(200)

EfSchemaCompare: Wrong difference reported on default value

Entity:
public class Parameter
{
[DatabaseGenerated (DatabaseGeneratedOption.Identity)]
public int ParameterId { get; set; }
public ValueAggregationTypeEnum ValueAggregationTypeId { get; set; }
public decimal? NumericValue { get; set; }
}
public enum ValueAggregationTypeEnum : byte { Invariable = 1, Minimum = 2, Maximum = 3, Average = 4 }
protected override void OnModelCreating (ModelBuilder modelBuilder)
{
modelBuilder.Entity().Property (p => p.ValueAggregationTypeId).HasDefaultValue (ValueAggregationTypeEnum.Invariable);
}

SQL (SQL Server 2014):
CREATE TABLE [dbo].[Parameters] (
[ParameterId] INT IDENTITY (1, 1) NOT NULL,
[NumericValue] DECIMAL (9, 2) NULL,
[ValueAggregationTypeId] TINYINT DEFAULT ((1)) NOT NULL,
CONSTRAINT [PK_Parameters] PRIMARY KEY CLUSTERED ([ParameterId] ASC)
);

CompareEfWithDb returns:
DIFFERENT: Parameter->Property 'ValueAggregationTypeId', default value sql. Expected = , found = 1

EfSchemaCompare Does not detect missing Alternate Key

Testing with EfCore.TestSupport 3.1.1 nuget package.

I don't see this listed in https://github.com/JonPSmith/EfCore.TestSupport/wiki/EfSchemaCompare-limitations,

It seems like it should be able to be treated similarly to a unique index, which is detected when missing.

Duplication:

CREATE DATABASE [FooDb]
GO

USE [FooDb]
GO
CREATE TABLE [dbo].[Foos](
	[Id] [int] IDENTITY(1,1) NOT NULL,
	[FooName] [nvarchar](450) NOT NULL,
	CONSTRAINT [PK_Foos] PRIMARY KEY CLUSTERED ([Id])
) ON [PRIMARY]
GO
public class Foo
{
    [Key]
    public int Id { get; set; }

    public string FooName { get; set; }
}

public class FooContext : DbContext
{
    public FooContext(DbContextOptions<FooContext> options) : base(options) { }

    public DbSet<Foo> Foos { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        //Test passes, but shouldn't if HasAlternateKey is used.  EF generates in this case:
        //CONSTRAINT [AK_Foos_FooName] UNIQUE NONCLUSTERED ([FooName] ASC)
        modelBuilder.Entity<Foo>()
            .HasAlternateKey(x => x.FooName);

        //Test fails as expected if unique index configuration is used
        //modelBuilder.Entity<Foo>()
        //    .HasIndex(x => x.FooName)
        //    .IsUnique();
    }
}

public class FooTest
{
    [Fact]
    public void Blah()
    {
        //--------- Arrange ---------//
        string connectionString = "Server=(localdb)\\mssqllocaldb;Database=FooDb;Trusted_Connection=True;";
        var dbOptions = new DbContextOptionsBuilder<FooContext>().UseSqlServer(connectionString).Options;

        var config = new CompareEfSqlConfig();

        var comparer = new CompareEfSql(config);

        using (var fooContext = new FooContext(dbOptions))
        {
            //--------- Act ---------//
            var hasErrors = comparer.CompareEfWithDb(connectionString, fooContext);

            //--------- Assert ---------//
            Assert.False(hasErrors, comparer.GetAllErrors);
        }
    }
}

Look for appsettings.json in output folder

Hi Jon,

I'd like to be able publish my test project to run on a separate machine. As far as I can tell EfCore.TestSupport will only look for appsettings.json up a directory from the /bin folder the .dll lives in.

Does EfCore.TestSupport allow the appsettings.json to live anywhere else? If not, are you opposed to this? Do you have any thoughts on how I might go about changing this?

Thanks,
Andrew

Request: Create a separate respository / NuGet package for SeedDatabase feature

After coming across your article:

Getting better data for unit testing your EF Core applications

I was looking forward to using your “Seed from Production” feature for EF Core 5.0 tests. But alas, you decided to leave that feature behind in EFCore.TestSupport v3.2.0.

Would it be possible to move that feature into a separate repository and NuGet package so that I could use that feature together with EfCoreTestSupport v5.0.0?

Thanks.

By the way, I found the first version of you book so helpful that I went and bought the 2nd versions as well.

EfSchemaCompare: add check on alternate keys

Currently the EfSchemaCompare feature does not support the checking of alternate keys.

Here are the notes on how I might do it.

  • I can get the primary key and any alternate keys using the entityType.GetKeys() method
  • The DatabaseModel's Table property has a list called UniqueConstraints, which holds any non-foreign key, non-primary key, columns that have a unique constraint on them.

Support for EF Core 5

Currently tests are failing becouse seems that library tries to call internal EF Core method that are not available now. Do you plan to update the library?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.