6

POSTで、検証が失敗し、モデル状態エラーでViewModelを同じビューに送り返す前に、すべてのSelectLists、ReadOnlyフィールドなどのViewModelを再構築しますか?現在、Fill First Time(GET Edit-Methodの場合)/ドメインオブジェクトからViewModelsを再構築するための個別のメソッドがあります。これは、DRYになることができ、新しい読み取り専用プロパティを追加するたびに2つのメソッドを変更する必要がないようにするためのベストプラクティスです。 ViewModelに?

私の解決策:このパターンに従った

ここで提案されているパターンに従います:https : //stackoverflow.com/a/2775656/57132IModelBuilderの実装

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 にリダイレクトされます。

ModelStateTempDataPost-Redirect-Get に続いて に転送され、GET 呼び出しで使用できるようになります。ビュー モデルは、GET 呼び出しで 1 か所に構築されます。例:

    [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

最も簡単な解決策は、ビューモデルをメソッドに渡し、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 に答える