Asp.net mvc重建ViewModel的最佳做法是什么?

时间:2013-01-14 19:32:16

标签: asp.net-mvc

在POST时,如果验证失败并且在将ViewModel发送回具有模型状态错误的同一视图之前,您是否为所有SelectLists,ReadOnly字段等重建ViewModel? 现在我有单独的Fill First Time方法(对于GET Edit-Method)/从域对象重建ViewModel,最好的做法是什么,所以我可以干,也不必在每次添加新的readonly属性时更改两种方法到ViewModel?

我的解决方案:遵循此模式

此处建议采用以下模式:https://stackoverflow.com/a/2775656/57132 在IModelBuilder实现中

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

CompleteViewModel(ViewModel viewModel)
{
  //Fill all ReadOnly & SelectLists
  ...
}

我使用此解决方案的原因是因为我不希望在服务器上存储内容以通过HTTP请求进行检索

3 个答案:

答案 0 :(得分:7)

我不重建它,因为我没有留在POST。我遵循POST-REDIRECT-GET模式,所以如果我使用POST HTTP方法发布到/ User / Edit / 1,我会被重定向到/ User / Edit / 1 uasing GET。

ModelState转移到TempData以关注重定向后获取并可在GET通话时使用。在GET调用中,View模型构建在一个地方。例如:

    [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);
    }

这是导入/导出ModelState的属性代码:

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);
    }
}

答案 1 :(得分:1)

最简单的解决方案是将viewModel传递给null

的方法和帐户
private MyViewModel BuildViewModel(MyViewModel model = null)
{
    model = model ?? new MyViewModel();
    model.ReadOnlyList = new .....
    .
    .
    return model;
}

for Create:

 var model = BuildViewModel();

重建:

 model = buildViewModel(model);

答案 2 :(得分:0)

我喜欢上面的@LukLed答案 - 看起来非常有趣。如果你想要另一种选择,这就是我现在所做的。

在我的服务层,我有一个方法来构建我的视图模型。我在GET上调用它并将视图模型返回到视图。在POST上,我从传入ID构建模型,然后使用TryUpdateModel(模型)。从那里,你可以做任何你喜欢的事情(保存,检查模型状态等)。使用此方法,您只有1个构建方法,并且只有在模型更改时才需要更新一次(即将来添加/删除属性等)。

[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());
    }
}