In trying to reduce the size of my MVC controllers, I am refactoring a lot of logic out into services (though this same issue would apply if it was being factored into models). I'm often finding that I was directly setting ViewData and/or TempData with information I wanted to be displayed to the user, like:
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");
}
Of course, as soon as you move into a service class, you lose direct access to ViewData, TempData, and methods like View() or RedirectToAction(), so I'm trying to figure out the best practice for dealing with it. Two solutions come to mind:
Create a class containing various pieces of information about what the controller should do, to be passed back from the services, like:
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; } }Try and allow the service methods to have direct access to things like
ViewDataandTempData, eg.: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"] }); } //... }... and then in the controller:
return _svcEvent.ToggleUserAttended(123, 234, Request, TempData, ViewData, (name, routeVals) => RedirectToAction(name, routeVals));
For number 1, the controller would have more logic to do in looking at the ResponseInfo object and determining whether to redirect, display a view, plugging the error or info message into TempData or ViewData, etc. Number 2 would pretty much allow a one-liner controller, but you're making the service very aware of controller-specific stuff. However, the service is always going to be closely tied in with the controller anyway so is this a problem? Would 1 or 2 be best practice, or something else I haven't listed?