开发者

How to parse a Uuid stored as a char(16)?

I have a Firebird database which stores Uuid values in a char(16) field. In my C# program, I need to retrieve these values in order to use them in subsequent queries. But the values I get back from the database are "garbage". (e.g. ¿­ñ)êNµmÏc—ÝX) I've tried various methods of reading the data as a byte array and using that to create a local Guid, but that hasn't gotten me anywhere. The closest I've gotten was by using Encoding.ASCII.GetBytes() which did give me a "valid" guid, however it doesn't match the "real" guids in the database. I know they don't match because (1) the database has a UDF which converts the 'garbage' into a human readable string and (2) when I manually copy that human readable guid into my application, using it to create a new local Guid, and use THAT value in my queries, I get the correct results. (Whereas guids created from byte arrays do not yield correct results.) I also tried IDataRecord.GetBytes() but that resulted in an InvalidCastException ("Unable to cast object of type 'System.String' to type 'System.Byte[]'.")

This application uses databases created by another, unrelated product and I have no control over its structure, nor can I use things like stored procedures. I also cannot use the UDF's I mentioned earlier because they are going bye-bye soon. The available UDF's apparently are simple wrappers around UuidToString and UuidFromString. I suppose I could use those functions in my own code, but I'd rather not if there's another way that doesn't involve interop.

Ultimately, the I need the guid to work in a method that looks something like this:

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;
}

Questions:

(1) Why isn't my tactic of using a byte array to create a guid resulting in a "correct" guid?

(2) What other tactics might I use to extract these guids and store them as locally as such?

Thanks!

Edit:

Here's an example of what I've got in front of me. From the database:

How to parse a Uuid stored as a char(16)?

Resulting char array from the garbage string:

How to parse a Uuid stored as a char(16)?

Using the tactic suggested by @Alexei Levenkov, the resulting guid (1) is very close but not exa开发者_如何学JAVActly correct (2).

(1): fca3120b-511e-4269-b88f-d053a34b3513

(2): fca3120b-5184-4269-b88f-d053a34b3596

SOME garbage strings do turn out correctly, but this is an example of one that does not. This is how I'm implementing Alexei's suggestion, as I didn't see a Select method for strings:

// 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) looks like byte representation of the GUID. Try converting each character of the string to a byte array and than creating GUID out of it

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

For reverse conversion use guid.ToByteArray() and convert it to string with ASCII encoding.

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


in Firebird 2.5 you have built-in function to make this


This question is pretty old but I just had similar problem, while working with Firebird 2.0 (no built-in UUID generator).

So, the main problem with the code presented above was wrong parameter type (binary). It should either be FbDbType.Char or FbDbType.Guid. Below is a working example.

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());


When inserting guids values, pass them in your query as

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

when reading, you can correct the badly parsed value from the Firebird .NET Data Provider using the below class:

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

Note: Do not use this class when this bug is fixed.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