繁体   English   中英

Map 用 Dapper 引导字符串

[英]Map string to guid with Dapper

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

我的问题是如何让 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 列一个别名 map 它是私有财产而不是实际财产:

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”类型的 object 转换为“System.String”类型。

该异常是由传递给Parse(object value)而不是字符串的实例System.Guid引起的。


所以简单的答案是在 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()注册它。

我一起破解了一个解决方案。 据我所知,没有办法指示 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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM