如何解析存储为char(16)的Uuid?

时间:2011-04-20 15:37:57

标签: c# sql guid firebird

我有一个Firebird数据库,它在一个char(16)字段中存储Uuid值。在我的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并将它们存储在本地?

谢谢!

编辑:

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

enter image description here

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

enter image description here

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

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

某些垃圾字符串确实正确,但这是一个没有的例子。这就是我实现阿列克谢的建议,因为我没有看到字符串的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());

4 个答案:

答案 0 :(得分:3)

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

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

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

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

答案 1 :(得分:2)

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

答案 2 :(得分:1)

这个问题相当陈旧,但我在使用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());

答案 3 :(得分:0)

插入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);
    }
}

注意:修复此错误后,请不要使用此类。