Dapper TypeHandler.SetValue() 方法未被调用

14

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

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

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

以下是复现异常的代码


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();
        }
    }
}

你最终解决了如何让它工作的问题吗?当我尝试将自定义对象数组传递到查询中时(例如,“SELECT * FROM myTable WHERE id = ANY(:ids)”,并且typeof(:ids) == typeof(myType[])),我遇到了类似的问题。 - Jeff G
不幸的是,也许值得在 Github 上的 Dapper 问题列表中重新发布查询。 - Bertol
2个回答

15

如果有其他人遇到类似问题,我找到了一种处理 Guid 的解决方案,无需使用包装。

Dapper 中的问题在于 Dapper 搜索匹配 DbTypeTypeHandler 实现的顺序。Dapper 更喜欢 SqlMapper#LookupDbType 中的 "本机" DbType 来处理 Guid。为了让 Dapper 使用您自己的实现,除了添加自己的 TypeHandler 外,还必须删除默认映射:

SqlMapper.AddTypeHandler<Guid>(new GuidTypeHandler());
SqlMapper.RemoveTypeMap(typeof(Guid));
SqlMapper.RemoveTypeMap(typeof(Guid?));

目前我在使用SQLite时采用基于string的实现:

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();
    }
}

-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; } }
}

处理程序类:
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();
    }
}

一个使用包装类的DTO类:
public class FooDto
{
    public OracleGuid Id { get; set; }
    public string Name { get; set; }
}

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接