Asp.net mvc What is the best practice for restoring ViewModel?

In POST, if the check fails, and before sending the ViewModel to the same view with model state errors, do you rebuild the ViewModel for all SelectLists, ReadOnly, etc. fields? right now I have separate methods for Fill First Time (for GET Edit-Method) / Rebuild ViewModels from domain objects, which is best practice, so I can be DRY, and also do not need to change two methods every time I add a new readonly property to ViewModel?

My solution: Following this pattern

The following template suggested here: stack overflow

Build(..) { var viewModel = new ViewModel(); // and Fill all Non-ReadOnly fields ... ... call CompleteViewModel(viewModel) } CompleteViewModel(ViewModel viewModel) { //Fill all ReadOnly & SelectLists ... } 

The reason I went with this solution is because I don’t want to store files on the server for upload via HTTP requests

+6
source share
3 answers

I do not rebuild it because I do not stay at POST. I follow the POST-REDIRECT-GET pattern, so if I send a message to / User / Edit / 1 using the HTTP POST method, I will be redirected to / User / Edit / 1 uasing GET.

ModelState ported to TempData to follow Post-Redirect-Get and be available when calling GET. The view model is built in one place, when calling GET. Example:

  [HttpPost] [ExportModelStateToTempData] public ActionResult Edit(int id, SomeVM postedModel) { if (ModelState.IsValid) { //do something with postedModel and then go back to list return RedirectToAction(ControllerActions.List); } //return back to edit, because there was an error return RedirectToAction(ControllerActions.Edit, new { id }); } [ImportModelStateFromTempData] public ActionResult Edit(int id) { var model = //create model here return View(ControllerActions.Edit, model); } 

This is the code to import / export ModelState attributes:

 public abstract class ModelStateTempDataTransferAttribute : ActionFilterAttribute { protected static readonly string Key = typeof(ModelStateTempDataTransferAttribute).FullName; } public class ExportModelStateToTempDataAttribute : ModelStateTempDataTransferAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { //Only export when ModelState is not valid if (!filterContext.Controller.ViewData.ModelState.IsValid) { //Export if we are redirecting if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult)) { filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState; } } base.OnActionExecuted(filterContext); } } public class ImportModelStateFromTempDataAttribute : ModelStateTempDataTransferAttribute { public override void OnActionExecuted(ActionExecutedContext filterContext) { ModelStateDictionary modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary; if (modelState != null) { //Only Import if we are viewing if (filterContext.Result is ViewResult) { filterContext.Controller.ViewData.ModelState.Merge(modelState); } else { //Otherwise remove it. filterContext.Controller.TempData.Remove(Key); } } base.OnActionExecuted(filterContext); } } 
+7
source

The simplest solution would be to pass you the viewModel to the method and account for null

 private MyViewModel BuildViewModel(MyViewModel model = null) { model = model ?? new MyViewModel(); model.ReadOnlyList = new ..... . . return model; } 

for Create:

  var model = BuildViewModel(); 

recovery:

  model = buildViewModel(model); 
+1
source

I like @LukLed answer above - it looks very interesting. If you need another option, here is what I am doing now.

In my service layer, I have a method for creating my view model. I call this in a GET and return the view model to the view. In POST, I create a model from the incoming ID, and then TryUpdateModel (model). From there you can do whatever you want (save, check the state of the model, etc.). With this method, you have only 1 build method and you need to update it only once if your model changes (i.e. add / remove properties in the future, etc.).

 [HttpGet] public ActionResult AssessFocuses(int apaID) { var model = this.apaService.BuildAssessFocusesViewModel(apaID); return this.View(model); } [HttpPost] public ActionResult AssessFocuses(int apaID, string button) { var model = this.apaService.BuildAssessFocusesViewModel(apaID); this.TryUpdateModel(model); switch (button) { case ButtonSubmitValues.Back: case ButtonSubmitValues.Next: case ButtonSubmitValues.Save: case ButtonSubmitValues.SaveAndClose: { try { this.apaService.SaveFocusResults(model); } catch (ModelStateException<AssessFocusesViewModel> mse) { mse.ApplyTo(this.ModelState); } if (!this.ModelState.IsValid) { this.ShowErrorMessage(Resources.ErrorMsg_WEB_ValidationSummaryTitle); return this.View(model); } break; } default: throw new InvalidOperationException(string.Format(Resources.ErrorMsg_WEB_InvalidButton, button)); } switch (button) { case ButtonSubmitValues.Back: return this.RedirectToActionFor<APAController>(c => c.EnterRecommendationsPartner(model.ApaID)); case ButtonSubmitValues.Next: return this.RedirectToActionFor<APAController>(c => c.AssessCompetenciesPartner(model.ApaID)); case ButtonSubmitValues.Save: this.ShowSuccessMessage(Resources.Msg_WEB_NotifyBarSuccessGeneral); return this.RedirectToActionFor<APAController>(c => c.AssessFocuses(model.ApaID)); case ButtonSubmitValues.SaveAndClose: default: return this.RedirectToActionFor<UtilityController>(c => c.CloseWindow()); } } 
0
source

All Articles