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

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

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

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

4回答

1365閲覧

デバッグ困難なワンライナーを行に分ける

meshkit

総合スコア72

C#

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

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

0クリップ

投稿2018/01/19 01:07

編集2018/01/19 07:19

###前提・実現したいこと
下記コードがいわゆるワンライナーで書いてあって、理解とデバッグに大変な困難を感じています。
処理ごとに、行を分けるにはどうしたらよいでしょう?

###該当のソースコード

C#

1public static List<ProductModel> GetModel(string productcode=""){ 2 List<SqlParameter> sqlParameter = new List<SqlParameter>(); 3 string sql = Properties.Resources.SelectM_ProductSKUM_Product; 4 if (!string.IsNullOrEmpty(productcode)) 5 { 6 sql += @" WHERE [productcode] = @productcode"; 7 sqlParameter.Add(new SqlParameter("@productcode", productcode)); 8 } 9 10 return DBAccess.Select<ProductModel>( 11 new SqlCommandPair(sql.ToString(), 12 sqlParameter.ToArray()), 13 reader => 14 { 15 return new ProductModel() 16 { 17 ProductCode = DBAccess.GetString(reader[0], string.Empty), 18 ProductName = DBAccess.GetString(reader[1], string.Empty) 19 }; 20 }); 21}

SQL

1SELECT [dbo].[Product].[ProductCode] 2 ,[ProductName] 3 FROM [dbo].[Product]

###試したこと
処理は内側からなので、内側のオブジェクトを取り出してみたところ、readerがない、とエラーになります。
なるほどreaderはラムダ式で渡しているので、ないですね・・・。

var workobject = new ProductModel() { ProductCode = DBAccess.GetString(reader[0], string.Empty), ProductName = DBAccess.GetString(reader[1], string.Empty) };

ない、ということはわかるのですが、具体的な対処方法で手が止まっています。
コードで教えていただきたく。

C#

1new SqlCommandPair(sql.ToString(), 2sqlParameter.ToArray()),

の部分は、SQLに値を入れている、ということは理解できています。

C#

1ProductCode = DBAccess.GetString(reader[0], string.Empty), 2ProductName = DBAccess.GetString(reader[1], string.Empty)

がデータベースから得たデータを、Modelに入れていることも理解できています。

現在、データベースからModelに入れるときに、nullになる値があるので、それをdebugするのに、どこで値を確認したらよいのかわからずに困っています。

イメージですが。

C#

1var databaseData = DBAccess.GetData(sql); 2List<ProductModel> returnmodels = new List<ProductModel> (); 3foreach (var data in databaseData){ 4 var item = new ProductModel() 5 item.ProductCode = data.GetString(reader[0], string.Empty), 6 item.ProductName = data.GetString(reader[1], string.Empty) 7 returnmodels.Add(item) 8} 9return returnmodels;

のようなコードにしたいです。

SqlCommandPairは、

public SqlCommandPair(string command, params SqlParameter[] args) { Command = command; Parameters = args; }

です。
DBAccess.Selectは、

C#

