差分

ナビゲーションに移動 検索に移動

ツリー構造を持ったレコードをクラス化する2

8,120 バイト追加, 2019年7月22日 (月) 13:30
ページの作成:「以前「ツリー構造を持ったレコードをクラス化する」ってのを上げたんだけど…DBだったら自分自身のテーブルにリレーシ…」
以前「[[ツリー構造を持ったレコードをクラス化する]]」ってのを上げたんだけど…DBだったら自分自身のテーブルにリレーション張ればもっと簡単にできることに、いまさら気がついたんだ…(^_^;)恥ずかしながら、DBの知識が陳腐なことを露呈させちゃったんだよ…

ということで…ツリー構造テーブルのDBにおける定石はおいておいて…テーブルの内容だよ

== テーブルの内容 ==
テーブルの内容は前回とほぼ同じなんだ…違うところは自分自身にリレーションしているところだよ。(自分自身にリレーションするので、基本的にルートノードから子ノードへ順に登録しないと、規約違反になっちゃうから注意してね)

{| class="wikitable"
|+ OyakoTable
! 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
|}

{| class="wikitable"
|+ DBのリレーション
! 親項目 !! 子項目 !! 参照性合成<br/>(Acccess) !! フィールドの連鎖更新<br/>(Access) !! レコードの連鎖削除<br/>(Access)
|-
| OyakoTable.ID || OyakoTable.ParentID || チェックをはずす || チェックをはずす || チェックをはずす
|}
* 2015/7/28更新 - 「チェックを入れる」となっていましたが「チェックをはずす」に修正しました。チェックをはずさないとレコード削除のときに困ります。<br/>ちなみに型付データセットのリレーションは「リレーションのみ」にしておきます。
* 2015/8/5追記 - 要は型付データセット上(データセットデザイナ上)では自由にリレーションを張ってもいいのですが、DB上では最低限のリレーションのみの設定にします。出ないと、データセットとDBで不整合の様な状態になってしまい、実行時にエラーで悩まされることになってしまいます。

ルートノードから順番に登録していたら、テーブルがこんな状態にはならないと思うけど…色々やってたらこうなっちゃったってことにしたいんだ。

== ノードのクラス ==
ノードのクラスは以下のようにしてみたよ。前回と違って、クラス内にレコード持ってみたんだ。そうすれば、レコード内の情報を書き換えるのが楽になるからね。あと、ツリーを上方・下方に行き来できるように、親のクラスも持たせるようにしてみたんだよ。

<source lang="csharp">
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; }
}
}
}
</source>

== 階層構造の生成 ==
生成処理は再起処理で行っているよ。
<source lang="csharp">
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;
}
}
}
</source>

前のロジックよりすっきりしたよね!!<br/>
前のロジックの使い道を考えたんだけど…CSVのときは使えるかもしれないね…(データセットにCSVを読み込めば上記のロジックで行けちゃうけどね…)

[[Category:C♯]]
[[Category:Dataset]]
[[Category:RDB]]

案内メニュー