ASP.NET MVC - Service layer, single or many servic

2019-06-03 02:39发布

问题:

I'm starting to implement a service layer to my MVC project to thin down some bloated controllers (it also has repository / unitofwork pattern).

My question is if you have a complicated view model for a page with lots of child objects etc, and quite a lot of logic going on behind the scenes (to give you an idea the controller the original developer wrote had almost 4000 lines of code!!) is it OK to have multiple services going off doing their thing? or should I just have one big ReportService which does everything?

My controller is starting to look like this? and if I carry on I could end up having quite a lot of different services being called to build up the view model.

Does this look OK or is it starting to go in the wrong direction?

public ViewResult Index(int? reportId)
    {
        // get the base report object
        var reportService = new ReportService();
        var report = reportService.GetByReportId(reportId);
        var model = Mapper.Map<Report, ReportViewModel>(report);

        // get the current active user
        var userService = new UserService();
        var user = userService.GetCurrentUser();
        model.User = Mapper.Map<User, ReportViewModel.UserViewModel>(user);

        // get the first unread message
        var messageService = new MessageService();
        var message = messageService.GetFirstUnread(user.Id);
        model.Message = Mapper.Map<Message, ReportViewModel.MessageViewModel>(message);

        // get the category navigation
        var categoryService = new CategoryService();
        var categoryNavigation = categoryService.GetCategoryNavigation(report.Id);
        model.CategoryNavigation = Mapper.Map<IEnumerable<Category>, IEnumerable<ReportViewModel.CategoryNavigationViewModel>>(categoryNavigation);

        return View(model);
    }

回答1:

It's fine to have multiple small services in your controller. However, there is one thing that's wrong here:

You services should be available through the entire controller and injected through the constructor to achieve loose-coupling.

So something like this:

private readonly IReportService _reportService;
private readonly IUserService _userService;

public SomeConstructor(IReportService reportService, IUserService userService, etc.) 
{
    _reportService = reportService;
    _userService = userService;
    // etc
}


回答2:

That does look like a good approach, an alternative approach would to be split some of this up by using Child Actions - the best solution will depend upon your specific use case though.

If, for example, the ViewModel property CategoryNavigation was being used by the view to create a sort of navigation 'widget' that might be useful in several different Views, you might be better spliting this off into a ChildAction e.g.

[ChildActionOnly]
public ActionResult CategoryNavigationWidget(int reportId)
{
    // get the category navigation
    var categoryService = new CategoryService();
    var categoryNavigation = categoryService.GetCategoryNavigation(report.Id);

    return PartialView(categoryNavigation);
}

Any View could then render that ChildAction by going:

   @{ Html.RenderAction("CategoryNavigationWidget", "Report", 
           new { reportId = Model.ReportId }); }

Whether or not this is a good idea will probably depend upon whether or not the 'widget' is reusable.