模型活页夹可能有助于该过程,但我不认为它是一个完整的解决方案。一方面,模型绑定器在实例化模型时可能不“知道”您的意图。例如,它怎么知道你稍后会在 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
}
}