C# ASP.NET MVC2中嵌套属性的UpdateModel

C# ASP.NET MVC2中嵌套属性的UpdateModel,c#,asp.net-mvc-2,C#,Asp.net Mvc 2,我目前正在以个人项目的形式第二次学习MvcMusicStore教程。这次我尝试使用nHibernate和一些使用Castle Windsor的IoC,而不是实体框架 Ive遇到的第一个障碍是嵌套类和模型绑定。我可以显示相册列表,然后编辑它们。只要我编辑标题、价格或AlbumArtUrl字段,我就能够将更改持久化到数据库中。当我试图编辑艺术家或流派字段时,会发生以下错误 “无法更新“MvcMusicStore.Models.Album”类型的模型。” 这个错误发生在调用UpdateModel时,所

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

Ive遇到的第一个障碍是嵌套类和模型绑定。我可以显示相册列表,然后编辑它们。只要我编辑标题、价格或AlbumArtUrl字段,我就能够将更改持久化到数据库中。当我试图编辑艺术家或流派字段时,会发生以下错误

“无法更新“MvcMusicStore.Models.Album”类型的模型。”

这个错误发生在调用UpdateModel时,所以我假设它与绑定有关,但我完全不知道如何继续。从我对StackOverflow和其他地方所做的搜索来看,这似乎是一个常见的问题,但到目前为止我还没有找到解决方案

我希望有人能发现我做错了什么,并提出解决方案

相册

StoreManagerViewModel

public class StoreManagerViewModel
{
    public Album Album { get; set; }
    public IList<Artist> Artists { get; set; }
    public IList<Genre> Genres { get; set; }
}
更新1:

如果我在UpdateModel上设置断点并查看ValueProvider.GetValue(“Album.Artist”),我可以看到更新的ArtistId。这对我来说意味着我完全误解了它的工作原理

我真的应该让UpdateModel处理它可以处理的属性吗

UpdateModel(album, "Album", new string[] { "Title", "Price", "AlbumArtUrl" });
然后根据ValueProvider中的ArtistId、GenreId手动获取对象来更新艺术家和流派属性

更新2

一种修复

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

这是可行的,我不知道这类事情应该怎么做。如果不是的话,请有人纠正我的错误。

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

此外,如果您有强类型视图,则可以将模型用作控制器参数,而不是FormCollection。另外,排除已编辑相册的id也是一种很好的做法

ActionResult Edit([Bind(Exclude = "AlbumId")]Album album)
您将从表单中获取相册所有字段以进行编辑

关于您的代码:
现在,我在您的代码中看到,您在db中通过id获得当前相册

 var album = AlbumRepository.GetById(id);

然后尝试使用它执行一些操作,但不清楚如何从视图绑定信息。

为了解决此问题,我在ViewModel中创建了一个链接到相关对象的属性,然后绑定到该属性:


型号 查看

m、 用户(机场)%>:
m、 UserAirportId,新选择列表(Model.Airports,“Id”,“Name”))%>
m、 用户(机场)%%>
视图模型
public类UserViewModel
{
公共用户{get;set;}
公共列表{get;set;}
公共列表角色{get;set;}
//通过UpdateModel绕过复杂对象的非绑定
公共Guid UserAirportId
{
得到
{
if(User!=null&&User.Airport!=null)
返回User.Airport.Id;
返回Guid.Empty;
}
设置
{
if(User==null)
返回;
var airport=Airports.Where(m=>m.Id==value).FirstOrDefault();
如果(机场==null)
返回;
User.Airport=机场;
}
}
}

有趣的是,如果我使用我的模型作为控制器参数,而不是FormCollection,则艺术家和流派属性都为Null。完全正确。如果您想在Artisters和Genere中生成非NULL,则必须使控制器可用于获取此类模型。流派和艺术家是StoreManagerViewModel的一部分,而不是专辑。因此,您必须了解在编辑操作中真正需要什么。您想只更新数据库中的相册条目还是同时更新一些相关信息。我认为阅读有关MVC基础知识、模型和数据绑定的内容对你很有用。试着在这里找到这本书:老实说,我对你说的有点困惑,艺术家和流派都是专辑的一部分,我只想更新专辑记录。对不起。我错过了。我想你说的是StoreManagerViewModelth中的IList。为什么在艺术家和流派中为空的问题在于如何绑定视图。DropDownList(“Artist”…是不正确的,请尝试在所有其他字段中使用它。Html.DropDownListFor(model=>model.Artist…),我认为这是一个解决方案
<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>
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);
ActionResult Edit([Bind(Exclude = "AlbumId")]Album album)
 var album = AlbumRepository.GetById(id);
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;
        }
    }
}