Можно ли создать общий метод @helper с помощью Razor?

Я пытаюсь написать помощника в Razor, который выглядит следующим образом:

@helper DoSomething<T, U>(Expression<Func<T, U>> expr) where T : class

К сожалению, парсер считает, что <T - это начало элемента HTML, и я получаю синтаксическую ошибку. Возможно ли создать помощника с Razor, который является общим методом? Если да, то какой синтаксис?

Ответ 1

Нет, это невозможно. Вместо этого вы могли бы написать обычный HTML-помощник.

public static MvcHtmlString DoSomething<T, U>(
    this HtmlHelper htmlHelper, 
    Expression<Func<T, U>> expr
) where T : class
{
    ...
}

а затем:

@(Html.DoSomething<SomeModel, string>(x => x.SomeProperty))

или если вы нацеливаете модель на первый общий аргумент:

public static MvcHtmlString DoSomething<TModel, TProperty>(
    this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, TProperty>> expr
) where T : class
{
    ...
}

который позволит вам вызвать его так (предполагая, что ваше представление строго типизировано, но это безопасное предположение, потому что все представления должны быть строго типизированы: -)):

@Html.DoSomething(x => x.SomeProperty)

Ответ 2

Этот можно реализовать внутри вспомогательного файла с синтаксисом @functions, но если вы хотите, чтобы читаемость в стиле бритвы вы ссылались на вас, вам также нужно будет вызвать обычного помощника, чтобы выполнить HTML подходит и закончен.

Обратите внимание, что функции в файле Helper являются статическими, поэтому вам все равно нужно передать экземпляр HtmlHelper со страницы, если вы собираетесь использовать его методы.

например. Просмотров \MyView.cshtml:

@MyHelper.DoSomething(Html, m=>m.Property1)
@MyHelper.DoSomething(Html, m=>m.Property2)
@MyHelper.DoSomething(Html, m=>m.Property3)

App_Code\MyHelper.cshtml:

@using System.Web.Mvc;
@using System.Web.Mvc.Html;
@using System.Linq.Expressions;
@functions
{
    public static HelperResult DoSomething<TModel, TItem>(HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
    {
        return TheThingToDo(html.LabelFor(expr), html.EditorFor(expr), html.ValidationMessageFor(expr));
    }
}
@helper TheThingToDo(MvcHtmlString label, MvcHtmlString textbox, MvcHtmlString validationMessage)
{
    <p>
        @label
        <br />
        @textbox
        @validationMessage
    </p>
}
...

Ответ 3

Во всех случаях TModel будет одинаковым (модель объявлена ​​для представления), и в моем случае TValue будет одинаковой, поэтому я смог объявить тип аргумента Expression:

@helper FormRow(Expression<Func<MyViewModel, MyClass>> expression) {
  <div class="form-group">
    @(Html.LabelFor(expression, new { @class = "control-label col-sm-6 text-right" }))
    <div class="col-sm-6">
      @Html.EnumDropDownListFor(expression, new { @class = "form-control" })
    </div>
    @Html.ValidationMessageFor(expression)
  </div>
}

Если ваши поля модели все string, вы можете заменить MyClass на string.

Возможно, было бы неплохо определить два или три помощника с указанным TValue, но если у вас есть еще что-то, что создаст какой-то уродливый код, я действительно не нашел хорошего решения. Я попробовал обернуть @helper из функции, помещенной внутри блока @functions {}, но у меня никогда не было этого, чтобы работать по этому пути.

Ответ 4

если ваша основная проблема заключается в том, чтобы получить значение атрибута name для привязки с использованием лямбда-выражения, похоже на @Html.TextBoxFor(x => x.MyPoperty), и если ваш компонент имеет очень сложные html-теги и должен быть реализован на помощнике бритвы, тогда почему бы не просто создать метод расширения HtmlHelper<TModel> для разрешения имени привязки:

namespace System.Web.Mvc
{
    public static class MyHelpers
    {
        public static string GetNameForBinding<TModel, TProperty>
           (this HtmlHelper<TModel> model, 
            Expression<Func<TModel, TProperty>> property)
        {
            return ExpressionHelper.GetExpressionText(property);
        }
    }
}

ваш помощник бритвы должен быть как обычно:

@helper MyComponent(string name)
{
    <input name="@name" type="text"/>
}

то здесь вы можете использовать его

@TheHelper.MyComponent(Html.GetNameForBinding(x => x.MyProperty))