Изменение сущностей в EntityFramework
У меня есть следующий сценарий:
- сущности загружаются из базы данных.
- пользователь может принять решение применить изменения к сущности или отменить редактирование.
Один из них представляется пользователю в форме (WPF UserControl), где пользователь может редактировать свойства этой сущности.
Как бы я реализовал нечто подобное с EntityFramework?
Моя проблема заключается в том, что, когда я привязываю пользовательский интерфейс непосредственно к свойствам сущности, каждый изменение мгновенно применяется к сущности. Я хочу отложить это до момента, когда пользователь нажимает OK и сущность успешно проверяется.
Я думал о загрузке сущностей с помощью NoTracking и вызове ApplyPropertyChanges после проверки обособленной сущности, но я не совсем уверен в правильном способе сделать это. Документальная часть и EntityFramework на MSDN является очень редкие.
Другой способ, который я мог бы придумать, - это Refresh сущность с StoreWins, но мне не нравится сброс изменений при отмене вместо применения изменений при ОК.
Есть ли у кого-нибудь хороший учебник или образец?
3 ответов:
Один из вариантов - это то, что вы сказали сделать запрос без отслеживания.
Затем клиент может изменить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.
Способ, которым я это делаю, у меня в основном есть viewmodel для сущности, которая принимает сущность в качестве параметра конструктора (в основном объект-оболочка).
В BeginEdit я копирую свойства сущности в свою viewmodel, поэтому, если я отменяю CancelEdit, данные изменяются только в ViewModel, а исходная сущность не изменилась. В EndEdit я просто применяю свойства ViewModel к сущности снова или, конечно, только если проверка прошла успешно.
Для проверки я использую методы IDataErrorInfo. Я просто реализую IDataErrorInfo.Ошибка, так что он проверяет каждое имя свойства через IDataErrorInfo[string columnName] и объединяет возможные сообщения об ошибках. Если он пуст, то все в порядке. (не уверен, что ошибка должна использоваться таким образом, но я это делаю)
Если к моей исходной сущности присоединены другие сущности, например клиент.Заказы, я создаю их как вложенные модели представлений в исходном объекте. модель представления. Исходная ViewModel вызывает методы Begin-,Cancel-,EndEdit / Error своих подмоделей в собственных реализациях этих методов.
Это немного больше работы,но я думаю, что это стоит того, потому что между BeginEdit и EndEdit, вы можете быть уверены, что ничего не меняется без вашего ведома. И наличие фрагмента кода для свойств INotifyPropertyChanged-enabled тоже очень помогает.
Нормальный способ сделать это-привязка к чему-то, что реализует
IEditableObject. Если и как это вписывается в структуру сущностей, я не уверен.
Comments