使用MVC EditorFor模板&存储SelectList ID时出现问题注释(.net 4,mvc3)

时间:2011-03-23 20:26:55

标签: .net asp.net asp.net-mvc asp.net-mvc-3

我目前可以使用以下模型注释,视图模型和自定义编辑器模板填充并显示下拉列表 - 但是在持续提交时遇到问题(更多内容见下文):

//Domain
Public class Person
{
  public string FirstName {get;set;}
  public string LastName {get;set;}
  public string ClassName {get;set;}
  public int ClassId {get;set;}
  ...
}

//ViewModel
Public class PersonViewModel
{
  public Person Person {get;set;}
  [UIHint("SelectList")]
  public IEnumerable<SelectListItems> Classes {get;set;}

  public PersonViewModel
  {
    Person = new Person();
  }
}

//Controller
public ActionResult Create()
{
  var PersonViewModel = new PersonViewModel();
  _PersonService.PopulateClassList(PersonViewModel);
  return View("Create", PersonViewModel);
} 

[HttpPost]
public ActionResult Create(PersonViewModel PersonViewModel)
{
try
  {
  if (!ModelState.IsValid)
  {
    _PersonService.PopulateClassList(PersonViewModel);
    return View("Create", PersonViewModel);
  }

  var Person = new Person();
  Person.InjectFrom(PersonViewModel.Person);    
  _PersonService.Save(Person);

  return RedirectToAction("Index");
}
catch
{
  return View();
}
}

//View (just the pertinent part)
@Html.EditorFor(m => m.Classes)<br />
@Html.EditorFor(m => m.Person)

由于viewmodel注释

,会自动使用以下模板
// Views/Shared/EditorTemplates/SelectList.cshtml
@model IEnumerable<SelectListItem>
@Html.DropDownListFor(m => m, Model)

就像我提到的,上面工作正常 - 因为在下拉列表中显示正确,但显然在当前状态下它没有将classID(或名称)保存到Person实体,我理解问题但不知道如何解决它。

我的问题:

  1. 如何将所选值从下拉列表保存到Person实体

  2. 如何将ID和名称保存到Person实体中 - 我猜测只有一个隐藏的字段可以填充onchange事件(这听起来很奇怪,我确定,但它是存储在文档DB中,因此它没有标准化 - 我最后可能只存储Name)

  3. 提前致谢! 麦克

1 个答案:

答案 0 :(得分:3)

我认为在您的viewmodel中,您需要更改代码以使Classes为

IEnumerable<SelectListItem>

不是<SelectListItems>,您需要为所选项目添加字段。

Public class PersonViewModel
{
  public Person Person {get;set;}
  public IEnumerable<SelectListItem> Classes {get;set;}
  public string SelectedClassId {get; set;}
}

然后,您可以在视图中使用这样的代码来填充下拉列表。

@Html.DropDownListFor(x => x.SelectedClassId, Model.Classes, new { id = "classSelect" })

[编辑]

我仔细看了一下你正在做什么,看起来你正在创建一个表单来为新人输入信息,然后为那个人选择一个类,然后创建新的Person并保持它们。

首先,您不应该将PersonViewModel传递给您的服务。视图模型不是您的域的一部分。它应该用于促进View和Controller之间的通信。

其次,由于你还没有Person,你的_PersonService.PopulateClassList()方法应该更像这样。

public IQueryable<Class> GetClasses()
{
     //Data access logic
     //return classes .AsQueryable()
}

这样,您可以在需要时获取类列表。你的控制器看起来就像这样。

[HttpPost]
public ActionResult Create(PersonViewModel model)
{
    try
    {
        if (!ModelState.IsValid)
        {
            model.Classes = _PersonService.GetClasses().Select(c => new SelectListItem
                                                               {
                                                                   Text = c.Name,
                                                                   Value = "" + c.Id
                                                               };
            return View("Create", PersonViewModel);
        }
            model.Person.ClassId = model.SelectedClassId;
            //You can ditch ClassName since you'll just look it up by ID
            _PersonService.Save(model.Person);
            return RedirectToAction("Index");
}

[回答您的问题]

如果要从控制器中获取SelectList逻辑,可以修改模型中的属性,使其看起来像这样。

Public class PersonViewModel
{
  public Person Person {get;set;}
  public string SelectedClassId {get; set;}
  public IEnumerable<SelectListItem> Classes 
  {
      get
      { return _PersonService.GetClasses().Select(c => new SelectListItem
                                                           {
                                                               Text = c.Name,
                                                               Value = "" + c.Id
                                                           };
      }
  }
}

这将在读取该属性时访问该服务。我无法确定,但你可能需要在最后调用.AsEnumerable()。

我正在玩下拉框,看看我是否可以绑定到模型中的复杂对象。更多信息。

<强> [持续]

是的,您可以将下拉值直接绑定到模型中Person的ClassId属性。我能够在我的模型/视图中使用以下代码来完成它。我的Person和Class对象可能与你的完全不同,但你会明白这一点。

另外,我不确定你的域名,但是如果一个类可以在没有人的情况下存在,那么将它们的数据访问逻辑分成PersonRepository和ClassRepository(或PersonService等等)可能更有意义。 ,但我会把域式思维留给你。

<强>模型

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using DropDownTest.Classes;
    using System.Web.Mvc;

    namespace DropDownTest.Models
    {
        public class PersonViewModel
        {
            private ClassRepository _classRepository = new ClassRepository();

            public Person Person { get; set; }
            public IEnumerable<SelectListItem> Classes
            {
                get
                {
                    return _classRepository.GetClasses().Select(c => new SelectListItem
                                                               {
                                                                   Text = c.Name,
                                                                   Value = "" + c.ClassId
                                                               });
                }
            }
        }
    }

查看

@model DropDownTest.Models.PersonViewModel

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

@using (Html.BeginForm("Create", "Person", FormMethod.Post))
{
    <fieldset>
        <legend>New Person</legend>
        @Html.LabelFor(c => c.Person.FirstName)
        @Html.TextBoxFor(c => c.Person.FirstName)
        <br />

        @Html.LabelFor(c => c.Person.LastName)
        @Html.TextBoxFor(c => c.Person.LastName)
        <br />

        @Html.LabelFor(c => c.Person.ClassId)
        @Html.DropDownListFor(c => c.Person.ClassId, Model.Classes, new { id = "classSelect" })
        <input type="submit" value="submit" />
    </fieldset>
}

类库(在这种情况下将其视为服务) 这显然是假的,但是如果你使用抽象存储库,你可以在单元测试中使用这样的东西,并使用你的真实存储库进行生产使用,只要实现了IClassRepository并且你使用了某种依赖注入(DI) - 就像Ninject一样 - 但这是一个完全不同的主题:)。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace DropDownTest.Classes
{
    public class ClassRepository
    {
        private static IQueryable<Class> _classes = new List<Class>
        {
            new Class { ClassId = 0, Name = "Class 0" },
            new Class { ClassId = 1, Name = "Class 1" },
            new Class { ClassId = 2, Name = "Class 2" }
        }.AsQueryable();

        public IQueryable<Class> GetClasses()
        {
            return _classes;
        }
    }
}