将TempData和/或ViewData传递给服务是一种好习惯吗?

时间:2013-03-19 10:42:27

标签: asp.net-mvc oop viewdata tempdata

在尝试减小我的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");
}

当然,只要您进入服务类,就会失去对ViewDataTempData以及View()RedirectToAction()等方法的直接访问权限,因此我我试图弄清楚处理它的最佳做法。我想到了两个解决方案:

  1. 创建一个类,其中包含有关控制器应该执行的操作的各种信息,以便从服务中传回,如:

    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; }
    }
    
  2. 尝试并允许服务方法直接访问ViewDataTempData等内容,例如:

    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));
    
  3. 对于数字1,控制器在查看ResponseInfo对象并确定是否重定向,显示视图,将错误或信息消息插入TempData或{{}时可以有更多逻辑。 1}}等等。数字2几乎允许一个单行控制器,但你使服务非常了解控制器特定的东西。但是,无论如何,服务总是与控制器密切相关,这是一个问题吗? 1或2是最佳做法,还是我未列出的其他内容?

1 个答案:

答案 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我非常支持匿名类作为视图模型等)