我正在使用Dapper编写一些负载测试工具,这些工具需要访问PostgreSQL数据库。这个特定版本的PostgreSQL不支持GUID,因此GUID值被存储为32个字符的字符串。将GUID转换为字符串使用someGuid.ToString("N")
,将字符串转换回Guid可以使用new Guid(stringValueFromColumn)
。
我的问题是如何让Dapper读取这些字符串并将它们转换回Guid?
我尝试修改DbType映射,但那并不起作用。
我正在使用Dapper编写一些负载测试工具,这些工具需要访问PostgreSQL数据库。这个特定版本的PostgreSQL不支持GUID,因此GUID值被存储为32个字符的字符串。将GUID转换为字符串使用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?));
}
RemoveTypeMap
调用也很重要。 - Arshia001guid.ToByteArray()
和new Guid((byte[])value)
。 - Hinek也许最简单的方法(无需等待dapper)是拥有第二个属性:
public Guid Foo {get;set;}
public string FooString {
get { return Foo.ToString("N"); }
set { Foo = new Guid(value); }
}
在你的查询中,将该列别名为FooString
。
当然,这引发了一个问题:dapper是否应支持此类私有属性?我想说:可能需要。
VARCHAR(36)
用于System.Guid
时,会抛出以下异常:
@Cpt.Ohlund的解决方案使其可行。System.InvalidCastException:从'System.String'到'System.Guid'的转换无效。
CHAR(36)
,则会自动映射到System.Guid
!如果应用了@Cpt.Ohlund的解决方案,则会出现另一个异常:
该异常是由传递给System.InvalidCastException:无法将类型为'System.Guid'的对象强制转换为类型'System.String'。
Parse(object value)
的实例System.Guid
而不是字符串引起的。
CHAR(36)
就可以了。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 现在支持私有属性,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....
UserIDString = value;
而不是 UserID = value;
?否则我认为你陷入了一个无限循环。 - StayOnTargetGetClassDeserializer
方法,以便在属性是 GUID 时强制将非装箱类型转换为字符串。
接下来,我重用了为枚举生成构造函数调用的代码。// 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