不过,我无法找到一种非常优雅的方法,而且我想知道其他人的解决方案是不会以一个非常混乱的控制器动作结束。
>为表示空白Person视图模型的视图发出GET请求
>发回(有效)数据
控制器将发布的数据绑定到人员视图模型上
>如果绑定失败,我需要执行与(1)中相同的操作,但使用某些数据,而不是空白对象和错误
>如果绑定成功,我需要将虚拟机中的属性映射到真实的模型上
>验证模型
>如果验证已通过:保存人员,提交,将用户详细信息映射到显示虚拟机并在视图中返回
>如果验证失败,请执行与(1)中相同的操作,但会出现一些数据和错误
在控制器中执行所有操作(忽略GET)当然不是SRP或DRY。
我试图想到一种打破这个过程的方式,以便它遵守SRP,是干净,模块化和最重要的可测试。
人民解决这个问题是什么?
我一直在尝试使用自定义的控制器操作 – 调用者来将问题分解成各种方法,智能模型绑定器和简单的强力,但是我还没有遇到一个满意的解决方案。
美国因为它增加了这么多的复杂性,说服我为什么我甚至需要打扰
解决方法
>创建一个绑定和验证视图模型的绑定
>创建一个绑定从数据库获取实体(或者只是在控制器中这样做)
>在超类中调用继承的Save方法。此方法将使用viewmodel和将要更新的实体,并执行您在步骤中列出的所有工作。
动作方法如下所示:
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); } }
就可测试性而言,您可以测试传递有效/无效视图模型和实体的保存方法。您可以分别测试模型映射器的实现,视图模型的有效状态和实体的有效状态。
通过使基本控制器通用化,您可以在域中的每个实体/ viewmodel组合中重复此模式,如果您正在创建许多控制器来执行相同的操作。
我很有兴趣听到别人对此的评论。伟大的问题。