JonPSmith / EfCore.TestSupport

Tools for helping in unit testing applications that use Entity Framework Core
https://www.thereformedprogrammer.net/new-features-for-unit-testing-your-entity-framework-core-5-code/
Other
352 stars 53 forks source link

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

Closed adamjez closed 4 years ago

adamjez commented 4 years ago

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.

JonPSmith commented 4 years ago

Wow, that is very odd. I can only thing that the linux .NET SDK doesn't have all the StringComparer types.

I'm not using linux so could you get a list of the static values in the StringComparer on linux .NET SDK. Thanks.

adamjez commented 4 years ago

I made simple console app targetting netcoreapp3.1 in ubuntu (mcr.microsoft.com/dotnet/core/sdk:3.1-bionic) docker image:

var comparers = new[]
{
    (str: "Ordinal",                    comp: StringComparer.Ordinal),
    (str: "OrdinalIgnoreCase",          comp: StringComparer.OrdinalIgnoreCase),
    (str: "CurrentCultureIgnoreCase",   comp: StringComparer.CurrentCultureIgnoreCase),
    (str: "CurrentCulture",             comp: StringComparer.CurrentCulture),
    (str: "InvariantCulture",           comp: StringComparer.InvariantCulture),
    (str: "InvariantCultureIgnoreCase", comp: StringComparer.InvariantCultureIgnoreCase),
};

var pairs = /* I removed the code for the code sample brevity */

string output = "";
foreach (var (first, second) in pairs)
{
    var areEqual = first.comp.Equals(second.comp);
    if (areEqual)
    {
        output += $"{first.comp.GetType().Name} ({first.str}) is equal to {second.comp.GetType().Name} ({second.str})\n";
    }
}

Console.WriteLine(output);

outputs:

CultureAwareComparer (CurrentCultureIgnoreCase) is equal to CultureAwareComparer (InvariantCultureIgnoreCase)
CultureAwareComparer (CurrentCulture) is equal to CultureAwareComparer (InvariantCulture)

In the case of Windows the console output is empty.

The easiest fix I can think of is to use switch expression instead of Dictionary.

JonPSmith commented 4 years ago

That is great information, and a very sensible suggestion of a switch. I will try an look at this soon, but I have a few things to do next, I'll let you know when I have released a new NuGet.

JonPSmith commented 4 years ago

Hi @adamjez,

I have released a new version on NuGet (3.1.1) which contains a fix to your problem. Had to use istacked ifs instead of switch as I needed to do an .Equal to compare the StringComparer.

Please try this update and let me know if it works.

adamjez commented 4 years ago

Hi @JonPSmith, It works :-) Thank you!