C# 使用ActionName属性时如何获取操作名称?
通过在C#6中引入C# 使用ActionName属性时如何获取操作名称?,c#,asp.net-core,controller,asp.net-core-mvc,C#,Asp.net Core,Controller,Asp.net Core Mvc,通过在C#6中引入nameof操作符,您可以通过编程方式获取操作名称,而无需使用魔术字符串: <p>@Html.ActionLink("Contact", nameof(HomeController.Contact), "Home")</p> 在本例中,nameof(HomeController.Contact)将返回字符串“Contact”和URL“”,而正确的URL应该是“”,因为[ActionName(“Contact2”)]属性。否。您不能。因为属性名称已经是一
nameof
操作符,您可以通过编程方式获取操作名称,而无需使用魔术字符串:
<p>@Html.ActionLink("Contact", nameof(HomeController.Contact), "Home")</p>
在本例中,
nameof(HomeController.Contact)
将返回字符串“Contact”和URL“”,而正确的URL应该是“”,因为[ActionName(“Contact2”)]
属性。否。您不能。因为属性名称已经是一个魔术字符串,并且出于所有目的,控制器方法的名称本身就是一个魔术字符串(在您为操作使用隐式名称的情况下)。神奇的弦并不坏,它们只是经常被误用。在这种情况下,您最好只使用一个常量
internal const string ContactActionName2 = nameof(ContactActionName2);
及
及
应该足以满足您的用例
然而,因为每个人都在为此大吵大闹,所以我决定去找一个不依赖字符串的解决方案(除了一个你无法避免依赖的方法-动作名)。我不喜欢这个解决方案,因为1)它的杀伤力太大,2)它仍然只是访问一个字符串值,这可以更简单地使用一个常量来完成,3)你实际上必须把整个方法调用写成一个表达式,4)每次使用它时它都会分配一个表达式
public static class ActionNameExtensions<TController>
{
public static string FindActionName<T>(Expression<Func<TController, T>> expression)
{
MethodCallExpression outermostExpression = expression.Body as MethodCallExpression;
if (outermostExpression == null)
{
throw new ArgumentException("Not a " + nameof(MethodCallExpression));
}
return outermostExpression.Method.GetCustomAttribute<ActionNameAttribute>().Name;
}
}
公共静态类ActionNameExtensions
{
公共静态字符串FindActionName(表达式)
{
MethodCallExpression OutermosteExpression=表达式。主体为MethodCallExpression;
if(最外层表达式==null)
{
抛出新的ArgumentException(“不是”+nameof(MethodCallExpression));
}
返回outermostExpression.Method.GetCustomAttribute().Name;
}
}
用法示例:
public class HomeController : Controller
{
[ActionName("HelloWorld")]
public string MyCoolAction(string arg1, string arg2, int arg4)
{
return ActionNameExtensions<HomeController>.FindActionName(
controller => controller.MyCoolAction("a", "b", 3)
);
}
}
公共类HomeController:控制器
{
[ActionName(“HelloWorld”)]
公共字符串mycolaction(字符串arg1、字符串arg2、整数arg4)
{
返回ActionNameExtensions.FindActionName(
controller=>controller.mycolAction(“a”、“b”和“3”)
);
}
}
可以编写重载来接受方法,而不返回void
。虽然这有点奇怪,因为这应该用于控制器方法,通常返回一个值
…如果操作方法使用[ActionName]属性,是否有方法获得正确的操作名称(并避免使用魔术字符串)?也许是通过nameof()和扩展方法的组合
[ActionName("Contact2")]
public ActionResult Contact()
{
// ...
}
你可以使用反射。以下是一个内联版本:
<p>@(
(
(ActionNameAttribute)(
typeof(HomeController)
.GetMethod(nameof(HomeController.Contact))
.GetCustomAttributes(typeof(ActionNameAttribute), false)[0]
)
).Name
)</p>
@(
(
(ActionNameAttribute)(
类型(家庭控制器)
.GetMethod(名称(HomeController.Contact))
.GetCustomAttributes(typeof(ActionNameAttribute),false)[0]
)
).姓名
)
以下是与a相同的操作:
@函数{
公共字符串GetActionName(类型控制器,字符串方法名){
var method=controller.GetMethod(methodName);
var attributeType=typeof(ActionNameAttribute);
var attribute=method.GetCustomAttributes(attributeType,false)[0];
返回(作为ActionNameAttribute的属性);
}
}
@GetActionName(typeof(HomeController),nameof(HomeController.Contact))
以下是与通用剃须刀功能相同的操作:
@functions {
public string GetActionName<T>(string methodName) {
var controllerType = typeof(T);
var method = controllerType.GetMethod(methodName);
var attributeType = typeof(ActionNameAttribute);
var attribute = method.GetCustomAttributes(attributeType, false)[0];
return (attribute as ActionNameAttribute).Name;
}
}
<p>@(GetActionName<HomeController>(nameof(HomeController.Contact)))</p>
@函数{
公共字符串GetActionName(字符串方法名){
var控制器类型=类型(T);
var method=controllerType.GetMethod(methodName);
var attributeType=typeof(ActionNameAttribute);
var attribute=method.GetCustomAttributes(attributeType,false)[0];
返回(作为ActionNameAttribute的属性);
}
}
@(GetActionName(nameof(HomeController.Contact)))
剩下的就是在GetActionName
函数中添加防御性编程(例如null
检查)
您的问题特别询问了扩展方法。据我所知,扩展方法不会提供太多改进,因为我们使用的是类型和方法,而扩展方法使用的是对象。如果您不喜欢,可以使用泛型执行一些非常奇特的逻辑:
public static class HtmlHelperExtensions
{
public static IHtmlContent ActionLink<TController>(
this IHtmlHelper htmlHelper,
string linkText,
string actionName)
where TController : ControllerBase
{
var suffix = nameof(Controller);
var controllerName = typeof(TController).Name.Replace(suffix, "");
var method = typeof(TController).GetMethod(actionName);
var attributeType = typeof(ActionNameAttribute);
var attribute = method.GetCustomAttributes(attributeType, false);
actionName = attribute.Length == 0
? actionName
: (attribute[0] as ActionNameAttribute).Name;
return htmlHelper.ActionLink(linkText, actionName);
}
}
公共静态类HtmlHelperExtensions
{
公共静态IHTML内容操作链接(
这个IHtmlHelper htmlHelper,
字符串链接文本,
字符串(actionName)
其中TController:ControllerBase
{
var后缀=控制器名称;
var controllerName=typeof(TController).Name.Replace(后缀“”);
var method=typeof(TController).GetMethod(actionName);
var attributeType=typeof(ActionNameAttribute);
var attribute=method.GetCustomAttributes(attributeType,false);
actionName=属性。长度==0
?actionName
:(属性[0]作为ActionNameAttribute)。名称;
返回htmlHelper.ActionLink(linkText,actionName);
}
}
如果您测试过这个,它可能会抱怨(很肯定会)签名已经存在,因此您可能必须重命名该方法。在任何情况下,您都可以像这样使用它:
@(Html.ActionLink<HomeController>("Link Text", nameof(HomeController.Index)))
@(Html.ActionLink(“链接文本”,nameof(HomeController.Index)))
您可能需要调整您的问题。无论如何都不能使用nameof()
来执行此操作。你可能只想问“如何做”,而不是“如何用nameof()做”@maccettura很好的建议,谢谢。我已经更新了这个问题。如果你明白了,那么你就会明白为什么它在这里不起作用。我可以问一下你的用例是什么吗?ControllerContext.ActionDescriptor.ActionName
这可能会有帮助吗?他的观点是使用属性而不需要魔法字符串。将一个魔术字符串移动到一个常数,仍然会使其成为魔术字符串,如果控制器发生更改,则代码在运行时中断。@ErikPhilips您不认为魔术字符串已经存在了吗?[ActionName(“Contact2”)]
?基于ASP.NET MVC属性的路由是基于魔术字符串的。
@functions {
public string GetActionName<T>(string methodName) {
var controllerType = typeof(T);
var method = controllerType.GetMethod(methodName);
var attributeType = typeof(ActionNameAttribute);
var attribute = method.GetCustomAttributes(attributeType, false)[0];
return (attribute as ActionNameAttribute).Name;
}
}
<p>@(GetActionName<HomeController>(nameof(HomeController.Contact)))</p>
public static class HtmlHelperExtensions
{
public static IHtmlContent ActionLink<TController>(
this IHtmlHelper htmlHelper,
string linkText,
string actionName)
where TController : ControllerBase
{
var suffix = nameof(Controller);
var controllerName = typeof(TController).Name.Replace(suffix, "");
var method = typeof(TController).GetMethod(actionName);
var attributeType = typeof(ActionNameAttribute);
var attribute = method.GetCustomAttributes(attributeType, false);
actionName = attribute.Length == 0
? actionName
: (attribute[0] as ActionNameAttribute).Name;
return htmlHelper.ActionLink(linkText, actionName);
}
}
@(Html.ActionLink<HomeController>("Link Text", nameof(HomeController.Index)))