37

The newly introduced nameof operator is useful in making my code my "typed".

Instead of

return RedirectToAction("Edit");

we can write

return RedirectToAction(nameof(Edit));

But for to get a controller's name is not that straightforward, because we have a Controller suffix. Just want to know if I want to have a

return RedirectToAction(nameof(Index), controllernameof(Home));

to take the place of

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

how can we implement the controllernameof operator?

undefined
  • 2,939
  • 4
  • 23
  • 35
Blaise
  • 21,314
  • 28
  • 108
  • 169

8 Answers8

9

Maybe an extension method like the following would suit your needs:

public static class ControllerExtensions
{
  public static string ControllerName(this Type controllerType)
  {
     Type baseType = typeof(Controller);
     if (baseType.IsAssignableFrom(controllerType))
     {
        int lastControllerIndex = controllerType.Name.LastIndexOf("Controller");
        if (lastControllerIndex > 0)
        {
           return controllerType.Name.Substring(0, lastControllerIndex);
        }
     }

     return controllerType.Name;
  }
}

Which you could invoke like so:

return RedirectToAction(nameof(Index), typeof(HomeController).ControllerName());
stephen.vakil
  • 3,492
  • 1
  • 18
  • 23
  • Well, this method will be called each time route is requested. `nameof` operator is somewhat better concerning performance as it works at compile-time. – Pavel Voronin Apr 14 '16 at 14:44
  • Very good point, @voroninp. Perhaps an extension method that just takes the string result of `nameof(ControllerName)` would be more efficient - though even use of extensions at all might not be fully performant. – stephen.vakil Apr 14 '16 at 15:26
7

No, there is no such possibility. You might be intertested to use T4MVC instead.

T4MVC- a T4 template for ASP.NET MVC apps that creates strongly typed helpers that eliminate the use of literal strings in many places.

e.g. instead of

@Html.ActionLink("Dinner Details", "Details", "Dinners", new { id = Model.DinnerID }, null)

T4MVC lets you write

@Html.ActionLink("Dinner Details", MVC.Dinners.Details(Model.DinnerID))
Dmitry Pavlov
  • 30,789
  • 8
  • 97
  • 121
TomTom
  • 61,059
  • 10
  • 88
  • 148
7

Totally understand your desire to not use magic strings! Between the comments above and this article. I've started using the following in a base controller which my other controllers inherit from:

public RedirectToRouteResult RedirectToAction<TController>(Expression<Func<TController, string>> expression, object routeValues)
{
    if (!(expression.Body is ConstantExpression constant))
    {
        throw new ArgumentException("Expression must be a constant expression.");
    }

    string controllerName = typeof(TController).Name;

    controllerName = controllerName.Substring(0, controllerName.LastIndexOf("Controller"));

    return RedirectToAction(constant.Value.ToString(), controllerName, routeValues);
}

public RedirectToRouteResult RedirectToAction<TController>(Expression<Func<TController, string>> expression)
{
    return RedirectToAction(expression, null);
}

I then use :

 return RedirectToAction<HomeController>(a => nameof(a.Index));

and

 return RedirectToAction<HomeController>(a => nameof(a.Index), new { text= "searchtext" });
Jon Ryan
  • 1,497
  • 1
  • 13
  • 29
5

The solution that solves the case of the XControllerController would look more like:

String nameStr = nameof(FlightControllerController);
nameStr = nameStr.Substring(0, nameStr.LastIndexOf("Controller"));
3

Based on Jon Ryan's answer:

public class BaseController : Controller
{
    public RedirectToRouteResult RedirectToAction<T>(string ActionName, object routeValues) where T : BaseController
    {
        string controllerName = typeof(T).Name;
        controllerName = controllerName.Substring(0, controllerName.LastIndexOf("Controller"));
        return RedirectToAction(ActionName, controllerName, routeValues);
    }
    
    public RedirectToRouteResult RedirectToAction<T>(string ActionName) where T : BaseController
    {
        return RedirectToAction<T>(ActionName, null);
    }
}

Used like so:

return RedirectToAction<AccountController>(nameof(AccountController.Login));
return RedirectToAction<AccountController>(nameof(AccountController.Login), new { text= "searchtext" });

Extension for razor page:

public static string ControllerName(this string controllerName)
{
    return controllerName.Substring(0, controllerName.LastIndexOf("Controller"));
}

Which you can use like this:

nameof(AccountController).ControllerName()
CarenRose
  • 1,266
  • 1
  • 12
  • 24
3

In C# 8 and later you can use the range operator to remove the last 10 chars:

nameof(AccountController)[..^10]

The 10 can be replaced with a constant, if you’re not into the whole brevity thing.

kevm
  • 41
  • 2
2

To achieve this kind of thing, I took @stephen.vakil's answer, merged with @huysentruitw's answer and wrote this:

namespace Helpers
{
    public static class ControllerHelper
    {
        public static string Nameof<TController>() where TController : Controller
        {
            var name = typeof(TController).Name;

            var indexOfControllerText = name.LastIndexOf("Controller");
            if (indexOfControllerText > 0)
            {
                return name.Substring(0, indexOfControllerText);
            }

            return name;
        }
    }
}

To use this, first you have to add "using static"

using static Helpers.ControllerHelper;

And then use it in the code like this

var controllerName = Nameof<TestController>();
bafsar
  • 1,080
  • 2
  • 16
  • 16
  • How to get Action Name in a controller? Like var actionName = Nameof(TestController); – DoomerDGR8 Aug 05 '22 at 07:58
  • 1
    @HassanGulzar, no. It's only for controller. Maybe you can use this and create an helper class: https://stackoverflow.com/a/18248594/2374053 But, if you are just looking name of an action without instance, then you can use this usage: nameof(TestController.Index) – bafsar Aug 05 '22 at 15:27
0

Based on @Trương Quốc Khánh answer, I've modified the extension method by adding some checks:

public static class StringExtensions
{
    public static string ControllerName(this string controllerName)
    {
        if (!controllerName.EndsWith("Controller") || controllerName == "Controller")
            throw new ArgumentException($"{controllerName} is not a valid name for a Controller class");

        return controllerName.Substring(0, controllerName.LastIndexOf("Controller"));
    }
}

Usage:

nameof(Controller).ControllerName()
fsbflavio
  • 664
  • 10
  • 22