8,120 バイト追加,
2019年7月22日 (月) 13:30 以前「[[ツリー構造を持ったレコードをクラス化する]]」ってのを上げたんだけど…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]]