我正在测试Dapper将对象加载/持久化到Oracle数据库,并且为了管理Oracle的Guid存储,我需要SqlMapper.TypeHandler<Guid>
。从数据库加载Guid列时,会调用Parse方法,但是当我尝试使用Guid参数执行SQL语句时,我得到以下异常:
System.ArgumentException未处理;消息=值不会下降 在预期范围内.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;
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();
}
}
}
答案 0 :(得分:0)
我通过编写.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包,所以我还没有尝试过。
答案 1 :(得分:0)
万一其他人因类似问题偶然发现这篇文章,我发现了一种无需包装即可处理Guid
的解决方案。
Dapper中的问题是Dapper搜索匹配的DbType
和TypeHandler
实现的顺序。 Dapper偏爱DbType
(在Guid
中使用“本地” SqlMapper#LookupDbType
。为了使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();
}
}