Skip to content

SqlMapper.TypeHandler<DateTimeOffset?> - Parse works, SetValue never called #1780

@voltagex

Description

@voltagex

Dapper.SqlMapper.AddTypeHandler is not working as I would expect.

You will need to add the following PackageReference to your csproj (see dotnet/efcore#28124), but this issue is related to the mapping itself, not the strict table support - strict tables just show the problem better.

		<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.0" />
using Microsoft.Data.Sqlite;
using System.Data;
using Dapper;

namespace BugReport
{
    public class DateTimeToTimestampHandler : SqlMapper.TypeHandler<DateTimeOffset?>
    {
        public override void SetValue(IDbDataParameter parameter, DateTimeOffset? value)
        {
            Console.WriteLine($"SetValue was hit, input value is {value.Value}");
            parameter.Value = value.Value.ToUnixTimeSeconds();
        }
        public override DateTimeOffset? Parse(object value)
        {
            Console.WriteLine($"Parse was hit, input value is {value} - converted to {DateTimeOffset.FromUnixTimeSeconds((long)value)}");
            return DateTimeOffset.FromUnixTimeSeconds((long)value);
        }
    }

    public class BugReport
    {
        public static void Main()
        {
            DateTimeOffset? myTimestamp = DateTimeOffset.UtcNow;
            Dapper.SqlMapper.AddTypeHandler(new DateTimeToTimestampHandler());

            var connection = new SqliteConnection("Data Source=:memory:");
            connection.Open();
            SQLitePCL.raw.sqlite3_trace(connection.Handle, (_, statement) => Console.WriteLine($"Sent to SQLite: {statement}"), null);

            Console.WriteLine("SQLite version is " + connection.ExecuteScalar("SELECT sqlite_version()"));
            connection.Execute("CREATE TABLE BugReport (ThisIsAnIntColumn INTEGER) STRICT");
            connection.Execute("INSERT INTO BugReport Values (1653915600)");
            var firstSelect = connection.Query<DateTimeOffset?>("SELECT * FROM BugReport");
            Console.WriteLine($"Mapped result is {firstSelect.First()}");
            try
            {
                connection.Execute("INSERT INTO BugReport VALUES (@MyTimestamp)", new { MyTimestamp = myTimestamp });
            }

            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

        }
    }
}

Expected: DateTimeToTimestampHandler.SetValue is called, INTEGER (long) value inserted into the table

Actual:

Sent to SQLite: SELECT sqlite_version()
SQLite version is 3.38.3
Sent to SQLite: CREATE TABLE BugReport (ThisIsAnIntColumn INTEGER) STRICT
Sent to SQLite: INSERT INTO BugReport Values (1653915600)
Sent to SQLite: SELECT * FROM BugReport
Parse was hit, input value is 1653915600 - converted to 30/05/2022 1:00:00 PM +00:00
Mapped result is 30/05/2022 1:00:00 PM +00:00
Sent to SQLite: INSERT INTO BugReport VALUES ('2022-05-30 13:03:01.4304754+00:00')
SQLite Error 19: 'cannot store TEXT value in INTEGER column BugReport.ThisIsAnIntColumn'.

SetValue is never called, and ToString is called on the DateTimeOffset instead.

I'm using the following versions:

		<PackageReference Include="Dapper" Version="2.0.123" />
		<PackageReference Include="Microsoft.Data.Sqlite" Version="6.0.5" />
		<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.0" />

on .NET 6.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions