Dapper DateTime to Protobuf TimeStamp

invalid cast from 'system.datetime' to 'google.protobuf.wellknowntypes.timestamp'

Arjun Krishna

2 minute read

I was trying blazor with Grpc-web in .net core and wanted it to work with a postgres sample table. I was feeling lazy and did not want to create a DTO object in between. This attempt to save time became a time waster very fast. :)

I received an error “Invalid cast from ‘System.DateTime’ to ‘Google.Protobuf.WellKnownTypes.Timestamp’”. I immediately thought of adding a DTO and populate the data and then translate it to protobuf type. I then thought, this is just a sample, not office work, so no deadline. I just started looking into dapper (I am new to it, yep… have been living under a rock…hehe) and how it handles translation. Thankfully their documentation is straight forward. I found out that you can inject a Type Handler very easily.

solution

Added a class specifically for Protobuf’s Timestamp. On their website there were other beautiful examples with lot of checks and switch statements to make generic Type handlers which helped me in understanding how to solve my little annoyance at hand.

     public class ProtobufTimestampHandler : SqlMapper.TypeHandler<Google.Protobuf.WellKnownTypes.Timestamp>
    {        
        public override void SetValue(IDbDataParameter parameter, Google.Protobuf.WellKnownTypes.Timestamp value)
        {
            parameter.Value = value;
        }        

        public override Google.Protobuf.WellKnownTypes.Timestamp Parse(object value)
        {
            return Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(DateTime.SpecifyKind((DateTime)value,DateTimeKind.Utc));            
        }
    }

and then used it right before calling the DataQuery (I did not want to waste more time on this adding it to some common bootstrap, just wanted to play with Grpc stuff).

        SqlMapper.ResetTypeHandlers();
        SqlMapper.AddTypeHandler(new ProtobufTimestampHandler());
        using (var connection = new NpgsqlConnection(_connectionString))
        {
            connection.Open();                                        
            return connection.Query<T>(sql: commandText, param: paramList, commandTimeout: commandtimeout);
        }

and it worked! I am just reading data… so did not test the set operation yet. I am sure, something needs to be done there too. Most probably tomorrow will hit that area. Time to go and eat ice cream with kids and watch a movie.

Adding it to this blog because I’ll forget it within a week.