GUID
ODP.NETではGUIDをサポートしています。GUIDは、任意のGUID値を保持するのに十分な大きさのRAW(16)
列に挿入できます。ただし、GUIDを適切に処理するには注意が必要です。これは、Guid(byte[ ])
コンストラクタが使用され、Guid構造体のToByteArray()
メソッドが呼び出されると、NET Guid構造がGUID値の整数ベースの部分に対してバイト値を逆の順序にするためです。
たとえば、入力パラメータとしてGuidがODP.NETに指定されている場合、ODP.NETはRAWバイトとしてOracle Databaseに値を渡す前に、ToByteArray()
メソッドを呼び出します。GUIDが最初に次のバイト・シーケンスを持つ場合、
9D4E51F764A940E4854D667F0DD61093
GUIDのbyte[ ]
表現が次のように変更されます
F7514E9DA964E440854D667F0DD61093
これは、RAW(16)
列に挿入される前に、ODP.NETによって内部的に呼び出されるToByteArray()
メソッドを呼び出すと、前述のように処理されるという意味です。
Oracle Databaseから同じ列値が取得されると、取得される値は次のようになります
F7514E9DA964E440854D667F0DD61093
このように、データベースで格納されます。OracleDataReader
オブジェクトでGetGuid()
メソッドを呼び出すと、ODP.NETは、Guid(byte[ ])
コンストラクタを使用してGuid構造を構築し、これを元のバイト・シーケンスに戻します。
9D4E51F764A940E4854D667F0DD61093
つまり、アプリケーション内のGUIDのバイト・シーケンスは、データベースに格納されている順序とまったく同じではないということです。言い換えると、次のようなリテラル・バイト値を使用してSQLを実行する場合
select * from ... where <raw/guid_column> = '9D4E51F764A940E4854D667F0DD61093';
行は返されません。次のものと同じGUIDが表に挿入されています。
F7514E9DA964E440854D667F0DD61093
アプリケーションで、次のような場合に、一致するGUIDを持つ行を問い合せることができます
-
Guid構造体を入力パラメータとして使用して、guid値をバインドする場合
-
Guid構造体上の
ToByteArray()
メソッド呼出しから返されたbyte[]
からSQLで使用されるバイト・リテラル値を作成する場合
アプリケーション開発者は、バイト・シーケンスが変更される可能性があるため、Guid(byte[ ])コンストラクタとGuid構造体のToByteArray()メソッドを呼び出す場合、注意する必要があります。次の単純なプログラムは、Guid(byte[ ])コンストラクタとToByteArray()メソッドの起動時に、GUID値の整数ベースの部分を戻す方法を示しています。
using System; using System.Text; using System.Data; using Oracle.ManagedDataAccess.Client; class T { static string ByteToString(byte[] data) { StringBuilder sb = new StringBuilder(16); foreach (var b in data) sb.Append($"{b:X2}"); return sb.ToString(); } static void Main() { try { OracleConnection con = new OracleConnection("user id=<user id>;password=<password>;data source=<data source>"); con.Open(); // // Generate a new GUID // Guid guid = Guid.NewGuid(); string original = guid.ToString().ToUpper().Replace("-", ""); Console.WriteLine("Original Guid : " + original); // // Drop the table // OracleCommand cmd = new OracleCommand("drop table test_guid_table", con); try { cmd.ExecuteNonQuery(); } catch {} // // Create the table // cmd.CommandText = "create table test_guid_table (col1 RAW(16), col2 VARCHAR2(64))"; cmd.ExecuteNonQuery(); // // Insert the newly generated GUID to the DB // cmd.CommandText = "insert into test_guid_table values (:1, 'new guid')"; cmd.Parameters.Add(string.Empty, OracleDbType.Raw); cmd.Parameters[0].Value = guid; cmd.ExecuteNonQuery(); // // Query from the test table // cmd.CommandText = "select * from test_guid_table"; OracleDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { // // Get the RAW data as byte[] // byte[] guid_byte_array = (byte[])reader.GetValue(0); Console.WriteLine("GetValue() as byte[] / as-is in DB: " + ByteToString(guid_byte_array)); Console.WriteLine(); // // Get the RAW data as Guid then convert to byte[] // Guid retrieved_guid = (Guid)reader.GetGuid(0); byte[] retrieved_guid_byte_array = retrieved_guid.ToByteArray(); Console.WriteLine("GetGuid() then Guid.ToString() : " + retrieved_guid_byte_array.ToString()); Console.WriteLine("GetGuid() then Guid.ToByteArray() : " + ByteToString(retrieved_guid.ToByteArray())); } // // Find a matching row by binding the original GUID as-is // cmd.Parameters.Clear(); Console.WriteLine("\nGuid Input Parameter : " + ByteToString(guid.ToByteArray())); cmd.CommandText = "select count(*) from test_guid_table where col1 = :1"; cmd.Parameters.Add(string.Empty, OracleDbType.Raw); cmd.Parameters[0].Value = guid; reader = cmd.ExecuteReader(); while (reader.Read()) { Console.WriteLine("Rows found by binding GUID : " + (decimal)reader.GetValue(0)); } // // Find a matching row by binding a byte[] from the original GUID // byte[] byte_array_param = guid.ToByteArray(); Console.WriteLine("\nbyte[] Input Parameter : " + ByteToString(byte_array_param)); cmd.CommandText = "select count(*) from test_guid_table where col1 = :1"; cmd.Parameters.Clear(); cmd.Parameters.Add(string.Empty, OracleDbType.Raw); cmd.Parameters[0].Value = byte_array_param; reader = cmd.ExecuteReader(); while (reader.Read()) { Console.WriteLine("Rows found by binding byte[] : " + (decimal)reader.GetValue(0)); } // // Find a matching row by matching the binary/raw data (inline) using Guid.ToByteArray() // cmd.CommandText = "select count(*) from test_guid_table where col1 = '" + ByteToString(guid.ToByteArray()) + "'"; Console.WriteLine("\nLiteral RAW (from byte array) : " + ByteToString(guid.ToByteArray())); cmd.Parameters.Clear(); reader = cmd.ExecuteReader(); while (reader.Read()) { Console.WriteLine("Rows found by inlined data : " + (decimal)reader.GetValue(0)); } // // Find a matching row by matching the binary/raw data (inline) using Guid.ToString() // cmd.CommandText = "select count(*) from test_guid_table where col1 = '" + original + "'"; Console.WriteLine("\nLiteral RAW (from string) : " + original); cmd.Parameters.Clear(); reader = cmd.ExecuteReader(); while (reader.Read()) { Console.WriteLine("Rows found by inlined data : " + (decimal)reader.GetValue(0)); } } catch (Exception ex) { Console.WriteLine(ex); } } }
サンプル・コードからのサンプル出力は次のようになります。
Original Guid : D54909F4169541CFA919F6752414909F GetValue() as byte[] / as-is in DB: F40949D59516CF41A919F6752414909F GetGuid() then Guid.ToString() : System.Byte[] GetGuid() then Guid.ToByteArray() : F40949D59516CF41A919F6752414909F Guid Input Parameter : F40949D59516CF41A919F6752414909F Rows found by binding GUID : 1 byte[] Input Parameter : F40949D59516CF41A919F6752414909F Rows found by binding byte[] : 1 Literal RAW (from byte array) : F40949D59516CF41A919F6752414909F Rows found by inlined data : 1 Literal RAW (from string) : D54909F4169541CFA919F6752414909F Rows found by inlined data : 0
ノート:
テストを実行するたびに、新しいGUIDまたは異なるGUIDが生成されます。