例のごとくこのようなデータ構造になっています。
MVPパターンを使っている場合に、ビューで更新されたエンティティオブジェクトをプレゼンターに渡し、プレゼンターではその渡されたものをそのまま使ってデータベースを更新したいときがあります。(あるんです。)
まずはビュー側でエンティティオブジェクトを作り、更新データを整えたらプレゼンターの更新用メソッドを呼び出します。
<
div class=”csharpcode”>
private void button1_Click(object sender, EventArgs e) {
Project project = new Project();
project.id = 31;
project.projectName ="T:" + DateTime.Now.Date.ToString("yy/MM/dd:);
presenter.Update(project);
}
プレゼンター側では引数で渡されたエンティティオブジェクトを使って更新します。ここでの更新方法についてはMSDNマガジン2008年7月号の階層型アーキテクチャの Entity Frameworkに書かれている方法そのままです。
public void Update(Project project) {
using (ImportEntities context = new ImportEntities()) {
//与えられたエンティティをコンテキストにアタッチする。
context.AttachTo("Project", project);
//更新されるプロパティの数が少なく特定できるなら
//以下のようにプロパティを直接指定してもよい。
//var stateEntity = context.ObjectStateManager.GetObjectStateEntry(project.EntityKey);
//var propName = "projectName";
//stateEntity.SetModifiedProperty(propName);
//すべてのプロパティを変更済みにする
project.SetAllModified(context);
//変更の反映
context.SaveChanges();
}
}
まず4行目で引数で渡されたエンティティをコンテキストにアタッチします。
5行目から9行目ですが、ビューで更新されるプロパティが特定できる場合には、コメント内にあるようなやり方でそのプロパティだけ更新済みにするやり方もあります。
11行目はMSDNマガジンに書かれているやり方で、拡張メソッドを使ってエンティティの持つ全てのプロパティを更新済みとしてしまう方法です。ビューでグリッドコントロールにバインドされている場合などどのプロパティが更新されているかわからない場合に箱の方法が良いと思います。(バインディング使えるならこんな事しなくて良いのかアハハハハ)
拡張メソッド:
public static class EntityExtensions
{
///
/// エンティティのすべてのプロパティを変更済みに設定する。
///
/// 要素の型(IEntityWithKey)
/// エンティティオブジェクト
/// コンテキストオブジェクト
public static void SetAllModified(this T entity, ObjectContext context)
where T : IEntityWithKey {
var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
var propertyNameList
= stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);
foreach (var propName in propertyNameList) {
stateEntry.SetModifiedProperty(propName);
}
}
}
全体コードは次のようになります。
using System.Data.Objects;
using System.Data.Objects.DataClasses;
namespace EF_TEST2
{
public partial class Form1 : Form
{
private Presenter presenter;
public Form1() {
InitializeComponent();
presenter = new Presenter();
}
private void button1_Click(object sender, EventArgs e) {
Project project = new Project();
project.id = 31;
project.projectName = "T:" + DateTime.Now.Date.ToString("yy/MM/dd");
presenter.Update(project);
}
}
public class Presenter
{
public void Update(Project project) {
using (ImportEntities context = new ImportEntities()) {
//与えられたエンティティをコンテキストにアタッチする。
context.AttachTo("Project", project);
//更新されるプロパティの数が少なく特定できるなら
//以下のようにプロパティを直接指定してもよい。
//var stateEntity = context.ObjectStateManager.GetObjectStateEntry(project.EntityKey);
//var propName = "projectName";
//stateEntity.SetModifiedProperty(propName);
//すべてのプロパティを変更済みにする
project.SetAllModified(context);
//変更の反映
context.SaveChanges();
}
}
}
public static class EntityExtensions
{
///
/// エンティティのすべてのプロパティを変更済みに設定する。
///
/// 要素の型(IEntityWithKey)
/// エンティティオブジェクト
/// コンテキストオブジェクト
public static void SetAllModified(this T entity, ObjectContext context)
where T : IEntityWithKey {
var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity.EntityKey);
var propertyNameList
= stateEntry.CurrentValues.DataRecordInfo.FieldMetadata.Select(pn => pn.FieldType.Name);
foreach (var propName in propertyNameList) {
stateEntry.SetModifiedProperty(propName);
}
}
}
}
今、どうしてこんなに解りにくくて面倒なんだろうと思いながらこの記事を書いているわけですが、ただわかっていれば確かにASP.NETをそのまま使うよりは綺麗なコードがかけるんだろうなぁと思います。まだまだ修行が足りませんね。
あと、パクっている参考にしているMSDNマガジンは良記事なのでMVPパターンに限らず Entity Framework を使って階層化アプリケーションを作ろうとしている型は一読されてみてはいかがでしょう。でも今は WCF RIA Service とか使ってEF自体がかなり低レベルな感じになっているから気にしなくても良いのかもね。
コメント