【问题标题】:How to parse a Uuid stored as a char(16)?如何解析存储为 char(16) 的 Uuid?
【发布时间】:2011-04-20 15:37:57
【问题描述】:

我有一个 Firebird 数据库,它将 Uuid 值存储在 char(16) 字段中。在我的 C# 程序中,我需要检索这些值以便在后续查询中使用它们。但是我从数据库中得到的值是“垃圾”。 (例如¿­ñ)êNµmÏc—ÝX)我尝试了各种方法将数据读取为字节数组并使用它来创建本地Guid,但这并没有让我有任何收获。我得到的最接近的是使用Encoding.ASCII.GetBytes(),它确实给了我一个“有效”的guid,但它与数据库中的“真实”guid 不匹配。我知道它们不匹配,因为(1)数据库有一个 UDF,它将“垃圾”转换为人类可读的字符串,以及(2)当我手动将该人类可读的 guid 复制到我的应用程序中时,使用它来创建一个新的本地Guid,并在我的查询中使用那个值,我得到了正确的结果。 (而从字节数组创建的 guid 不会产生正确的结果。)我也尝试了 IDataRecord.GetBytes() 但这导致了 InvalidCastException(“无法将类型为 'System.String' 的对象转换为类型 'System.Byte[]'。” )

此应用程序使用由另一个不相关产品创建的数据库,我无法控制其结构,也无法使用存储过程之类的东西。我也不能使用我之前提到的 UDF,因为它们很快就要再见了。可用的 UDF 显然是 UuidToStringUuidFromString 的简单包装器。我想我可以在我自己的代码中使用这些函数,但如果有另一种不涉及互操作的方法,我宁愿不这样做。

最终,我需要 guid 以如下所示的方法工作:

protected DataTable QueryDataTable(string query, string paramName, Guid guid)
{
    DataTable table = new DataTable();
    IDbCommand command = CreateDbCommand(query);
    if (command is FbCommand)
    {
        FbCommand fbCommand = (FbCommand)command;
        fbCommand.Parameters.Add(paramName, FbDbType.Binary).Value = 
            guid.ToByteArray();

        // Also tried passing in the byte[] generated by 
        // Encoding.Ascii.GetBytes.... didn't work.
        //fbCommand.Parameters.Add(paramName, FbDbType.Binary).Value = guid;

        FbDataAdapter adapter = new FbDataAdapter(fbCommand);
        adapter.Fill(table);
    }

    return table;
}

问题:

(1) 为什么我使用字节数组创建 guid 的策略不能产生“正确”的 guid?

(2) 我还可以使用哪些其他策略来提取这些 guid 并将它们存储在本地?

谢谢!

编辑:

这是我面前的一个例子。来自数据库:

从垃圾字符串产生的字符数组:

使用@Alexei Levenkov 建议的策略,生成的 guid (1) 非常接近但不完全正确 (2)。

(1): fca3120b-511e-4269-b88f-d053a34b3513
(2):fca3120b-5184-4269-b88f-d053a34b3596

有些垃圾字符串确实正确,但这是一个错误的例子。这就是我实施 Alexei 建议的方式,因为我没有看到字符串的 Select 方法:

// table is a DataTable
List<byte> bytes = new List<byte>();
string blah = (string)table.Rows[0][0];
foreach (char c in blah.ToCharArray())
{
    bytes.Add((byte)c);
}

Guid guid = new Guid(bytes.ToArray());

【问题讨论】:

  • char 数组是什么样的(16 个字符是什么)?预期的 GUI 是什么?
  • @Steve,我更新了我的问题。

标签: c# sql guid firebird


【解决方案1】:

Char(16) 看起来像 GUID 的字节表示。尝试将字符串的每个字符转换为字节数组,然后从中创建 GUID

new Guid("0000000000000000".Select(c=> (byte)c).ToArray())

对于反向转换,使用 guid.ToByteArray() 并将其转换为具有 ASCII 编码的字符串。

Encoding.ASCII.GetString(Guid.Empty.ToByteArray())

【讨论】:

  • 太接近了!查看我的问题的更新。还有什么建议吗?
  • Select 来自 System.Linq 作为 IEnumerable 的扩展,但您的实现也很好。我不确定为什么有些字符会被转换为像 82XX 这样大的值。我会尝试将此字段作为字节数组而不是字符串来获取(如果不四处搜索,我不知道该怎么做)。
  • 好的,谢谢。我还没有找到一种将字段作为字节数组读取的方法,但到目前为止我很感激你的帮助。
【解决方案2】:

在 Firebird 2.5 中,您有内置函数来制作 this

【讨论】:

  • 信不信由你,当我使用 CHAR_TO_UUID() 时,它返回的 guid 值与 UDF 完全不同。当我开始工作时,我会更新我的问题,以展示我使用原生 CHAR_TO_UUID() 函数与 UDF 相比得到了什么。
  • (呃,我的意思是上面的 UUID_TO_CHAR()。)
  • 显然我昨天在尝试这些功能时搞砸了。他们完美地工作。感谢您指出对我来说显而易见的事情。 =P
【解决方案3】:

这个问题已经很老了,但我在使用 Firebird 2.0(没有内置 UUID 生成器)时遇到了类似的问题。

因此,上述代码的主要问题是错误的参数类型(二进制)。它应该是 FbDbType.Char 或 FbDbType.Guid。下面是一个工作示例。

