Skip to content

TypeHandler is not called for parameterised constructor #461

@JoeStead

Description

@JoeStead

We've had to create a TypeHandler to handle DateTimeOffset because SQLite has no concept of date/time, so it is saved as a string, which is fine. When querying the data back out, the Parse method is only called if it is being serialised to a type with a parameterless constructor. If a constructor exists which initialises all the properties the type handler is not called, which results in a "InvalidOperationException" with the following

A parameterless default constructor or one matching signature (System.String Id, System.String SomeValue, System.String SomeDateValue) is required for DapperTypeHandler.ParameterisedTypeConstructor materialization

Note how it is looking for SomeDateValue to be a string still.

I'm not sure if I'm explaining this the best way, I haven't been using Dapper for very long, so this may not be the best way of solving this problem. Below is a small, reproducible example using an in memory SQLite database.

using System;
using System.Data;
using System.Data.SQLite;
using Dapper;

namespace DapperTypeHandler
{
    class Program
    {
        static void Main(string[] args)
        {
            SqlMapper.AddTypeHandler(new DateTimeOffsetHandler());

            var dbConnection = new SQLiteConnection("Data Source=:memory:");
            dbConnection.Open();
            dbConnection.Execute(@"CREATE TABLE SomeTable (
                                      Id                VARCHAR,
                                      SomeValue         VARCHAR,
                                      SomeDateValue     DATETIMEOFFSET
                                    )");

            dbConnection.Execute(
                "INSERT INTO SomeTable (Id, SomeValue, SomeDateValue) VALUES (@Id, @SomeValue, @SomeDateValue)",
                new
                {
                    Id = "id",
                    SomeValue = "what up?",
                    SomeDateValue = new DateTimeOffset(2016, 02, 15, 16, 0, 0, TimeSpan.Zero)
                });

            var parameterlessWorks = dbConnection.Query<ParameterlessTypeConstructor>("SELECT * FROM SomeTable");

            //throws about not being able to find constructor (It expects the DateTime field to be a string still)
            var parameterDoesNot = dbConnection.Query<ParameterisedTypeConstructor>("SELECT * FROM SomeTable"); 

        }
    }

    class DateTimeOffsetHandler : SqlMapper.TypeHandler<DateTimeOffset>
    {
        public override void SetValue(IDbDataParameter parameter, DateTimeOffset value)
        {
            parameter.Value = value.ToString();
        }

        public override DateTimeOffset Parse(object value)
        {
            return DateTimeOffset.Parse(value.ToString());
        }
    }

    class ParameterlessTypeConstructor
    {
        public string Id { get; set; }

        public string SomeValue { get; set; }
        public DateTimeOffset SomeDateValue { get; set; }
    }

    class ParameterisedTypeConstructor
    {
        public ParameterisedTypeConstructor(string id, string someValue, DateTimeOffset someDateValue)
        {
            Id = id;
            SomeValue = someValue;
            SomeDateValue = someDateValue;
        }

        public string Id { get; set; }

        public string SomeValue { get; set; }
        public DateTimeOffset SomeDateValue { get; set; }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions