6

在 POST 上,如果验证失败并且在将 ViewModel 发送回具有模型状态错误的同一 View 之前,您是否为所有 SelectLists、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 请求进行检索

4

3 回答 3

7

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

ModelState被转移到TempData跟随 Post-Redirect-Get 并在 GET 调用时可用。视图模型在 GET 调用时构建在一个地方。例子:

    [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);
    }
}
于 2013-01-14T19:52:01.953 回答
1

最简单的解决方案是将您的 viewModel 传递给该方法并考虑 null

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

对于创建:

 var model = BuildViewModel();

重建:

 model = buildViewModel(model);
于 2013-01-14T19:37:05.213 回答
0

我喜欢上面@LukLed 的回答——看起来很有趣。如果您想要其他选择,这就是我目前所做的。

在我的服务层中,我有一种方法来构建我的视图模型。我在 GET 上调用它并将视图模型返回到视图。在 POST 上,我从传入的 ID 构建模型,然后是 TryUpdateModel(model)。从那里,您可以做任何您喜欢的事情(保存、检查模型状态等)。使用此方法,您只有 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());
    }
}
于 2013-01-15T11:15:58.070 回答