【问题标题】:using ViewModels for POST actions in MVC elegantly在 MVC 中优雅地使用 ViewModel 进行 POST 操作
【发布时间】:2010-11-20 15:56:21
【问题描述】:

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

但是,我找不到一种非常优雅的方法,我想知道其他人的解决方案是什么,不会导致控制器操作非常混乱。

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

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

在控制器操作中执行所有这些操作(忽略 GET)当然不是 SRP 或 DRY。

我试图想出一种方法来打破这个过程,以便它确实遵守 SRP、干净、模块化并且最重要的是可测试。

人们对此有什么解决方案?

我一直在尝试使用自定义控制器动作调用程序将关注点分离为单独的方法、智能模型绑定器和简单的蛮力,但我还没有找到满意的解决方案。

附:因为它增加了如此多的复杂性,所以说服我为什么我什至需要打扰

【问题讨论】:

标签: asp.net-mvc viewmodel


【解决方案1】:

我也有同样的不适。我唯一的解决方法是执行以下操作:

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

动作方法如下所示:

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);
    }
}

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

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

我很想听听其他人对此的看法。好问题。

【讨论】:

  • 绝对有趣的想法,但 OpenRASTA 仍然是更好的选择 :)
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    我在download of valueinjecter 中的 asp.net mvc 示例应用程序 中有很多好的解决方案(我用来将 ViewModel 映射到/从实体映射的映射器,您还可以将 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
                }
            }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-05-31
      • 1970-01-01
      • 1970-01-01
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多