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

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

ただいまの
回答率

90.51%

  • C#

    7149questions

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

  • Visual Studio

    1838questions

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

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 539

meshkit

score 62

前提・実現したいこと

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

該当のソースコード

public static List<ProductModel> GetModel(string productcode=""){
    List<SqlParameter> sqlParameter = new List<SqlParameter>();
    string sql = Properties.Resources.SelectM_ProductSKUM_Product;
    if (!string.IsNullOrEmpty(productcode))
    {
        sql += @" WHERE [productcode] = @productcode";
        sqlParameter.Add(new SqlParameter("@productcode", productcode));
    }

    return DBAccess.Select<ProductModel>(
                        new SqlCommandPair(sql.ToString(),
                        sqlParameter.ToArray()),
                        reader =>
                        {
                            return new ProductModel()
                            {
                                ProductCode = DBAccess.GetString(reader[0], string.Empty),
                                ProductName = DBAccess.GetString(reader[1], string.Empty)
                            };
                        });
}
SELECT [dbo].[Product].[ProductCode]
            ,[ProductName]
    FROM [dbo].[Product]

試したこと

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

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

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

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


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

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


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

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

イメージですが。

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


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

SqlCommandPairは、

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


です。
DBAccess.Selectは、

        public static List<T> Select<T>(SqlCommandPair selectCommand, Func<SqlDataReader, T> getDatumFunc)
        {
            var returnlists = new List<T>();

            using (var connection = new SqlConnection(Properties.Settings.Default.DBConnectionString))
            {
                try
                {
                    using (var sqlCommand = new SqlCommand(selectCommand.Command, connection))
                    {
                        foreach (var parameter in selectCommand.Parameters)
                        {
                            sqlCommand.Parameters.Add(parameter);
                        }

                        connection.Open();

                        using (var reader = sqlCommand.ExecuteReader())
                        {
                            try
                            {
                                // モデルにデータを移す
                                while (reader.Read())
                                {
                                    returnlists.Add(getDatumFunc(reader));
                                }
                            }
                            finally
                            {
                                reader.Close();
                            }
                        }
                    }
                }
                finally
                {
                    connection.Close();
                }
            }

            return returnlists;
        }


でした。

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

Visual Studio 2015 Pro

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 4

checkベストアンサー

+1

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


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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/19 10:47 編集

    sqlとdata追記します。

    キャンセル

  • 2018/01/19 14: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));
    }
    ここをラムダ式で外部から処理を指定できるようにするか、自分でやるかの違いです。

    キャンセル

  • 2018/01/19 16: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していないので、配列の添え字だと頻繁に書き換えなければならないことと、データベースから出てくる値が数字ばかりで、ほとんど見分けがつかないためです。

    キャンセル

  • 2018/01/19 17:10

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

    キャンセル

  • 2018/01/19 17:13

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

    キャンセル

  • 2018/01/19 17:15

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

    キャンセル

+1

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

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

    var returnmodels = new List<ProductModel>();
    using (var con = new SqlConnection("ConnectionString"))
    {
        using (var cmd = con.CreateCommand())
        {
            cmd.CommandText = sql;

            using (var reader = cmd.ExecuteReader())
            {
                while (reader.Read())
                {
                    var item = new ProductModel();
                    item.ProductCode = reader.IsDBNull(0) ? "" : reader[0].ToString();
                    item.ProductName = reader.IsDBNull(1) ? "" : reader[1].ToString();
                    returnmodels.Add(item);
                }
            }
        }
    }
    return returnmodels;

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/19 10:52

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

    キャンセル

  • 2018/01/19 11:06 編集

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

    キャンセル

  • 2018/01/19 16:20

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

    キャンセル

  • 2018/01/19 16:31

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

    キャンセル

  • 2018/01/19 16:40

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

    キャンセル

  • 2018/01/19 16:45

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

    キャンセル

  • 2018/01/19 17:06

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

    キャンセル

  • 2018/01/19 17:09

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

    キャンセル

  • 2018/01/19 17:49

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

    キャンセル

+1

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

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

public static List<ProductModel> GetModel(string productcode=""){
    List<SqlParameter> sqlParameter = new List<SqlParameter>();
    string sql = Properties.Resources.SelectM_ProductSKUM_Product;
    if (!string.IsNullOrEmpty(productcode))
    {
        sql += @" WHERE [productcode] = @productcode";
        sqlParameter.Add(new SqlParameter("@productcode", productcode));
    }

    SqlCommandPair commandPair = new SqlCommandPair(sql, sqlParameter.ToArray());
    return DBAccess.Select<ProductModel>(commandPair, GetProductModel);
}

private ProductModel GetProductModel(DbDataReader reader)
{
    return new ProductModel()
    {
        ProductCode = DBAccess.GetString(reader[0], string.Empty),
        ProductName = DBAccess.GetString(reader[1], string.Empty)
    };
}

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

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

でなく

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+1

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

public static List<ProductModel> GetModel(string productcode=""){
    List<SqlParameter> sqlParameter = new List<SqlParameter>();
    string sql = Properties.Resources.SelectM_ProductSKUM_Product;

    if (!string.IsNullOrEmpty(productcode))
    {
        sql += @" WHERE [productcode] = @productcode";
        sqlParameter.Add(new SqlParameter("@productcode", productcode));
    }

    // SqlCommandPairのインスタンス作成を分解する  - - - - - - 
    // new SqlCommandPair(sql.ToString(), sqlParameter.ToArray()),
    var stringSql = sql.ToString();
    var sqlParameterArray = sqlParameter.ToArray();

    var sqlCommandPair = new SqlCommandPair(stringSql, sqlParameterArray);
    // - - - - - - - - - - - - - - 

    // 関数を変数に格納
    var createProductModelFunc = new Func<SqlDataReader, ProductModel>(CreateProductModel);

    // DBAccess.Select<ProductModel> の結果を変数に格納
    var result = DBAccess.Select<ProductModel>(
                        sqlCommandPair,
                        createProductModelFunc);

    return result;
}

// ラムダ式をメソッドの形に変更
// reader =>
// {
//     return new ProductModel()
//    {
//        ProductCode = DBAccess.GetString(reader[0], string.Empty),
//        ProductName = DBAccess.GetString(reader[1], string.Empty)
//    };
//});
// ↓ ↓ ↓ ↓ ↓
private static ProductModel CreateProductModel(SqlDataReader reader)
{
    var result = new ProductModel();
    result.ProductCode = DBAccess.GetString(reader[0], string.Empty);
    result.ProductName = DBAccess.GetString(reader[1], string.Empty);
    return result;
}

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/01/19 17:34

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

    みささま本当に感謝します。よい週末を。

    キャンセル

  • 2018/01/19 18:10

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

    キャンセル

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

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C#

    7149questions

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

  • Visual Studio

    1838questions

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