ViewModel Property Null in Post Action

2019-09-13 07:17发布

问题:

This is now fixed. A combination of Ish's suggestion below plus adding calls to @HiddenFor in the view resolved the problem.

I have an ASP.NET MVC 5 web application where users can mark a defect as resolved. I want to display a list of potentially related defects, with check-boxes that users can tick to indicate that yes, this is the same defect, and should also be marked as resolved.

So I have a View Model with a property that is a collection, each member of which contains a defect object property and Boolean IsSameDefect property. This all works fine in the GET action method and in the view. I can display the related defects and tick the boxes.

The problem arises in the POST action when I want to update the data. At this point the property (the collection of potentially related defects) is null. I'm having a hard time trying to figure out how to pass this data back to the controller?

Code as requested ...

// GET: /DefectResolution/Create
public ActionResult Create(int ciid)
{
    int companyId = User.CompanyID();
    DefectResolutionCreateViewModel drcvm = new DefectResolutionCreateViewModel(ciid, companyId);
    return View(drcvm);
}

// POST: /DefectResolution/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(DefectResolutionCreateViewModel drcvm)
{
    DefectResolutions currentResolution = drcvm.DefectResolution;
    currentResolution.CreatedOn = System.DateTime.Now;
    currentResolution.UserID = User.UserID();

    if (ModelState.IsValid)
    {
        unitOfWork.DefectResolutionRepository.Insert(currentResolution);

        if (currentResolution.ResolutionStatusID == 2)
        {
            //code breaks here as drcvm.RelatedUnresolvedDefects is null
            foreach (var relatedDefect in drcvm.RelatedUnresolvedDefects)
            {
                if (relatedDefect.IsSameDefect)
                {
                    DefectResolutions relatedResolution = new DefectResolutions();
                    relatedResolution.ChecklistID = relatedDefect.RelatedChecklist.ChecklistID;
                    relatedResolution.CreatedOn = System.DateTime.Now;
                    relatedResolution.ResolutionNote = currentResolution.ResolutionNote;
                    relatedResolution.ResolutionStatusID = currentResolution.ResolutionStatusID;
                    relatedResolution.UserID = User.UserID();
                }
            }
        }

        unitOfWork.Save();
        return RedirectToAction("Index", new { ciid = currentResolution.ChecklistID });
    }
    return View(drcvm);
}

In the view ...

@model Blah.ViewModels.DefectResolution.DefectResolutionCreateViewModel
@{
    ViewBag.Title = "Create Defect Resolution";
    var relatedDefects = Model.RelatedUnresolvedDefects;
}

... and later in the view ...

 @for (int i = 0; i < relatedDefects.Count(); i++ )
{
    <tr>
        <td>
            @Html.EditorFor(x => relatedDefects[i].IsSameDefect)
        </td>
    </tr>
}                       

I followed Ish's suggestion below, and modified the code to refer to Model.RelatedUnresolvedDefects directly instead of using a variable as I had been doing. This does get me a bit further. The view model's RelatedUnresolvedDefects property is no longer null. But only RelatedUnresolvedDefects.IsSameDefect has a value. RelatedUnresolvedDefects.RelatedChecklist is null. Here's the controller code again showing where it now breaks ...

// POST: /DefectResolution/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(DefectResolutionCreateViewModel drcvm)
{
    DefectResolutions currentResolution = drcvm.DefectResolution;
    currentResolution.CreatedOn = System.DateTime.Now;
    currentResolution.UserID = User.UserID();

    if (ModelState.IsValid)
    {
        unitOfWork.DefectResolutionRepository.Insert(currentResolution);

        if (currentResolution.ResolutionStatusID == 2)
        {
            //prior to change, code used to break here
            foreach (var relatedDefect in drcvm.RelatedUnresolvedDefects)
            {
                if (relatedDefect.IsSameDefect)
                {
                    DefectResolutions relatedResolution = new DefectResolutions();

                    //code now breaks here because relatedDefect.RelatedChecklist is null
                    relatedResolution.ChecklistID = relatedDefect.RelatedChecklist.ChecklistID;
                    relatedResolution.CreatedOn = System.DateTime.Now;
                    relatedResolution.ResolutionNote = currentResolution.ResolutionNote;
                    relatedResolution.ResolutionStatusID = currentResolution.ResolutionStatusID;
                    relatedResolution.UserID = User.UserID();
                }
            }
        }

        unitOfWork.Save();
        return RedirectToAction("Index", new { ciid = currentResolution.ChecklistID });
    }
    return View(drcvm);
}

回答1:

Without knowing your code.I suggest you to use for loop instead of foreach while rendering the defects in View (.cshtml).

Editing Answer based on your code.
Following statement in the view creating problem

 var relatedDefects = Model.RelatedUnresolvedDefects;

You should directly iterate over the Model.RelatedUnresolvedDefects property in the loop.

             @for (int i = 0; i < Model.RelatedUnresolvedDefects.Count(); i++ )
                        {
                            <tr>
                                <td>
                                    @Html.EditorFor(x => Model.RelatedUnresolvedDefects[i].IsSameDefect)
                                </td>
                            </tr>
                        }