Area, Multiple Namespaces, and Routes

Jul 12, 2010 at 6:50 PM
Edited Jul 13, 2010 at 2:31 PM

I'm having an issue with resolving controller parameters when a single area has controllers spread across multiple namespaces and routes.  The controllers in the Admin namespace are never found because the DefaultControllerTypeResolver.FindNamespaceForArea always returns the Resources namespace.  Is this by design or am I missing an XML attribute?

            context.MapRoute(
                "Scheduler_Resources",
                "Scheduler/Resources/{controller}.mvc/{action}/{id}",
                new
                {
                    action = "Index",
                    id = ""
                },
                new string[] { "Application.Areas.Scheduler.Controllers.Resources" }
            );

            context.MapRoute(
                "Scheduler_Admin",
                "Scheduler/Admin/{controller}.mvc/{action}/{id}",
                new
                {
                    action = "Index",
                    id = ""
                },
                new string[] { "Application.Areas.Scheduler.Controllers.Admin" }
            );
            context.MapRoute(
                "Scheduler",
                "Scheduler/{controller}.mvc/{action}/{id}",
                new
                {
                    action = "Index",
                    id = ""
                }
            );

Coordinator
Jul 20, 2010 at 6:35 AM

Have you tried using the latest source code? (Source code tab above, download the latest code base and check for the 2.1.0 branch...)

Jul 20, 2010 at 10:23 PM

The latest, 2.1.0 branch has the same issue.  I believe the root issue is the DefaultControllerTypeResolver.FindNamespacesForArea doesn't actually find all namespaces for an area, but rather finds all of the namespaces for the first route with a namespace.  I'm able to temporarily work around the issue by making the following modifications (changes in bold).

        /// <summary>
        /// Finds the namespaces for area.
        /// </summary>
        /// <param name="area">The area.</param>
        /// <param name="routes">The routes.</param>
        /// <returns>
        /// A namespaces for area represented as a <see cref="string"/> instance
        /// </returns>
        protected IEnumerable<string> FindNamespacesForArea(string area, RouteCollection routes)
        {
            List<string> allRoutesNamespaces = new List<string>();

            foreach (var route in routes.OfType<Route>().Where(r => r.DataTokens != null))
            {
                if ((route.DataTokens["area"] != null
                    && route.DataTokens["area"].ToString() == area
                    && route.DataTokens["Namespaces"] != null)
                    || (route.DataTokens["area"] == null && route.DataTokens["Namespaces"] != null))
                {
                    //return route.DataTokens["Namespaces"] as IEnumerable<string>;
                    	     allRoutesNamespaces.AddRange(route.DataTokens["Namespaces"] as IEnumerable<string>);
                }
            }

            if (allRoutesNamespaces.Count > 0)
               {
                   return allRoutesNamespaces as IEnumerable<string>;
               }

            return null;
        }

and
        /// <summary>
        /// Gets the controller type within namespaces.
        /// </summary>
        /// <param name="area">The area.</param>
        /// <param name="controller">The controller.</param>
        /// <param name="namespaces">The namespaces.</param>
        /// <returns>
        /// A controller type within namespaces represented as a <see cref="Type"/> instance 
        /// </returns>
        protected Type GetControllerTypeWithinNamespaces(string area, string controller, HashSet<string> namespaces)
        {
            ... [unchanged code removed for clarity]

                            if (foundType == null)
                            {
                                foundType = possibleType;
                            }
                            // SDA - 7/20/10 - Modified to handle the same type name.
                                     else if (possibleType.Equals(foundType))
                                     { 
                                          // NOTE: Do nothing.  This is not ambiguous because it's literally the same type.
                                          //       This handles duplicate type names from namespaces included in a wild card namespace.
                                     }
                            else
                            {
                                throw new AmbiguousControllerException("Ambiguous controller. Found multiple controller types for " + controller + "Controller.");
                            }

            ... [unchanged code removed for clarity]
         }

Coordinator
Jul 22, 2010 at 11:20 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.