Setting selected property in SelectList for each o

2019-08-31 02:10发布

问题:

Follow up question to : Populate a list property from delimited string

The problem I'm having is the DropDown boxes in my Edit view are not retaining/selecting the the proper item based on the AssignedUserID property within BugAssignment model.

Models:

public class BugAssignment
{
    public int BugAssignmentID { get; set; }
    public int BugNumber { get; set; }
    public int? AssignedUserID { get; set; }
    public virtual User AssignedUser { get; set; }
    public virtual Status Status { get; set; }
    public IEnumerable<SelectListItem> Users { get; set; }
}

public class BugAssignmentList
{
    public BugAssignmentList()
    {
        BugAssignments = new List<BugAssignment>();
    }
    public int BugAssignmentListID { get; set; }
    public string Name { get; set; }
    public List<BugAssignment> BugAssignments { get; set; }
}

Edit/Details ViewModel

public class BugAssignmentListDetailsViewModel
{
    public int BugAssignmentListID { get; set; }
    public string Name { get; set;}
    public List<BugAssignment> BugAssignments { get; set; }
}

I have the following two methods in my controller:

    public ActionResult Edit(int id)
    {
        var balData = db.BugAssignmentLists.Include(bn => bn.BugAssignments).ToList();
        //this is a reference to the bug assignment list object whose ID was passed in
        BugAssignmentList bugAssignmentList = balData.Where(b => b.BugAssignmentListID == id).FirstOrDefault();
        //loop through the bug assignment list and for each bug assignment
        foreach (BugAssignment b in bugAssignmentList.BugAssignments)
        {
            //create the select list for each BugAssignment object
            b.Users = users.Select(u => new SelectListItem
            {
                Value = u.UserID.ToString(),
                Text = u.FullName,
                Selected = u.UserID == b.AssignedUserID //<<THIS IS WHERE IT IS MESSING UP
            });

        }
        BugAssignmentListDetailsViewModel detailsVM = new BugAssignmentListDetailsViewModel
        {
            BugAssignmentListID = id
        };
        if (bugAssignmentList != null)
        {
            detailsVM.BugAssignments = bugAssignmentList.BugAssignments;
            detailsVM.Name = bugAssignmentList.Name;
        }
        return View(detailsVM);
    }

    // POST
    [HttpPost]
    public ActionResult Edit(BugAssignmentListDetailsViewModel viewModel)
    {
        var balData = db.BugAssignmentLists.Include(bn => bn.BugAssignments).ToList();
        var users = db.Users.ToList();
        BugAssignmentList bugAssignmentList = balData.Where(b => b.BugAssignmentListID == viewModel.BugAssignmentListID).FirstOrDefault();
        bugAssignmentList.Name = viewModel.Name;
        bugAssignmentList.BugAssignments = viewModel.BugAssignments;

        if (ModelState.IsValid)
        {
            UpdateModel(bugAssignmentList, "BugAssignmentList");
            db.Entry(bugAssignmentList).State = EntityState.Modified;
            db.SaveChanges();
            return RedirectToAction("Index");
        }
        return View(bugAssignmentList);
    }

View:

<table>    
<thead>
    <th>
        Bug Number
    </th>
    <th>
        Assigned User
    </th>
</thead>
    @for(int i=0; i<Model.BugAssignments.Count(); i++)
    {
        <tr>
            <td style="text-align: center;">
                @Html.EditorFor(modelItem => Model.BugAssignments[i].BugNumber)
            </td>
            <td>
                @Html.DropDownListFor(modelItem => Model.BugAssignments[i].AssignedUserID, Model.BugAssignments[i].Users, "-- Select User --")
                @Html.HiddenFor(modelItem => Model.BugAssignments[i].BugAssignmentID)
            </td>
        </tr>
    }
</table>

The POST is working fine. Looking in the database, each BugAssignment in the BugAssignments collection within BugAssignmentList is updating with the proper AssignedUserID from the dropdown box in my Edit view, so it's not the POST method.

The problem is in the Edit(int id). What's happening is very odd. I've put a breakpoint right before the foreach loop and I looked inside of bugList.BugAssignments. All the AssignedUserID's are correct, but after the loop completes, and I inspect the list again, all the SelectListItems in BugAssignment.Users have the same User selected.

What is wrong with the following code that would cause this?

    foreach (BugAssignment b in bugAssignmentList.BugAssignments)
    {
        //create the select list for each BugAssignment object
        b.Users = users.Select(u => new SelectListItem
        {
            Value = u.UserID.ToString(),
            Text = u.FullName,
            Selected = u.UserID == b.AssignedUserID //<<THIS IS WHERE IT IS MESSING UP
        });

    }

Also, as a side note: I want the interface to look something like this (which is why I need the SelectList to be on the BugAssignment model, unless there is an easier way. I don't want to write a BugAssignment controller/view I want someone to be able to quickly assign users to bug numbers like the following:

Here is another screenshot that better shows what is happening:

回答1:

I believe this is a closure issue. Try creating a local copy of b.assignedUserID:

foreach (BugAssignment b in bugAssignmentList.BugAssignments)
{
    var assignedUserID = b.AssignedUserID;

    //create the select list for each BugAssignment object
    b.Users = users.Select(u => new SelectListItem
    {
        Value = u.UserID.ToString(),
        Text = u.FullName,
        Selected = (u.UserID == assignedUserID),
    });

}


回答2:

I'm guessing this is an issue with closures. Since you are generating the SelectListItem inside a lambda, then it is getting a copy of only the first b.AssignedUserID and using it everywhere. Some reading materials on this: Is there a reason for C#'s reuse of the variable in a foreach?

I think this modification might fix the problem:

foreach (BugAssignment b in bugAssignmentList.BugAssignments)
    {
        var bugAssignedUser = b.AssignedUserID;
        //create the select list for each BugAssignment object
        b.Users = users.Select(u => new SelectListItem
        {
            Value = u.UserID.ToString(),
            Text = u.FullName,
            Selected = u.UserID == bugAssignedUser  //<<THIS IS WHERE IT IS MESSING UP
        });

    }