Extension method to get a controller name without suffix - c#

I don't want to repeat the following
nameof(HomeController).Replace(nameof(Controller), string.Empty)
everytime I need to supply controller name without suffix "Controller".
Is there any elegant way to simplify? I attempted to create an extension method as follows but I don't know how to get the instance of the controller in question from a cshtml view.
using Microsoft.AspNetCore.Mvc;
namespace WebApplication1
{
public static class Utilities
{
public static string BareName(this Controller controller)
{
return nameof(controller.GetType).Replace(nameof(Controller), string.Empty);
}
}
}
I want to invoke the following, for example, in view page.
#Html.ActionLink("something", nameof(HomeController.someaction), nameof(HomeController).Replace(nameof(Controller), string.Empty))

You could create a separate extension method on ActionContext which resolves the controller name from the ActionDescriptor.
public static string GetControllerName(this ActionContext actionContext)
{
return (actionContext.ActionDescriptor as ControllerActionDescriptor)?.ControllerName;
}
You don't need to trim the "Controller" part from the string in this case. Call it in your view like this:
var controllerName = ViewContext.GetControllerName();

Assuming that you want the name of the controller which is not currently handling the request, you want something like this:
public static string BareName<T>() where T: Controller
{
return typeof(T).Name.Replace(nameof(Controller), string.Empty);
}
You can then use it like:
#Html.ActionLink("something", nameof(HomeController.someaction), Utilities.BareName<HomeController>())
I'm still not sure why Microsoft hasn't put this as part of the ASP.NET Core core package...

If you're in a view, just use ViewContext.RouteData.Values["controller"].
UPDATE (based on edited question)
So, yeah, it wasn't clear before what you were actually trying to achieve. Basically, you want to be able to specify the controller/action params of methods like Html.Action without using hard-coded strings. As far as extension methods go, your best bet is an IHtmlHelper extension like:
public static class IHtmlHelperExtensions
{
public static string GetControllerName<TController>(this IHtmlHelper helper)
where TController : Controller
{
return typeof(TController).GetName().Replace("Controller", "");
}
}
Which you can then use like:
Html.GetControllerName<HomeController>()
However, this still feels clunky to me. Honestly, if you're looking to avoid hard-coding things, I'd recommend naming your routes and using a static class to house the names:
public static class RouteNames
{
public const string Home = "Home";
}
Then:
public class HomeController : Controller
{
[HttpGet("", Name = RouteNames.Home)]
public IActionResult Index() => View();
}
And then finally:
#Html.RouteLink(RouteNames.Home, RouteNames.Home)
Or via tag helper:
<a asp-route="#RouteNames.Home">#RouteNames.Home</a>

If you want to get controller, action or area name inside a view or controller, here are some options:
you just need a reference to an ViewContext or HttpContext or IHtmlHelper object:
////////////////////////////////
// with an IHtmlHelper object //
////////////////////////////////
var request = htmlHelper.ViewContext.HttpContext.Request;
var controllerName = request.RouteValues["controller"].ToString();
var actionName = request.RouteValues["action"].ToString();
// if area exists
var areaName = request.RouteValues["area"].ToString();
//////////////////////////////////
// with an `ViewContext` object //
//////////////////////////////////
var controllerName = ViewContext.RouteData.Values["controller"].ToString();
var actionName = ViewContext.RouteData.Values["action"].ToString();
// if area exists
var areaName = ViewContext.RouteData.Values["area"].ToString();
So an extension method for IHtmlHelper could be as follows:
public static class IHtmlHelperExtensions
{
public static string GetControllerName(this IHtmlHelper htmlHelper) =>
htmlHelper.ViewContext.RouteData.Values["controller"].ToString();
}

[HttpGet]
public IActionResult Get([FromRoute]string controller)
{
//controller will be Controller name without suffix "Controller".
}

var controllerName = ControllerContext.ActionDescriptor.ControllerName.Replace(nameof(Controller), string.Empty);

Related

Viewengine using a static class on controller view method

I am working on themes at present and the majority of work is going well am just at the phase where I am figuring out what view to present to the user.
This would be on a controller method so far I have.
public static class ThemeViewExtensions
{
public static string GetViewPath(RazorView view , string viewName = "Default")
{
var theme = "Default";
var themeViewPath = $"/Themes/{theme}{view.ViewPath}/{viewName}.cshtml";
return viewPath;
}
}
But for some reason I cannot access it here yes I have my using statement in
public IActionResult Index()
{
return View(this.GetViewPath());
}
What I want to happen is to be able to use it as above and be able to get the ViewPath and name that is being called for example /Controller/Action/ViewName whatever cshtml is that possible on a view?