1 public static List<T> Select<T>(SqlCommandPair selectCommand, Func<SqlDataReader, T> getDatumFunc) 2 { 3 var returnlists = new List<T>(); 4 5 using (var connection = new SqlConnection(Properties.Settings.Default.DBConnectionString)) 6 { 7 try 8 { 9 using (var sqlCommand = new SqlCommand(selectCommand.Command, connection)) 10 { 11 foreach (var parameter in selectCommand.Parameters) 12 { 13 sqlCommand.Parameters.Add(parameter); 14 } 15 16 connection.Open(); 17 18 using (var reader = sqlCommand.ExecuteReader()) 19 { 20 try 21 { 22 // モデルにデータを移す 23 while (reader.Read()) 24 { 25 returnlists.Add(getDatumFunc(reader)); 26 } 27 } 28 finally 29 { 30 reader.Close(); 31 } 32 } 33 } 34 } 35 finally 36 { 37 connection.Close(); 38 } 39 } 40 41 return returnlists; 42 }

でした。

###補足情報(言語/FW/ツール等のバージョンなど)
Visual Studio 2015 Pro

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

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

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

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

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

guest

回答4

0

meshkit様が提示したコードを分解してみました。

C#

1public static List<ProductModel> GetModel(string productcode=""){ 2 List<SqlParameter> sqlParameter = new List<SqlParameter>(); 3 string sql = Properties.Resources.SelectM_ProductSKUM_Product; 4 5 if (!string.IsNullOrEmpty(productcode)) 6 { 7 sql += @" WHERE [productcode] = @productcode"; 8 sqlParameter.Add(new SqlParameter("@productcode", productcode)); 9 } 10 11 // SqlCommandPairのインスタンス作成を分解する - - - - - - 12 // new SqlCommandPair(sql.ToString(), sqlParameter.ToArray()), 13 var stringSql = sql.ToString(); 14 var sqlParameterArray = sqlParameter.ToArray(); 15 16 var sqlCommandPair = new SqlCommandPair(stringSql, sqlParameterArray); 17 // - - - - - - - - - - - - - - 18 19 // 関数を変数に格納 20 var createProductModelFunc = new Func<SqlDataReader, ProductModel>(CreateProductModel); 21 22 // DBAccess.Select<ProductModel> の結果を変数に格納 23 var result = DBAccess.Select<ProductModel>( 24 sqlCommandPair, 25 createProductModelFunc); 26 27 return result; 28} 29 30// ラムダ式をメソッドの形に変更 31// reader => 32// { 33// return new ProductModel() 34// { 35// ProductCode = DBAccess.GetString(reader[0], string.Empty), 36// ProductName = DBAccess.GetString(reader[1], string.Empty) 37// }; 38//}); 39// ↓ ↓ ↓ ↓ ↓ 40private static ProductModel CreateProductModel(SqlDataReader reader) 41{ 42 var result = new ProductModel(); 43 result.ProductCode = DBAccess.GetString(reader[0], string.Empty); 44 result.ProductName = DBAccess.GetString(reader[1], string.Empty); 45 return result; 46}

上記コードのCreateProductModel関数の中でブレークポイントを設ければ、変数の中身を確認することができると思いので、お試しください。

以上です。
少しでもお力になれれば幸いでございます。

投稿2018/01/19 08:22

g_uo

総合スコア212

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

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

meshkit

2018/01/19 08:34

ありがとうございます。みなさまのお力添えのおかげで、ほぼ同じコードに到達しました。 これなら理解できます。 ただ、じっさいに動かしているのですが、CreateProductModelのなかでbreakポイントを置いても、readerのなかは見えないんです。 まあ、コードも理解できましたし、 item.ProductCode = DBAccess.GetString(reader["ProductCode"], string.Empty); のように数字もなくなったので、とりあえず困っていることはなくなりました。 みささま本当に感謝します。よい週末を。
g_uo

2018/01/19 09:10

コメントありがとうございます。 解決して何よりです。よい週末をお過ごしください。
guest

0

reader がどこから出てきたか困っているみたいですが、これはデリゲートの仮パラメータです。

提示されたソースを書き換えてまったく同じ働きをするものにしてみるとこうなります。

C#

1public static List<ProductModel> GetModel(string productcode=""){ 2 List<SqlParameter> sqlParameter = new List<SqlParameter>(); 3 string sql = Properties.Resources.SelectM_ProductSKUM_Product; 4 if (!string.IsNullOrEmpty(productcode)) 5 { 6 sql += @" WHERE [productcode] = @productcode"; 7 sqlParameter.Add(new SqlParameter("@productcode", productcode)); 8 } 9 10 SqlCommandPair commandPair = new SqlCommandPair(sql, sqlParameter.ToArray()); 11 return DBAccess.Select<ProductModel>(commandPair, GetProductModel); 12} 13 14private ProductModel GetProductModel(DbDataReader reader) 15{ 16 return new ProductModel() 17 { 18 ProductCode = DBAccess.GetString(reader[0], string.Empty), 19 ProductName = DBAccess.GetString(reader[1], string.Empty) 20 }; 21}

SqlCommandPairFunc<DbDataReader, ProductModel> の二つの引数を持つ DBAccess.Select<ProductModel> を呼び出しています。

C#

1return DBAccess.Select<ProductModel>(commandPair, GetProductModel());

でなく

C#

1return DBAccess.Select<ProductModel>(commandPair, GetProductModel);

であることに注意してください。

GetProductModel はデリゲートとして渡され、コールバックされます。
その際に DBAccess.Select<ProductModel>() 内で reader が作られ渡されます。

投稿2018/01/19 07:39

編集2018/01/19 07:44
Zuishin

総合スコア28656

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

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

0

DBAccessクラスは何者ですか?
それが分からないとSelectメソッドで行っている中身を解析することは出来ません。
もっと具体的に言えばreader変数がどの型なのかが知りたい。
(多分SqlDataReaderだと思いますけど)
もしDBAccessクラスが内製のオレオレライブラリだとしたらQAサイトで答えは出せません、社内の人に聞いてください。

理想的なコードを標準クラスのみで実現するのなら以下のとおりですかね~。
※未実装未検証未動作なのでテイストだけ感じてください

C#

1 var returnmodels = new List<ProductModel>(); 2 using (var con = new SqlConnection("ConnectionString")) 3 { 4 using (var cmd = con.CreateCommand()) 5 { 6 cmd.CommandText = sql; 7 8 using (var reader = cmd.ExecuteReader()) 9 { 10 while (reader.Read()) 11 { 12 var item = new ProductModel(); 13 item.ProductCode = reader.IsDBNull(0) ? "" : reader[0].ToString(); 14 item.ProductName = reader.IsDBNull(1) ? "" : reader[1].ToString(); 15 returnmodels.Add(item); 16 } 17 } 18 } 19 } 20 return returnmodels;

あとは蛇足で予測ですが、Dapperとか使ったほうがお好みのコードになると思います。
DapperはMicro-ORMと呼ばれるもので、詳しくはググってください。

投稿2018/01/19 01:46

ShikaTech

総合スコア468

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

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

meshkit

2018/01/19 01:52

ありがとうございます。 DBAccessのところ、補足しました。 ここに飛ぶのも理解できてなかったところです。 ご推察のとおり、SQLconnectionしてます。 reader変数の型。SqlDataReaderでよいでしょうか?
ShikaTech

2018/01/19 02:07 編集

別の方のコメントに記述されたものを、質問を編集して本文に記述してください。 コードフォーマットされていないコードを回答者に読めというのはマナー違反かと。 そして思うに、まずはオリジナルのクラス(DBAccessのこと)を使わずに標準コードのみで実装し、100%理解してから別のライブラリを使うことをおすすめします! さらに言えばDBAccessの代わりにDapperを使うことを強めにおすすめします。
meshkit

2018/01/19 07:20

たしかに。コード修正しました。
ShikaTech

2018/01/19 07:31

バッチリです、読みやすくなりました。 たぶん質問者さんは、NULLが入りうるreaderの中身をデバッグで覗きたいってのが最終目標だと思いますが、sh_akiraさんがおっしゃった通りちょっと書き換えれば覗けます。 ブレークポイントを「Select」に掛けてません? ラムダ式の中を覗くには「=>」の先でブレークする必要があります、すなわち質問者さんの元々のコードにおける「return new ProductModel()」の辺りですね。
meshkit

2018/01/19 07:40

読みにくくて本当に失礼しました。 いまは、 item.ProductName = DBAccess.GetString(reader[1], string.Empty);<=この行 でブレークしてみています。 あとは、DB.selectの、 returnlists.Add(getDatumFunc(reader)); の行です。 これでreaderを見ているのですが…。
ShikaTech

2018/01/19 07:45

あーわかった。 たぶんSqlDataReaderの仕様を分かってない状態でデバッグしてます。 添字アクセスだと困るのなら GetOrdinal とか調べると幸せになれると思いますよ。
meshkit

2018/01/19 08:06

ありがとうございます!!! GetOrdinalで、かなーり幸せになりました!!!!
meshkit

2018/01/19 08:09

で、readerの中身はやはり見れないですか? 幸せになったので、見れなくてもよいですが、やや心残りです。
meshkit

2018/01/19 08:49

気持ちに余裕ができたので、dapper調べました。 これは・・・。ご機嫌すぎますね。 すぐには使えないですが(もう今日の頭は使い切った…)。 ありがとうございます!
guest

0

ベストアンサー

charp

1 reader => 2 { 3 var item = new ProductModel(); 4 item.ProductCode = DBAccess.GetString(reader[0], string.Empty); 5 item.ProductName = DBAccess.GetString(reader[1], string.Empty); 6 return item; 7 });

とりあえずラムダ式の部分を少し展開するとこうなります。これで足りますか?
それ以上になるとDBAccessやSqlCommandPairが何か分からないので、
提示できません。

投稿2018/01/19 01:27

sh_akira

総合スコア380

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

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

meshkit

2018/01/19 07:17 編集

sqlとdata追記します。
sh_akira

2018/01/19 05:36

具体的に何をしたいのかが分かりません。 NULLが入っているかどうかを"デバッグ"したいだけであれば 私が提示したコードを var item = new ProductModel(); item.ProductCode = DBAccess.GetString(reader[0], string.Empty); item.ProductName = DBAccess.GetString(reader[1], string.Empty); if (item.ProductCode == NULL) { // ??? } return item; とでもすれば何か見れます。 質問文最後のイメージの形のコードにしたいのであれば DBAccess.SelectとShikaTechさんの回答を合わせれば良いと思います。 ほとんど同じ形をしているのが分かると思います。 while (reader.Read()) { returnlists.Add(getDatumFunc(reader)); } ここをラムダ式で外部から処理を指定できるようにするか、自分でやるかの違いです。
meshkit

2018/01/19 07:24

やりたいことがわかりました。 いまは、 { var item = new ProductModel(); item.ProductCode = DBAccess.GetString(reader[0], string.Empty); item.ProductName = DBAccess.GetString(reader[1], string.Empty); return item; }); のところで、item.ProductCodeを入れるのにreader[0]とハードコーディングしています。 そもそもsqlからとってくる段階で、ProductCodeを取ってきているわけだから、reader.Productcodeみたいにとれるのではないか、と考えています。 ところが、案に相違して、readerでクイックビューしても、 - 結果ビュー 結果ビューを展開すると、IEnumerable が列挙されます。 Empty "列挙は結果を生成しませんでした" string となって、なかを見ることができません。 readerのなかは、どこで止めれば見ることができて、reader.Productcodeみたいにとれないか、というのがやりたいことです。 なぜreader[0]でなく、reader.Productcodeとしたいかというと、データベースの形がまだfixしていないので、配列の添え字だと頻繁に書き換えなければならないことと、データベースから出てくる値が数字ばかりで、ほとんど見分けがつかないためです。
sh_akira

2018/01/19 08:10

なるほど。要は item.ProductCode = DBAccess.GetString(reader["ProductCode"], string.Empty); こういう事ですね。
meshkit

2018/01/19 08:13

そういうことです。このコード、動くのですか?
meshkit

2018/01/19 08:15

ためしました。動きます! ますます幸せになりました!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問