Jack B
Jack B

Reputation: 391

Populate the Defaults RouteValueDictionary with attribute routing

I'm upgrading an ASP.NET MVC 4 project to MVC 5 and want to use attribute routing instead of convention routing. So far, so good, but I have one issue with populating the Defaults RouteValueDictionary. How can this be accomplished with attribute routing?

I am using multiple routes for the same action, each passing a different enum value to determine which type the Action is. The value of the enum will not be visible in the route directly though! This is important, otherwise I could use the value of the enum parameter in the route template.

My simplified Controller Action:

public class MyController : Controller
{
    public ActionResult MyAction(MyType myTypeValue)
    {
        // ...
    }
}

public enum MyType
{
    FirstOption,
    SecondOption
}

My old convention routes:

routes.Add("First", new Route("a-route", new { controller = "MyController", action = "MyAction", myTypeValue = MyType.FirstOption }));
routes.Add("Second", new Route("a-total/different-route", new { controller = "MyController", action = "MyAction", myTypeValue = MyType.Second }));

With attribute routing i was expecting to use something like this:

Route["a-route", new { myTypeValue = MyType.FirstOption }]
Route["a-total/different-route", new { myTypeValue = MyType.SecondOption }]

But unfortunately, this does not exists. I've tried to make a custom RouteAttribute that accepts an object to populate the Defaults RouteValueDictionary:

public class MyRouteAttribute : RouteFactoryAttribute
{
    private RouteValueDictionary _defaults;

    public Route(string template, object defaults)
    :base(template)
    {
        _defaults = new RouteValueDictionary(defaults);
    }

    public override RouteValueDictionary Defaults
    {
        get { return _defaults; }
    }
}

But this is not working since the route attribute cannot handle anonymous types compile time.

Does anyone know a way to get this working one way or another?

"Just make two different actions" is not an option here.

Upvotes: 1

Views: 1059

Answers (1)

NightOwl888
NightOwl888

Reputation: 56849

First of all, it is unclear why you would want to change from convention-based routing to the (less flexible) attribute-based routing, especially considering some of the features you are interested in are not supported by the latter.

But if you are insistent on changing to attribute routing just because it "looks cool", then you have a couple of options.

Option 1: Make Separate Action Methods

If you use 2 different actions and return one action from the first, you generally won't have to rewrite logic. But this is the only native support in attribute routing for setting optional parameters. An example of how you can support optional parameters with Enum can be found here.

[Route("a-route")]
public ActionResult MyAction(MyType myTypeValue = MyType.FirstOption)
{
    return View("Index");
}

[Route("a-total/different-route")]
public ActionResult My2ndAction(MyType myTypeValue = MyType.SecondOption)
{
    return MyAction(myTypeValue);
}

Option 2: Hack the Attribute Routing Framework

Microsoft intentionally made the attribute routing framework non-extensible by using several internal/private types to load the RouteValueCollection with the attribute routes.

You could potentially hack the attribute routing framework to provide your own logic as I have done here. This requires using Reflection, but since it runs at the start of the application rather than per-request the overall performance impact will be minimal.

But depending on your requirements, you may need to copy more of the logic from the MVC attribute routing framework to populate your routes, which may not be worth the effort. In my simple case of supporting multiple cultures it was. In your case you will need to support your own attribute types with additional parameters, which will be more challenging.


But if you need more flexibility than this, I would suggest sticking with the convention-based routing.

  1. Attributes have limitations on which datatypes are supported as opposed to code-based solutions.
  2. Several features including populating default route values, using constraints, and building custom routes are either much more difficult or not supported when using attribute routing.

The bottom line is, attribute routing is not the holy grail of routing. It is another routing option added in MVC 5 which can be used under a limited subset of routing scenarios of which convention-based routing is capable of. It is not and should not be viewed as a routing "upgrade" just because it happens to not have been an option until MVC 5.

Upvotes: 1

Related Questions