Dapper TypeHandler.SetValue() not being called

c# dapper

Question

I am testing Dapper to load / persist objects to an Oracle database, and to manage Oracle's Guid storage I need a SqlMapper.TypeHandler<Guid>. When loading a Guid column from the database the Parse method is called, but when I attempt to execute an SQL statement using a Guid parameter I get the following exception:

System.ArgumentException was unhandled; Message=Value does not fall within the expected range.Source=Oracle.DataAccess.

In debug I can see that my handler's Parse() method is being called when loading my class from the database, but the SetValue() mdethod is not.

The code to reproduce the exception is below


CREATE TABLE foo (id     RAW (16) NOT NULL PRIMARY KEY,
                  name   VARCHAR2 (30) NOT NULL);

INSERT INTO foo (id, name) VALUES (SYS_GUID (), 'Bar');

COMMIT;

using System;
using System.Linq;
using Dapper;
using Oracle.DataAccess.Client;

namespace Program
{
    public class Foo
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

    class GuidTypeHandler : SqlMapper.TypeHandler<Guid>
    {
        public override Guid Parse(object value)
        {
            Console.WriteLine("Handling Parse of {0}", value);

            var inVal = (byte[])value;
            byte[] outVal = new byte[] { inVal[3], inVal[2], inVal[1], inVal[0], inVal[5], inVal[4], inVal[7], inVal[6], inVal[8], inVal[9], inVal[10], inVal[11], inVal[12], inVal[13], inVal[14], inVal[15] };
            return new Guid(outVal);
        }

        public override void SetValue(System.Data.IDbDataParameter parameter, Guid value)
        {
            Console.WriteLine("Handling Setvalue of {0}", value);

            var inVal = value.ToByteArray();
            byte[] outVal = new byte[] { inVal[3], inVal[2], inVal[1], inVal[0], inVal[5], inVal[4], inVal[7], inVal[6], inVal[8], inVal[9], inVal[10], inVal[11], inVal[12], inVal[13], inVal[14], inVal[15] };
            parameter.Value = outVal;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SqlMapper.AddTypeHandler<Guid>(new GuidTypeHandler());
            var conn = new OracleConnection(Resources.ConnectionString);
            var def = new CommandDefinition("select id, name from foo");

            conn.Open();

            var foo = conn.Query<Foo>(def).First();
            Console.WriteLine(foo.Id + "; " + foo.Name);

            foo.Name = "New Bar";

            def = new CommandDefinition(
                "UPDATE foo SET name = :name WHERE id = :id",
                parameters: new { ID = foo.Id, NAME = foo.Name });

            var rows = conn.Execute(def);
            Console.WriteLine("{0} rows inserted", rows);

            Console.ReadLine();
        }
    }
}

Popular Answer

I worked around this issue by writing a wrapper around the .NET Guid class. Not ideal since you end up with the wrapper in your DTO classes but it works.

The wrapper class:

public class OracleGuid
{
    private Guid dotNetGuid;

    public OracleGuid(Guid guid)
    {
        this.dotNetGuid = guid;
    }

    public OracleGuid(Byte[] byteArray)
    {
        this.dotNetGuid = new Guid(byteArray);

    }

    public Guid InternalGuid { get { return dotNetGuid; } }
}

The handler class:

public class OracleGuidHandler : SqlMapper.TypeHandler<OracleGuid>
{
    public override OracleGuid Parse(object value)
    {
        return new OracleGuid((byte[]) value);
    }

    public override void SetValue(System.Data.IDbDataParameter parameter, OracleGuid value)
    {
        parameter.Value = value.InternalGuid.ToByteArray();
    }
}

A DTO class that makes use of the wrapper class:

public class FooDto
{
    public OracleGuid Id { get; set; }
    public string Name { get; set; }
}

Note I'm using RAW(16) to store these in Oracle, not the built in Oracle Guids.

EDIT Looks like this may have been a bug and may have been fixed: https://github.com/StackExchange/dapper-dot-net/issues/253. Doesn't look like it has made it into the NuGet package yet so I haven't tried it out yet.




Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why