Question

My solution has a deep folder structure matching the business structure, going 3-4 levels deep, eg.:

\Franchises
   \Regionals
      \Billing
         \Monthly
         \...
         \...
      \...
      \...
   \...
   \...
\...
\...

The folder structures in the Domain, Processes and Reports projects are consistent with this structure.

MVC is a pain. By default it only allows 1 level deep:

\Controllers\BlahBlah

Using MVC Areas I can get 2 levels deep (klutzily adding two more folders to the path):

\Areas\Franchises\Controllers\BlahBlah

This is nowhere near enough to reflect the business's deep structure.

I am hesitant to vertically slice the WebUI into multiple projects as this requires further work to integrate, and seems like overkill just to impose abusiness structure.

Is there a way of imposing arbitrary folder levels into an MVC project? Should I just manually hard code all the controller routings?

Was it helpful?

Solution

It is not necessarily to map every controller physycal folder to route, you can organize you folder structure as you want, but if you want automatically map routes to all tree substructure - you can explore subnamespaces in your Controllers folder and automatically map it to you routes without any hardcode, there is my solution:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Routing;

namespace WebApplication2
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
            routes.MapAllRoutesToFolders();
        }
    }

    public static class RouteExtensions
    {
        public static void MapAllRoutesToFolders(this RouteCollection routes)
        {
            const string commonControllerUrl = "{controller}/{action}/{id}";
            var folderMappings = GetSubClasses<Controller>()
                .Select(x => new
                {
                    Path = string.Join("/", x.FullName
                        .Split(".".ToCharArray(), StringSplitOptions.RemoveEmptyEntries)
                        .SkipWhile(c => c != "Controllers"))
                        .TrimStart("/Controllers/".ToCharArray())
                        .Replace(x.Name, string.Empty),
                    Name = x.Name.Replace("Controller", string.Empty)
                });
            var controllerPaths =
                folderMappings.Where(s => !string.IsNullOrEmpty(s.Path))
                    .Select(x => new
                    {
                        ControllerName = x.Name,
                        Path = x.Path + commonControllerUrl
                    });
            foreach (var controllerPath in controllerPaths)
            {
                routes.MapRoute(null, controllerPath.Path, new { controller = controllerPath.ControllerName, action = "Index", id = UrlParameter.Optional });
            }
        }

        private static IEnumerable<Type> GetSubClasses<T>()
        {
            return Assembly.GetCallingAssembly().GetTypes().Where(
                type => type.IsSubclassOf(typeof(T))).ToList();
        }
    }
}

In that case, you folders structure should be like this:

enter image description here

After that you can check it in your browser:

enter image description here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top