我正在使用Dapper来敲除一些需要访问PostgreSQL数据库的负载测试工具。 PostgreSQL的这个特定版本本身不支持GUID,因此GUID值存储为32个字符串。使用someGuid.ToString("N")
将值转换为字符串,可以使用new Guid(stringValueFromColumn)
转换回Guid。
我的问题是如何让Dapper读取字符串并将其转换回Guids?
我尝试修改DbType映射,但这不起作用。
答案 0 :(得分:18)
这样做的最简单方法(无需等待精简)也许是第二个属性:
public Guid Foo {get;set;}
public string FooString {
get { return Foo.ToString("N"); }
set { Foo = new Guid(value); }
}
在您的查询中,将列别名为FooString
。
当然,这会引发一个问题:应该为这类事情支持私人财产吗?我说的话:可能。
答案 1 :(得分:4)
似乎PostgreSQL中已经有UUID类型。但是@ Cpt.Ohlund的solution对于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)
而不是字符串引起的。
因此,简单的答案是在MySQL和MariaDB中使用CHAR(36)
,它将正常工作。
但是,如果您需要处理列的任何字符串类型,则必须使用改进的@ 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()
注册。
答案 2 :(得分:3)
这是一个老问题,但我觉得它需要更新,因为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....
答案 3 :(得分:3)
我知道这是一个老问题,但是对于像今天这样绊脚石的任何人,我都会发布解决方案。
我正在使用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?));
}
答案 4 :(得分:2)
我一起破解了一个解决方案。据我所知,没有办法指示Dapper为特定类型生成备用绑定代码,因此我修改了GetClassDeserializer
方法,以便在属性为guid时强制unbox类型为字符串。
接下来,我重新使用了为枚举生成构造函数调用的代码。
这是修改后的代码片段(从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]
答案 5 :(得分:0)
我希望能有所帮助。
我不必在查询中使用别名。我做了什么:
public abstract class Entity
{
protected Entity()
{
Id = Guid.NewGuid().ToString();
}
public string Id
{
get; set;
}
}
在您的表中,类型为varchar