RedirectToAction with nameof(NameofController) fails to locate the action

According to the inlined documentation, ControllerBase.RedirectToAction takes both the action name and the controller name:
// Parameters:
// actionName:
// The name of the action.
//
// controllerName:
// The name of the controller.
public virtual RedirectToActionResult RedirectToAction(string actionName, string controllerName);
Now, let's assume I want to redirect to the following action:
[Route("Whatever")]
public class WhateverController : Controller
{
[HttpGet("Overview")]
public IActionResult Overview()
{
return View();
}
}
Naturally, I wanted to use the nameof operator:
[Route("Home")]
public class HomeController : Controller
{
[HttpGet("Something")]
public IActionResult Something()
{
return RedirectToAction(
nameof(WhateverController.Overview), // action name
nameof(WhateverController) // controller name
);
}
}
But that call fails with the error InvalidOperationException: No route matches the supplied values.
I know I could hardcode the controller name to "whatever" instead of using the nameof operator, but is there a way to get the proper name from the class name?
The problem is nameof(WhateverController) returns WhateverController, not (Whatever) that you and routing system expect.
You may use nameof(WhateverController).Replace("Controller", "") to get what you want.
Edit:
If all you want is not hard-coded controller/action names, Then it's better to use something like R4MVC.
nameof(WhateverController) will return "WhateverController". RedirectToAction is expecting to take your controller's name in the form of "Whatever".
Using nameof instead of hardcoding strings is definitely good (in a lot of circumstances) but it looks like that's what's throwing you off in this case.
I am not a fan of extension methods because it pollutes the API in this case, but if you want, it is not the worst idea.
public static class StringExtensions
{
/// <summary>
/// Removes the word "Controller" from the string.
/// </summary>
public static string RemoveController(this string value)
{
string result = value.Replace("Controller", "");
return result;
}
}
Usage
nameof(WhateverController).RemoveController();
A Better Approach
Instead of extension method, create a base controller and put the method in it.
public class ControllerBase : Controller
{
/// <summary>
/// Removes the word "Controller" from the string.
/// </summary>
protected string _(string value)
{
string result = value.Replace("Controller", "");
return result;
}
}
If I think a method name serves no purpose, I sometimes use _ but you can replace it with a name if you want such as RemoveController.
Usage
public class SomeController : ControllerBase
{
public ActionResult Index(string value)
{
return RedirectToAction(nameof(WhateverController.Overview), _(nameof(WhateverController)));
}
}
You can see from above usage how _ stays out of the way and improves readability. Again this is what I do so if you don't like it, you don't need to do this.

Get Name of Controller from ActionFilter

I'm writing an ActionFilter and would like to have a more type-safe way to use RedirectToRouteResult. While investigating this, I wondered if there was a way to get the name (as a string) of any of my controllers. So for example, I would like to get "Home" from my HomeController, or "Admin" from my Admin controller. Is this at all possible?
From a filter context you can get the controller name by using:
public class MyFilter : IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
//That will give you "HomeController"
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
//You can remove the "Controller" part, by replacing it with an empty string, like:
var justTheController = controllerName.Replace("Controller", string.Empty);
}
}
Have you tried this. You know you can substitute typeof by GetType on instance variable. if you have HomeController instance homeCtrl....you can do homeCtrl.GetType
var fullName= typeof(HomeController).Name;
var partialName = fullName.Remove(fullName.IndexOf("Controller"));
Actually there are ways to get the controller name :
filterContext.Controller will give you an object from where you
can deduce the controller name as
filterContext.Controller.GetType().Name
You always have the controller in the route values and can deduce as Request.RequestContext.RouteData.Values("controller").ToString()
Write extension method
public static class ControllerStringExtension
{
private const string ControllerString = "Controller";
public static string Short(this string value)
{
if (value.EndsWith(ControllerString))
{
//Remove 'Controller' from end of value name.
return value.Remove(value.Length - ControllerString.Length);
}
throw new ApplicationException("Should be used only for Controller names.");
}
}
than use: Redirect to 'home' controler and 'index' action.
public RedirectToRouteResult Something()
{
return RedirectToAction(nameof(HomeController.Index), nameof(HomeController).Short());
}

How to use Url.Action() in a class file?

