【问题标题】:Having to repopulate viewmodel when modelstate is invalid due to not sending all data in such as drop down box list由于未在下拉框列表等中发送所有数据而导致模型状态无效时必须重新填充视图模型
【发布时间】:2013-10-12 16:55:46
【问题描述】:

在我的项目中,我创建了一个调查,我将此调查链接到一家公司并选择用户参与。我选择调查模板,添加额外的问题并设置有关调查的信息,例如开始和结束日期。

总而言之,结果是一个复杂的视图,其中包含来自域许多部分的数据。 我为此视图创建了一个 ViewModel,其中包括所有公司的列表等内容(因为我需要在下拉列表中选择公司)。

现在令人烦恼的是,当我提交保存时,如果我有模型状态错误,那么我想重新显示视图,但是由于我获取的所有数据(例如所有公司的列表)都不是从客户端发送的(即使视图模型支持它)我必须在再次显示表单之前再次填充所有这些列表/变量。

我想我本可以创建一个创建下拉列表的视图,但由于下拉列表需要将其值插入到 CompanyId 下的调查中,这也不明显。

我认为自定义模型绑定器也可以解决问题。我在这里要求对那里的最佳解决方案有所了解。如果我宁愿重新考虑整个策略。

【问题讨论】:

  • 我通常有一个单独的方法(甚至是一个单独的类),其唯一目的是从视图模型中放入/获取数据。然后,此功能需要了解操作的类型,例如你是第一次填充吗?你是从现有的域模型中填充的吗?您是否正在使用不在 POST 中的数据来补充视图模型? (例如您的下拉列表示例)
  • 嗯听起来不错,你能从你的代码中粘贴一个例子吗?

标签: .net asp.net-mvc-4 asp.net-mvc-viewmodel asp.net-mvc-views


【解决方案1】:

模型活页夹可能有助于该过程,但我不认为它是一个完整的解决方案。一方面,模型绑定器在实例化模型时可能不“知道”您的意图。例如,它怎么知道你稍后会在 action 方法中认为模型无效?

我通常有一个单独的方法(甚至是一个单独的类),其唯一目的是管理适合情况的视图模型数据的构建。

action 方法然后告诉这个助手它想要什么,让它作为进程的管理者来服务于它的目的。例如,控制器可能会决定:

  • 需要准备一个新的视图模型。
  • 需要准备新的视图模型,但使用查询字符串中的值初始化选择属性。
  • 应构建视图模型来表示现有域模型。
  • 应保留视图模型的用户写入数据,但应重建其他字段(例如您的下拉列表示例)。

我将这种视图模型的构建称为“组合”。本质上,您正在将向视图呈现完整视图模型所需的数据汇总在一起。这些数据可能来自不同的地方。

我描述了composition process in more detail here。我已经编写了一个完整的框架来支持 ASP.Net MVC 的组合模式(封闭源代码,只是由于时间的原因)。我发现它使支持复杂的视图模型变得更加容易,并且大大提高了我的代码重用性。

简单示例

此示例将进程保留在控制器内部(而不是单独的类),并专注于指定一些简单的选项。

[Flags]
public enum CompositionOptions
{
    PopulateFromDomainModel = 1,
    Hydrate = 2
}

[HttpGet]
public ActionResult Edit( int id)
{
    var model = new ViewModel();
    // the controller states exactly what it wants
    Compose( model, CompositionOptions.PopulateFromDomainModel | CompositionOptions.Hydrate, id );
}

[HttpPost]
public ActionResult Edit( ViewModel model )
{
    if( !ModelState.IsValid )
    {
        // Rebuild values which weren't contained in the POST. Again, the
        // controller states exactly what it needs.
        Compose( model, CompositionOptions.Hydrate );
        return View( model );
    }

    // Use POST-redirect-GET pattern, allowing the page to reload with the changes
    return RedirectToAction( "Edit", new { id = model.Id } );
}

private void Compose( ViewModel model, CompositionOptions options, int? id = null )
{
    // This logic can become quite complex, but you are generally checking for
    // existing data source (domain models) and what you should populate and
    // what fields you should preserve.

    if( id != null && options.HasFlag( CompositionOptions.PopulateFromDomainModel ) )
    {
        // get your domain model from a repository and populate the 
        // properties of the view model
    }

    if( options.HasFlag( CompositionOptions.Hydrate ) )
    {
        // set values on the view model which won't be included in 
        // a POST, and thus must be rebuilt with every roundtrip
    }
}

【讨论】:

  • 在像这样的普通控制器中,增加了可读性,但代码重用看到了轻微的好处。我认为您谈论的框架会产生更大的影响。
【解决方案2】:

一种可能性当然是 Tim 建议的自动补液。

但是我最终采用的方法是 Tim G Thomas 在这里建议的:http://timgthomas.com/2013/09/simplify-client-side-validation-by-adding-a-server/

这意味着数据永远不会离开客户端,除非它是有效的,这意味着不需要再水化。 它确实需要 javascript,这对我来说是可以接受的,因为您再也不需要重新水化视图了。

【讨论】:

  • 我现在可以在使用这种方法后确认它非常有效,并且极大地简化了我的生活。
猜你喜欢
  • 2018-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-12
  • 1970-01-01
相关资源
最近更新 更多