没有调用Dapper TypeHandler.SetValue()

Ber*_*tol 8 c# dapper

我正在测试Dapper将对象加载/持久化到Oracle数据库,并且管理Oracle的Guid存储我需要一个SqlMapper.TypeHandler<Guid>.从数据库加载Guid列时,会调用Parse方法,但是当我尝试使用Guid参数执行SQL语句时,我得到以下异常:

System.ArgumentException未处理; Message = Value不在预期范围内.Source = Oracle.DataAccess.

在调试中我可以看到我从数据库加载我的类时调用了我的处理程序的Parse()方法,但是SetValue()mdethod却没有.

重现异常的代码如下


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;
Run Code Online (Sandbox Code Playgroud)
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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Tro*_*tun 8

万一其他人偶然发现这篇文章有类似的问题,我找到了一种Guid无需包装器即可处理s的解决方案。

Dapper 中的问题在于 Dapper 搜索匹配DbTypeTypeHandler实现的顺序。DbType对于 a Guid(in SqlMapper#LookupDbType),Dapper 更喜欢“本机” 。为了让 Dapper 使用您自己的实现,除了添加您自己的实现之外,您还必须删除默认映射TypeHandler

SqlMapper.AddTypeHandler<Guid>(new GuidTypeHandler());
SqlMapper.RemoveTypeMap(typeof(Guid));
SqlMapper.RemoveTypeMap(typeof(Guid?));
Run Code Online (Sandbox Code Playgroud)

我目前在使用stringSQLite 时使用基于 - 的实现:

public class GuidAsStringHandler : SqlMapper.TypeHandler<Guid>
{
    public override Guid Parse(object value)
    {
        return new Guid((string) value);
    }

    public override void SetValue(IDbDataParameter parameter, Guid value)
    {
        parameter.Value = value.ToString();
    }
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*bos -1

我通过围绕 .NET Guid 类编写一个包装器来解决这个问题。这并不理想,因为您最终会在 DTO 类中使用包装器,但它可以工作。

包装类:

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; } }
}
Run Code Online (Sandbox Code Playgroud)

处理程序类:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

使用包装类的 DTO 类:

public class FooDto
{
    public OracleGuid Id { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

注意我使用 RAW(16) 将它们存储在 Oracle 中,而不是内置的 Oracle Guid。

编辑 看起来这可能是一个错误并且可能已被修复: https: //github.com/StackExchange/dapper-dot-net/issues/253。看起来它还没有进入 NuGet 包,所以我还没有尝试过。