使用Dapper将字符串映射到guid

时间:2011-05-05 14:03:33

标签: c# sql orm dapper

我正在使用Dapper来敲除一些需要访问PostgreSQL数据库的负载测试工具。 PostgreSQL的这个特定版本本身不支持GUID,因此GUID值存储为32个字符串。使用someGuid.ToString("N")将值转换为字符串,可以使用new Guid(stringValueFromColumn)转换回Guid。

我的问题是如何让Dapper读取字符串并将其转换回Guids?

我尝试修改DbType映射,但这不起作用。

6 个答案:

答案 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