为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
qq_遁去的一_1 2021-12-05 17:10:25
随着nameofC# 6 中运算符的引入,您可以通过编程方式获取操作名称,而无需魔法字符串:<p>@Html.ActionLink("Contact", nameof(HomeController.Contact), "Home")</p>如果您不更改视图的名称,这将非常有效。但是,如果操作方法正在使用该[ActionName]属性,有没有办法获得正确的操作名称(并避免使用魔法字符串)?也许通过组合nameof()和扩展方法?[ActionName("Contact2")]public ActionResult Contact(){        // ...}在本例中,nameof(HomeController.Contact)将返回字符串“Contact”和 URL“ http://localhost:2222/Home/Contact ”,而正确的 URL 应该是“ http://localhost:2222/Home/Contact2 ”,因为[ActionName("Contact2")]属性。
查看完整描述

3 回答

?
慕莱坞森

TA贡献1810条经验 获得超4个赞

不,你不能。因为属性名称已经是一个魔法字符串,并且对于所有意图和目的,控制器方法的名称本身就是一个魔法字符串(在您使用隐式操作名称的情况下)。魔术弦并不坏,只是经常被误用。在这种情况下,您最好只使用常量。


internal const string ContactActionName2 = nameof(ContactActionName2);


[ActionName(ContactActionName2)]


HomeController.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;

    }

}

用法示例:


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)

        );

    }

}

可以编写一个重载来接受没有void返回的方法。虽然这有点奇怪,因为它假设用于控制器方法,通常返回一个值。


查看完整回答
反对 回复 2021-12-05
?
ibeautiful

TA贡献1993条经验 获得超5个赞

...如果操作方法使用 [ActionName] 属性,有没有办法获得正确的操作名称(并避免使用魔法字符串)?也许通过 nameof() 和扩展方法的组合?


您可以使用反射。这是一个内联版本:


<p>@(

    (

        (ActionNameAttribute)(

            typeof(HomeController)

            .GetMethod(nameof(HomeController.Contact))

            .GetCustomAttributes(typeof(ActionNameAttribute), false)[0]

        )

    ).Name

)</p>

这是与剃刀功能相同的操作:


@functions {

    public string GetActionName(Type controller, string methodName) {

        var method = controller.GetMethod(methodName);

        var attributeType = typeof(ActionNameAttribute);

        var attribute = method.GetCustomAttributes(attributeType, false)[0];

        return (attribute as ActionNameAttribute).Name;

    }

}


<p>@GetActionName(typeof(HomeController), nameof(HomeController.Contact))</p>

这是与通用剃刀函数相同的操作:


@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>

剩下的就是向函数添加防御性编程(例如null检查)GetActionName。


您的问题专门询问了扩展方法。据我所知,扩展方法不会提供太大的改进,因为我们正在使用类型和方法,而扩展方法适用于对象。


查看完整回答
反对 回复 2021-12-05
?
红糖糍粑

TA贡献1815条经验 获得超6个赞

如果你不喜欢你可以用泛型做一些非常奇特的逻辑:


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)))


查看完整回答
反对 回复 2021-12-05
  • 3 回答
  • 0 关注
  • 319 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信