Skip to content

Bug/Regression doing a query with a jsonb column as string with 2.1.* #2049

Closed
@lucax88x

Description

@lucax88x

Ciao!

I've reproduced the issue there:

https://github.com/lucax88x/dapper-bug

So, if you have a postgresql db, and you use a jsonb column, and you use netwonsoft as deserializer such as:

NpgsqlConnection.GlobalTypeMapper.UseJsonNet(settings: settings);

and perform a a query like

 await conn.QueryFirstOrDefaultAsync<string>("select \"Content\" from public.\"Posts\"");

will throw an exception with

 ---> System.InvalidCastException: Object must implement IConvertible.
   at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at Dapper.SqlMapper.GetValue[T](DbDataReader reader, Type effectiveType, Object val) in /_/Dapper/SqlMapper.cs:line 1366
   --- End of inner exception stack trace ---
   at Dapper.SqlMapper.ThrowDataException(Exception ex, Int32 index, IDataReader reader, Object value) in /_/Dapper/SqlMapper.cs:line 3928
   at Dapper.SqlMapper.QueryRowAsync[T](IDbConnection cnn, Row row, Type effectiveType, CommandDefinition command) in /_/Dapper/SqlMapper.Async.cs:line 496

This happens only with 2.1.*, if I revert to 2.0.151 everything works perfectly.

This is probably the PR that breaks it.

ps: without json.net it also work perfectly, it's just a combination of json.net and jsonb.

Activity

mgravell

mgravell commented on Mar 5, 2024

@mgravell
Member

I imagine that what's happening here is that npgsql is doing the deserialize, and giving us something that isn't a string (but rather: a rich object) - and we're expecting it to be a string. This is probably related to Dapper (vanilla) using the untyped data read API in some cases. Dapper.AOT does a better job of preferring typed data, so I imagine that Dapper.AOT would explicitly request a string, and receive a string. Any chance you can try the same query with Dapper AOT enabled? Or provide a full repro?

lucax88x

lucax88x commented on Mar 5, 2024

@lucax88x
Author

There's a github link with a full repro on top.

Clone the repo, and run the tests, it will fire up a db with test containers and run the query.

I can try with AOT tomorrow!

mgravell

mgravell commented on Mar 6, 2024

@mgravell
Member
mgravell

mgravell commented on Mar 6, 2024

@mgravell
Member

apologies, I overlooked the repro; taking a peek

mgravell

mgravell commented on Mar 6, 2024

@mgravell
Member

nit: await using var conn = new NpgsqlConnection(_connectionString);, but: great repro, thanks - looking

mgravell

mgravell commented on Mar 6, 2024

@mgravell
Member

I'm a little confused... it... works? (I haven't changed anything except the connection string)

image

and at the command-line:

   09:39:49  DapperBug.Tests  main   8.0.200   13.796s   
➜ dotnet test
  Determining projects to restore...
  All projects are up-to-date for restore.
  DapperBug -> C:\Code\dapper-bug\DapperBug\bin\Debug\net8.0\DapperBug.dll
  DapperBug.Tests -> C:\Code\dapper-bug\DapperBug.Tests\bin\Debug\net8.0\DapperBug.Tests.dll
Test run for C:\Code\dapper-bug\DapperBug.Tests\bin\Debug\net8.0\DapperBug.Tests.dll (.NETCoreApp,Version=v8.0)
Microsoft (R) Test Execution Command Line Tool Version 17.9.0 (x64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: < 1 ms - DapperBug.Tests.dll (net8.0)

What should I be seeing?

mgravell

mgravell commented on Mar 6, 2024

@mgravell
Member

it is possible that this can be fixed with a one-liner in SqlMapper.cs L217:

-                [typeof(string)] = DbType.String,
+                [typeof(string)] = new(DbType.String, TypeMapEntryFlags.SetType | TypeMapEntryFlags.UseGetFieldValue),

can check as soon as we can repro

on a per-consumer-project basis, we can check this with

SqlMapper.AddTypeMap(typeof(string), DbType.String, true);
lucax88x

lucax88x commented on Mar 6, 2024

@lucax88x
Author

I'm a little confused... it... works? (I haven't changed anything except the connection string)

image

and at the command-line:

   09:39:49  DapperBug.Tests  main   8.0.200   13.796s   
➜ dotnet test
  Determining projects to restore...
  All projects are up-to-date for restore.
  DapperBug -> C:\Code\dapper-bug\DapperBug\bin\Debug\net8.0\DapperBug.dll
  DapperBug.Tests -> C:\Code\dapper-bug\DapperBug.Tests\bin\Debug\net8.0\DapperBug.Tests.dll
Test run for C:\Code\dapper-bug\DapperBug.Tests\bin\Debug\net8.0\DapperBug.Tests.dll (.NETCoreApp,Version=v8.0)
Microsoft (R) Test Execution Command Line Tool Version 17.9.0 (x64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: < 1 ms - DapperBug.Tests.dll (net8.0)

What should I be seeing?

I'm a little confused... it... works? (I haven't changed anything except the connection string)

image

and at the command-line:

   09:39:49  DapperBug.Tests  main   8.0.200   13.796s   
➜ dotnet test
  Determining projects to restore...
  All projects are up-to-date for restore.
  DapperBug -> C:\Code\dapper-bug\DapperBug\bin\Debug\net8.0\DapperBug.dll
  DapperBug.Tests -> C:\Code\dapper-bug\DapperBug.Tests\bin\Debug\net8.0\DapperBug.Tests.dll
Test run for C:\Code\dapper-bug\DapperBug.Tests\bin\Debug\net8.0\DapperBug.Tests.dll (.NETCoreApp,Version=v8.0)
Microsoft (R) Test Execution Command Line Tool Version 17.9.0 (x64)
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
A total of 1 test files matched the specified pattern.

Passed!  - Failed:     0, Passed:     1, Skipped:     0, Total:     1, Duration: < 1 ms - DapperBug.Tests.dll (net8.0)

What should I be seeing?

Wow.

This is extremely weird, I lost two hours for that.

So, stay with me:
update the git repo
run the compose from the root
docker compose -f docker-compose.yml up

now, you have 2 tests from the solution
-CreateTables
-RunAloneNotTogetherWithCreateTables

  1. Try to run CreateTables ALONE so you have the structure.
  2. Now run the RunAloneNotTogetherWithCreateTable ALONE and it should give the Exception above (now reproduced)

But, if for some reason, you try to run both of them together, CreateTables will fail because table is already there (which is OK), but the RunAloneNotTogetherWithCreateTables will be green (WTF?)!

Something must happening with assembly...

Gonna try with your map.

lucax88x

lucax88x commented on Mar 6, 2024

@lucax88x
Author
SqlMapper.AddTypeMap(typeof(string), DbType.String, true);

I can confirm it works.

mgravell

mgravell commented on Mar 6, 2024

@mgravell
Member

great; that's a workaround, and we'll get the code fixed for next release

mgravell

mgravell commented on Mar 6, 2024

@mgravell
Member
Npgsql.PostgresException : 3D000: database "test" does not exist

so... I don't think it is connecting to the containerized database, but... I think we understand the problem well enough now

lucax88x

lucax88x commented on Mar 6, 2024

@lucax88x
Author

Create the db in the container :)

lucax88x

lucax88x commented on Mar 6, 2024

@lucax88x
Author

thanks for the quick answer btw.

17 remaining items

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      Participants

      @mgravell@roji@lucax88x

      Issue actions

        Bug/Regression doing a query with a jsonb column as string with 2.1.* · Issue #2049 · DapperLib/Dapper