Guid newGuid = Guid.NewGuid();
Guid retrieved = Guid.Empty;
    using (FbConnection conn = new FbConnection(connectionString)) {
        conn.Open();

        using (FbCommand cmd = conn.CreateCommand()) {
        // first create the table for testing
        cmd.CommandText = "recreate table GUID_test (guid char(16) character set octets)";
        cmd.ExecuteNonQuery();
    }

    using (FbCommand cmd = conn.CreateCommand()) {
        // inserting GUID into db table  
        cmd.CommandText = "insert into GUID_test values (@guid)";

        // classic way, works good
        //cmd.Parameters.Add("@guid", FbDbType.Char, 16).Value = newGuid.ToByteArray();

        // another way, maybe better readability, but same result
        cmd.Parameters.Add("@guid", FbDbType.Guid).Value = newGuid;

        cmd.ExecuteNonQuery();
    }

    using (FbCommand cmd = conn.CreateCommand()) {
        // reading GUID back from db  
        cmd.CommandText = "select first 1 guid from GUID_test";

        retrieved = (Guid)cmd.ExecuteScalar();
    }


    using (FbCommand cmd = conn.CreateCommand()) {
        // drop the table, it has no real application
        cmd.CommandText = "drop table GUID_test";
        cmd.ExecuteNonQuery();
    }
}
MessageBox.Show(newGuid.Equals(retrieved).ToString());

【讨论】:

    【解决方案4】:

    插入 guid 值时,将它们作为查询传递给

     "INSERT INTO MyTable(GuidCol) VALUES (CHAR_TO_UUID(" + yourGuid.ToString() + "))"
    

    阅读时,您可以使用以下类从 Firebird .NET 数据提供程序中纠正错误解析的值:

    public class FirebirdCorrectingReader : IDataReader {
        private readonly IDataReader _decoratedReader;
    
        public FirebirdCorrectingReader(IDataReader decoratedReader) {
            _decoratedReader = decoratedReader; 
        }
    
        #region DataReader Impl
    
        public void Dispose() {
            _decoratedReader.Dispose();
        }
    
        public string GetName(int i) {
            return _decoratedReader.GetName(i);
        }
    
        public string GetDataTypeName(int i) {
            return _decoratedReader.GetDataTypeName(i);
        }
    
        public Type GetFieldType(int i) {
            return _decoratedReader.GetFieldType(i);
        }
    
        public object GetValue(int i) {
            var result = _decoratedReader.GetValue(i);
            if (result is Guid) {
                result = CorrectGuid((Guid)result);
            }
            return result;
        }
    
        public int GetValues(object[] values) {
            return _decoratedReader.GetValues(values);
        }
    
        public int GetOrdinal(string name) {
            return _decoratedReader.GetOrdinal(name);
        }
    
        public bool GetBoolean(int i) {
            return _decoratedReader.GetBoolean(i);
        }
    
        public byte GetByte(int i) {
            return _decoratedReader.GetByte(i);
        }
    
        public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length) {
            return _decoratedReader.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
        }
    
        public char GetChar(int i) {
            return _decoratedReader.GetChar(i);
        }
    
        public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length) {
            return _decoratedReader.GetChars(i, fieldoffset, buffer, bufferoffset, length);
        }
    
        public Guid GetGuid(int i) {
            return CorrectGuid(_decoratedReader.GetGuid(i));
        }
    
        public short GetInt16(int i) {
            return _decoratedReader.GetInt16(i);
        }
    
        public int GetInt32(int i) {
            return _decoratedReader.GetInt32(i);
        }
    
        public long GetInt64(int i) {
            return _decoratedReader.GetInt64(i);
        }
    
        public float GetFloat(int i) {
            return _decoratedReader.GetFloat(i);
        }
    
        public double GetDouble(int i) {
            return _decoratedReader.GetDouble(i);
        }
    
        public string GetString(int i) {
            return _decoratedReader.GetString(i);
        }
    
        public decimal GetDecimal(int i) {
            return _decoratedReader.GetDecimal(i);
        }
    
        public DateTime GetDateTime(int i) {
            return _decoratedReader.GetDateTime(i);
        }
    
        public IDataReader GetData(int i) {
            return _decoratedReader.GetData(i);
        }
    
        public bool IsDBNull(int i) {
            return _decoratedReader.IsDBNull(i);
        }
    
        public int FieldCount { get { return _decoratedReader.FieldCount; } }
    
        object IDataRecord.this[int i] {
            get { return _decoratedReader[i]; }
        }
    
        object IDataRecord.this[string name] {
            get {return _decoratedReader[name]; }
        }
    
        public void Close() {
            _decoratedReader.Close();
        }
    
        public DataTable GetSchemaTable() {
            return _decoratedReader.GetSchemaTable();
        }
    
        public bool NextResult() {
            return _decoratedReader.NextResult();
        }
    
        public bool Read() {
            return _decoratedReader.Read();
        }
    
        public int Depth { get { return _decoratedReader.Depth; } }
        public bool IsClosed { get { return _decoratedReader.IsClosed; } }
        public int RecordsAffected { get { return _decoratedReader.RecordsAffected; } }
    
        #endregion
    
        public static Guid CorrectGuid(Guid badlyParsedGuid) {
            var rfc4122bytes = badlyParsedGuid.ToByteArray();
            if (BitConverter.IsLittleEndian) {
                Array.Reverse(rfc4122bytes, 0, 4);
                Array.Reverse(rfc4122bytes, 4, 2);
                Array.Reverse(rfc4122bytes, 6, 2);
            }
            return new Guid(rfc4122bytes);
        }
    }
    

    注意:修复了这个bug后不要使用这个类。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      • 1970-01-01
      • 2022-08-17
      • 1970-01-01
      相关资源
      最近更新 更多