質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

8260閲覧

DataTableに設定された検索結果を、自作のEntityで簡単に受け取りたい

cha-ra

総合スコア40

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2016/07/22 11:19

###前提・実現したいこと
DataTableにセットされたSQLの結果を、自前のクラスに移し替えたいのですが、
DataRowに詰まっている値の型と、受け手側の型が一致しない為にキャストエラーとなってしまいます。

出来る限りEntityクラスの数値項目等は int か decimal で統一して、
テーブルの桁数変更等の影響を受けないようにしたいのですが、
DBの型とEntityの型を厳密にリンクさせずに、
検索結果をキャストエラーとさせずに取得する方法はないものでしょうか?

Javaで言うBeanUtilsの様なクラスがあれば良いのですが。。。

###発生している問題・エラーメッセージ

指定されたキャストは有効ではありません

###該当のソースコード
対象テーブルの定義

SQL

1Person ( 2 Age NUMBER(3) NOT NULL 3, Name VARCHAR2(20) NOT NULL 4, JobType NUMBER(1) 5, Weight NUMBER(3,3) 6)

実行エラーとなる箇所

c#

1 DataTable dt = selectAllPerson(); 2 BindingList<Person>( 3 dt.AsEnumerable().Select(r => new Person() { 4 Age = (int)(short) r["Age"] // エラーは発生しないが、内部の型を知っている必要があるのでNG 5 , Name = (string) r["Name"] 6 , JobType = r.Field<int?>("JobType") // #キャストエラー (内部がobject{short}) 7 , Weight = r.Field<decimal?>("Weight") // #キャストエラー (内部がobject{float}) 8 }

c#

1class Person { 2 public int Age { get; set; } 3 public string Name { get; set; } 4 public int? JobType { get; set; } 5 public decimal? Weight { get; set; } 6}

###試したこと
キャストエラーとなる部分を色々と書き換えてみましたが、どれも上手くいきませんでした。

// DBNull でエラー , JobType = System.Convert.ToInt32(r["JobType"]) , JobType = ((IConvertible)r["JobType"]).ToInt32(null) // そもそもビルドエラー , JobType = DBNull.Value.Equals(r["JobType"]) ? null : Convert.ToInt32( r["JobType"]) // 値取得はできるが、受け手の型が増える毎にユーティリティメソッドを増やす? , JobType = DbUtility.getInt(r["JobType"]) // ----------------------------------------------------- static int? getInt(object o) { if (DBull.Value.Equals(o)) return null; return System.Convert.ToInt32(o); }

###補足情報(言語/FW/ツール等のバージョンなど)
・VisualStudio2013
・WindowsFormアプリケーション
・最終的にBindingListに保存して、DataGridView.DataSourceにバインドします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

自己解決

DataColumnの様なEntityの列情報クラスの作成と、
変換用のメソッドを用意したユーティリティクラスを作成することで、
一旦対応することにしました。

Entityの列情報クラスを定義する上で、
型情報とデリゲート用のメソッドを同期させる部分や、
変数名と同一のフィールド名を定義している部分など、
重複する箇所や、定義を一致させなければならない箇所が多くて、
正直記述していて気持ちの良いものではありませんでした。。。

ユーティリティクラス

c#

1static class DBUtility { 2 public static bool isNull(object o) { 3 if(o == null) return true; 4 if (DBNull.Value.Equals(o)) return true; 5 return false; 6 } 7 public static int getInt(object o) { 8 return Convert.ToInt32(o); 9 } 10 public static int? getNullableInt(object o) { 11 if (DBUtility.isNull(o)) return null; 12 return getInt(o); 13 } 14 // 以下似たようなメソッドがいくつか 15}

列情報クラス

c#

1class CustomColumn<T> { 2 public CustomColumn(string name, Type type, ValueGetCallback callback) { 3 Name = name; 4 Type = type; 5 this.valueGetCallback = callback; 6 } 7 public string Name { get; private set; } 8 public Type Type { get; private set; } 9 10 public delegate T ValueGetCallback(object o); 11 private ValueGetCallback valueGetCallback; 12 13 public T getValue(System.Data.DataRow row) { 14 return valueGetCallback(row[Name]); 15 } 16}

Entityの列定義クラス

c#

1class PersonTable { 2 // 型引数T, Type, ValueGetCallbackの定義 が、全て合っていないと実行時にエラーとなりそうな部分が不安なので、 3 // とりあえず、テーブル設計書から自動でクラスを作成できる様にしています。 4 5 public static readonly CustomColumn<int> Age 6 = new CustomColumn<int>("Age", typeof(int), DBUtility.getInt); 7 8 public static readonly CustomColumn<string> Name 9 = new CustomColumn<string>("Name", typeof(string), DBUtility.getStr); 10 11 public static readonly CustomColumn<int?> JobType 12 = new CustomColumn<int?>("JobType", typeof(int?), DBUtility.getNullableInt); 13 14 public static readonly CustomColumn<decimal?> Weight 15 = new CustomColumn<decimal?>("Weight", typeof(decimal?), DBUtility.getNullableDecimal); 16} 17

DataTableからEntityへの値渡し

c#

1DataTable dt = selectAllPerson(); 2BindingList list = new BindingList<Person>( 3 dt.AsEnumerable().Select(r => new Person() { 4 Age = PersonTable.Age.getValue(r), 5 Name = PersonTable.Name.getValue(r), 6 JobType = PersonTable.JobType.getValue(r), 7 Weight = PersonTable.Weight.getValue(r), 8 }).ToList());

投稿2016/07/26 02:08

cha-ra

総合スコア40

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

パッと思いついたのは以下2点です。

①DataColumnをデータ型を明示して用意してやったDataTableに対して、
データを移し替える。(DataTable.Cloneとかでいける?)

②一旦全て文字列にキャスト(ToString)してから目的のデータ型にキャストし直す

もっといい方法があるかもしれないですけど…

投稿2016/07/22 13:58

Panzer_vor

総合スコア1636

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

cha-ra

2016/07/26 01:36

せっかく回答頂けたのに申し訳ないのですが、 どちらもNULL値の場合の処理が煩わしくて、上手くできませんでした。 >①DataColumnをデータ型を明示して用意してやったDataTableに対して、 >データを移し替える。(DataTable.Cloneとかでいける?) >②一旦全て文字列にキャスト(ToString)してから目的のデータ型にキャストし直す 最終的にUtilityメソッドを増やしていく形で実装することになりました。 回答ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問