Jacob Mattison
Jacob Mattison

Reputation: 51052

custom controller namespace handling

I'm moving an existing application from ASP.NET webforms to ASP.NET MVC.

There are some page names that exist both under the admin directory and in the public directory, e.g. /help/ as well as /admin/help/

The namespacing matches the directory structure, so the /help page will be in MyApplication.Help, while the /admin/help will be in MyApplication.Admin.Help. I'd like to keep that setup for the controllers, so ideally I'd like to have MyApplication.Help.HelpController and MyApplication.Admin.Help.HelpController.

I've set up routes like so

    routes.MapRoute(
      "Admin",
        // Route name
      "admin/{controller}/{action}/{id}",
        // URL with parameters
      new { controller = "Home", action = "Index", id = "" },
        // Parameter defaults
    new []{"MyApplication.Admin.*"}
        //namespaces to find controller in
    ).DataTokens["UseNamespaceFallback"] = false;

    routes.MapRoute(
      "Default",
        // Route name
      "{controller}/{action}/{id}",
        // URL with parameters
      new { controller = "Home", action = "Index", id = "" },
        // Parameter defaults
    new [] { "MyApplication.*" }
        //namespaces to find controller in
    ).DataTokens["UseNamespaceFallback"] = false;

With this setup, the /admin/help route works fine, but the /help route fails, complaining that multiple types match the controller called "help" (because both MyApplication.Help.HelpController and MyApplication.Admin.Help.HelpController apparently match the MyApplication.* namespace I've given).

Is it possible to either

  1. set up the namespaces for the two routes so that the second one matches "anything but Admin"

  2. set up the namespaces for the two routes so that the second one only matches when there's a single layer of folders but not two layers, or

  3. write a custom method that tells the route what namespace to look in for the controller (so I can implement this logic myself)?

Unfortunately changing the folder heirarchy (e.g. put all of the non-admin pages under a "Public" folder/namespace) isn't an option (because I'm moving incrementally to MVC, so some pages are still served as webforms).

Upvotes: 2

Views: 1100

Answers (1)

Jacob Mattison
Jacob Mattison

Reputation: 51052

So here's what I ended up doing, at least for now. For the non-admin route, I created a method that will list all of the appropriate namespaces and register them with the route at application startup. Presumably that is slowing down application startup somewhat, but I can't perceive a difference.

If anyone has a better approach, I'm happy to hear it.

routes.MapRoute(
  "Admin",
    // Route name
  "admin/{controller}/{action}/{id}",
    // URL with parameters
  new { controller = "Home", action = "Index", id = "" },
    // Parameter defaults
new []{"MyApplication.Admin.*"}
    //namespaces to find controller in
).DataTokens["UseNamespaceFallback"] = false;

routes.MapRoute(
  "Default",
    // Route name
  "{controller}/{action}/{id}",
    // URL with parameters
  new { controller = "Home", action = "Index", id = "" },
    // Parameter defaults
GetNonAdminNamespaces()
    //namespaces to find controller in
).DataTokens["UseNamespaceFallback"] = false;


  private static string[] GetNonAdminNamespaces()
  {
      var namespaces = new HashSet<string>();
      System.Type[] allTypes =  Assembly.GetCallingAssembly().GetTypes();
      foreach (var item in allTypes)
      {
          string ns = item.Namespace;
          if ( !string.IsNullOrEmpty(ns) && ns.StartsWith("MyApplication.") && !ns.StartsWith("MyApplication.Admin."))
          {
              namespaces.Add(ns);
          }
      }
      return namespaces.ToArray();
  }

Upvotes: 1

Related Questions