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

发布于 2024-11-02 16:14:34 字数 2506 浏览 1 评论 0原文

我有一个 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 显然是 UuidToString< 的简单包装器/a> 和 UuidFromString。我想我可以在我自己的代码中使用这些函数,但如果有另一种不涉及互操作的方法,我宁愿不这样做。

最终,我需要 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) 我还可以使用哪些其他策略来提取这些指南并将它们存储在本地?

谢谢!

编辑:

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

在此处输入图像描述

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

在此处输入图像描述

使用 @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());

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:

enter image description here

Resulting char array from the garbage string:

enter image description here

Using the tactic suggested by @Alexei Levenkov, the resulting guid (1) is very close but not exactly 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());

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

揪着可爱 2024-11-09 16:14:34

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

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

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

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

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())
很酷不放纵 2024-11-09 16:14:34

在 Firebird 2.5 中,您有内置函数可以生成 this

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

彻夜缠绵 2024-11-09 16:14:34

这个问题很老了,但我在使用 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());

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());
飘然心甜 2024-11-09 16:14:34

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

注意:出现此错误时不要使用此类是固定的。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文