C# 使用ActionName属性时如何获取操作名称?

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”)]属性。否。您不能。因为属性名称已经是一

通过在C#6中引入
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)))