2

我有一个简单的模型,它使用多选列表框来处理多对多 EF 关系。

在我的Create行动中,我得到了错误

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

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

文章.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; }
    }

位置.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; }
    }
}

我有一个视图模型:

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

创建.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);

}

这一切都工作得很好,我的位置列表框被正确填充:

在此处输入图像描述

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

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

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

希望有人能帮忙!

4

1 回答 1

1

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

您最好的选择是在您的视图模型中包含一个位置 ID 列表,类型为 string 或 int 以接受发布的值,然后在您的 post 方法中创建 Location 对象并用正确的 ID 填充它们。

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

编辑:

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

无论如何,您想要做的是(注意,如果您希望在呈现视图时选择它们,则必须填充 SelectedLocationID):

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
}
于 2013-04-22T07:46:56.830 回答