С введением оператора nameof
в C # 6 вы можете программно получить имя действия, не используя волшебную строку:
<p>@Html.ActionLink("Contact", nameof(HomeController.Contact), "Home")</p>
Это прекрасно работает, если вы не измените название представления.
Однако есть ли способ получить правильное имя действия (и избежать магических строк), если метод действия использует атрибут [ActionName]
? Возможно, через комбинацию nameof()
и метода расширения?
[ActionName("Contact2")]
public ActionResult Contact()
{
// ...
}
В этом примере nameof(HomeController.Contact)
вернет строку «Контакт» и URL «http: // localhost : 2222 / Home / Contact ", тогда как правильный URL должен быть" http: // localhost: 2222 / Home / Contact2 "из-за атрибута [ActionName("Contact2")]
.
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
возвратов. Хотя это немного странно, поскольку предполагается, что он используется для методов контроллера, которые обычно возвращают значение.
Если вам не нравится, вы можете сделать довольно причудливую логику с Generics:
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)))
... есть ли способ получить правильное имя действия (и избежать магических строк), если метод действия использует атрибут [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
.
Ваш вопрос задан конкретно по поводу метода расширения. Насколько я могу судить, метод расширения не принес бы большого улучшения, потому что мы работаем с типами и методами, тогда как методы расширения работают с объектами.
Похожие вопросы
Новые вопросы
c#
C # (произносится как «резкий») - это высокоуровневый, статически типизированный язык программирования с несколькими парадигмами, разработанный Microsoft. Код C # обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, включая, среди прочего, .NET Framework, .NET Core и Xamarin. Используйте этот тег для вопросов о коде, написанном на C # или в формальной спецификации C #.