views:

610

answers:

2

In my application I have controller named Snippets both in default area (in application root) and in my area called Manage. I use T4MVC and custom routes, like this:

routes.MapRoute(
    "Feed",
    "feed/",
    MVC.Snippets.Rss()
);

And I get this error:

Multiple types were found that match the controller named 'snippets'. This can happen if the route that services this request ('{controller}/{action}/{id}/') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.

The request for 'snippets' has found the following matching controllers: Snippets.Controllers.SnippetsController Snippets.Areas.Manage.Controllers.SnippetsController

I know that there are overloads for MapRoute that take namespaces argument, but there are no such overloads with T4MVC support. May be I'm missing something? The possible syntax can be:

routes.MapRoute(
    "Feed",
    "feed/",
    MVC.Snippets.Rss(),
    new string[] {"Snippets.Controllers"}           
);

or, it seems quite good to me to have namespace as T4MVC property:

routes.MapRoute(
    "Feed",
    "feed/",
    MVC.Snippets.Rss(),
    new string[] {MVC.Snippets.Namespace}           
);

Thanks in advance!

+2  A: 

Makes sense. I guess you're just the first one to run into this. Try replacing all the MapRoute methods in T4MVC.tt by the following:

    public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result) {
        return MapRoute(routes, name, url, result, null /*namespaces*/);
    }

    public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, object defaults) {
        return MapRoute(routes, name, url, result, defaults, null /*constraints*/, null /*namespaces*/);
    }

    public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, string[] namespaces) {
        return MapRoute(routes, name, url, result, null /*defaults*/, namespaces);
    }

    public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, object defaults, object constraints) {
        return MapRoute(routes, name, url, result, defaults, constraints, null /*namespaces*/);
    }

    public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, object defaults, string[] namespaces) {
        return MapRoute(routes, name, url, result, defaults, null /*constraints*/, namespaces);
    }

    public static Route MapRoute(this RouteCollection routes, string name, string url, ActionResult result, object defaults, object constraints, string[] namespaces) {
        // Start by adding the default values from the anonymous object (if any)
        var routeValues = new RouteValueDictionary(defaults);

        // Then add the Controller/Action names and the parameters from the call
        foreach (var pair in result.GetRouteValueDictionary()) {
            routeValues.Add(pair.Key, pair.Value);
        }

        var routeConstraints = new RouteValueDictionary(constraints);

        // Create and add the route
        var route = new Route(url, routeValues, routeConstraints, new MvcRouteHandler());

        if (namespaces != null && namespaces.Length > 0) {
            route.DataTokens = new RouteValueDictionary();
            route.DataTokens["Namespaces"] = namespaces;
        }

        routes.Add(name, route);
        return route;
    }

Note that you can get strong typing on the controller namespace without T4MVC's help simply by writing:

 string[] { typeof(MyApplication.Controllers.SnippetsController).Namespace }

I should add that ideally, you would not have to pass the Namespaces at all, since your intent to target a specific controller is already captured in the MVC.Snippets.Rss() call. However, I couldn't find an obvious way to make this work without big changes to T4MVC.

Anyway, please review and test the change, and let me know how it works for you. If it looks good, I'll get it in.

Thanks!

David Ebbo
+1  A: 

David, thanks for the reply!

I've tested your solution and it seems to work for me. One quick question, it seems with new overloads I need to specify namespaces when I just want to add route constraints:

routes.MapRoute(
    "SnippetsList",
    "page-{page}/",
    MVC.Home.Index(),
    new {},
    new { page = @"^[0-9]+$" },
    new string[]{}
);

May be it is possible to add more overloads without to much overload hell?

If I understand your right "you would not have to pass the Namespaces at all, since your intent to target a specific controller is already captured in the MVC.Snippets.Rss() call" we can use this approach:

May be the better way will be to add property Namespace to IT4MVCActionResult, add aditional parameter Namespace to constructor of T4MVC_ActionResult and use it something like this in T4MVC "actions": return new T4MVC_ActionResult(Area, Name, ActionNames.Index, this.GetType().BaseType.Namespace);

Later inside MapRoute() (where actual logic is placed) we can use result.Namespace for all routes.

Is it good way in your opinion?

artvolk
David Ebbo
David, I really appriciate your help! I'll look into MvcContrib contribute guide...
artvolk
I've tested the updated code and it works. Will this be included in the next T4MVC version?
artvolk
I have committed the change (see http://mvccontrib.codeplex.com/SourceControl/list/changesets). So it'll be in the next build.
David Ebbo