问题
I have created a viewmodel
public VMPosition
{
public VMPosition(){}//for model binder
public VMPosition(int EmployeeID)
{
PositionStatusList = new SelectList(_repo.getStatuses);
//populate other properties
}
public int CurrentPositionID { get; set; }
public int EmployeeID { get; set; }
public int CurrentPositionHistoryID { get; set; }
public bool AddingNew { get; set; }
public bool ClosingCurrent { get; set; }
public string CurrentPosition { get; set; }
public DateTime CurrentPositionStartDate { get; set; }
public string ReasonForDeparture { get; set; }
public SelectList PositionStatusList { get; set; }
}
My GET ActionResult is defined as
public ActionResult UpdatePosition(int id)
{
return View(new VMPosition(id));
}
My POST actionresult is defined as
public ActionResult UpdatePosition(int id, VMPosition Position)
{
if(ModelState.IsValid){
Position Current = new Position{Position.Title etc..}
//save to db
return redirectToAction("someAction");
}
return View(Position);//here is the problem
}
My SelectList is populated in a constructor that accepts one parameter. Modelbinder cannot and should not call the constructor if the modelstate is invalid. I will have to return the View with model object (which in this case don't contain SelectList value). How can handle this scenario when using view Models.
I can manually populate these values in actionresult but that will violate the DRY principle. However, for the purposes of this question, I'd like help addressing the bigger design question.
回答1:
When dealing with dropdowns in my viewmodels, I usually have a single property associated with the selected list item's value and I have a property that returns a list of selectlistitems. Then, I use Html.DropDownListFor(m => m.ValueProperty, Model.DropDownValues) to render the dropdown.
I imagine in your scenario, you don't have a value corresponding to the selected listitem's value?
Edit: Here's an example from one of my apps...
public class MyVM
{
public int MyObjectId { get; set; }
public List<SelectListItem> MyObjectList
{
get
{
List<SelectListItem> list = (from o in MyObjects select new SelectListItem
{ Value = o.ObjectId.ToString(), Text = o.ObjectName }).ToList();
list.Insert(0, new SelectListItem
{ Value = "0", Text = "[Select an object]" });
return list;
}
}
}
<%: Html.DropDownListFor(m => m.MyObjectId, Model.MyObjectList)%>
You might have noticed the LINQ query that populates the list. In this example, I have a list (MyObjects) that was already populated by AutoMapper. You could simply return a static list if you prefer.
回答2:
Why not follow the convention which I reckon the majority of people use? You have coupled your ViewModel to your repo which I would also recommend changing. By putting the repo.GetStatuses inside your Controller/Action is simple and it works. I also prefer putting the SelectList inside my view and have the ViewModel house the list of items - but that's my personal preference. You can then clearly see/understand what type of objects your ViewModel deals with. DRY is a principle not a requirement.
ViewModel
public VMPosition
{
public int StatusId { get; set; }
public IList<Status> StatusList { get; set; }
}
Controller
public ActionResult UpdatePosition(int id)
{
var model = new VMPosition(id);
model.StatusList = _repo.getStatuses;
return View(model);
}
public ActionResult UpdatePosition(int id, VMPosition Position)
{
if(!ModelState.IsValid)
{
Position.StatusList = _repo.getStatuses;
return View(Position);
}
...
}
View
<%= Html.DropDownListFor(m => m.StatusId, new SelectList(Model.StatusList)...
Edit - Refactor PopulateSelectLists
public ActionResult UpdatePosition(int id)
{
var model = new VMPosition(id);
PopulateSelectLists(model);
return View(model);
}
public ActionResult UpdatePosition(int id, VMPosition Position)
{
if(!ModelState.IsValid)
{
PopulateSelectLists(Position);
return View(Position);
}
...
}
private void PopulateSelectLists(VMPosition Position)
{
Position.StatusList = _repo.GetStatuses;
Position.OtherSelectList = ...
...
}
来源:https://stackoverflow.com/questions/4334943/viewmodels-with-selectlist-design-decison