我想说的是,您在客户端交互的两个方向上都重用了 ViewModel 这个术语。如果您已经阅读了足够多的 ASP.NET MVC 代码,您可能已经看到了 ViewModel 和 EditModel 之间的区别。我认为这很重要。
ViewModel 表示呈现视图所需的所有信息。这可能包括在静态非交互位置呈现的数据,以及纯粹用于执行检查以确定要呈现的内容的数据。Controller GET 操作通常负责为其 View 打包 ViewModel。
一个 EditModel(或者可能是一个 ActionModel)表示执行用户想要为该 POST 执行的操作所需的数据。所以 EditModel 真的是在试图描述一个动作。这可能会从 ViewModel 中排除一些数据,尽管相关,但我认为重要的是要意识到它们确实不同。
一个想法
也就是说,您可以很容易地拥有一个从 Model -> ViewModel 开始的 AutoMapper 配置,以及一个从 EditModel -> Model 开始的不同配置。那么不同的Controller动作只需要使用AutoMapper即可。该死的 EditModel 可以有一个函数来验证它对模型的属性并将这些值应用于模型本身。它没有做任何其他事情,并且您在 MVC 中有 ModelBinders 可以将 Request 映射到 EditModel 。
另一个想法
除此之外,我最近一直在考虑的事情与 ActionModel 的想法不同的是,客户端向您发送的内容实际上是对用户执行的几个操作的描述,而不仅仅是一大堆数据。这肯定需要客户端上的一些 Javascript 来管理,但我认为这个想法很有趣。
本质上,当用户在您呈现给他们的屏幕上执行操作时,Javascript 将开始创建操作对象列表。一个示例可能是用户在员工信息屏幕上。他们更新了姓氏并添加了一个新地址,因为该员工最近结婚了。在幕后,这会在列表中生成一个ChangeEmployeeName
和一个AddEmployeeMailingAddress
对象。用户单击“保存”以提交更改,然后您提交两个对象的列表,每个对象仅包含执行每个操作所需的信息。
您将需要一个比默认的更智能的 ModelBinder,但好的 JSON 序列化程序应该能够处理客户端操作对象到服务器端操作对象的映射。服务器端的(如果您在 2 层环境中)可以轻松地拥有完成他们使用的模型上的操作的方法。因此,Controller 操作最终只会获取要拉取的 Model 实例的 Id 以及要对其执行的操作列表。或者动作中有 id 以保持它们非常独立。
所以也许这样的事情会在服务器端实现:
public interface IUserAction<TModel>
{
long ModelId { get; set; }
IEnumerable<string> Validate(TModel model);
void Complete(TModel model);
}
[Transaction] //just assuming some sort of 2-tier with transactions handled by filter
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
var errors = new List<string>();
foreach( var action in actions )
{
// relying on ORM's identity map to prevent multiple database hits
var employee = _employeeRepository.Get(action.ModelId);
errors.AddRange(action.Validate(employee));
}
// handle error cases possibly rendering view with them
foreach( var action in editModel.UserActions )
{
var employee = _employeeRepository.Get(action.ModelId);
action.Complete(employee);
// against relying on ORMs ability to properly generate SQL and batch changes
_employeeRepository.Update(employee);
}
// render the success view
}
这确实使回发操作相当通用,因为您依靠 ModelBinder 为您提供正确的 IUserAction 实例和您的 IUserAction 实例来执行正确的逻辑本身或(更有可能)使用信息调用模型。
如果您在 3 层环境中,则可以将 IUserAction 制作为简单的 DTO,以便跨越边界并以类似的方法在应用层上执行。根据您如何执行该层,它可以很容易地拆分并仍然保留在事务中(想到的是 Agatha 的请求/响应并利用 DI 和 NHibernate 的身份映射)。
无论如何,我确信这不是一个完美的主意,它需要客户端的一些 JS 来管理,而且我还没有能够做一个项目来看看它是如何展开的,但是这篇文章试图思考如何到那里再回来,所以我想我会给出我的想法。我希望它有所帮助,我很想听听其他管理交互的方法。