我正在使用 Dapper 敲定一些需要访问 PostgreSQL 数据库的负载测试工具。这个特定版本的 PostgreSQL 本身不支持 GUID,因此 GUID 值存储为 32 个字符串。使用 将值转换为字符串someGuid.ToString("N")
,可以使用 转换回 Guid new Guid(stringValueFromColumn)
。
我的问题是如何让 Dapper 读取字符串并将它们转换回 Guid?
我尝试修改 DbType 映射,但这不起作用。
我正在使用 MySql,但它也有同样的问题,因为我将 Guid 存储为字符串。为了修复映射而不必为列设置别名,我使用了以下内容:
public class MySqlGuidTypeHandler : SqlMapper.TypeHandler<Guid>
{
public override void SetValue(IDbDataParameter parameter, Guid guid)
{
parameter.Value = guid.ToString();
}
public override Guid Parse(object value)
{
return new Guid((string)value);
}
}
在我的 Startup.cs 中:
public void ConfigureServices(IServiceCollection services)
{
SqlMapper.AddTypeHandler(new MySqlGuidTypeHandler());
SqlMapper.RemoveTypeMap(typeof(Guid));
SqlMapper.RemoveTypeMap(typeof(Guid?));
}
也许最简单的方法(无需等待 dapper)是拥有第二个属性:
public Guid Foo {get;set;}
public string FooString {
get { return Foo.ToString("N"); }
set { Foo = new Guid(value); }
}
在您的查询中,将该列别名为FooString
.
当然,那么这就提出了一个问题:dapper 是否应该支持这种类型的东西的私有属性?我说:可能。
这是一个老问题,但我觉得它需要更新,因为 Dapper 现在支持私有属性,Marc 在他的回答中提到了这些属性。
private String UserIDString { get; set; }
public Guid UserID
{
get
{
return new Guid(UserIDString);
}
private set
{
UserID = value;
}
}
然后在 SQL 中为您的 ID 列提供一个别名,以将其映射到私有属性而不是实际属性:
SELECT UserID AS UserIDString FROM....
看起来 PostgreSQL 中已经有 UUID 类型了。但是@Cpt.Ohlund 的解决方案对于 2020 年的 MySQL/MariaDB 来说仍然很棒。
但是解决方案本身可能会导致问题。
当VARCHAR(36)
用于System.Guid
抛出以下异常时:
System.InvalidCastException:从“System.String”到“System.Guid”的无效转换。
@Cpt.Ohlund 的解决方案使它起作用。
但是,如果该列是,CHAR(36)
那么它会System.Guid
自动映射到!如果应用@Cpt.Ohlund 的解决方案,将会有另一个例外:
System.InvalidCastException:无法将“System.Guid”类型的对象转换为“System.String”类型。
异常是由System.Guid
传递给的实例Parse(object value)
而不是字符串引起的。
所以简单的答案是CHAR(36)
在 MySQL 和 MariaDB 中使用它,它就可以工作。
但是,如果您需要处理列的任何字符串类型,则必须使用改进的@Cpt.Ohlund 解决方案:
public class MySqlGuidTypeHandler : SqlMapper.TypeHandler<Guid>
{
public override void SetValue(IDbDataParameter parameter, Guid guid)
{
parameter.Value = guid.ToString();
}
public override Guid Parse(object value)
{
// Dapper may pass a Guid instead of a string
if (value is Guid)
return (Guid)value;
return new Guid((string)value);
}
}
并使用SqlMapper.AddTypeHandler()
.
我一起破解了一个解决方案。据我所知,没有办法指示 Dapper 为特定类型生成备用绑定代码,所以我修改了GetClassDeserializer
方法,如果属性是 guid,则强制取消装箱类型为字符串。接下来,我重新使用了为枚举生成构造函数调用的代码。
这是修改后的代码片段(从 rev.rf6d62f91f31a 的第 761 行开始):
// unbox nullable enums as the primitive, i.e. byte etc
var nullUnderlyingType = Nullable.GetUnderlyingType( item.Info.Type );
var unboxType = nullUnderlyingType != null && nullUnderlyingType.IsEnum ? nullUnderlyingType : item.Info.Type;
if( unboxType == typeof(Guid))
{
unboxType = typeof (string);
}
il.Emit( OpCodes.Unbox_Any, unboxType ); // stack is now [target][target][typed-value]
if ( ( item.Info.Type == typeof( Guid ) && unboxType == typeof( string ) )
|| ( nullUnderlyingType != null && nullUnderlyingType.IsEnum ) )
{
il.Emit( OpCodes.Newobj, item.Info.Type.GetConstructor( new[] { nullUnderlyingType ?? unboxType} ) );
}
il.Emit( OpCodes.Callvirt, item.Info.Setter ); // stack is now [target]
我希望这能有所帮助。
我不必在查询中使用别名。我做了什么:
public abstract class Entity
{
protected Entity()
{
Id = Guid.NewGuid().ToString();
}
public string Id
{
get; set;
}
}
在您的表格中,类型为 varchar