0

Visual Studio 2012 デバッガーを実行していますが、productViewModelList パラメーターに、ビューが [HTTPPOST] Edit アクションに渡すすべての値が含まれていないことがわかりました。理由がわかりません。ブレークポイントを挿入し、productViewModelList の値を確認した場所については、以下のコード サンプルのコメントを参照してください。

以下の値が productViewModelList に与えられます。

BrandId = 0、BrandName = "6"、BrandSelectListItem = null、ID = 5、Name = "クラッチ"、Price = 10.0

  • BrandID が正しくありません。DropDownList のビューで、「Catatonics Inc.」を割り当てます。データベースで確認した ID は 6 です。
  • BrandName は、BrandID にあるはずの「6」を示しています。BrandName は「Catatonics Inc.」である必要があります。
  • BrandSelectList アイテムは SelectListItem タイプのオブジェクトであり、ビューの DropDownList アイテムに入る値が含まれています。DropDownList には値が正しく表示されますが、BrandSelectList はnull[httpPost] 編集アクションが実行されたときです。DropDownList の Selected 項目にアクセスする必要があります。
  • 他のすべての値、ID、名前、および価格は正しいです。

ここに私のコードのいくつかのクラスがあります。

MedicalProductController

public class MedicalProductController : Controller
{
    private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();

    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit(IEnumerable<MedicalProductViewModel> productViewModelList)
    {
        // I have a breakpoint inserted here, and check productViewModelList with debugger.  

        var modelList = GetMedicalProductList(productViewModelList);
        if (ModelState.IsValid)
        {
            foreach (var model in modelList)
                _db.Entry(model).State = EntityState.Modified;

            _db.SaveChanges();
            return RedirectToAction("Index");
        }

        return View(productViewModelList);
    }
}

MedicalProductMapper

public class MedicalProductMapper
{

    public IEnumerable<MedicalProductViewModel> MapMedicalProductViewModel(IEnumerable<MedicalProduct> productList, IEnumerable<Brand> brandList)
    {
        var brandSelectListItem = brandList.Select(b => new SelectListItem()
                                                {
                                                    Text = b.Name,
                                                    Value = b.ID.ToString()
                                                });

        var viewModelList = productList.Select(p => new MedicalProductViewModel() 
                                {
                                    BrandID = p.BrandID,
                                    BrandName = brandList.SingleOrDefault(b => b.ID == p.BrandID).Name,
                                    BrandSelectListItem = brandSelectListItem,
                                    ID = p.ID,
                                    Price = p.Price,
                                    Name = p.Name
                                });

        return viewModelList;
    }

    public IEnumerable<MedicalProduct> MapMedicalProductList(IEnumerable<MedicalProductViewModel> viewModelList)
    {
        var modelList = viewModelList.ToArray().Select( viewModel => new MedicalProduct()
        {
            Name = viewModel.Name,
            Price = viewModel.Price,
            BrandID = Convert.ToInt32(viewModel.BrandSelectListItem.Select(b => b.Value.ToString()))
        });

        return modelList;
    }
}

EDIT.cshtml

@model IEnumerable<MvcMedicalStore.Models.MedicalProductViewModel>

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

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

    <fieldset>
        <legend>MedicalProduct</legend>
        @Html.EditorFor(m => m)        
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

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

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

EditorTemplates/MedicalProductViewModel.cshtml

(これは、Edit.cshtml のディレクトリのサブディレクトリにあります。)

@model MvcMedicalStore.Models.MedicalProductViewModel

@Html.HiddenFor(item => Model.ID)

<div class="editor-label">
    @Html.LabelFor(item => Model.Name)
</div>
<div class="editor-field">
    @Html.EditorFor(item => Model.Name)
    @Html.ValidationMessageFor(item => Model.Name)
</div>

<div class="editor-label">
    @Html.LabelFor(item => Model.Price)
</div>
<div class="editor-field">
    @Html.EditorFor(item => Model.Price)
    @Html.ValidationMessageFor(item => Model.Price)
</div>

<div class="editor-label">
    @Html.LabelFor(item => Model.BrandName)
</div>
<div class="editor-field">
    @Html.DropDownListFor(item => Model.BrandName, Model.BrandSelectListItem)
    @Html.ValidationMessageFor(item => Model.BrandName)
</div>

編集:

ブランド

public class Brand
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(30)]
    public string Name { get; set; }
}

医療製品

public class MedicalProduct
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.Currency)]
    public double Price { get; set; }

    // is a foreign key
    public int BrandID { get; set; }
}

MedicalProductViewModel

public class MedicalProductViewModel
{
    [Key]
    public int ID { get; set; }

    [Required]
    [StringLength(50)]
    public string Name { get; set; }

    [Required]
    [DataType(DataType.Currency)]
    public double Price { get; set; }

    public int BrandID { get; set; }

    [DisplayFormat(NullDisplayText="[Generic]")]
    public string BrandName { get; set; }

    public IEnumerable<SelectListItem> BrandSelectListItem { get; set; }
}
4

1 に答える 1

0

のフォーム要素がないBrandIDため、モデル バインダーがフォーム POST をモデルに変換するとき、デフォルト値のint0 が使用されます。フォーム要素は次のBrandNameとおりです。

@Html.DropDownListFor(item => Model.BrandName, Model.BrandSelectListItem)

これはおそらく ( の定義とBrandSelectListItem、記述した観察された動作に基づいて)要素が使用可能な値をテキストとして使用し、使用可能な値を値として使用するselect要素を作成します。要素が HTTP POST に含まれている場合、選択された値が投稿されます。したがって、選択された を取得していますが、モデルのそのプロパティにバインドすることにより、値に割り当てています。optionBrandNameBrandIDselectBrandIDBrandName

あなたが本当に欲しいのはこれのようです:

@Html.DropDownListFor(item => Model.BrandID, Model.BrandSelectListItem)

これにより、次のようになります。

BrandId = 6, BrandName = "", BrandSelectListItem = null, ID = 5, Name = "Crutch", Price = 10.0

まだBrandName値がありませんが、この場合本当に必要ですか? 重複データです。BrandNameモデルは、既知の から を動的に決定できますBrandID。したがってBrandName、モデルの読み取り/書き込みプロパティである必要はありません。BrandNameモデルの に基づいてをフェッチする読み取り専用プロパティにすることができBrandIDます。それは次のような単純なものかもしれません:

public string BrandName
{
    get
    {
        var brandName = GetBrandName(BrandID);
        return brandName;
    }
}

この場合GetBrandName、データ ストアから値をフェッチするように実装します。または、それが十分に明確で簡潔であれば、すべてをインラインで実行します。それはあなた次第です。クラスレベルのプライベート変数に値をキャッシュして、一度だけ取得する必要があるようにすることもできます。(その変数をクリアするか、BrandID変更するたびに再取得してください。)

両方のプロパティをそのままにしておくこともできますが、その場合は、オブジェクトが UI で過ごす時間も含めて、オブジェクトの存続期間中、それらが同期されていることを確認する必要があります。同じ情報が 2 つの場所に記録されている場合は常に、同期を維持する責任があります。これは決して楽しいことではありません。この場合、ビューに 2 つの要素が必要になり (2 つ目の要素は非表示のフォーム フィールド、または非表示のスタイルのドロップダウン リストである可能性があります)、JavaScript を記述して、いつでもいずれかを更新する必要があります。もう1つは変わります。

于 2013-10-04T19:29:42.033 に答える