2013-03-19 50 views
1

在试图降低我的MVC控制器的尺寸,我重构了很多逻辑伸到服务(不过,如果它被纳入模型同样的问题也适用)。我经常发现我是直接设置ViewData和/或TempData的信息我想被显示给用户,如:将TempData和/或ViewData传递给服务是否是一种好的做法?

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

对于编号1中,控制器将有更多的逻辑看ResponseInfo对象,并确定是否重定向做,显示视图,堵塞错误或信息消息分成TempDataViewData等Number 2几乎可以让一个单线控制器,但是你让服务非常清楚控制器的具体内容。但是,服务总是与控制器紧密相连,所以这是一个问题吗?会1或2是最佳做法,还是我没有列出的其他东西?

+1

GOD NO。将这样的事情转移到ActionFilter中......不要让你的服务访问像ViewData和TempData这样的前端概念。 – 2013-03-19 10:50:50

+0

@SimonWhitehead那么你基本上是说将这些东西留在控制器中?那些错误消息本身呢?一个更完整的答案将不胜感激。 – Jez 2013-03-19 11:00:22

+0

您有没有考虑使用'ModelState'来显示这些错误?您可以在服务层错误列表和“ModelStateDictionary”之间映射UI层。这将您的服务从您的用户界面中分离出来。你甚至可以编写你自己的'ActionResult',这样它就可以很好地返回它,并且使得你的动作显着缩小。我现在不是靠近PC来键入示例。 – 2013-03-19 11:34:51

回答

1

也许我误解的东西,但我觉得很奇怪,你要提供相关演示关注到服务的信息。国际海事组织,这是一个非常糟糕的主意,因为服务是业务逻辑,不应该依赖表示层。

我真的不喜欢你们俩都不建议作为在我看来,他们模糊了介绍和服务之间的界限的做法。

举个例子,一个服务应该抛出,如果它不能找到用户,然后控制器应该处理此错误使用适当的UI机制:错误消息,HTTP错误或任何适合您的应用程序。同样,该服务如何知道在哪里重定向?它知道GUI?你如何计划在其他上下文中使用它(API,非Web GUI,不管)?

我最常做的是建立一个基于表单/参数的命令/ DTO,并提供服务。然后任何服务客户端都可以使用相同的命令。如果我需要显示数据,我可以根据需要提供服务/仓库,并将其映射到表示形式(如果需要),然后将其放入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(); 
} 

请注意,这是一个例证 - 我通常做的完全不同,但我已经制定了很多其他的基础设施(例如我没有明确地处理这样的例外,我的服务有时只有一个方法Execute(IEnumerable<Command> cmds),我对匿名类作为查看模型等有很好的支持,等等)

+0

你能举一个基于表单/参数的命令/ dto的例子吗? – Jez 2013-03-19 10:59:41

+0

当然,只要记住这是一个例子 - 我通常做的很不一样,但我有很多其他的基础设施 – 2013-03-19 11:23:11

+0

嗯,所以你依靠100%的例外来传达错误发生在服务?这似乎也不是很好的做法;一些错误已经足够普遍,足以预测到不需要例外。 – Jez 2013-03-19 11:27:47

相关问题