SQLを発行した結果の列名を取得する方法です。
DbDataRecordからSchemaInfoを取得して列名と型を取得しようと思います。
C#でSQLをダイレクトに実行する場合、コネクションを取得しコマンドを発行した後、SqliteDataReader で結果を読み取るのが一般的です。その後、列を foreach で DbDataRecord にして回したりしますが、結果の列名が何か気になるときが時があると思います。
この時、DbDataRecord 型を使用していますが、実際は System.Data.Common.DataRecordInternal 型が返ってきていて、この DataRecordInternal の内部にスキーマの情報が SchemaInfo型の配列として保持されています。ただ DataRecordInternal は internal 型のため通常外部からはアクセスできません。同じくスキーマ情報が記録されている SchemaInfo 型も internal なのでアクセスできません。
なのでリフレクションを使用してオブジェクトの内部データを強引に引き抜きたいと思います。
確認環境
- VisualStudio2017
- .NET 4.6.2
- SQLite3
実装コード
まず、結果を入れるために SchemaInfo を以下のように定義します。
// SchemaInfo.cs using System; public class SchemaInfo { public readonly Type Type; public readonly string Name = ""; public readonly string TypeName = ""; public SchemaInfo(Type type, string name, string typeName) { Type = type; Name = name; TypeName = typeName; } }
次にデータを引き抜くための拡張メソッドを以下の通り定義します。
// DbDataRecordExtensions.cs using System; using System.Data.Common; using System.Reflection; public static class DbDataRecordExtensions { public static SchemaInfo[] GetSchemaInfo(this DbDataRecord self) { // (1) System.Data.Common.SchemaInfo[] を取得する Type type = self.GetType(); FieldInfo field = type.GetField("_schemaInfo", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance); // (2) 配列を順番にアクセスしてフィールドを自作のオブジェクトに詰める Array schemaInfoArray = field.GetValue(self) as Array; SchemaInfo[] retArray = new SchemaInfo[schemaInfoArray.Length]; Type t = null; for (int i = 0; i < schemaInfoArray.Length; i++) { object item = schemaInfoArray.GetValue(i); if (t == null) t = item.GetType(); FieldInfo fieldName = t.GetField("name"); string name = fieldName.GetValue(item) as string; FieldInfo fieldTypeName = t.GetField("typeName"); string typeName = fieldTypeName.GetValue(item) as string; FieldInfo fieldType = t.GetField("type"); Type scType = fieldType.GetValue(item) as Type; retArray[i] = new SchemaInfo(scType, name, typeName); } return retArray; } }
実際の使用方法ですが、任意のレコードを取得する以下の実装があったとします。
internal void QueryTest(string dbPath) { // テーブル定義は以下の通り // a (int) | b (int) | c (string) string query = "SELECT * FROM _table"; using (var connection = new SqliteConnection(GetConnectionString(dbPath))) { connection.Open(); using (SqliteCommand command = connection.CreateCommand()) { command.CommandText = query; command.ExecuteNonQuery(); using (SqliteDataReader reader = command.ExecuteReader()) { bool once = false; foreach (DbDataRecord item in reader) { if (!once) // 何度も実行しない { once = true; // ★ここでスキーマ情報を取得する SchemaInfo[] infoArray = item.GetSchemaInfo(); // 以下のように情報が取得できる // [0] INTEGER, a // [1] INTEGER, b // [2] TEXT, c } for (int i = 0; i < item.FieldCount; i++) { var temp = item[i]; Console.Write($"{temp}, "); } } } } } }
これで列名が取得できるようになりました。