ASP.NET MVC 3 EF CodeFirst - редактирование элементов DBContext - PullRequest
8 голосов
/ 13 августа 2011

Я был разочарован этим как день ... пожалуйста, помогите.

У меня есть раздел счетов, в котором я могу динамически добавлять новые элементы, добавляя / удаляя элементы DOM с помощью JQuery...я уже выяснила, как правильно называть эти элементы, чтобы они соответственно и правильно отображались в модели и отправлялись в параметр действия.

Итак, допустим, у меня в контроллере есть действие, которое называется Edit with paramтипа Invoice

[HttpPost]
public virtual ActionResult Edit(Invoice invoice) {
    if (ModelState.IsValid) {
         //code discussed below
    }
    return RedirectToAction("Index");
}

Параметр invoice содержит все данные, которые я хочу.У меня нет проблем с этим.Ниже приведена объектная модель.

public class Invoice
{
    [Key]
    public int ID { get; set; }
    public InvoiceType InvoiceType { get; set; }
    public string Description { get; set; }
    public int Amount { get; set; }
    public virtual ICollection<InvoiceItem> InvoiceItems { get; set; }
}

Обратите внимание на коллекцию InvoiceItems - она ​​содержит коллекцию динамически вставленных элементов счета-фактуры.Ниже приведена модель InvoiceItem

[Table("InvoiceItems")]
public class InvoiceItem
{
    [Key]
    public int ID { get; set; }

    [ForeignKey("Invoice")]
    public int InvoiceID { get; set; }
    public Invoice Invoice { get; set; }

    public string Description { get; set; }
    public int Amount { get; set; }
}

И вот где я застрял.

Я не могу вставить / обновить элементы коллекции InvoiceItems коллекции Invoice в базу данных случайно.Я много гуглял и нашел эти решения - к сожалению, ни одно из них не работает.

Решение # 1 - какой-то странный код с SetValues:

Invoice origInvoice = db.Invoices.Single(x => x.ID == invoice.ID);
var entry = db.Entry(origInvoice);
entry.OriginalValues.SetValues(invoice);
entry.CurrentValues.SetValues(invoice);
db.SaveChanges();

Решение # 2 - добавление нового метода обновления в мой DbContext:

public void Update<T>(T entity) where T : class
{
   this.Set<T>().Attach(entity);
   base.ObjectContext.ObjectStateManager.ChangeObjectState(entity,System.Data.EntityState.Modified);
 }

Решение # 3 - присоединение существующего, но измененного объекта к контексту в соответствии сhttp://blogs.msdn.com/b/adonet/archive/2011/01/29/using-dbcontext-in-ef-feature-ctp5-part-4-add-attach-and-entity-states.aspx

db.Entry(invoice).State = EntityState.Modified;
db.SaveChanges();

Как работает решение № 1: обновляет все свойства, кроме недавно добавленных InvoiceItems.SetValues ​​() игнорирует свойство ICollection.Это сработало бы, если бы я добавил эту строку непосредственно перед db.SaveChanges ();

origEntry.Entity.InvoiceItems = invoice.InvoiceItems;

... но я не верю, что это правильный подход вообще.

Как работает решение № 2: не работает вообще, поскольку в классе DbContext нет свойства ObjectContext.

Как работает решение № 3: Это решение будет обновленосчет-фактура, но в противном случае игнорирует InvoiceItems.


Дополнительная информация: следующим является фрагмент javascript, который используется для создания элементов элемента, которые сопоставлены со свойствами InvoiceItem.Последний вопрос: что я делаю не так?Что плохого в том, чтобы новая коллекция предметов относительно другой модели автоматически вставлялась в базу данных?Почему это игнорируется, когда родительская модель успешно обновляется?

Редактировать 1: исправления

1 Ответ

6 голосов
/ 13 августа 2011

DbEntityEntry.CurrentValues.SetValues только обновляет скалярные и сложные свойства.Это не заботится о свойствах навигации.Вот почему изменения в вашей коллекции InvoiceItems не записываются в базу данных.Та же проблема с настройкой состояния Invoice на Modified.Это помечает только скалярные и сложные свойства как измененные, но не как навигационные свойства.

К сожалению, EF не предлагает простой метод, который бы анализировал, какие элементы в коллекции навигации были добавлены, удалены или изменены только по сравнениюк исходному состоянию в базе данных, если изменения были сделаны, пока сущности были отсоединены от контекста (на веб-странице в вашем примере).

Это означает, что вы должны выполнить это сравнение между исходным состоянием вбаза данных и новое измененное состояние сами.

Примеры кода, как это сделать, приведены здесь в ответах, например: Отношение не может быть изменено, поскольку одно или несколько свойств внешнего ключа не являются-nullable Примеры вполне соответствуют вашей ситуации, просто замените ParentItem на Invoice и ChildItems на InvoiceItems.

Вы также можете взглянуть на этот вопрос из несколькихдней назад для дополнительной информации: WCF, Entity Framework 4.1 и State of Entity TheНа самом деле многие другие вопросы касаются той же проблемы, что и отсутствие поддержки обновления графов обособленных объектов (что является довольно распространенным сценарием), является одним из более неприятных ограничений Entity Framework.

(Oneпримечание: в контроллере MVC есть метод UpdateModel.Я не уверен, способен ли этот метод обновить полный граф объектов (добавлять, удалять и изменять элементы в коллекции).Лично я не использую его, и если бы я использовал его, то не для работы с моделью БД, а только для обновлений ViewModel.)

...