- PR -

TreeViewの再帰的に呼び出しについて

投稿者投稿内容
SL
大ベテラン
会議室デビュー日: 2008/05/02
投稿数: 183
投稿日時: 2009-03-15 21:37
お世話になります。

TreeView にデータベースにあるデータを親と子を再帰的に呼び出して追加したいのですが再帰呼び出しがうまくいきません。
コードは下記のようにしました。下記を参考にして
http://www.atmarkit.co.jp/fdotnet/dotnettips/427asptreefromdb1/asptreefromdb1.html
繰り返し呼び出していると db.Open(); でエラーで止まってしまいます。
エラーは、下記のようにダイアログで表示され
「'System.StackOverflowException' のハンドルされていない例外が mscorlib.dll で発生しました。」
で、db.Open() の dbの内容を調べると、「Database = 現在のスレッドがスタック オーバーフロー状態であるため、式を評価できません。」
とあります。どうしたら再帰呼び出しがうまくできますか?
教えてください。

C# ASP.NET VS2005

コード:
    private void CreateNode(String parent, TreeNodeCollection nodes)
    {
        ConnectionStringSettings setting = ConfigurationManager.ConnectionStrings["KensaInfoConnectionString"];
        DbProviderFactory factory = DbProviderFactories.GetFactory(setting.ProviderName);

        using (DbConnection db = factory.CreateConnection()) {
            db.ConnectionString = setting.ConnectionString;

            DbCommand comm = factory.CreateCommand();
            comm.CommandText = "SELECT TreeItemID, TreeItemTitle, TreeItemLevel, TreeItemIsParent, TreeSubItemLevel, TreeSubItemPos, IsValid FROM TreeItemMenu WHERE (IsValid = 1) ORDER BY TreeItemLevel";
            comm.Connection = db;
            db.Open();

            // サブツリーの為の初期化
            DbDataReader reader = comm.ExecuteReader();

            string ItemID, ItemTitle, ItemLevel, ItemIsParent, SubItemLevel, SubItemPos;
            while (reader.Read())
            {
                TreeNode node = new TreeNode();

                if (!reader.IsDBNull(0))
                    ItemID = reader.GetString(0);
                else
                    ItemID = "";
                        :
                        :
                if (!reader.IsDBNull(3))
                    ItemIsParent = reader.GetString(3);
                else
                    ItemIsParent = "";

                    
                if (ItemIsParent != "#")
                {
                    this.CreateNode(reader.GetString(1), node.ChildNodes);        // 
                } else 
                    node.SelectAction = TreeNodeSelectAction.None;

                nodes.Add(node);
            }
        }
    }

Hongliang
ぬし
会議室デビュー日: 2004/12/25
投稿数: 576
投稿日時: 2009-03-15 21:55
再帰する回数が多すぎるからでしょう。
StackOverflow は数回数十回の再帰で起こるようなものではないですから、再帰の終了条件が間違っている可能性が高いです。
King
ぬし
会議室デビュー日: 2008/06/20
投稿数: 284
投稿日時: 2009-03-15 22:06
db.Open して Close しないまま再帰で呼び出しているから

db.Open
db.Open
db.Open
db.Open
・・・

と何回もしていると思います。
毎回 Open するのでなく Connection を持ち回ったりすれば良いのでは
ないでしょうか。
SL
大ベテラン
会議室デビュー日: 2008/05/02
投稿数: 183
投稿日時: 2009-03-15 23:45
お世話になります。

> db.Open して Close しないまま再帰で呼び出しているから
ということをもとに下記のようにdb.Open();の前で単にクローズしましたが、
どうやら、意味が違っていrたようです。変化ありません。db には、いつもなにがしかのデータがあるようです。常に Close() していますが、同様のエラーが発生しました。

コード:
if(db != null) 
  db.Close();
db.Open();



> 毎回 Open するのでなく Connection を持ち回ったりすれば良いのでは
とは、どういう意味でしょうか?
よくわかりません。
確かに、Hongliangさんが言うように、「再帰する回数が多すぎるからでしょう。 」
ということは、薄々感じていたのですが参考にしたコードの意味が少しわからないためどうしていいのかわかりませんでした。
「少し意味のわからない箇所」とは、
このコードの場合、this.CreateNode() より再帰的に自分自身を読んでます。
これはいいですが、この関数の最初からコードが実行されているなら常にDBの読みが最初から行われているためDBの読み出しのポイントが移動しない。または常に最後まで読み内容が、どうどうめぐり?つまり無限ループ?になっている?
と思っています。よって今回のエラーになる。
でも、ここから先、どうコードを書いたらいいかわかりません。

もう少し教えてください。
AlexSuns
会議室デビュー日: 2005/03/14
投稿数: 5
お住まい・勤務地: 桜島のちかく
投稿日時: 2009-03-16 09:35
SQLの問題ですね。毎回同じSQLを発行していては終了しません。
もう一度、WHERE句のところを見直してみてください。
King
ぬし
会議室デビュー日: 2008/06/20
投稿数: 284
投稿日時: 2009-03-16 09:41
コード:

if(db != null) 
    db.Close();
db.Open();


こう書いたとしてもその using ステートメント内でしか db のスコープはないから
絶対にこのタイミングで Open していることは無いですよね?
だから無意味だと思います。

私が言っているのは再帰的に呼ばれるこの関数全てで同一の Connection インスタンスを
使ってはどうか、という事です。
提示されているソースでは再帰的に呼ばれるたびに Connection のインスタンスが生成され
Open を行い、 Close しないまま次の再帰呼び出しが行われています。
(再帰が無いとしても Close の記述が無いですがなぜですか?)

コード:

static void Main()
{
    CreateNode(ref db, aaa, bbb)
}

private void CreateNode(ref DbConnection db, String parent, TreeNodeCollection nodes)
{
    while (...)
    {
        CreateNode(db, ccc, ddd);
    }
}


という感じで Connection や Command を持ちまわったりしたいです。
(ref いりますっけ?)
セラフ
ベテラン
会議室デビュー日: 2005/12/01
投稿数: 95
お住まい・勤務地: 東北の顔の形といえば
投稿日時: 2009-03-16 09:44
サンプルを見直したほうがよいと思います。

サンプルでは、DBから一気に全てのデータを取得しているわけではなく、パラメータを使用して、現在のノードを親(parent)とするデータをSELECTして、そのSELECT結果を再帰的に呼び出しています。

一方SLさんのソースでは、親と子という概念がないため、再帰が破綻して無限ループになっています。

提示のソースの何処が悪いというより、考え方が誤っていると思われます。

親があるかどうかやどの階層なのかを見ただけでは、ツリーはできません。
そのデータはどの親の子なのかが関連付けられていないと、ツリーになりえませんので、もう一度データとロジック両方を見直してみてください。
King
ぬし
会議室デビュー日: 2008/06/20
投稿数: 284
投稿日時: 2009-03-16 09:47
AlexSuns さん
セラフ さん

すみません。
よく見たらその通りでした。

皆さん

見当違いのこと言ってすみません。
(でも少し気になる)

スキルアップ/キャリアアップ(JOB@IT)