「ツリー構造を持ったレコードをクラス化する2」の版間の差分
		
		
		
		
		
		
		ナビゲーションに移動
		検索に移動
		
			
		
		
		
		
		
		
		
		
	
| Rin-scrooge (トーク | 投稿記録)   (ページの作成:「以前「ツリー構造を持ったレコードをクラス化する」ってのを上げたんだけど…DBだったら自分自身のテーブルにリレーシ…」) | 
| (相違点なし) | 
2019年7月22日 (月) 13:30時点における版
以前「ツリー構造を持ったレコードをクラス化する」ってのを上げたんだけど…DBだったら自分自身のテーブルにリレーション張ればもっと簡単にできることに、いまさら気がついたんだ…(^_^;)恥ずかしながら、DBの知識が陳腐なことを露呈させちゃったんだよ…
ということで…ツリー構造テーブルのDBにおける定石はおいておいて…テーブルの内容だよ
テーブルの内容
テーブルの内容は前回とほぼ同じなんだ…違うところは自分自身にリレーションしているところだよ。(自分自身にリレーションするので、基本的にルートノードから子ノードへ順に登録しないと、規約違反になっちゃうから注意してね)
| ID : int | Name : String | ParentID : int | 
|---|---|---|
| 0 | 子1-3 | 2 | 
| 1 | 親2 | 3 | 
| 2 | 親1 | 3 | 
| 3 | root | -1 | 
| 4 | 子1-1 | 2 | 
| 5 | 孫2-1-2 | 7 | 
| 6 | 子1-2 | 2 | 
| 7 | 子2-1 | 1 | 
| 8 | 孫2-1-1 | 7 | 
| 9 | 子2-2 | 1 | 
| 親項目 | 子項目 | 参照性合成 (Acccess) | フィールドの連鎖更新 (Access) | レコードの連鎖削除 (Access) | 
|---|---|---|---|---|
| OyakoTable.ID | OyakoTable.ParentID | チェックをはずす | チェックをはずす | チェックをはずす | 
- 2015/7/28更新 - 「チェックを入れる」となっていましたが「チェックをはずす」に修正しました。チェックをはずさないとレコード削除のときに困ります。
 ちなみに型付データセットのリレーションは「リレーションのみ」にしておきます。
- 2015/8/5追記 - 要は型付データセット上(データセットデザイナ上)では自由にリレーションを張ってもいいのですが、DB上では最低限のリレーションのみの設定にします。出ないと、データセットとDBで不整合の様な状態になってしまい、実行時にエラーで悩まされることになってしまいます。
ルートノードから順番に登録していたら、テーブルがこんな状態にはならないと思うけど…色々やってたらこうなっちゃったってことにしたいんだ。
ノードのクラス
ノードのクラスは以下のようにしてみたよ。前回と違って、クラス内にレコード持ってみたんだ。そうすれば、レコード内の情報を書き換えるのが楽になるからね。あと、ツリーを上方・下方に行き来できるように、親のクラスも持たせるようにしてみたんだよ。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//追加
using System.Collections.ObjectModel;
namespace TreeClassSample
{
    public class OyakoClass
    {
        // 親クラスのインスタンスを持つフィールドだよ
        public OyakoClass Parent { set; get; }
        //このクラスの情報が格納されているレコードを持つよ
        public OyakoTable_DataSet.OyakoTableRow OyakoTableRow { set; get; }
        //子供クラスを持つためのコンテナだよ
        //(TreeViewで使うことを前提にObservableCollectionにしてみたよ)
        public ObservableCollection<OyakoClass> Children { set; get; }
        public OyakoClass()
        {
            //コンストラクタでObservableCollectionのインスタンスを生成するよ
            this.Children = new ObservableCollection<OyakoClass>();
            //親クラスの登録はイベントでサクッとしているんだ
            this.Children.CollectionChanged += Children_CollectionChanged;
        }
        //コンテナの内容が変更されたときのイベントハンドラだよ
        void Children_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            //追加されたときだけ親クラスを登録しているよ
            if(e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                foreach(OyakoClass f_OyakoClass in e.NewItems)
                {
                    f_OyakoClass.Parent = this;
                }
            }
        }
        //以下、レコード内容にアクセスするためのプロパティだよ
        public Int32 ID
        {
            set { this.OyakoTableRow.ID = value; }
            get { return this.OyakoTableRow.ID; }
        }
        public String Name
        {
            set { this.OyakoTableRow.NodeName = value; }
            get { return this.OyakoTableRow.NodeName; }
        }
        public Int32 ParentID
        {
            set { this.OyakoTableRow.ParentID = value; }
            get { return this.OyakoTableRow.ParentID; }
        }
    }
}
階層構造の生成
生成処理は再起処理で行っているよ。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
//追加
using TreeClassSample.OyakoTable_DataSetTableAdapters;
namespace TreeClassSample
{
    public partial class MainWindow : Window
    {
        private OyakoTable_DataSet m_OyakoTable_DataSet;
        private OyakoClass m_RootOyakoClass;
        public MainWindow()
        {
            InitializeComponent();
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            //レコード取得
            this.m_OyakoTable_DataSet = new OyakoTable_DataSet();
            new OyakoTableTableAdapter().Fill(this.m_OyakoTable_DataSet.OyakoTable);
            //Rootノード取得
            OyakoTable_DataSet.OyakoTableRow[] l_RootOyakoTableRows =
                this.m_OyakoTable_DataSet.OyakoTable
                .Where(x => x.IsParentIDNull() == true)
                .ToArray();
            //取得チェック
            if(l_RootOyakoTableRows.Length != 1)
            {
                MessageBox.Show("「ルートノードがない」か「ルートノードが複数行!」だよ!");
                return;
            }
            //ツリー生成
            this.m_RootOyakoClass = this.CreateOyakoClass(l_RootOyakoTableRows[0]);
        }
        /// <summary>
        /// 「OyakoClass」を作る再起処理だよ
        /// </summary>
        /// <param name="p_OyakoTableRow">RootのDataRowオブジェクトだよ。</param>
        /// <returns>RootのOyakoClassを返却するよ。</returns>
        private OyakoClass CreateOyakoClass(OyakoTable_DataSet.OyakoTableRow p_OyakoTableRow)
        {
            //返却用のOyakoClassを生成するよ。
            OyakoClass l_ResultOyakoClass = new OyakoClass();
            l_ResultOyakoClass.OyakoTableRow = p_OyakoTableRow;
            //再起で子供を作りながら、返却用クラスのChildrenに追加するよ。
            foreach (OyakoTable_DataSet.OyakoTableRow f_OyakoTableRow in p_OyakoTableRow.GetChildRows("OyakoTableOyakoTable"))
            {
                l_ResultOyakoClass.Children.Add(this.CreateOyakoClass(f_OyakoTableRow));
            }
            return l_ResultOyakoClass;
        }
    }
}
前のロジックよりすっきりしたよね!!
前のロジックの使い道を考えたんだけど…CSVのときは使えるかもしれないね…(データセットにCSVを読み込めば上記のロジックで行けちゃうけどね…)

