Как обновить сложную модель в ASP.NET MVC 3

Я пытаюсь обновить сложную модель в одном представлении. Я использую ASP.NET MVC3, Entity Framework сначала с кодом, единицей работы, общим шаблоном репозитория... но когда я пытаюсь обновить модель, я получаю эту ошибку:

Произошло нарушение ограничения ссылочной целостности: значения свойств, определяющие ссылочные ограничения, не согласованы между основным и зависимыми объектами в отношении.

Вот моя упрощенная модель представления:

public class TransactionViewModel
{
     public Transaction Transaction { get; set; }
     public bool IsUserSubmitting { get; set; }
     public IEnumerable<SelectListItem> ContractTypes { get; set; }
}

Вот моя упрощенная сложная модель и в качестве примера одно из ее свойств навигации. Транзакция имеет отношение один к одному со всеми своими свойствами навигации:

public class Transaction
{
    [Key]
    public int Id { get; set; }

    public int CurrentStageId { get; set; }

    public int? BidId { get; set; }

    public int? EvaluationId { get; set; }

    public virtual Stage CurrentStage { get; set; }

    public virtual Bid Bid { get; set; }

    public virtual Evaluation Evaluation { get; set; }

}

public class Bid
{
    [Key]
    public int Id { get; set; }

    public string Type { get; set; }

    public DateTime? PublicationDate { get; set; }

    public DateTime? BidOpeningDate { get; set; }

    public DateTime? ServiceDate { get; set; }

    public string ContractBuyerComments { get; set; }

    public string BidNumber { get; set; }

    public DateTime? ReminderDate { get; set; }

    public DateTime? SubmitDate { get; set; }

}

Используя ту же модель представления, я могу создать объект транзакции, который будет заполнять базу данных следующим образом.

Идентификатор: 1, CurrentStageId: 1, BidId: 1, EvaluationId: 1

но когда я пытаюсь обновить свойства в этих свойствах навигации, эта строка вызывает ошибку в контроллере:

[HttpPost]
public ActionResult Edit(TransactionViewModel model)
{
    if (ModelState.IsValid)
    {
        -> unitOfWork.TransactionRepository.Update(model.Transaction);
           unitOfWork.Save();
           return RedirectToAction("List");
    }
}

В общем репозитории:

public virtual void Update(TEntity entityToUpdate)
{
 -> dbSet.Attach(entityToUpdate);
    context.Entry(entityToUpdate).State = EntityState.Modified;
}

Проблема еще более усложняется, поскольку я должен иметь возможность редактировать любые поля (свойства) в любом свойстве навигации в объекте транзакции в одном представлении.


person ljustin    schedule 13.01.2012    source источник


Ответы (1)


Я считаю, что исключение означает следующее:

Значения свойств, которые определяют ссылочные ограничения... (это значение свойства первичного ключа (= Id) Bid и значение свойства внешнего ключа (= BidId) Transaction)

... несовместимы... (= имеют разные значения)

... между принципалом... (= Bid)

... и зависимый... (= Transaction)

... объекты в отношениях.

Итак, это выглядит следующим образом: Когда связыватель модели MVC создает параметр TransactionViewModel as для действия Edit, model.Transaction.BidId и model.Transaction.Bid.Id различаются, например:

  • model.Transaction.BidId.HasValue это true, а model.Transaction.Bid это null
  • model.Transaction.BidId.HasValue это false, но model.Transaction.Bid не null
  • model.Transaction.BidId.Value != model.Transaction.Bid.Id

(Первый пункт, вероятно, не проблема. Я предполагаю, что у вас ситуация 2.)

То же самое относится к CurrentStage и Evaluation.

Возможные решения:

  • Установите для этих свойств одинаковые значения, прежде чем вызывать метод Update вашего репозитория (=hack)
  • Привяжите TransactionViewModel.Transaction.BidId и TransactionViewModel.Transaction.Bid.Id к двум скрытым полям формы с одинаковым значением, чтобы связыватель модели заполнил оба свойства.
  • Используйте также ViewModel для вашего внутреннего свойства Transaction (а также для свойств навигации внутри Transaction), который адаптирован к вашему представлению и который вы можете соответствующим образом сопоставить с сущностями в вашем действии контроллера.

И последний момент, о котором следует упомянуть, это то, что эта строка ...

context.Entry(entityToUpdate).State = EntityState.Modified;

... не помечает связанные объекты (Transaction.Bid) как измененные, поэтому изменения Transaction.Bid не сохраняются. Вы также должны установить состояние связанных объектов на Modified.

Боковое примечание. Если у вас нет дополнительного сопоставления с Fluent API для EF, все ваши отношения являются не "один к одному", а "один ко многим", поскольку у вас есть отдельные свойства FK. Для отношений "один к одному" с EF требуются общие первичные ключи.

person Slauma    schedule 14.01.2012
comment
Вы были совершенно правы! Использование общих первичных ключей для взаимно-однозначных отношений и ручная установка идентификаторов в контроллере решили проблему. Спасибо! - person ljustin; 25.01.2012
comment
У меня возникла аналогичная проблема, когда я написал общий базовый класс репозитория, который могли реализовать все другие репозитории. Убедившись, что используются только идентификаторы связанных объектов, а не весь связанный объект, я решил свою проблему. - person Garry; 05.12.2012
comment
Джунхо, у меня проблема. Можете ли вы помочь, как вы установили значения вручную в контроллере, пожалуйста? - person mmssaann; 14.06.2013