В настоящее время я работаю над учебным пособием по MvcMusicStore во второй раз как личный проект. На этот раз вместо Entity Framework я пытаюсь использовать nHibernate, а также IoC, используя Castle Windsor.
Первое препятствие, с которым я столкнулся, связано с вложенными классами и привязкой модели. Я могу отобразить список альбомов, а затем редактировать их. Я также могу сохранять изменения в базе данных, пока я редактирую поля Title, Price или AlbumArtUrl. Когда я пытаюсь отредактировать поля Artist или Genre, возникает следующая ошибка.
"Не удалось обновить модель типа 'MvcMusicStore.Models.Album'."
Эта ошибка возникает при вызове UpdateModel, поэтому я предполагаю, что это как-то связано с привязкой, но я совершенно ошарашен, как поступить. Судя по поискам в StackOverflow и в других местах, это кажется распространенной проблемой, но я до сих пор не нашел решения.
Я надеюсь, что кто-то может заметить, что я здесь делаю неправильно, и предложить решение.
Альбом
public class Album
{
public virtual int AlbumId { get; set; }
public virtual Artist Artist { get; set; }
public virtual Genre Genre { get; set; }
public virtual string Title { get; set; }
public virtual decimal Price { get; set; }
public virtual string AlbumArtUrl { get; set; }
}
StoreManagerViewModel
public class StoreManagerViewModel
{
public Album Album { get; set; }
public IList<Artist> Artists { get; set; }
public IList<Genre> Genres { get; set; }
}
Edit.aspx
<%@ Page Language="C#" MasterPageFile="/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcMusicStore.ViewModels.StoreManagerViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit - <%: Model.Album.Title %>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Edit Album</h2>
<% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>Edit Album</legend>
<%: Html.EditorFor(model => model.Album,
new { Artists = Model.Artists, Genres = Model.Genres}) %>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
<% } %>
<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
</asp:Content>
Album.ascx
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcMusicStore.Models.Album>" %>
<div class="editor-label">
<%: Html.LabelFor(model => model.Title) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Title) %>
<%: Html.ValidationMessageFor(model => model.Title) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Price) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Price, String.Format("{0:F}", Model.Price)) %>
<%: Html.ValidationMessageFor(model => model.Price) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.AlbumArtUrl) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.AlbumArtUrl) %>
<%: Html.ValidationMessageFor(model => model.AlbumArtUrl) %>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Artist)%>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.Artist, new SelectList(ViewData["Artists"] as IEnumerable, "ArtistId", "Name", Model.Artist))%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Genre)%>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.Genre, new SelectList(ViewData["Genres"] as IEnumerable, "GenreId", "Name", Model.Genre))%>
</div>
StoreManagerController
public ActionResult Edit(int id)
{
var viewModel = new StoreManagerViewModel
{
Album = AlbumRepository.GetById(id),
Genres = GenreRepository.GetAll().ToList(),
Artists = ArtistRepository.GetAll().ToList(),
};
return View(viewModel);
}
[HttpPost, UnitOfWork]
public ActionResult Edit(int id, FormCollection formValues)
{
var album = AlbumRepository.GetById(id);
try
{
UpdateModel(album, "Album");
AlbumRepository.SaveOrUpdate(album);
return RedirectToAction("Index");
}
catch
{
var viewModel = new StoreManagerViewModel
{
Album = album,
Genres = GenreRepository.GetAll().ToList(),
Artists = ArtistRepository.GetAll().ToList(),
};
return View(viewModel);
}
}
ОБНОВЛЕНИЕ 1:
Если я остановлюсь на UpdateModel и посмотрю на ValueProvider.GetValue ("Album.Artist"), я увижу обновленный ArtistId. Это наводит на мысль, что я совершенно не понял, как это должно работать.
Должен ли я позволить UpdateModel обрабатывать свойства, которые ему нравятся, так:
UpdateModel(album, "Album", new string[] { "Title", "Price", "AlbumArtUrl" });
А затем вручную обновить свойства Artist и Genre, выбирая объекты самостоятельно на основе ArtistId, GenreId в ValueProvider?
ОБНОВЛЕНИЕ 2
Исправление сортов?
Album.ascx
<div class="editor-label">
<%: Html.LabelFor(model => model.Artist)%>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.Artist, new SelectList(ViewData["Artists"] as IEnumerable, "ArtistId", "Name", Model.Artist.ArtistId))%>
</div>
<div class="editor-label">
<%: Html.LabelFor(model => model.Genre)%>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(model => model.Genre, new SelectList(ViewData["Genres"] as IEnumerable, "GenreId", "Name", Model.Genre.GenreId))%>
</div>
StoreManagerController
UpdateModel(album, "Album", new string[] { "Title", "Price", "AlbumArtUrl" });
album.Artist = ArtistRepository.GetById(Int32.Parse(formValues["Album.Artist"]));
album.Genre = GenreRepository.GetById(Int32.Parse(formValues["Album.Genre"]));
AlbumRepository.SaveOrUpdate(album);
Это работает, я не знаю, как это делать, или нет. Буду признателен, если кто-то исправит меня, если это не так?