代码之家  ›  专栏  ›  技术社区  ›  Daniel Congrove

如何在使用action name属性时获取操作名?

  •  2
  • Daniel Congrove  · 技术社区  · 7 年前

    随着 nameof

    <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 回复  |  直到 7 年前
        1
  •  3
  •   cwharris    7 年前

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

    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

        2
  •  1
  •   Shaun Luttin    7 年前

    …如果操作方法使用[action name]属性,是否有方法获得正确的操作名(并避免使用幻字符串)?也许是通过nameof()和扩展方法的结合?

    你可以使用反射。以下是内联版本:

    <p>@(
        (
            (ActionNameAttribute)(
                typeof(HomeController)
                .GetMethod(nameof(HomeController.Contact))
                .GetCustomAttributes(typeof(ActionNameAttribute), false)[0]
            )
        ).Name
    )</p>
    

    这里的操作与 razor function :

    @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 功能。


    你的问题是关于扩展方法的。据我所知,扩展方法不会提供太多改进,因为我们使用的是类型和方法,而扩展方法使用的是对象。

        3
  •  1
  •   Erik Philips Gabriel Costa    7 年前

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