【问题标题】:UpdateModel for Nested Properties in ASP.NET MVC2ASP.NET MVC2 中嵌套属性的 UpdateModel
【发布时间】:2011-05-17 20:22:02
【问题描述】:

我目前正在以个人项目的形式第二次学习 MvcMusicStore 教程。这次我尝试使用 nHibernate 和一些使用 Castle Windsor 的 IoC,而不是实体框架。

我遇到的第一个障碍与嵌套类和模型绑定有关。我能够显示专辑列表,然后对其进行编辑。只要我编辑 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; }
}

编辑.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>

专辑.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" });

然后通过自己根据 ValueProvider 中的 ArtistId、GenreId 获取对象来手动更新 Artist 和 Genre 属性?

更新 2

某种修复?

专辑.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);

这行得通,我不知道这种事情应该怎么做。如果不是这样,我会很感激有人让我正确吗?

【问题讨论】:

    标签: c# asp.net-mvc-2


    【解决方案1】:

    你必须在UpdateModel()中提供你的逻辑

    此外,如果您有强类型视图,您可以将模型用作控制器参数,而不是 FormCollection。排除已编辑专辑的 id 也是一个好习惯。

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

    您将在相册中获取表单中的所有字段进行编辑。

    关于您的代码:
    现在我在您的代码中看到您通过 id 当前专辑在您的数据库中获得

     var album = AlbumRepository.GetById(id);
    

    然后尝试用它做一些动作,不清楚你是如何从视图中绑定信息的。

    【讨论】:

    • 有趣的是,如果我使用我的模型作为控制器参数而不是 FormCollection,那么 Artist 和 Genre 属性都是 Null。
    • 没错。如果您想在 Artists 和 Genere 中生成不为 NULL 的值,您必须让控制器可用于获取此类模型类型。流派和艺术家是 StoreManagerViewModel 的一部分,而不是专辑。因此,您必须了解您在编辑操作中真正需要什么。您是只想更新数据库中的专辑条目,还是还想更新一些相关信息。我认为阅读 MVC 基础知识以及模型和数据绑定对您很有用。尝试在此处查找书籍:stackoverflow.com/questions/240905/best-asp-net-mvc-book
    • 老实说,我对你所说的有点困惑,艺术家和流派都是专辑的一部分,我只想更新专辑记录。
    • 对不起。我错过了。我认为您在谈论 IList in StoreManagerViewModel
    • 在 Artist 和 Genre 中获得 null 的问题在于如何绑定视图。 Html.DropDownList("Artist"... 不正确尝试在所有其他领域中使用它。Html.DropDownListFor(model=>model.Artist... 我认为这是一个解决方案
    【解决方案2】:

    为了解决这个问题,我在 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; }
    }
    

    查看

           <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> 
    

    视图模型

        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;
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-10
      • 1970-01-01
      相关资源
      最近更新 更多