【问题标题】:ASP.net MVC DropDownList value used to Create new object用于创建新对象的 ASP.net MVC DropDownList 值
【发布时间】:2010-01-18 06:26:10
【问题描述】:

这似乎是一个非常琐碎的问题,我已经坚持了大约一个小时,我不明白我做错了什么。

我有一个 ViewModel:

public class SongFormViewModel
    {
        public Song Song { get; set; }
        public SelectList AlbumList { get; set; }

        public SongFormViewModel(Song song, IQueryable<Album> albumList)
        {
            Song = song;
            AlbumList = new SelectList(albumList, "AlbumId", "Title", song.AlbumId);  
        }
    }

我有一个创建视图:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NightSpot.Models.SongFormViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Create</h2>

    <%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

    <% using (Html.BeginForm()) {%>

        <fieldset>
            <legend>Fields</legend>
            <p>
                <label for="AlbumId">AlbumId:</label>
                <%= Html.DropDownList("AlbumId", Model.AlbumList) %>
                <%= Html.ValidationMessage("AlbumId", "*") %>
            </p>
            <p>
                <label for="Title">Title:</label>
                <%= Html.TextBox("Title") %>
                <%= Html.ValidationMessage("Title", "*") %>
            </p>
            <p>
                <label for="TrackNumber">TrackNumber:</label>
                <%= Html.TextBox("TrackNumber") %>
                <%= Html.ValidationMessage("TrackNumber", "*") %>
            </p>
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%=Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

我有一个 SongsController Create 方法:

        //
        // POST: /Songs/Create

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(Song song)
        {
            if (ModelState.IsValid)
            {
                try
                {
                    repository.Add(song);
                    repository.Save();

                    return RedirectToAction("Details", new { id = song.SongId });
                }
                catch
                {
                    ModelState.AddRuleViolations(song.GetRuleViolations());
                }
            }

            return View(new SongFormViewModel(song, repository.FindAllAlbums()));
        }

当我导航到 /Songs/Create URL 时,我看到了预期的 UI。我的下拉列表包含数据库中所有有效专辑的列表(来自专辑表),并且我已经验证每个 AlbumId 是值,每个 Title 是文本。太好了。

所以现在当我填写表格并点击“保存”时,我收到一条错误消息,告诉我需要选择专辑。当我在 Visual Web Developer 中单步调试调试器时,我看到标题和曲目编号已正确填充,但我的 Album 对象为 NULL,AlbumId 仍为 0。有什么想法吗?

更新

听从 Matt 的建议,我更新了我的 SongsController 以使用 SongFormViewModel 而不是 Song。这是新的控制器:

        //
        // POST: /Songs/Create

        [AcceptVerbs(HttpVerbs.Post)]
        public ActionResult Create(SongFormViewModel song)
        {
            Song newSong = song.Song;
            newSong.AlbumId = (int) song.AlbumList.SelectedValue;

            if (ModelState.IsValid)
            {
                try
                {
                    repository.Add(newSong);
                    repository.Save();

                    return RedirectToAction("Details", new { id = song.Song.SongId });
                }
                catch
                {
                    ModelState.AddRuleViolations(song.Song.GetRuleViolations());
                }
            }

            return View(new SongFormViewModel(newSong, repository.FindAllAlbums()));
        }

我启动了我的 /Songs/Create URL 并收到一条消息,说 SongFormViewModel 需要一个无参数的构造函数。

所以我做了一个。

当我重新运行 /Songs/Create URL 时,我的 SongsController 中的这一行出现“对象引用未设置为对象的实例”异常:

Line 93:             newSong.AlbumId = (int) song.AlbumList.SelectedValue;

想法?

更新

好的,所以我根据 Matt 的建议更新了我的观点,即在字段前加上“Song”:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NightSpot.Models.SongFormViewModel>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Create
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Create</h2>

    <%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

    <% using (Html.BeginForm()) {%>

        <fieldset>
            <legend>Fields</legend>
            <p>
                <label for="Song.AlbumId">AlbumId:</label>
                <%= Html.DropDownList("Song.AlbumId", Model.AlbumList) %>
                <%= Html.ValidationMessage("Song.AlbumId", "*") %>
            </p>
            <p>
                <label for="Song.Title">Title:</label>
                <%= Html.TextBox("Song.Title") %>
                <%= Html.ValidationMessage("Song.Title", "*") %>
            </p>
            <p>
                <label for="Song.TrackNumber">TrackNumber:</label>
                <%= Html.TextBox("Song.TrackNumber") %>
                <%= Html.ValidationMessage("Song.TrackNumber", "*") %>
            </p>
            <p>
                <input type="submit" value="Create" />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%=Html.ActionLink("Back to List", "Index") %>
    </div>

</asp:Content>

不幸的是,行为是相同的。 Null SelectedValue 和一个空的 Song 对象。

【问题讨论】:

  • 所以我猜这 song.AlbumList 在你的操作方法中是空的? song.Song 是空的,还是进来没问题?
  • 哦,现在这很有趣...SongFormViewModel.AlbumList.SelectedValue 为 null,并且 SongFormViewModel.Song 在调用 new Song() 构造函数后看起来处于一种状态。与以前不同,song.Song 上甚至没有填充文本框。
  • 啊!这可能是因为您在视图中的 Html.TextBox() 调用仍然指向“TrackNumber”之类的属性,而不是隧道进入“Song.TrackNumber”(因为您正在编辑 ViewModel 而不是直接编辑歌曲)。
  • 没有骰子。还是同样的问题。
  • 好的,我们已经达到了我的知识极限。我会把我的答案留在那里,我们会看看其他人是否可以提供帮助。如果您有一段时间没有得到答案,赏金可能会吸引更多回复。

标签: c# asp.net-mvc


【解决方案1】:

您正在尝试像经典 ASP.NET 一样使用 MVC

MVC 返回原始 HTTP post 值,并且 MVC 的默认助手尝试将它们转换为 basic 模型

您可以获取所选列表项的值,例如AlbumId,但不包括 AlbumList.SelectedValue 等复杂类型

试试这样的

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(int albumId, ...)

SongFormViewModel 在这里没有帮助您。它并不能反映您看起来真正想要的东西。它应该有像当前选择的专辑这样的字段

【讨论】:

    【解决方案2】:

    我自己刚刚在学习这些东西,但据我了解,将 song.AlbumId 传递给 SelectList 的构造函数只会设置列表的 initial 选定值。它不会将列表“绑定”到您的歌曲对象的 Album 属性。

    在您的 Create 方法中,您必须手动分配由 SongFormViewModel.Albums 列表中的选定项目表示的专辑。在这种情况下,您可以使用SelectedValue 属性来检索用户选择的专辑的 ID。您需要将其转换为 int(或 Guid - 无论 AlbumId 是什么类型)。

    当然,这意味着您需要通过 Create 方法访问 ViewModel 类,而不仅仅是创建的 Song。我相当确定您可以将参数类型从 Song 更改为 SongFormViewModel 并让默认绑定器为您处理。

    让我知道你的进展 - 我很想了解更多关于这些东西的信息,所以我很想看到最终的解决方案!

    【讨论】:

    • 谢谢,马特!我会试试看!
    【解决方案3】:

    编辑

    如果您想保留现有模型。将您的 SongFormViewModel 更改为此

    public class SongFormViewModel
    {
            public Song Song { get; set; }
            public SelectList AlbumList { get; set; }
    }
    

    然后改变

    return View(new SongFormViewModel(song, repository.FindAllAlbums()));
    

    var songFormViewModel = new SongFormViewModel();
    songFormViewModel.AlbumList = repository.FindAllAlbums();
    songFormViewModel.Song = song;
    return View(songFormViewModel);
    

    --

    您的视图应该是 Song 类型的强类型,而不是 SongFormViewModel。所以改变

    Inherits="System.Web.Mvc.ViewPage<NightSpot.Models.SongFormViewModel>"
    

    Inherits="System.Web.Mvc.ViewPage<NightSpot.Models.Song>"
    

    现在摆脱前缀字段更改。应该可以的。

    【讨论】:

    • 那么我将如何访问我的下拉列表的值?
    • 您需要将其保存在 Get 请求中的 ViewData 中。 ViewData["albumList"] = new List{//Your entries here}.
    • 您使用 SongFormViewModel 的方法也是可以的。您遇到的问题是因为您的控制器。带有参数的构造函数永远不会被调用,因为 ControllerFactory 不知道如何将 Song 和 IQueryable 传递给它,从哪里获取它。因此它抱怨无参数构造函数。您可以编写自己的 ControllerFactory 或使用依赖注入。
    • 啊哈……这很有道理。我逐步完成了我的 Create 方法并查看了 ModelState.Keys 和 ModelState.Values,看起来 AlbumId 甚至没有被绑定,因为唯一的条目是 TrackNumber 和 Title。我会调查的!感谢您的洞察力!
    • 我编辑了答案,如果您更喜欢使用现有模型,您现在应该可以了。 Matt 对您的控制器是正确的,如果您的视图是 SongFormViewModel 类型,它应该接受 SongFormViewModel。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-30
    相关资源
    最近更新 更多