优雅地使用ViewModels在MVC中进行POST操作

时间:2009-08-18 16:17:02

标签: asp.net-mvc viewmodel

目前我正在将我的域对象传递给我的视图,并从POST直接绑定到它们。每个人都说这很糟糕,所以我试图添加ViewModel概念。

然而,我找不到一种方法来非常优雅地做到这一点,而且我想知道其他人的解决方案是什么,最终不会出现非常混乱的控制器动作。

说一些“添加人”功能的典型过程如下所示:

  1. 对表示空白Person viewmodel的视图发出GET请求
  2. 回发(中)有效数据
  3. controller将发布的数据绑定到人员视图模型
  4. 如果绑定失败,我需要执行与(1)中相同的操作,但需要一些数据,而不是空白对象和错误
  5. 如果绑定成功,我需要将属性从VM映射到真实模型
  6. 验证模型
  7. 如果验证通过:保存人员,提交,将用户详细信息映射到显示VM并在视图中返回
  8. 如果验证失败,请执行与(1)中相同的操作,但有一些数据和错误
  9. 在控制器动作中执行所有这些操作(忽略GET)当然不是SRP或DRY。

    我试图想办法打破这个过程,以便它遵守SRP,干净,模块化,最重要的是可测试。

    人们有什么解决方案?

    我一直在尝试使用自定义控制器动作调用来将问题分解为单独的方法,智能模型绑定器和简单的暴力,但我还没有遇到过满意的解决方案。

    P.S。因为它增加了如此多的复杂性,说服我为什么我甚至需要打扰

3 个答案:

答案 0 :(得分:6)

我也感到同样的不适。我唯一的办法就是做到以下几点:

  1. 创建绑定器以绑定并验证视图模型
  2. 创建一个活页夹以从数据库中获取实体(或者只是在控制器中执行此操作)
  3. 在超类中调用继承的Save方法。此方法采用视图模型和将要更新的实体,并完成您在步骤中列出的所有工作。
  4. 动作方法如下所示:

    public ActionResult Whatever(TViewModel viewModel, TEntity entity)
    {
        return Save(viewModel, entity);
    }
    

    基本控制器具有通用定义,如下所示:

    public abstract BaseController<TEntity, TViewModel>
        where TEntity : Entity
        where TViewModel : ViewModel
    

    构造函数有两个依赖项,一个用于实体存储库,另一个用于模型映射器,如下所示:

    protected BaseController(IRepository<TEntity> repository, IMapper<TEntity, TViewModel> mapper)
    

    有了这个,你就可以编写一个受保护的Save方法,可以从子类中的控制器动作调用它,如下所示:

    protected ActionResult Save(TViewModel viewModel, TEntity entity)
    {
        if (!ModelState.IsValid)
            return View(viewModel);
    
        _mapper.Map(viewModel, entity);
        if (!entity.IsValid)
        {
            // add errors to model state
            return View(viewModel);
        }
    
        try
        {
            _repository.Save(entity);
            // either redirect with static url or add virtual method for defining redirect in subclass.
        }
        catch (Exception)
        {
            // do something here with the exception
            return View(viewModel);
        }
    }
    

    就可测试性而言,您可以测试传入有效/无效视图模型和实体的save方法。您可以分别测试模型映射器的实现,视图模型的有效状态以及实体的有效状态。

    通过使基本控制器具有通用性,如果您要创建许多控制器来执行相同的操作,则可以为域中的每个实体/视图模型组合重复此模式。

    我很想知道其他人对此有什么看法。好问题。

答案 1 :(得分:2)

MVVM(ViewModel)模式绝对是最适合的模式,我有一个类似的问题,关于几天前回到一个动作 - 这里是链接:MVVM and ModelBinders in the ASP.NET MVC Framework

结果是您可以使用Bind属性回发所需的复杂类型。

答案 2 :(得分:0)

我在 asp.net mvc示例应用程序 中有很多很好的解决方案,它位于download of valueinjecter(用于将ViewModels映射到/来自的映射器)实体,您也可以将FormCollection / Request映射到实体)

这是一个:

    public class TinyController :Controller
        {
            private readonly IModelBuilder<Person, PersonViewModel> modelBuilder;

            public TinyController()
            {
                modelBuilder = new PersonModelBuilder();
            }

            public ActionResult Index()
            {
                return View(modelBuilder.BuildModel(new PersonRepository().Get()));
            }

            [HttpPost]
            public ActionResult Index(PersonViewModel model)
            {
                if (!ModelState.IsValid)
                    return View(modelBuilder.RebuildModel(model));

                   var entity = modelBuilder.BuildEntity(model);
...
//save it or whatever
            }
        }