列表框上的MVC模型状态验证失败

时间:2013-04-22 02:15:40

标签: asp.net asp.net-mvc entity-framework

我有一个简单的模型,它使用多选列表框来实现许多EF关系。

在我的Create操作中,我收到了错误

  

从“System.String”类型到“MyProject.Models.Location”类型的参数转换失败,因为没有类型转换器可以在这些类型之间进行转换。

我有两个模型,一篇文章和一个位置:

Article.cs

namespace MyProject.Models
{
    public class Article
    {
        public Article()
        {
            Locations = new List<Location>();
        }

        [Key]
        public int ArticleID { get; set; }

        [Required(ErrorMessage = "Article Title is required.")]
        [MaxLength(200, ErrorMessage = "Article Title cannot be longer than 200 characters.")]
        public string Title { get; set; }

        public virtual ICollection<Location> Locations { get; set; }
    }

Location.cs:

namespace MyProject.Models
{
    public class Location
    {
        [Key]
        public int LocationID { get; set; }

        [Required(ErrorMessage = "Location Name is required.")]
        [MaxLength(100, ErrorMessage = "Location Name cannot be longer than 100 characters.")]
        public string Name { get; set; }

        public virtual ICollection<Article> Articles { get; set; }
    }
}

我有一个ViewModel:

namespace MyProject.ViewModels
{
    public class ArticleFormViewModel
    {
        public Article article { get; set; }
        public virtual List<Location> Locations { get; set; }

        public ArticleFormViewModel(Article _article, List<Location> _locations)
        {
            article = _article;
            Locations = _locations;
        }
    }
}

create.cshtml:

@model MyProject.ViewModels.ArticleFormViewModel
<h2>Create</h2>

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Article</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.article.Title)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.article.Title)
            @Html.ValidationMessageFor(model => model.article.Title)
        </div>
        <h3>Locations</h3>
        @Html.ListBoxFor(m=>m.article.Locations,new MultiSelectList(Model.Locations,"LocationID","Name"))
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

最后我的控制器动作:

// GET: /Article/Create

public ActionResult Create()
{

    var article = new Article();
    var AllLocations = from l in db.Locations
                      select l;

    ArticleFormViewModel viewModel = new ArticleFormViewModel(article, AllLocations.ToList());

    return View(viewModel);


}

//
// POST: /Article/Create

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Article article)
{
    var errors = ModelState.Values.SelectMany(v => v.Errors);

    if (ModelState.IsValid)
    {
        var locations = Request.Form["article.Locations"];
        if (locations != null)
        {
            var locationIDs = locations.Split(',');
            foreach (var locationID in locationIDs)
            {
                int id = int.Parse(locationID);
                Location location = db.Locations.Where(l => l.LocationID == id).First();
                article.Locations.Add(location);
            }
        }

        db.Articles.Add(article);
        db.SaveChanges();
        return RedirectToAction("Index");
    }

    var AllLocations = from l in db.Locations
                       select l;
    ArticleFormViewModel viewModel = new ArticleFormViewModel(article, AllLocations.ToList());
    return View(viewModel);

}

这一切都运行得很好,我的位置列表框已正确填充:

enter image description here

如果我没有选择位置,那么我的模型会正确保存。如果我选择一个或多个位置,那么我的Model.IsValid检查将失败,并显示异常

  

从“System.String”类型到“MyProject.Models.Location”类型的参数转换失败,因为没有类型转换器可以在这些类型之间进行转换。

但是,如果我删除了ModelState.IsValid检查,那么即使出现错误,我的值也会正确保存到数据库中 - 只是我失去了对模型标题等内容的验证。

希望有人可以提供帮助!

1 个答案:

答案 0 :(得分:1)

除非您创建类型转换器,否则无法直接将列表框的结果绑定到这样的复杂对象。原因在于MVC只能处理发布的HTTP值,在这种情况下,HTTP值是包含所选ID的字符串数组。这些字符串不直接映射到Locations对象(即数字1不能直接转换为ID为1的Locations对象。)

最好的办法是在View Model中输入一个类型为string或int的位置ID列表以接受发布的值,然后在post方法中创建Location对象并用正确的ID填充它们。

仅供参考,您的代码工作的原因是您绕过模型绑定并直接转到Request.Form集合。您会注意到绑定的Article对象将没有任何Location对象。

编辑:

即使没有这个问题,我甚至都看不到你的代码是如何工作的。您的ArticleFormViewModel没有无参数构造函数,因此在模型绑定中会失败(除非您有自定义模型绑定器)。

无论如何,你要做的就是这个(注意,如果你想在渲染视图时选择它们,你必须填充SelectedLocationIDs):

public class ArticleFormViewModel
{
    ...
    List<int> SelectedLocationIDs { get; set; }
    ...
}

然后,在您的视图中,您有:

@Html.ListBoxFor(m=>m.SelectedLocationIDs, 
    new MultiSelectList(Model.Locations,"LocationID","Name"))

在Post方法中,您可以使用以下内容代替调用Request.Form的代码:

foreach(var locationID in article.SelectedLocationIDs) {
    ... // look up your locations and add them to the model
}