EFのクラスをMVVMのINotifyPropertyChangedに対応させる裏技

WPFでEF(Entitiy Framework)を使っていると、リスト表示には楽なのだが、データを反映するためにはINotifyPropertyChangedを継承させなくてはいけなくて、そこが面倒くさい。いちいち、ViewModel クラスで EF のクラスをくるめば良いのだが、それを自動化したい。
特にマスター管理用の画面なんかを作るときは、ASP.NET MVCのように足場を自動化させておきたいというのもある。

EFで出力されるクラスは、下記のようなクラスなので、

public partial class 社員名
{
    public int 社員ID { get; set; }
    public Nullable<int> 部署ID { get; set; }
    public Nullable<int> 営業担当FLG { get; set; }
    public string 社員名1 { get; set; }
}

MVVM対応のINotifyPropertyChangedインターフェースを継承したこんなクラスにしたいわけだ。

public partial class 社員名 : ObservableObject 
{
    private int _社員ID ;
    public int 社員ID {
    	get { return _社員ID; }
    	set { SetProperty( ref _社員ID, value, nameof(社員ID)); }
    }
    private Nullable<int> _部署ID ;
    public Nullable<int> 部署ID {
    	get { return _部署ID; }
    	set { SetProperty( ref _部署ID, value, nameof(部署ID)); }
    }
    private Nullable<int> _営業担当FLG ;
    public Nullable<int> 営業担当FLG {
    	get { return _営業担当FLG; }
    	set { SetProperty( ref _営業担当FLG, value, nameof(営業担当FLG)); }
    }
    private string _社員名1 ;
    public string 社員名1 {
    	get { return _社員名1; }
    	set { SetProperty( ref _社員名1, value, nameof(社員名1)); }
    }
}

こうしておくと、リストビューで一覧表示をさせておいて、選択時にテキストボックスへ表示、そしてテキストボックスでの変更がリストビューに反映される、というところが自動化される。

Model1.ttを直接書き替える

元のクラスにスクリプトを通して、MVVM対応のクラスを作ろうかとも思ったのだが、もっと簡単に EF で出力される Model1.tt を直接書き替えてしまう。T4 で書かれている Model1.tt はファイルに保存した途端にテンプレートに従って各クラスが出力される。

public class CodeStringGenerator
{
	...
    public string Property(EdmProperty edmProperty)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
			// ★ここを書き替え
@"private {1} _{2} ;
	{0} {1} {2} {{
		get {{ return _{2}; }}
		set {{ SetProperty( ref _{2}, value, nameof({2})); }}
	}}",

			// "{0} {1} {2} {{ {3}get; {4}set; }}",
            Accessibility.ForProperty(edmProperty),
            _typeMapper.GetTypeName(edmProperty.TypeUsage),
            _code.Escape(edmProperty),
            _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
            _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
    }
	...
    public string EntityClassOpening(EntityType entity)
    {
        return string.Format(
            CultureInfo.InvariantCulture,
			// ★ここを書き替え
            "{0} {1}partial class {2}{3} : ObservableObject ",
            Accessibility.ForType(entity),
            _code.SpaceAfter(_code.AbstractOption(entity)),
            _code.Escape(entity),
            _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
    }
	...
}

大ざっぱだが、CodeStringGeneratorクラスのPropertyメソッドとEntityClassOpeningメソッドの内容を書き替えてしまう。

EntityClassOpening は、エンティティのクラスを出力するところなので、INotifyPropertyChangedインターフェースを実装した「ObservableObject」クラスを継承するように書き替える。ObservableObjectクラスは別途自前で用意しておく。
Property メソッドは、プロパティを出力するところなので、SetProperty メソッドを呼び出して変更通知が飛ぶようにする。

これを保存すると、各エンティティのクラスが、

public partial class 社員名 : ObservableObject 
{
    private int _社員ID ;
    public int 社員ID {
    	get { return _社員ID; }
    	set { SetProperty( ref _社員ID, value, nameof(社員ID)); }
    }
	...
}

のように書き変わる。

のような画面が、バインドだけで作れるようになる。

カテゴリー: 開発, C# パーマリンク