UpdateModel для вложенных свойств в ASP.NET MVC2 - PullRequest
1 голос
/ 30 ноября 2010

В настоящее время я работаю над учебным пособием по 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);

Это работает, я не знаю, как это делать, или нет. Буду признателен, если кто-то исправит меня, если это не так?

Ответы [ 2 ]

0 голосов
/ 08 декабря 2010

Чтобы взломать эту проблему, я создал свойство в ViewModel, которое ссылается на связанный объект, а затем сделал привязку к этому свойству:


Модель

public class User
{
    [Required(ErrorMessage = "Your first name is required.")]
    public string FirstName { get; set; }

    [Required(ErrorMessage = "Your last name is required.")]
    public string LastName { get; set; }        


    [Required(ErrorMessage = "A logon name is required.")]
    public string Logon { get; set; }

    [Required(ErrorMessage = "A password is required.")]
    public string Password { get; set; }

    [Required(ErrorMessage = "A role is required.")]
    public string Role { get; set; }

    [Required(ErrorMessage = "An airport is required.")]
    public Airport Airport { get; set; }

    [DisplayName("Is Active")]
    bool IsActive { get; set; }
}

View

       <div class="form-field">
            <div class="field-label"><%= Html.LabelFor(m => m.User.Airport) %>:</div>
            <div class="field-input"><%= Html.DropDownListFor(m => m.UserAirportId, new SelectList(Model.Airports, "Id", "Name"))%></div>
            <div class="field-result"><%= Html.ValidationMessageFor(m => m.User.Airport) %></div>
        </div> 

ViewModel

    public class UserViewModel
{
    public User User { get; set; }
    public List<Airport> Airports { get; set; }
    public List<String> Roles { get; set; }

    //Hack to get around non-binding of complex objects by UpdateModel
    public Guid UserAirportId
    {
        get
        {
            if (User != null && User.Airport != null)
                return User.Airport.Id;

            return Guid.Empty;
        }

        set
        {
            if (User == null)
                return;

            var airport = Airports.Where(m => m.Id == value).FirstOrDefault();

            if (airport == null)
                return;

            User.Airport = airport;
        }
    }
}
0 голосов
/ 30 ноября 2010

Вы должны предоставить свою логику в UpdateModel()

Также, если у вас строго типизированное представление, вы можете использовать вашу модель в качестве параметра контроллера вместо FormCollection. Также хорошей практикой является исключение идентификатора отредактированного альбома.

ActionResult Edit([Bind(Exclude = "AlbumId")]Album album)

И вы получите в альбом все поля формы для редактирования.

О вашем коде:
Теперь я вижу в вашем коде, что вы получаете по id текущего альбома в вашем db

 var album = AlbumRepository.GetById(id);

, а затем попытайтесь выполнить с ним какие-то действия, и неясно, как вы связываете информацию из вида.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...