模型绑定器可能有助于该过程,但我不认为它是一个完整的解决方案。一方面,模型绑定器在实例化模型时可能不“知道”您的意图。例如,它如何知道您稍后会在操作方法中认为模型无效?
我通常有一个单独的方法(甚至是一个单独的类),其唯一目的是管理适合情况的视图模型数据的构建。
然后 action 方法告诉这个助手它想要什么,允许它作为进程的管理者来服务于它的目的。例如,控制器可以决定:
- 需要准备一个新的视图模型。
- 需要准备一个新的视图模型,但使用查询字符串中的值初始化选择属性。
- 应该构建视图模型来表示现有的域模型。
- 应保留视图模型的用户写入数据,但应重建其他字段(例如,您的下拉列表示例)。
- 等等。
我将这种视图模型的构建称为“组合”。本质上,您正在将向视图呈现完整视图模型所需的数据汇总在一起。这些数据可能来自不同的地方。
我在这里更详细地描述了合成过程。我已经编写了一个完整的框架来支持 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
}
}