通过在C#6中引入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”,而由于{{1},正确的URL应该为“ http://localhost:2222/Home/Contact2” }属性。
答案 0 :(得分:3)
不。你不能因为属性名称已经是一个魔术字符串,并且就所有意图和目的而言,控制器方法的名称本身就是一个魔术字符串(在您为操作使用隐式名称的情况下)。魔术弦还不错,只是经常被滥用。在这种情况下,最好只使用一个常量。
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
的方法。尽管这很奇怪,但是因为这被假定用于通常返回值的控制器方法。
答案 1 :(得分:1)
...如果操作方法使用[ActionName]属性,是否可以获取正确的操作名称(避免使用魔术字符串)?也许通过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
检查)。
您的问题专门询问了扩展方法。据我所知,扩展方法不会提供太多改进,因为我们正在处理类型和方法,而扩展方法则用于对象。
答案 2 :(得分:1)
如果您不喜欢,可以使用泛型做一些漂亮的逻辑:
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)))