在尝试减小我的MVC控制器的大小时,我在服务中重构了很多逻辑(尽管如果它被考虑在模型中,同样的问题也会适用)。我经常发现我直接将ViewData
和/或TempData
设置为我想要向用户显示的信息,例如:
var repoUser = new UserRepository();
var foundUser = repoUser.GetUser(userId);
if (foundUser == null) {
ViewData["ErrorMessage"] = "Could not find user with ID {0}.".FormatWith(userId);
return View("UsersRegister");
}
当然,只要您进入服务类,就会失去对ViewData
,TempData
以及View()
或RedirectToAction()
等方法的直接访问权限,因此我我试图弄清楚处理它的最佳做法。我想到了两个解决方案:
创建一个类,其中包含有关控制器应该执行的操作的各种信息,以便从服务中传回,如:
public class ResponseInfo {
public string Name { get; set; }
public bool DoRedirect { get; set; }
public object RedirectRouteValues { get; set; }
public string InfoMessage { get; set; }
public string ErrorMessage { get; set; }
}
尝试并允许服务方法直接访问ViewData
和TempData
等内容,例如:
public ActionResult ToggleUserAttended(int eventId, int userId, HttpRequestBase request, TempDataDictionary tempData, ViewDataDictionary viewData, Func<string, object, ActionResult> redirectAction) {
//...
var repoUser = new UserRepository();
var foundUser = repoUser.GetUser(userId);
if (foundUser == null) {
tempData["ErrorMessage"] = "Could not find user with ID {0}.".FormatWith(userId);
return redirectAction("UsersRegister", new { id = eventId, returnUrl = request.QueryString["returnUrl"] });
}
//...
}
...然后在控制器中:
return _svcEvent.ToggleUserAttended(123, 234, Request, TempData, ViewData, (name, routeVals) => RedirectToAction(name, routeVals));
对于数字1,控制器在查看ResponseInfo
对象并确定是否重定向,显示视图,将错误或信息消息插入TempData
或{{}时可以有更多逻辑。 1}}等等。数字2几乎允许一个单行控制器,但你使服务非常了解控制器特定的东西。但是,无论如何,服务总是与控制器密切相关,这是一个问题吗? 1或2是最佳做法,还是我未列出的其他内容?
答案 0 :(得分:1)
也许我误解了某些内容,但我觉得很奇怪您想要向服务提供与演示问题相关的信息。 IMO,这是一个非常糟糕的主意,因为服务是业务逻辑,不应该依赖于表示层。
我不喜欢你建议的方法,因为在我看来,它们模糊了演示和服务之间的界限。
作为示例,如果服务找不到用户,则应抛出该服务,然后控制器应使用适当的UI机制处理此错误:错误消息,HTTP错误或任何适合您的应用程序的错误。同样,服务如何知道重定向的位置?它知道GUI吗?您打算如何在其他环境中使用它(API,非Web GUI,等等)?
我通常做的是根据表单/参数创建一个命令/ dto并提供给服务。然后任何服务客户端都可以使用相同的命令。如果我需要显示数据,我会询问服务/ repos以获取我需要的任何内容,并将其映射到表示形式(如果需要)并将其放入ViewData / TempData / Model / Whatever。
以下是一些示例(将其视为伪代码):
[HttpPost]
public ActionResult ChangeUserInfo(UserInfoModel model)
{
var cmd = new ChangeUserInfoCommand(CurrentUserId, model.FirstName, model.LastName);
try {
userSvc.UpdateUser(cmd);
return RedirectToAction(...);
}
catch(XxxxException e) { // e.g. something wrong (business logic)
TempData["Error"] = "an error occurred: " + e.FriendlyError();
return RedirectToAction(...); // error action
}
catch(SecurityException e) {
throw new HttpException(404, "NotFound");
}
}
public ActionResult ShowUsers()
{
var userInfo = svc.GetUsers().Select(u => { Id = u.id, FullName = u.First + " " + u.Last);
// or var userInfo = svc.GetUsers().Select(u => Mapper.To<UserDTO>(u));
return View(userInfo);
// or without model
// ViewData["users"] = userInfo;
// return View();
}
请注意,这是一个例子 - 我通常做的完全不同,但我有很多其他基础设施(例如,我没有明确处理这样的异常,我的服务有时只有一个方法{{1我非常支持匿名类作为视图模型等)