EntityFrameworkのエンティティの変更
-
03-07-2019 - |
質問
次のシナリオがあります:
- エンティティはデータベースからロードされます。
- そのうちの1つは、フォーム(WPF UserControl)でユーザーに提示され、ユーザーはそのエンティティのプロパティを編集できます。
- ユーザーは、エンティティに変更を適用するか、編集をキャンセルするかを決定できます。
EntityFrameworkでこのようなものを実装するにはどうすればよいですか
私の問題は、UIをエンティティのプロパティに直接バインドすると、すべての変更が即座にエンティティに適用されることです。ユーザーが[OK]を押してエンティティが検証されるまで、それを遅らせたい。
エンティティを NoTracking
でロードし、デタッチされたエンティティの検証後に ApplyPropertyChanges
を呼び出すことを考えましたが、それを行う正しい方法については完全にはわかりません。 MSDNのEntityFrameworkのドキュメントは非常にまばらです。
考えられるもう1つの方法は、 StoreWins
でエンティティを Refresh
することですが、Okで変更を適用する代わりに、Cancelで変更をリセットするのは嫌です。
良いチュートリアルやサンプルはありますか?
解決
1つのオプションは、トラッキングなしのクエリを行うことです。
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First(c => c.ID == 232);
その後、顧客はメモリ内で必要に応じて 'customer'
を変更できますが、実際にはコンテキストでは何も起こりません。
実際に変更を加えたいときは、これを行うことができます:
// get the value from the database
var original = ctx.Customers.First(c => c.ID == customer.ID);
// copy values from the changed entity onto the original.
ctx.ApplyPropertyChanges(customer); .
ctx.SaveChanges();
パフォーマンスまたは同時実行性の理由でクエリに不満がある場合、新しい拡張メソッドAttachAsModified(...)をObjectContextに追加できます。
次のようになります:
public static void AttachAsModified<T>(
this ObjectContext ctx,
string entitySet,
T entity)
{
ctx.AttachTo(entitySet, entity);
ObjectStateEntry entry =
ctx.ObjectStateManager.GetObjectStateEntry(entity);
// get all the property names
var propertyNames =
from s in entry.CurrentValues.DataRecordInfo.FieldMetadata
select s.FieldType.Name;
// mark every property as modified
foreach(var propertyName in propertyNames)
{
entry.SetModifiedProperty(propertyName);
}
}
これで、次のようなコードを記述できます。
ctx.Customers.MergeOption = MergeOption.NoTracking;
var customer = ctx.Customers.First();
// make changes to the customer in the form
ctx.AttachAsModified("Customers", customer);
ctx.SaveChanges();
そして、並行性や余分なクエリはなくなりました。
現在、唯一の問題はFKプロパティの処理です。おそらくここでヘルプのヒントのインデックスをご覧ください: http://blogs.msdn.com/alexj/archive/2009/03/26/index-of-tips.aspx
これが役立つことを願って
アレックス
他のヒント
IEditableObjectもお勧めします。さらに、IDataErrorInfoもお勧めします。
それを行う方法は、基本的に、コンストラクターパラメーター(基本的にはラッパーオブジェクト)としてエンティティを受け取るエンティティのビューモデルがあります。
BeginEditでは、エンティティプロパティをビューモデルにコピーするため、CancelEditを実行すると、ViewModelでのみデータが変更され、元のエンティティは変更されません。 EndEditでは、ViewModelプロパティをエンティティに再度適用するか、検証が成功した場合にのみ適用します。
検証には、IDataErrorInfoのメソッドを使用します。 IDataErrorInfo.Errorを実装するだけで、IDataErrorInfo [string columnName]を介して各プロパティ名をチェックし、最終的なエラーメッセージを連結します。空の場合は、すべて問題ありません。 (エラーがそのように使用されることを意図しているかどうかはわかりませんが、私はそれを行います)
Customer.Ordersなど、元のエンティティにアタッチされた他のエンティティがある場合、元のエンティティのViewModelにネストされたViewModelとしてそれらを作成します。元のViewModelは、そのサブモデルのBegin-、Cancel-、EndEdit / Errorメソッドを、それらのメソッドの独自の実装で呼び出します。
もう少し手間がかかりますが、BeginEditとEndEditの間で、気付かない限り何も変わらないことが確実だから、それだけの価値があると思います。また、INotifyPropertyChangedが有効なプロパティのコードスニペットがあると、非常に役立ちます。
これを行う通常の方法は、 IEditableObject
。エンティティフレームワークに適合するかどうか、またどのように適合するかはわかりません。