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

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

私はビューモデルを持っています:

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

}

これはすべて比較的うまく機能し、私の Locations リストボックスは適切に入力されています:

ここに画像の説明を入力

場所を選択しない場合、モデルは適切に保存されます。1 つ以上の場所を選択すると、Model.IsValid チェックが例外で失敗します

タイプ 'System.String' からタイプ 'MyProject.Models.Location' へのパラメーター変換は失敗しました。これらのタイプ間で変換できるタイプ コンバーターがないためです。

ただし、 ModelState.IsValid チェックを削除すると、エラーにもかかわらず、値はすべてデータベースに正しく保存されます。モデルのタイトルなどの検証が失われます。

誰かが助けてくれることを願っています!

4

1 に答える 1

1

型コンバーターを作成しない限り、リスト ボックスの結果をそのような複雑なオブジェクトに直接バインドすることはできません。その理由は、MVC がポストされた HTTP 値 (この場合は、選択された ID を含む文字列の配列) しか処理できないためです。これらの文字列は Locations オブジェクトに直接マップされません (つまり、数値 1 を ID が 1 の Locations オブジェクトに直接変換することはできません)。

最善の策は、投稿された値を受け入れるためにビュー モデルに 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
}
于 2013-04-22T07:46:56.830 に答える