How can I use Url.Action() in a class file of MVC project?
Like:
namespace _3harf
{
public class myFunction
{
public static void CheckUserAdminPanelPermissionToAccess()
{
if (ReferenceEquals(HttpContext.Current.Session["Loged"], "true") &&
myFunction.GetPermission.AdminPermissionToLoginAdminPanel(
Convert.ToInt32(HttpContext.Current.Session["UID"])))
{
HttpContext.Current.Response.Redirect(Url.Action("MainPage", "Index"));
}
}
}
}
You will need to manually create the UrlHelper class and pass the appropriate RequestContext. It could be done with something like:
var requestContext = HttpContext.Current.Request.RequestContext;
new UrlHelper(requestContext).Action("Index", "MainPage");
However, you are trying to achieve redirection based on authentication. I suggest you look at implementing a custom AuthorizeAttribute filter to achieve this kind of behavior to be more in line with the framework
Pass the RequestContext to your custom class from the controller. I would add a Constructor to your custom class to handle this.
using System.Web.Mvc;
public class MyCustomClass
{
private UrlHelper _urlHelper;
public MyCustomClass(UrlHelper urlHelper)
{
_urlHelper = urlHelper;
}
public string GetThatURL()
{
string url=_urlHelper.Action("Index", "Invoices");
//do something with url or return it
return url;
}
}
You need to import System.Web.Mvc namespace to this class to use the UrlHelper class.
Now in your controller, create an object of MyCustomClass and pass the controller context in the constructor,
UrlHelper uHelp = new UrlHelper(this.ControllerContext.RequestContext);
var myCustom= new MyCustomClass(uHelp );
//Now call the method to get the Paging markup.
string thatUrl= myCustom.GetThatURL();
#Simon Belanger's answer is perfectly working, but UrlHelper.Action() generates relative URLs and in my case i need the fully qualified absolute URL. So what i need to do is - i have to use one of the overload provided by UrlHelper.Action() method.
var requestContext = HttpContext.Current.Request.RequestContext;
string link = new UrlHelper(requestContext).Action("Index", "Home", null, HttpContext.Current.Request.Url.Scheme);
So let say if your application hosted on "https://myexamplesite.com" then above code will give you full url like this - "https://myexamplesite.com/Home/Index". Hope this answer will help those readers who will come across this link.
For those arriving late to this post, using .Net Core and .Net 5.0, You should try this;
private readonly IUrlHelperFactory _urlHelperFactory;
private readonly IActionContextAccessor _actionContextAccessor;
public EmailSenderService(IUrlHelperFactory urlHelperFactory,
IActionContextAccessor actionContextAccessor)
{
_urlHelperFactory = urlHelperFactory;
_actionContextAccessor = actionContextAccessor;
}
private string GenerateUrl(string action, string controller, object routeValues = null)
{
var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext);
return urlHelper.Action(action, controller, routeValues, _actionContextAccessor.ActionContext.HttpContext.Request.Scheme);
}
I tried to use #simion's answer and I was getting an invalid type in the constructor for UrlHelper. "cannot convert from System.Web.Routing.RequestContext to System.Net.Http.HttpRequestMessage"
So I used this
var urlHelper = new System.Web.Mvc.UrlHelper(HttpContext.Current.Request.RequestContext);
string url = urlHelper.Action("MainPage", "Index");
worked out for me.
You can also use this
return RedirectToAction("Index", "MainPage");
You simply need to pass Url property from your controller to your class file,
string CreateRoutingUrl(IUrlHelper url)
{
return url.Action("Action", "Controller");
}
and on your controller :
MyClass.CreateRoutingUrl(Url);

Application helper in ASP.NET MVC

I am searching for a way to make an application helper that allows you to define methods that you can call in all the controllers views. In Rails you get that for free but how can I accomplish this in ASP.NET MVC with c#?
The usual way is by writing extension methods to HtmlHelper - for example:
public static string Script(this HtmlHelper html, string path)
{
var filePath = VirtualPathUtility.ToAbsolute(path);
return "<script type=\"text/javascript\" src=\"" + filePath
+ "\"></script>";
}
Now in the view you can use Html.Script("foo"); etc (since the standard view has an HtmlHelper member called Html). You can also write methods in a base-view, but the extension method approach appears to be the most common.
I would suggest adding an extension method to the base controller class.
public static class ControllerExtensions
{
public static string Summin(this Controller c)
{
return string.Empty;
}
}
You can access the helper function in your controller:
public class MyController : Controller
{
public ActionResult Index()
{
this.Summin();
return View();
}
}

Categories