【问题标题】:Map string to guid with Dapper使用 Dapper 将字符串映射到 guid
【发布时间】:2011-08-19 10:20:47
【问题描述】:

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

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

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

【问题讨论】:

  • 尽管您在问题中突出地提到了这一点,但我错过了您提到您正在使用“本机不支持 GUID”的数据库。值得注意的是,在这样做的数据库中,可以将 tSQL(或类似)中的 UNIQUEIDENTIFIER 映射到 C# 中的 Guid (e.g.)

标签: c# sql orm dapper


【解决方案1】:

我一起破解了一个解决方案。据我所知,没有办法指示 Dapper 为特定类型生成备用绑定代码,因此我修改了 GetClassDeserializer 方法,如果属性是 guid,则强制取消装箱类型为字符串。 接下来,我重新使用了为枚举生成构造函数调用的代码。

这是修改后的代码 sn-p(从 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]

【讨论】:

  • 类型强制是我故意避免的,你介意在我们的错误跟踪器上发布一个功能请求,看看是否有必要适应,请记住,如果 postgres 中的 guid 会出现惊人的爆炸恰好是 31 个字符长...
  • @Sam - 嗯,GIGO;就个人而言,这不会打扰我。 L2S 在这里做了 一些 转换(包括通过名称作为字符串的枚举)。
  • @Sam 谢谢,我已经发布了功能请求。
【解决方案2】:

也许最简单的方法(无需等待 dapper)是拥有第二个属性:

public Guid Foo {get;set;}

public string FooString {
    get { return Foo.ToString("N"); }
    set { Foo = new Guid(value); }
}

在您的查询中,将该列别名为FooString

当然,那么这就提出了一个问题:dapper 是否应该支持这种类型的东西的私有属性?我说:可能。

【讨论】:

  • 嗯,这比在 Dapper 上破解要干净一些。允许设置私有属性(或字段)会很棒。
  • @Marnix - 是的,我打算添加这些
  • @Marnix - 现在都存在了,顺便说一句(在源代码中 - 我还没有部署 nuget 包)
  • @MarcGravell 你能用一个私有财产的例子来更新你的答案吗?
  • @MarcGravell 谢谢。我用 SQLite 遇到了这个问题,但我试图在 Contrib 库中使用 Get 。这意味着我将不得不改用 Query。
【解决方案3】:

这是一个老问题,但我觉得它需要更新,因为 Dapper 现在支持私有属性,Marc 在 his answer 中引用了这些属性。

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; 吗?否则我认为你有一个∞循环。
【解决方案4】:

我正在使用 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?));
    }

【讨论】:

  • 不知道为什么这不是最受好评的答案,它可以完美运行并且比其他方法更正式
  • 太棒了,比我的解决方案更好。
  • 真的不错,我刚在Sqlite上用过这个
  • 像魅力一样工作,谢谢!完全最简单的解决方案!
  • 有人将此标记为正确答案。此方法也可以正确处理匿名查询参数中的映射 GUID 字段。 RemoveTypeMap 调用也很重要。
【解决方案5】:

希望能帮到你。

我不必在查询中使用别名。我做了什么:

public abstract class Entity
{
    protected Entity()
    {
        Id = Guid.NewGuid().ToString();
    }

    public string Id
    {
        get; set; 
    }
}

在你的表中类型为 varchar

【讨论】:

    【解决方案6】:

    看起来 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'。

    该异常是由传递给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()注册它。

    【讨论】:

      猜你喜欢
      • 2018-11-05
      • 2013-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-23
      • 1970-01-01
      • 2012-08-21
      • 1970-01-01
      相关资源
      最近更新 更多