Связывание модели ASP.NET MVC 2 RC с NHibernate и выпадающими списками - PullRequest
4 голосов
/ 13 января 2010

У меня проблема с привязкой модели в моем приложении ASP.NET MVC 2 RC, которое использует NHibernate для доступа к данным. Мы пытаемся создать приложение с использованием Ruby on Rails и иметь очень простую архитектуру, в которой сущности домена используются на всем пути от базы данных до представления.

Приложение имеет несколько доменных сущностей, которые могут быть проиллюстрированы следующими двумя классами:

public class Product {
    ...

    public Category Category { get; set; }      
}

public class Category {
    public int Id { get; set; }
    public string Name { get; set; }
}

В представлении, которое отображает форму редактирования, есть следующий оператор для отображения раскрывающегося списка:

<%= Html.DropDownListFor(model => model.Category.Id, 
       new SelectList(ViewData["categories"] as IList<Category>, "Id", "Name"), 
       "-- Select Category --" ) %>

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

Метод действия, который получает сообщение формы, аналогичен следующему. Обратите внимание, что атрибут TransactionFilter добавляет обработку транзакции NHibernate и фиксирует транзакцию, если не возникает исключений и проверка завершается успешно.

[HttpPost]
[TransactionFilter]
public ActionResult Edit(int id, FormCollection collection) {
    var product = _repository.Load(id);

    // Update the product except the Id
    UpdateModel(product, null, null, new[] {"Id"}, collection);

    if (ModelState.IsValid) {
      return RedirectToAction("Details", new {id});
    }
    return View(product);
}

Моя проблема заключается в том, что для product.Category.Id задано значение, выбранное в форме, например, Category.Id = "2". Использование связующего устройства модели по умолчанию приводит к следующему типу исключения NHibernate:

identifier of an instance of Name.Space.Entities.Category was altered from 4 to 2

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

Я полагаю, что для решения этой проблемы можно создать собственный ModelBinder, но есть ли более простой способ заставить это работать? Могут ли (и должны ли) доменные сущности быть изменены, чтобы справиться с этим?

Ответы [ 3 ]

2 голосов
/ 03 октября 2012

Я решил аналогичную проблему со списками на моей странице редактирования, изменив следующую строку

@Html.DropDownListFor(x => x.Employee.Id, new SelectList(ViewBag.Employees, "Id", "DisplayName"))

от

@Html.DropDownListFor(x => x.Employee, new SelectList(ViewBag.Employees, "Id", "DisplayName"))

Поэтому я удалил «.Id», как предложил Брайан. До изменения модель содержала только идентификатор сотрудника, а после изменения механизм связывания также загружал все данные сотрудника в модель.

0 голосов
/ 08 июня 2011

Решение, которое мы выбрали в то время, было примерно таким:

TryUpdateModel(product, null, null, new[] {"Category"}, collection);
int categoryId;
if (int.TryParse(collection["Category.Id"], NumberStyles.Integer, CultureInfo.InvariantCulture, out categoryId) && categoryId > 0) {
    product.Category = _categoryRepository.Load(categoryId);
}
else {
    product.Category = null;
}

Мы просто указываем связующему модели исключать свойство ассоциации и обрабатывать его вручную. Не красиво, но работало в то время ....

0 голосов
/ 18 января 2010

До этого я без проблем использовал схожие методы с классами Linq to SQL. Я не думаю, что вам понадобится специальный ModelBinder для этого. UpdateModel должен обновлять передаваемый в него класс Product, а не присоединенный к нему подкласс Category. Проверьте HTML, созданный помощником DropDownListFor. Как называется элемент? Это должно быть имя поля внешнего ключа в таблице «Продукты» (например, «CategoryID» или «Product.CategoryID», а не «Category.Id»). Если это «Category.Id» - попробуйте изменить первый параметр DropDownListFor на «model => model.Category» или «model => model.CategoryID» (или любое другое поле внешнего ключа). Это должно привести к тому, что UpdateModel обновляет только поле внешнего ключа в классе Product, а не ID класса Category.

...