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が生成されます。