dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.81k stars 3.2k forks source link

DefaultTypeMappings do not seem to be applied in queries. #34850

Open ADD-David-Antolin opened 1 month ago

ADD-David-Antolin commented 1 month ago

File a bug

Include your code

DefaultTypeMappingsTests.zip

Change between SqlServer and Sqlite by modifying the method called in line 6.

Include stack traces

SqlServer:

System.InvalidCastException
  HResult=0x80004002
  Message=Failed to convert parameter value from a NonGenericWrapper to a Int32.
  Source=Microsoft.Data.SqlClient
  StackTrace:
   at Microsoft.Data.SqlClient.SqlCommand.<>c.<ExecuteDbDataReaderAsync>b__211_0(Task`1 result)
   at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke() in /_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/TaskContinuation.cs:line 180
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) in /_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs:line 179
--- End of stack trace from previous location ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) in /_/src/libraries/System.Private.CoreLib/src/System/Threading/ExecutionContext.cs:line 203
   at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread) in /_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs:line 2345
--- End of stack trace from previous location ---
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__18.MoveNext() in /_/src/EFCore.Relational/Storage/RelationalCommand.cs:line 658
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__18.MoveNext() in /_/src/EFCore.Relational/Storage/RelationalCommand.cs:line 719
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<InitializeReaderAsync>d__21.MoveNext() in /_/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs:line 401
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.<ExecuteAsync>d__7`2.MoveNext()
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<MoveNextAsync>d__20.MoveNext() in /_/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs:line 323
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult() in /_/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs:line 154
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleAsync>d__14`1.MoveNext() in /_/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs:line 138
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleAsync>d__14`1.MoveNext() in /_/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs:line 150
   at Program.<<<Main>$>g__ExecuteQueriesInContext|0_2>d.MoveNext() in G:\Source\repos\DefaultTypeMappingsTests\Program.cs:line 64
   at Program.<<<Main>$>g__ExecuteWithSqlServer|0_0>d.MoveNext() in G:\Source\repos\DefaultTypeMappingsTests\Program.cs:line 19
   at Program.<<Main>$>d__0.MoveNext() in G:\Source\repos\DefaultTypeMappingsTests\Program.cs:line 6
   at Program.<Main>(String[] args)

  This exception was originally thrown at this call stack:
    System.Convert.ChangeType(object, System.Type, System.IFormatProvider) in Convert.cs
    Microsoft.Data.SqlClient.SqlParameter.CoerceValue(object, Microsoft.Data.SqlClient.MetaType, out bool, out bool, bool)

Inner Exception 1:
InvalidCastException: Object must implement IConvertible.

Sqlite:

System.InvalidOperationException
  HResult=0x80131509
  Message=No mapping exists from object type DefaultTypeMappingsTests.NonGenericWrapper to a known managed provider native type.
  Source=Microsoft.Data.Sqlite
  StackTrace:
   at Microsoft.Data.Sqlite.SqliteValueBinder.Bind() in /_/src/Microsoft.Data.Sqlite.Core/SqliteValueBinder.cs:line 236
   at Microsoft.Data.Sqlite.SqliteParameter.Bind(sqlite3_stmt stmt) in /_/src/Microsoft.Data.Sqlite.Core/SqliteParameter.cs:line 225
   at Microsoft.Data.Sqlite.SqliteParameterCollection.Bind(sqlite3_stmt stmt) in /_/src/Microsoft.Data.Sqlite.Core/SqliteParameterCollection.cs:line 331
   at Microsoft.Data.Sqlite.SqliteCommand.<GetStatements>d__54.MoveNext() in /_/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs:line 323
   at Microsoft.Data.Sqlite.SqliteDataReader.NextResult() in /_/src/Microsoft.Data.Sqlite.Core/SqliteDataReader.cs:line 151
   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReader(CommandBehavior behavior) in /_/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs:line 312
   at Microsoft.Data.Sqlite.SqliteCommand.ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) in /_/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs:line 406
   at Microsoft.Data.Sqlite.SqliteCommand.<ExecuteDbDataReaderAsync>d__60.MoveNext() in /_/src/Microsoft.Data.Sqlite.Core/SqliteCommand.cs:line 420
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__18.MoveNext() in /_/src/EFCore.Relational/Storage/RelationalCommand.cs:line 658
   at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.<ExecuteReaderAsync>d__18.MoveNext() in /_/src/EFCore.Relational/Storage/RelationalCommand.cs:line 719
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<InitializeReaderAsync>d__21.MoveNext() in /_/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs:line 401
   at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable`1.AsyncEnumerator.<MoveNextAsync>d__20.MoveNext() in /_/src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs:line 323
   at System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable`1.ConfiguredValueTaskAwaiter.GetResult() in /_/src/libraries/System.Private.CoreLib/src/System/Runtime/CompilerServices/ConfiguredValueTaskAwaitable.cs:line 154
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleAsync>d__14`1.MoveNext() in /_/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs:line 138
   at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.<SingleAsync>d__14`1.MoveNext() in /_/src/EFCore/Query/ShapedQueryCompilingExpressionVisitor.cs:line 150
   at Program.<<<Main>$>g__ExecuteQueriesInContext|0_2>d.MoveNext() in G:\Source\repos\DefaultTypeMappingsTests\Program.cs:line 64
   at Program.<<<Main>$>g__ExecuteWithSqlite|0_1>d.MoveNext() in G:\Source\repos\DefaultTypeMappingsTests\Program.cs:line 36
   at Program.<<Main>$>d__0.MoveNext() in G:\Source\repos\DefaultTypeMappingsTests\Program.cs:line 6
   at Program.<Main>(String[] args)

Include provider and version information

EF Core version: Database provider: Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.Sqlite Target framework: .NET 8.0 Operating system: Windows 10 & 11 IDE: Visual Studio 2022 17.11.4

roji commented 1 month ago

Minimal repro:

await using var context = new BlogContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

var wrapper = new MyWrapper(8);
_ = await context.Blogs.Where(p => wrapper == p.Id).ToListAsync();

public class BlogContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder
            .UseSqlServer("Server=localhost;Database=test;User=SA;Password=Abcd5678;Connect Timeout=60;ConnectRetryCount=0;Encrypt=false")
            .LogTo(Console.WriteLine, LogLevel.Information)
            .EnableSensitiveDataLogging();

    protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
    {
        configurationBuilder.DefaultTypeMapping<MyWrapper>().HasConversion<MyWrapperConverter>();
    }

    private class MyWrapperConverter() : ValueConverter<MyWrapper, int>(v => v.Wrapped, v => new MyWrapper(v));
}

public class Blog
{
    public int Id { get; set; }
}

public record MyWrapper(int Wrapped)
{
    public static bool operator ==(MyWrapper left, int right) => left.Wrapped == right;
    public static bool operator !=(MyWrapper left, int right) => left.Wrapped != right;
}