14

ASP.NET MVC seems to be encouraging me to use hard-coded strings to refer to controllers and actions.

For example, in a controller:

return RedirectToAction("Index", "Home");

or, in a view:

Html.RenderPartial("Index", "Home");

I don't want hard-coded strings all over my code. What can I do to avoid this?

kristian
  • 22,731
  • 8
  • 50
  • 78
  • 1
    There is no problem with this IMHO. At some point you have to tell the code to point to a specific class or function. – Timbo Jun 30 '11 at 09:39
  • 2
    @Badger the problem is what if you rename your actions/controllers. You then have to somehow find all the hard-coded strings to update them and you can't rely on the compiler to tell you that you've missed something. – Dolbz Jun 30 '11 at 09:45
  • 1
    ReSharper can make this non-problem – driushkin Jun 30 '11 at 09:59
  • @Dolbz that's why you write unite tests :) – frennky Jun 30 '11 at 11:27
  • 2
    @frennky I'd rather not write unit tests for something that the compiler can reasonably find for me with zero effort ;) – Dolbz Jun 30 '11 at 14:09
  • @kristian - if you like my answer, please mark it as the accepted answer – mccow002 Jul 01 '11 at 14:52
  • @frennky how do I write unite tests, they sound cooler and more powerful than integration tests? :) – Anonymous Type Nov 01 '11 at 00:46

5 Answers5

15

It sounds to me like you want to use strongly typed redirects. I made a static helper class called RedirectionHelper that has the following method:

public static string GetUrl<T>(Expression<Action<T>> action, RequestContext requestContext, RouteValueDictionary values = null) where T : Controller
{
    UrlHelper urlHelper = new UrlHelper(requestContext);
    RouteValueDictionary routeValues = ExpressionHelper.GetRouteValuesFromExpression(action);

    if (values != null)
        foreach (var value in values)
            routeValues.Add(value.Key, value.Value);

    return urlHelper.RouteUrl(routeValues);
}

The only caveat is that you will have to use the Microsoft.Web.Mvc futures library available out on Nuget.

Now, for your controller, create a base controller that all controllers inherit from that has this method:

protected RedirectResult RedirectToAction<T>(Expression<Action<T>> action, RouteValueDictionary values = null) where T : Controller
{
    return new RedirectResult(RedirectionHelper.GetUrl(action, Request.RequestContext, values));
}

Now, in your action, all you have to do is say:

return RedirectToAction<Controller>(x => x.Index());

Likewise, you can write a html extension method that takes in the same parameters and builds your anchor tag.

Like you said above that you wanted, when you change Controller or Action names, your project will break at compile time and show you where the breaks occur. However, this will only occur in the controllers, seeing as how the views don't compile.

Hope this helps!

mccow002
  • 6,754
  • 3
  • 26
  • 36
5

Look at T4MVC this generates classes so you can have strongly typed actions and controller names. As it's still just a mapping to a string, refactoring won't cause the names in your views to update if you change a controller name for example.

After regenerating you will get compilation errors due to the names disappearing from your generated classes though so it's still useful in refactoring and catches problems that you can miss using hard-coded strings.

Dolbz
  • 2,078
  • 1
  • 16
  • 25
4

try T4MVC: http://mvccontrib.codeplex.com/wikipage?title=T4MVC

3

Not sure if somebody already added an extension method to one of the ASP.NET MVC related projects but here's a piece of code that you can use to create your own extension method:

public RedirectToRouteResult RedirectToAction<TController>(Expression<Action<TController>> action, RouteValueDictionary routeValues) where TController : Controller
    {
        RouteValueDictionary rv = Microsoft.Web.Mvc.Internal.ExpressionHelper.GetRouteValuesFromExpression(action);

        return RedirectToAction((string)rv["Action"], (string)rv["Controller"], routeValues ?? new RouteValueDictionary());
    }

    public ActionResult Index()
    {
        return RedirectToAction<DashboardController>(x => x.Index(), null);
    }

There's no parameters merging logic, so you'll have to add it by your own.

UPDATE: @mccow002 added a similar solution a few seconds before me, so I think his solution should be accepted.

0

I know this is an old topic, but when I was looking for an answer for ASP.NET 5 this theme first appeared. There is no need to hardcode anymore, just use nameof

[HttpGet]
public IActionResult List()
{
   ...
   return View();
}

[HttpPost]
public IActionResult Add()
{
    ...
    return RedirectToAction(nameof(List));
}
Serhii
  • 21
  • 5
  • This won't work if someone overrides the action name via the ActionName attribute e.g. `[ActionName("NewActionName")]` – David Spence May 19 '16 at 13:19
  • @DavidSpence Yeah, also it won't work with controllers as is, you need to implement extension method that will cut off "Controller" suffix. However that's something. – Serhii May 23 '16 at 17:08